use of com.ichi2.libanki.sched.DeckDueTreeNode in project Anki-Android by ankidroid.
the class SchedV2Test method test_review_limits.
@Test
public void test_review_limits() throws Exception {
Collection col = getColV2();
Deck parent = col.getDecks().get(addDeck("parent"));
Deck child = col.getDecks().get(addDeck("parent::child"));
DeckConfig pconf = col.getDecks().getConf(col.getDecks().confId("parentConf"));
DeckConfig cconf = col.getDecks().getConf(col.getDecks().confId("childConf"));
pconf.getJSONObject("rev").put("perDay", 5);
col.getDecks().updateConf(pconf);
col.getDecks().setConf(parent, pconf.getLong("id"));
cconf.getJSONObject("rev").put("perDay", 10);
col.getDecks().updateConf(cconf);
col.getDecks().setConf(child, cconf.getLong("id"));
Model m = col.getModels().current();
m.put("did", child.getLong("id"));
col.getModels().save(m, false);
// add some cards
for (int i = 0; i < 20; i++) {
Note note = col.newNote();
note.setItem("Front", "one");
note.setItem("Back", "two");
col.addNote(note);
// make them reviews
Card c = note.cards().get(0);
c.setQueue(QUEUE_TYPE_REV);
c.setType(CARD_TYPE_REV);
c.setDue(0);
c.flush();
}
// position 0 is default deck. Different from upstream
DeckDueTreeNode tree = col.getSched().deckDueTree().get(1);
// (('parent', 1514457677462, 5, 0, 0, (('child', 1514457677463, 5, 0, 0, ()),)))
assertEquals("parent", tree.getFullDeckName());
// paren, tree.review_count)t
assertEquals(5, tree.getRevCount());
assertEquals(10, tree.getChildren().get(0).getRevCount());
// .counts() should match
col.getDecks().select(child.getLong("id"));
col.reset();
assertEquals(new Counts(0, 0, 10), col.getSched().counts());
// answering a card in the child should decrement parent count
Card c = getCard();
col.getSched().answerCard(c, BUTTON_THREE);
assertEquals(new Counts(0, 0, 9), col.getSched().counts());
tree = col.getSched().deckDueTree().get(1);
assertEquals(4, tree.getRevCount());
assertEquals(9, tree.getChildren().get(0).getRevCount());
}
use of com.ichi2.libanki.sched.DeckDueTreeNode in project Anki-Android by ankidroid.
the class CardContentProvider method getDeckCountsFromDueTreeNode.
private JSONArray getDeckCountsFromDueTreeNode(DeckDueTreeNode deck) {
JSONArray deckCounts = new JSONArray();
deckCounts.put(deck.getLrnCount());
deckCounts.put(deck.getRevCount());
deckCounts.put(deck.getNewCount());
return deckCounts;
}
use of com.ichi2.libanki.sched.DeckDueTreeNode in project AnkiChinaAndroid by ankichinateam.
the class CardContentProvider method getDeckCountsFromDueTreeNode.
private JSONArray getDeckCountsFromDueTreeNode(DeckDueTreeNode deck) {
JSONArray deckCounts = new JSONArray();
deckCounts.put(deck.getLrnCount());
deckCounts.put(deck.getRevCount());
deckCounts.put(deck.getNewCount());
return deckCounts;
}
use of com.ichi2.libanki.sched.DeckDueTreeNode in project AnkiChinaAndroid by ankichinateam.
the class CardContentProvider method query.
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String order) {
if (!hasReadWritePermission() && shouldEnforceQueryOrInsertSecurity()) {
throwSecurityException("query", uri);
}
Collection col = CollectionHelper.getInstance().getCol(mContext);
if (col == null) {
throw new IllegalStateException(COL_NULL_ERROR_MSG);
}
Timber.d(getLogMessage("query", uri));
// Find out what data the user is requesting
int match = sUriMatcher.match(uri);
switch(match) {
case NOTES_V2:
{
/* Search for notes using direct SQL query */
String[] proj = sanitizeNoteProjection(projection);
String sql = SQLiteQueryBuilder.buildQueryString(false, "notes", proj, selection, null, null, order, null);
return col.getDb().getDatabase().query(sql, selectionArgs);
}
case NOTES:
{
/* Search for notes using the libanki browser syntax */
String[] proj = sanitizeNoteProjection(projection);
String query = (selection != null) ? selection : "";
List<Long> noteIds = col.findNotes(query);
if ((noteIds != null) && (!noteIds.isEmpty())) {
String sel = String.format("id in (%s)", TextUtils.join(",", noteIds));
String sql = SQLiteQueryBuilder.buildQueryString(false, "notes", proj, sel, null, null, order, null);
return col.getDb().getDatabase().query(sql);
} else {
return null;
}
}
case NOTES_ID:
{
/* Direct access note with specific ID*/
String noteId = uri.getPathSegments().get(1);
String[] proj = sanitizeNoteProjection(projection);
String sql = SQLiteQueryBuilder.buildQueryString(false, "notes", proj, "id=?", null, null, order, null);
return col.getDb().getDatabase().query(sql, new String[] { noteId });
}
case NOTES_ID_CARDS:
{
Note currentNote = getNoteFromUri(uri, col);
String[] columns = ((projection != null) ? projection : FlashCardsContract.Card.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, 1);
for (Card currentCard : currentNote.cards()) {
addCardToCursor(currentCard, rv, col, columns);
}
return rv;
}
case NOTES_ID_CARDS_ORD:
{
Card currentCard = getCardFromUri(uri, col);
String[] columns = ((projection != null) ? projection : FlashCardsContract.Card.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, 1);
addCardToCursor(currentCard, rv, col, columns);
return rv;
}
case MODELS:
{
Models models = col.getModels();
String[] columns = ((projection != null) ? projection : FlashCardsContract.Model.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, 1);
for (Long modelId : models.getModels().keySet()) {
addModelToCursor(modelId, models, rv, columns);
}
return rv;
}
case MODELS_ID:
{
long modelId = getModelIdFromUri(uri, col);
String[] columns = ((projection != null) ? projection : FlashCardsContract.Model.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, 1);
addModelToCursor(modelId, col.getModels(), rv, columns);
return rv;
}
case MODELS_ID_TEMPLATES:
{
/* Direct access model templates */
Models models = col.getModels();
Model currentModel = models.get(getModelIdFromUri(uri, col));
String[] columns = ((projection != null) ? projection : CardTemplate.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, 1);
try {
JSONArray templates = currentModel.getJSONArray("tmpls");
for (int idx = 0; idx < templates.length(); idx++) {
JSONObject template = templates.getJSONObject(idx);
addTemplateToCursor(template, currentModel, idx + 1, models, rv, columns);
}
} catch (JSONException e) {
throw new IllegalArgumentException("Model is malformed", e);
}
return rv;
}
case MODELS_ID_TEMPLATES_ID:
{
/* Direct access model template with specific ID */
Models models = col.getModels();
int ord = Integer.parseInt(uri.getLastPathSegment());
Model currentModel = models.get(getModelIdFromUri(uri, col));
String[] columns = ((projection != null) ? projection : CardTemplate.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, 1);
try {
JSONObject template = getTemplateFromUri(uri, col);
addTemplateToCursor(template, currentModel, ord + 1, models, rv, columns);
} catch (JSONException e) {
throw new IllegalArgumentException("Model is malformed", e);
}
return rv;
}
case SCHEDULE:
{
String[] columns = ((projection != null) ? projection : FlashCardsContract.ReviewInfo.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, 1);
long selectedDeckBeforeQuery = col.getDecks().selected();
long deckIdOfTemporarilySelectedDeck = -1;
// the number of scheduled cards to return
int limit = 1;
int selectionArgIndex = 0;
// parsing the selection arguments
if (selection != null) {
// split selection to get arguments like "limit=?"
String[] args = selection.split(",");
for (String arg : args) {
// split arguments into key ("limit") and value ("?")
String[] keyAndValue = arg.split("=");
try {
// check if value is a placeholder ("?"), if so replace with the next value of selectionArgs
String value = "?".equals(keyAndValue[1].trim()) ? selectionArgs[selectionArgIndex++] : keyAndValue[1];
if ("limit".equals(keyAndValue[0].trim())) {
limit = Integer.valueOf(value);
} else if ("deckID".equals(keyAndValue[0].trim())) {
deckIdOfTemporarilySelectedDeck = Long.valueOf(value);
if (!selectDeckWithCheck(col, deckIdOfTemporarilySelectedDeck)) {
// if the provided deckID is wrong, return empty cursor.
return rv;
}
}
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
}
}
}
// retrieve the number of cards provided by the selection parameter "limit"
col.getSched().deferReset();
for (int k = 0; k < limit; k++) {
Card currentCard = col.getSched().getCard();
if (currentCard == null) {
break;
}
int buttonCount = col.getSched().answerButtons(currentCard);
JSONArray buttonTexts = new JSONArray();
for (int i = 0; i < buttonCount; i++) {
buttonTexts.put(col.getSched().nextIvlStr(mContext, currentCard, i + 1));
}
addReviewInfoToCursor(currentCard, buttonTexts, buttonCount, rv, col, columns);
}
if (deckIdOfTemporarilySelectedDeck != -1) {
// if the selected deck was changed
// change the selected deck back to the one it was before the query
col.getDecks().select(selectedDeckBeforeQuery);
}
return rv;
}
case DECKS:
{
List<DeckDueTreeNode> allDecks = col.getSched().deckDueList();
String[] columns = ((projection != null) ? projection : FlashCardsContract.Deck.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, allDecks.size());
for (DeckDueTreeNode deck : allDecks) {
long id = deck.getDid();
String name = deck.getFullDeckName();
addDeckToCursor(id, name, getDeckCountsFromDueTreeNode(deck), rv, col, columns);
}
return rv;
}
case DECKS_ID:
{
/* Direct access deck */
String[] columns = ((projection != null) ? projection : FlashCardsContract.Deck.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, 1);
List<DeckDueTreeNode> allDecks = col.getSched().deckDueList();
long deckId;
deckId = Long.parseLong(uri.getPathSegments().get(1));
for (DeckDueTreeNode deck : allDecks) {
if (deck.getDid() == deckId) {
addDeckToCursor(deckId, deck.getFullDeckName(), getDeckCountsFromDueTreeNode(deck), rv, col, columns);
return rv;
}
}
return rv;
}
case DECK_SELECTED:
{
long id = col.getDecks().selected();
String name = col.getDecks().name(id);
String[] columns = ((projection != null) ? projection : FlashCardsContract.Deck.DEFAULT_PROJECTION);
MatrixCursor rv = new MatrixCursor(columns, 1);
JSONArray counts = new JSONArray(Arrays.asList(col.getSched().counts()));
addDeckToCursor(id, name, counts, rv, col, columns);
return rv;
}
default:
// Unknown URI type
throw new IllegalArgumentException("uri " + uri + " is not supported");
}
}
use of com.ichi2.libanki.sched.DeckDueTreeNode in project AnkiChinaAndroid by ankichinateam.
the class ReminderService method getDeckOptionDue.
// getDeckOptionDue information, will recur one time to workaround collection close if recur is true
@Nullable
private List<DeckDueTreeNode> getDeckOptionDue(Collection col, long dConfId, boolean recur) {
// are working
if (col.getDb() == null || col.getDecks().getConf(dConfId) == null) {
Timber.d("Deck option %s became unavailable while ReminderService was working. Ignoring", dConfId);
return null;
}
List<DeckDueTreeNode> decks = new ArrayList<>();
try {
// This loop over top level deck only. No notification will ever occur for subdecks.
for (DeckDueTreeNode node : col.getSched().deckDueTree()) {
JSONObject deck = col.getDecks().get(node.getDid(), false);
// Dynamic deck has no "conf", so are not added here.
if (deck != null && deck.optLong("conf") == dConfId) {
decks.add(node);
}
}
return decks;
} catch (Exception e) {
if (recur) {
Timber.i(e, "getDeckOptionDue exception - likely database re-initialization from auto-sync. Will re-try after sleep.");
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Timber.i(ex, "Thread interrupted while waiting to retry. Likely unimportant.");
Thread.currentThread().interrupt();
}
return getDeckOptionDue(col, dConfId, false);
} else {
Timber.w(e, "Database unavailable while working. No re-tries left.");
}
}
return null;
}
Aggregations