use of com.ichi2.async.ProgressSender 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;
}
Aggregations