Search in sources :

Example 1 with ParsedNode

use of com.ichi2.libanki.template.ParsedNode in project Anki-Android by ankidroid.

the class Model method parsedNodes.

/**
 * @return A list of parsed nodes for each template's question. null in case of exception
 */
public List<ParsedNode> parsedNodes() {
    JSONArray tmpls = getJSONArray("tmpls");
    List<ParsedNode> nodes = new ArrayList<>(tmpls.length());
    for (JSONObject tmpl : tmpls.jsonObjectIterable()) {
        String format_question = tmpl.getString("qfmt");
        ParsedNode node = null;
        try {
            node = ParsedNode.parse_inner(format_question);
        } catch (TemplateError er) {
            Timber.w(er);
        }
        nodes.add(node);
    }
    return nodes;
}
Also used : ParsedNode(com.ichi2.libanki.template.ParsedNode) JSONObject(com.ichi2.utils.JSONObject) JSONArray(com.ichi2.utils.JSONArray) ArrayList(java.util.ArrayList) TemplateError(com.ichi2.libanki.template.TemplateError)

Example 2 with ParsedNode

use of com.ichi2.libanki.template.ParsedNode in project Anki-Android by ankidroid.

the class Models method _availStandardOrds.

/**
 * Given a joined field string and a standard note type, return available template ordinals
 */
public static ArrayList<Integer> _availStandardOrds(Model m, String[] sfld, List<ParsedNode> nodes, boolean allowEmpty) {
    Set<String> nonEmptyFields = m.nonEmptyFields(sfld);
    ArrayList<Integer> avail = new ArrayList<>(nodes.size());
    for (int i = 0; i < nodes.size(); i++) {
        ParsedNode node = nodes.get(i);
        if (node != null && !node.template_is_empty(nonEmptyFields)) {
            avail.add(i);
        }
    }
    if (allowEmpty && avail.isEmpty()) {
        /* According to anki documentation:
            When adding/importing, if a normal note doesn’t generate any cards, Anki will now add a blank card 1 instead of refusing to add the note. */
        avail.add(0);
    }
    return avail;
}
Also used : ParsedNode(com.ichi2.libanki.template.ParsedNode) ArrayList(java.util.ArrayList)

Example 3 with ParsedNode

use of com.ichi2.libanki.template.ParsedNode in project Anki-Android by ankidroid.

the class Collection method genCards.

/**
 * @param snids All ids of nodes of a note type, separated by comma
 * @param model
 * @param task Task to check for cancellation and update number of card processed
 * @return Cards that should be removed because they should not be generated
 * @param <T>
 */
public <T extends ProgressSender<Integer> & CancelListener> ArrayList<Long> genCards(String snids, @NonNull Model model, @Nullable T task) {
    int nbCount = noteCount();
    // For each note, indicates ords of cards it contains
    HashMap<Long, HashMap<Integer, Long>> have = HashUtil.HashMapInit(nbCount);
    // For each note, the deck containing all of its cards, or 0 if siblings in multiple deck
    HashMap<Long, Long> dids = HashUtil.HashMapInit(nbCount);
    // For each note, an arbitrary due of one of its due card processed, if any exists
    HashMap<Long, Long> dues = HashUtil.HashMapInit(nbCount);
    List<ParsedNode> nodes = null;
    if (model.getInt("type") != Consts.MODEL_CLOZE) {
        nodes = model.parsedNodes();
    }
    try (Cursor cur = mDb.query("select id, nid, ord, (CASE WHEN odid != 0 THEN odid ELSE did END), (CASE WHEN odid != 0 THEN odue ELSE due END), type from cards where nid in " + snids)) {
        while (cur.moveToNext()) {
            if (isCancelled(task)) {
                Timber.v("Empty card cancelled");
                return null;
            }
            @NonNull Long id = cur.getLong(0);
            @NonNull Long nid = cur.getLong(1);
            @NonNull Integer ord = cur.getInt(2);
            @NonNull Long did = cur.getLong(3);
            @NonNull Long due = cur.getLong(4);
            @Consts.CARD_TYPE int type = cur.getInt(5);
            // existing cards
            if (!have.containsKey(nid)) {
                have.put(nid, new HashMap<>());
            }
            have.get(nid).put(ord, id);
            // and their dids
            if (dids.containsKey(nid)) {
                if (dids.get(nid) != 0 && !Utils.equals(dids.get(nid), did)) {
                    // cards are in two or more different decks; revert to model default
                    dids.put(nid, 0L);
                }
            } else {
                // first card or multiple cards in same deck
                dids.put(nid, did);
            }
            if (!dues.containsKey(nid) && type == Consts.CARD_TYPE_NEW) {
                dues.put(nid, due);
            }
        }
    }
    // build cards for each note
    ArrayList<Object[]> data = new ArrayList<>();
    long ts = getTime().maxID(mDb);
    long now = getTime().intTime();
    ArrayList<Long> rem = new ArrayList<>(mDb.queryScalar("SELECT count() FROM notes where id in " + snids));
    int usn = usn();
    try (Cursor cur = mDb.query("SELECT id, flds FROM notes WHERE id IN " + snids)) {
        while (cur.moveToNext()) {
            if (isCancelled(task)) {
                Timber.v("Empty card cancelled");
                return null;
            }
            @NonNull Long nid = cur.getLong(0);
            String flds = cur.getString(1);
            ArrayList<Integer> avail = Models.availOrds(model, Utils.splitFields(flds), nodes, Models.AllowEmpty.TRUE);
            if (task != null) {
                task.doProgress(avail.size());
            }
            Long did = dids.get(nid);
            // use sibling due if there is one, else use a new id
            @NonNull Long due;
            if (dues.containsKey(nid)) {
                due = dues.get(nid);
            } else {
                due = (long) nextID("pos");
            }
            if (did == null || did == 0L) {
                did = model.getDid();
            }
            // add any missing cards
            ArrayList<JSONObject> tmpls = _tmplsFromOrds(model, avail);
            for (JSONObject t : tmpls) {
                int tord = t.getInt("ord");
                boolean doHave = have.containsKey(nid) && have.get(nid).containsKey(tord);
                if (!doHave) {
                    // check deck is not a cram deck
                    long ndid;
                    try {
                        ndid = t.optLong("did", 0);
                        if (ndid != 0) {
                            did = ndid;
                        }
                    } catch (JSONException e) {
                        Timber.w(e);
                    // do nothing
                    }
                    if (getDecks().isDyn(did)) {
                        did = 1L;
                    }
                    // if the deck doesn't exist, use default instead
                    did = mDecks.get(did).getLong("id");
                    // give it a new id instead
                    data.add(new Object[] { ts, nid, did, tord, now, usn, due });
                    ts += 1;
                }
            }
            // note any cards that need removing
            if (have.containsKey(nid)) {
                for (Map.Entry<Integer, Long> n : have.get(nid).entrySet()) {
                    if (!avail.contains(n.getKey())) {
                        rem.add(n.getValue());
                    }
                }
            }
        }
    }
    // bulk update
    mDb.executeMany("INSERT INTO cards VALUES (?,?,?,?,?,?,0,0,?,0,0,0,0,0,0,0,0,\"\")", data);
    return rem;
}
Also used : ParsedNode(com.ichi2.libanki.template.ParsedNode) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) JSONException(com.ichi2.utils.JSONException) Cursor(android.database.Cursor) SuppressLint(android.annotation.SuppressLint) JSONObject(com.ichi2.utils.JSONObject) NonNull(androidx.annotation.NonNull) Map(java.util.Map) HashMap(java.util.HashMap)

Aggregations

ParsedNode (com.ichi2.libanki.template.ParsedNode)3 ArrayList (java.util.ArrayList)3 JSONObject (com.ichi2.utils.JSONObject)2 SuppressLint (android.annotation.SuppressLint)1 Cursor (android.database.Cursor)1 NonNull (androidx.annotation.NonNull)1 TemplateError (com.ichi2.libanki.template.TemplateError)1 JSONArray (com.ichi2.utils.JSONArray)1 JSONException (com.ichi2.utils.JSONException)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1