Search in sources :

Example 11 with DeckDueTreeNode

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());
}
Also used : Note(com.ichi2.libanki.Note) Model(com.ichi2.libanki.Model) Collection(com.ichi2.libanki.Collection) Deck(com.ichi2.libanki.Deck) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 12 with DeckDueTreeNode

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;
}
Also used : JSONArray(com.ichi2.utils.JSONArray)

Example 13 with DeckDueTreeNode

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;
}
Also used : JSONArray(com.ichi2.utils.JSONArray)

Example 14 with DeckDueTreeNode

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");
    }
}
Also used : JSONArray(com.ichi2.utils.JSONArray) JSONException(com.ichi2.utils.JSONException) MatrixCursor(android.database.MatrixCursor) Card(com.ichi2.libanki.Card) DeckDueTreeNode(com.ichi2.libanki.sched.DeckDueTreeNode) JSONObject(com.ichi2.utils.JSONObject) Note(com.ichi2.libanki.Note) Model(com.ichi2.libanki.Model) Collection(com.ichi2.libanki.Collection) ArrayList(java.util.ArrayList) List(java.util.List) Models(com.ichi2.libanki.Models)

Example 15 with DeckDueTreeNode

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;
}
Also used : DeckDueTreeNode(com.ichi2.libanki.sched.DeckDueTreeNode) JSONObject(com.ichi2.utils.JSONObject) ArrayList(java.util.ArrayList) Nullable(androidx.annotation.Nullable)

Aggregations

RobolectricTest (com.ichi2.anki.RobolectricTest)13 Test (org.junit.Test)13 Collection (com.ichi2.libanki.Collection)11 ArrayList (java.util.ArrayList)9 Card (com.ichi2.libanki.Card)8 Note (com.ichi2.libanki.Note)8 DeckDueTreeNode (com.ichi2.libanki.sched.DeckDueTreeNode)7 Deck (com.ichi2.libanki.Deck)6 JSONObject (com.ichi2.utils.JSONObject)6 Matchers.containsString (org.hamcrest.Matchers.containsString)6 Nullable (androidx.annotation.Nullable)5 Model (com.ichi2.libanki.Model)4 JSONArray (com.ichi2.utils.JSONArray)4 MatrixCursor (android.database.MatrixCursor)2 DeckConfig (com.ichi2.libanki.DeckConfig)2 JSONException (com.ichi2.utils.JSONException)2 HashMap (java.util.HashMap)2 List (java.util.List)2 AlarmManager (android.app.AlarmManager)1 Notification (android.app.Notification)1