Search in sources :

Example 51 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class Models method _reqForTemplate.

// 'String f' is unused upstream as well
@SuppressWarnings("PMD.UnusedLocalVariable")
private Object[] _reqForTemplate(Model m, ArrayList<String> flds, JSONObject t) {
    int nbFields = flds.size();
    String[] a = new String[nbFields];
    String[] b = new String[nbFields];
    Arrays.fill(a, "ankiflag");
    Arrays.fill(b, "");
    int ord = t.getInt("ord");
    String full = mCol._renderQA(1L, m, 1L, ord, "", a, 0).get("q");
    String empty = mCol._renderQA(1L, m, 1L, ord, "", b, 0).get("q");
    // if full and empty are the same, the template is invalid and there is no way to satisfy it
    if (full.equals(empty)) {
        return new Object[] { "none", new JSONArray(), new JSONArray() };
    }
    String type = "all";
    JSONArray req = new JSONArray();
    for (int i = 0; i < flds.size(); i++) {
        a[i] = "";
        // if no field content appeared, field is required
        if (!mCol._renderQA(1L, m, 1L, ord, "", a, 0).get("q").contains("ankiflag")) {
            req.put(i);
        }
        a[i] = "ankiflag";
    }
    if (req.length() > 0) {
        return new Object[] { type, req };
    }
    // if there are no required fields, switch to any mode
    type = "any";
    req = new JSONArray();
    for (int i = 0; i < flds.size(); i++) {
        b[i] = "1";
        // if not the same as empty, this field can make the card non-blank
        if (!mCol._renderQA(1L, m, 1L, ord, "", b, 0).get("q").equals(empty)) {
            req.put(i);
        }
        b[i] = "";
    }
    return new Object[] { type, req };
}
Also used : JSONArray(com.ichi2.utils.JSONArray) JSONObject(com.ichi2.utils.JSONObject)

Example 52 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class Collection method _renderQA.

public HashMap<String, String> _renderQA(long cid, Model model, long did, int ord, String tags, String[] flist, int flags, boolean browser, String qfmt, String afmt) {
    // data is [cid, nid, mid, did, ord, tags, flds, cardFlags]
    // unpack fields and create dict
    Map<String, String> fields = new HashMap<>();
    Map<String, Pair<Integer, JSONObject>> fmap = Models.fieldMap(model);
    for (String name : fmap.keySet()) {
        fields.put(name, flist[fmap.get(name).first]);
    }
    int cardNum = ord + 1;
    fields.put("Tags", tags.trim());
    fields.put("Type", model.getString("name"));
    fields.put("Deck", mDecks.name(did));
    String baseName = Decks.basename(fields.get("Deck"));
    fields.put("Subdeck", baseName);
    fields.put("CardFlag", _flagNameFromCardFlags(flags));
    JSONObject template;
    if (model.getInt("type") == Consts.MODEL_STD) {
        template = model.getJSONArray("tmpls").getJSONObject(ord);
    } else {
        template = model.getJSONArray("tmpls").getJSONObject(0);
    }
    fields.put("Card", template.getString("name"));
    fields.put(String.format(Locale.US, "c%d", cardNum), "1");
    // render q & a
    HashMap<String, String> d = new HashMap<>();
    d.put("id", Long.toString(cid));
    qfmt = TextUtils.isEmpty(qfmt) ? template.getString("qfmt") : qfmt;
    afmt = TextUtils.isEmpty(afmt) ? template.getString("afmt") : afmt;
    for (Pair<String, String> p : new Pair[] { new Pair<>("q", qfmt), new Pair<>("a", afmt) }) {
        String type = p.first;
        String format = p.second;
        if ("q".equals(type)) {
            format = fClozePatternQ.matcher(format).replaceAll(String.format(Locale.US, "{{$1cq-%d:", cardNum));
            format = fClozeTagStart.matcher(format).replaceAll(String.format(Locale.US, "<%%cq:%d:", cardNum));
        } else {
            format = fClozePatternA.matcher(format).replaceAll(String.format(Locale.US, "{{$1ca-%d:", cardNum));
            format = fClozeTagStart.matcher(format).replaceAll(String.format(Locale.US, "<%%ca:%d:", cardNum));
            // the following line differs from libanki // TODO: why?
            // fields.put("FrontSide", mMedia.stripAudio(d.get("q")));
            fields.put("FrontSide", d.get("q"));
        }
        String html = new Template(format, fields).render();
        html = ChessFilter.fenToChessboard(html, getContext());
        if (!browser) {
            // browser don't show image. So compiling LaTeX actually remove information.
            html = LaTeX.mungeQA(html, this, model);
        }
        d.put(type, html);
        // empty cloze?
        if ("q".equals(type) && model.getInt("type") == Consts.MODEL_CLOZE) {
            if (getModels()._availClozeOrds(model, flist, false).size() == 0) {
                String link = String.format("<a href=%s#cloze>%s</a>", Consts.HELP_SITE, "help");
                d.put("q", mContext.getString(R.string.empty_cloze_warning, link));
            }
        }
    }
    return d;
}
Also used : JSONObject(com.ichi2.utils.JSONObject) HashMap(java.util.HashMap) SuppressLint(android.annotation.SuppressLint) Pair(android.util.Pair) Template(com.ichi2.libanki.template.Template)

Example 53 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class SchedV2 method _deckNewLimitSingle.

/**
 * Maximal number of new card still to see today in deck g. It's computed as:
 * the number of new card to see by day according to the deck optinos
 * minus the number of new cards seen today in deck d or a descendant
 * plus the number of extra new cards to see today in deck d, a parent or a descendant.
 *
 * Limits of its ancestors are not applied, current card is not treated differently.
 */
public int _deckNewLimitSingle(@NonNull Deck g) {
    if (g.getInt("dyn") != 0) {
        return mDynReportLimit;
    }
    long did = g.getLong("id");
    @NonNull DeckConfig c = mCol.getDecks().confForDid(did);
    int lim = Math.max(0, c.getJSONObject("new").getInt("perDay") - g.getJSONArray("newToday").getInt(1));
    // So currentCard does not have to be taken into consideration in this method
    if (currentCardIsInQueueWithDeck(Consts.QUEUE_TYPE_NEW, did)) {
        lim--;
    }
    return lim;
}
Also used : NonNull(androidx.annotation.NonNull) DeckConfig(com.ichi2.libanki.DeckConfig)

Example 54 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class SchedV2 method _checkLeech.

/**
 * Leeches ****************************************************************** *****************************
 */
/**
 * Leech handler. True if card was a leech.
 *        Overridden: in V1, due and did are changed
 */
protected boolean _checkLeech(@NonNull Card card, @NonNull JSONObject conf) {
    int lf;
    lf = conf.getInt("leechFails");
    if (lf == 0) {
        return false;
    }
    // if over threshold or every half threshold reps after that
    if (card.getLapses() >= lf && (card.getLapses() - lf) % Math.max(lf / 2, 1) == 0) {
        // add a leech tag
        Note n = card.note();
        n.addTag("leech");
        n.flush();
        // handle
        if (conf.getInt("leechAction") == Consts.LEECH_SUSPEND) {
            card.setQueue(Consts.QUEUE_TYPE_SUSPENDED);
        }
        // notify UI
        if (mContextReference != null) {
            Activity context = mContextReference.get();
            leech(card, context);
        }
        return true;
    }
    return false;
}
Also used : Note(com.ichi2.libanki.Note) Activity(android.app.Activity)

Example 55 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class SchedV2 method _burySiblings.

/**
 * Sibling spacing
 * ********************
 */
protected void _burySiblings(@NonNull Card card) {
    ArrayList<Long> toBury = new ArrayList<>();
    JSONObject nconf = _newConf(card);
    boolean buryNew = nconf.optBoolean("bury", true);
    JSONObject rconf = _revConf(card);
    boolean buryRev = rconf.optBoolean("bury", true);
    // loop through and remove from queues
    Cursor cur = null;
    try {
        cur = mCol.getDb().query("select id, queue from cards where nid=? and id!=? " + "and (queue=" + Consts.QUEUE_TYPE_NEW + " or (queue=" + Consts.QUEUE_TYPE_REV + " and due<=?))", card.getNid(), card.getId(), mToday);
        while (cur.moveToNext()) {
            long cid = cur.getLong(0);
            int queue = cur.getInt(1);
            SimpleCardQueue queue_object;
            if (queue == Consts.QUEUE_TYPE_REV) {
                queue_object = mRevQueue;
                if (buryRev) {
                    toBury.add(cid);
                }
            } else {
                queue_object = mNewQueue;
                if (buryNew) {
                    toBury.add(cid);
                }
            }
            // even if burying disabled, we still discard to give
            // same-day spacing
            queue_object.remove(cid);
        }
    } finally {
        if (cur != null && !cur.isClosed()) {
            cur.close();
        }
    }
    // then bury
    if (!toBury.isEmpty()) {
        buryCards(Utils.collection2Array(toBury), false);
    }
}
Also used : JSONObject(com.ichi2.utils.JSONObject) ArrayList(java.util.ArrayList) Cursor(android.database.Cursor)

Aggregations

Card (com.ichi2.libanki.Card)222 Test (org.junit.Test)212 Collection (com.ichi2.libanki.Collection)179 Note (com.ichi2.libanki.Note)173 RobolectricTest (com.ichi2.anki.RobolectricTest)168 JSONObject (com.ichi2.utils.JSONObject)114 JSONArray (com.ichi2.utils.JSONArray)80 DeckConfig (com.ichi2.libanki.DeckConfig)72 ArrayList (java.util.ArrayList)48 NonNull (androidx.annotation.NonNull)33 Deck (com.ichi2.libanki.Deck)32 Intent (android.content.Intent)29 JSONException (com.ichi2.utils.JSONException)27 Model (com.ichi2.libanki.Model)26 Context (android.content.Context)23 SuppressLint (android.annotation.SuppressLint)22 Cursor (android.database.Cursor)22 HashMap (java.util.HashMap)21 Matchers.containsString (org.hamcrest.Matchers.containsString)20 List (java.util.List)17