Search in sources :

Example 16 with SECONDS_PER_DAY

use of com.ichi2.libanki.stats.Stats.SECONDS_PER_DAY in project Anki-Android by ankidroid.

the class SchedV2Test method test_filt_reviewing_early_normal.

@Test
public void test_filt_reviewing_early_normal() throws Exception {
    Collection col = getColV2();
    Note note = col.newNote();
    note.setItem("Front", "one");
    col.addNote(note);
    Card c = note.cards().get(0);
    c.setIvl(100);
    c.setQueue(QUEUE_TYPE_REV);
    c.setType(CARD_TYPE_REV);
    // due in 25 days, so it's been waiting 75 days
    c.setDue(col.getSched().getToday() + 25);
    c.setMod(1);
    c.setFactor(STARTING_FACTOR);
    c.startTimer();
    c.flush();
    col.reset();
    assertEquals(new Counts(0, 0, 0), col.getSched().counts());
    // create a dynamic deck and refresh it
    long did = addDynamicDeck("Cram");
    col.getSched().rebuildDyn(did);
    col.reset();
    // should appear as normal in the deck list
    /* todo sort
           assertEquals(1, sorted(col.getSched().deckDueTree().getChildren())[0].review_count);
        */
    // and should appear in the counts
    assertEquals(new Counts(0, 0, 1), col.getSched().counts());
    // grab it and check estimates
    c = getCard();
    assertEquals(4, col.getSched().answerButtons(c));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(Math.round(75 * 1.2) * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_TWO));
    assertThat(col.getSched().nextIvl(c, BUTTON_THREE), is((long) (75 * 2.5) * SECONDS_PER_DAY));
    assertThat(col.getSched().nextIvl(c, BUTTON_FOUR), is((long) (75 * 2.5 * 1.15) * SECONDS_PER_DAY));
    // answer 'good'
    col.getSched().answerCard(c, BUTTON_THREE);
    checkRevIvl(col, c, 90);
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    assertEquals(0L, c.getODue());
    // should not be in learning
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // should be logged as a cram rep
    assertEquals(3, col.getDb().queryLongScalar("select type from revlog order by id desc limit 1"));
    // due in 75 days, so it's been waiting 25 days
    c.setIvl(100);
    c.setDue(col.getSched().getToday() + 75);
    c.flush();
    col.getSched().rebuildDyn(did);
    col.reset();
    c = getCard();
    assertEquals(60 * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(100 * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_THREE));
    assertEquals(114 * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_FOUR));
}
Also used : Note(com.ichi2.libanki.Note) Collection(com.ichi2.libanki.Collection) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 17 with SECONDS_PER_DAY

use of com.ichi2.libanki.stats.Stats.SECONDS_PER_DAY in project Anki-Android by ankidroid.

the class SchedTest method test_cram.

@Test
public void test_cram() throws Exception {
    Collection col = getColV1();
    Note note = col.newNote();
    note.setItem("Front", "one");
    col.addNote(note);
    Card c = note.cards().get(0);
    c.setIvl(100);
    c.setQueue(QUEUE_TYPE_REV);
    c.setType(CARD_TYPE_REV);
    // due in 25 days, so it's been waiting 75 days
    c.setDue(col.getSched().getToday() + 25);
    c.setMod(1);
    c.setFactor(STARTING_FACTOR);
    c.startTimer();
    c.flush();
    col.reset();
    assertEquals(new Counts(0, 0, 0), col.getSched().counts());
    Card cardcopy = c.clone();
    // create a dynamic deck and refresh it
    long did = addDynamicDeck("Cram");
    col.getSched().rebuildDyn(did);
    col.reset();
    // should appear as new in the deck list
    // todo: which sort
    // and should appear in the counts
    assertEquals(new Counts(1, 0, 0), col.getSched().counts());
    // grab it and check estimates
    c = getCard();
    assertEquals(2, col.getSched().answerButtons(c));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_TWO));
    Deck cram = col.getDecks().get(did);
    cram.put("delays", new JSONArray(new double[] { 1, 10 }));
    col.getDecks().save(cram);
    assertEquals(3, col.getSched().answerButtons(c));
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_THREE));
    col.getSched().answerCard(c, BUTTON_TWO);
    // elapsed time was 75 days
    // factor = 2.5+1.2/2 = 1.85
    // int(75*1.85) = 138
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getODue());
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    // should be logged as a cram rep
    assertEquals(3, col.getDb().queryLongScalar("select type from revlog order by id desc limit 1"));
    // check ivls again
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_THREE));
    // when it graduates, due is updated
    c = getCard();
    col.getSched().answerCard(c, BUTTON_TWO);
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getDue());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // and it will have moved back to the previous deck
    assertEquals(1, c.getDid());
    // cram the deck again
    col.getSched().rebuildDyn(did);
    col.reset();
    c = getCard();
    // check ivls again - passing should be idempotent
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_THREE));
    col.getSched().answerCard(c, BUTTON_TWO);
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getODue());
    // fail
    col.getSched().answerCard(c, BUTTON_ONE);
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_THREE));
    // delete the deck, returning the card mid-study
    col.getDecks().rem(col.getDecks().selected());
    assertEquals(1, col.getSched().deckDueTree().size());
    c.load();
    assertEquals(1, c.getIvl());
    assertEquals(col.getSched().getToday() + 1, c.getDue());
    // make it due
    col.reset();
    assertEquals(new Counts(0, 0, 0), col.getSched().counts());
    c.setDue(-5);
    c.setIvl(100);
    c.flush();
    col.reset();
    assertEquals(new Counts(0, 0, 1), col.getSched().counts());
    // cram again
    did = addDynamicDeck("Cram");
    col.getSched().rebuildDyn(did);
    col.reset();
    assertEquals(new Counts(0, 0, 1), col.getSched().counts());
    c.load();
    assertEquals(4, col.getSched().answerButtons(c));
    // add a sibling so we can test minSpace, etc
    Card c2 = c.clone();
    c2.setId(0);
    c2.setOrd(1);
    c2.setDue(325);
    c2.flush();
    // should be able to answer it
    c = getCard();
    col.getSched().answerCard(c, BUTTON_FOUR);
    // it should have been moved back to the original deck
    assertEquals(1, c.getDid());
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) Deck(com.ichi2.libanki.Deck) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 18 with SECONDS_PER_DAY

use of com.ichi2.libanki.stats.Stats.SECONDS_PER_DAY in project Anki-Android by ankidroid.

the class SchedTest method test_nextIvlV1.

@Test
public void test_nextIvlV1() throws Exception {
    Collection col = getColV1();
    Note note = col.newNote();
    note.setItem("Front", "one");
    note.setItem("Back", "two");
    col.addNote(note);
    col.reset();
    DeckConfig conf = col.getDecks().confForDid(1);
    conf.getJSONObject("new").put("delays", new JSONArray(new double[] { 0.5, 3, 10 }));
    conf.getJSONObject("lapse").put("delays", new JSONArray(new double[] { 1, 5, 9 }));
    col.getDecks().save(conf);
    Card c = getCard();
    // new cards
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    assertEquals(30, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(180, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(4 * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_THREE));
    col.getSched().answerCard(c, BUTTON_ONE);
    // cards in learning
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    assertEquals(30, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(180, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(4 * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_THREE));
    col.getSched().answerCard(c, BUTTON_TWO);
    assertEquals(30, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(4 * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_THREE));
    col.getSched().answerCard(c, BUTTON_TWO);
    // normal graduation is tomorrow
    assertEquals(SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(4 * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_THREE));
    // lapsed cards
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c.setType(CARD_TYPE_REV);
    c.setIvl(100);
    c.setFactor(STARTING_FACTOR);
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(100 * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(100 * SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_THREE));
    // review cards
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c.setQueue(QUEUE_TYPE_REV);
    c.setIvl(100);
    c.setFactor(STARTING_FACTOR);
    // failing it should put it at 60s
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    // or 1 day if relearn is false
    conf.getJSONObject("lapse").put("delays", new JSONArray(new double[] {}));
    col.getDecks().save(conf);
    assertEquals(SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_ONE));
    // (* 100 1.2 SECONDS_PER_DAY)10368000.0
    assertEquals(10368000, col.getSched().nextIvl(c, BUTTON_TWO));
    // (* 100 2.5 SECONDS_PER_DAY)21600000.0
    assertEquals(21600000, col.getSched().nextIvl(c, BUTTON_THREE));
    // (* 100 2.5 1.3 SECONDS_PER_DAY)28080000.0
    assertEquals(28080000, col.getSched().nextIvl(c, BUTTON_FOUR));
    assertThat(without_unicode_isolation(col.getSched().nextIvlStr(getTargetContext(), c, BUTTON_FOUR)), is("10.8 mo"));
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 19 with SECONDS_PER_DAY

use of com.ichi2.libanki.stats.Stats.SECONDS_PER_DAY in project Anki-Android by ankidroid.

the class Sched method _answerLrnCard.

/**
 * @param ease 1=no, 2=yes, 3=remove
 */
@Override
protected void _answerLrnCard(@NonNull Card card, @Consts.BUTTON_TYPE int ease) {
    JSONObject conf = _lrnConf(card);
    @Consts.REVLOG_TYPE int type;
    if (card.isInDynamicDeck() && !card.getWasNew()) {
        type = Consts.REVLOG_CRAM;
    } else if (card.getType() == Consts.CARD_TYPE_REV) {
        type = Consts.REVLOG_RELRN;
    } else {
        type = Consts.REVLOG_LRN;
    }
    boolean leaving = false;
    // lrnCount was decremented once when card was fetched
    int lastLeft = card.getLeft();
    // immediate graduate?
    if (ease == Consts.BUTTON_THREE) {
        _rescheduleAsRev(card, conf, true);
        leaving = true;
    // graduation time?
    } else if (ease == Consts.BUTTON_TWO && (card.getLeft() % 1000) - 1 <= 0) {
        _rescheduleAsRev(card, conf, false);
        leaving = true;
    } else {
        // one step towards graduation
        if (ease == Consts.BUTTON_TWO) {
            // decrement real left count and recalculate left today
            int left = (card.getLeft() % 1000) - 1;
            card.setLeft(_leftToday(conf.getJSONArray("delays"), left) * 1000 + left);
        // failed
        } else {
            card.setLeft(_startingLeft(card));
            boolean resched = _resched(card);
            if (conf.has("mult") && resched) {
                // review that's lapsed
                card.setIvl(Math.max(Math.max(1, (int) (card.getIvl() * conf.getDouble("mult"))), conf.getInt("minInt")));
            } else {
            // new card; no ivl adjustment
            // pass
            }
            if (resched && card.isInDynamicDeck()) {
                card.setODue(mToday + 1);
            }
        }
        int delay = _delayForGrade(conf, card.getLeft());
        if (card.getDue() < getTime().intTime()) {
            // not collapsed; add some randomness
            delay *= Utils.randomFloatInRange(1f, 1.25f);
        }
        card.setDue(getTime().intTime() + delay);
        // due today?
        if (card.getDue() < mDayCutoff) {
            mLrnCount += card.getLeft() / 1000;
            // if the queue is not empty and there's nothing else to do, make
            // sure we don't put it at the head of the queue and end up showing
            // it twice in a row
            card.setQueue(Consts.QUEUE_TYPE_LRN);
            if (!mLrnQueue.isEmpty() && revCount() == 0 && newCount() == 0) {
                long smallestDue = mLrnQueue.getFirstDue();
                card.setDue(Math.max(card.getDue(), smallestDue + 1));
            }
            _sortIntoLrn(card.getDue(), card.getId());
        } else {
            // the card is due in one or more days, so we need to use the day learn queue
            long ahead = ((card.getDue() - mDayCutoff) / SECONDS_PER_DAY) + 1;
            card.setDue(mToday + ahead);
            card.setQueue(Consts.QUEUE_TYPE_DAY_LEARN_RELEARN);
        }
    }
    _logLrn(card, ease, conf, leaving, type, lastLeft);
}
Also used : JSONObject(com.ichi2.utils.JSONObject)

Example 20 with SECONDS_PER_DAY

use of com.ichi2.libanki.stats.Stats.SECONDS_PER_DAY in project Anki-Android by ankidroid.

the class Sched method _rescheduleLapse.

@Override
protected int _rescheduleLapse(@NonNull Card card) {
    JSONObject conf = _lapseConf(card);
    card.setLastIvl(card.getIvl());
    if (_resched(card)) {
        card.setLapses(card.getLapses() + 1);
        card.setIvl(_nextLapseIvl(card, conf));
        card.setFactor(Math.max(1300, card.getFactor() - 200));
        card.setDue(mToday + card.getIvl());
        // if it's a filtered deck, update odue as well
        if (card.isInDynamicDeck()) {
            card.setODue(card.getDue());
        }
    }
    // if suspended as a leech, nothing to do
    int delay = 0;
    if (_checkLeech(card, conf) && card.getQueue() == Consts.QUEUE_TYPE_SUSPENDED) {
        return delay;
    }
    // if no relearning steps, nothing to do
    if (conf.getJSONArray("delays").length() == 0) {
        return delay;
    }
    // record rev due date for later
    if (card.getODue() == 0) {
        card.setODue(card.getDue());
    }
    delay = _delayForGrade(conf, 0);
    card.setDue(delay + getTime().intTime());
    card.setLeft(_startingLeft(card));
    // queue 1
    if (card.getDue() < mDayCutoff) {
        mLrnCount += card.getLeft() / 1000;
        card.setQueue(Consts.QUEUE_TYPE_LRN);
        _sortIntoLrn(card.getDue(), card.getId());
    } else {
        // day learn queue
        long ahead = ((card.getDue() - mDayCutoff) / SECONDS_PER_DAY) + 1;
        card.setDue(mToday + ahead);
        card.setQueue(Consts.QUEUE_TYPE_DAY_LEARN_RELEARN);
    }
    return delay;
}
Also used : JSONObject(com.ichi2.utils.JSONObject)

Aggregations

RobolectricTest (com.ichi2.anki.RobolectricTest)14 Test (org.junit.Test)14 Card (com.ichi2.libanki.Card)12 Collection (com.ichi2.libanki.Collection)12 Note (com.ichi2.libanki.Note)12 JSONArray (com.ichi2.utils.JSONArray)10 DeckConfig (com.ichi2.libanki.DeckConfig)8 JSONObject (com.ichi2.utils.JSONObject)6 Deck (com.ichi2.libanki.Deck)4