Example #1
0
def setup_basic():
    global deck1, deck2, client, server
    deck1 = getEmptyDeck()
    # add a note to deck 1
    f = deck1.newNote()
    f["Front"] = u"foo"
    f["Back"] = u"bar"
    f.tags = [u"foo"]
    deck1.addNote(f)
    # answer it
    deck1.reset()
    deck1.sched.answerCard(deck1.sched.getCard(), 4)
    # repeat for deck2
    deck2 = getEmptyDeck(server=True)
    f = deck2.newNote()
    f["Front"] = u"bar"
    f["Back"] = u"bar"
    f.tags = [u"bar"]
    deck2.addNote(f)
    deck2.reset()
    deck2.sched.answerCard(deck2.sched.getCard(), 4)
    # start with same schema and sync time
    deck1.scm = deck2.scm = 0
    # and same mod time, so sync does nothing
    t = intTime(1000)
    deck1.save(mod=t)
    deck2.save(mod=t)
    server = LocalServer(deck2)
    client = Syncer(deck1, server)
Example #2
0
def test_counts():
    d = getEmptyDeck()
    # add a second group
    assert d.groupId("new group") == 2
    # for each card type
    for type in range(3):
        # and each of the groups
        for gid in (1,2):
            # create a new fact
            f = d.newFact()
            f['Front'] = u"one"
            d.addFact(f)
            c = f.cards()[0]
            # set type/gid
            c.type = type
            c.queue = type
            c.gid = gid
            c.due = 0
            c.flush()
    d.reset()
    # with the default settings, there's no count limit
    assert d.sched.counts() == (2,2,2)
    # check limit to one group
    d.qconf['groups'] = [1]
    d.reset()
    assert d.sched.counts() == (1,1,1)
    # we don't need to build the queue to get the counts
    assert d.sched.allCounts() == (2,2,2)
    assert d.sched.selCounts() == (1,1,1)
    assert d.sched.allCounts() == (2,2,2)
Example #3
0
def test_remove():
    deck = getEmptyDeck()
    # can't remove the default deck
    assertException(AssertionError, lambda: deck.decks.rem(1))
    # create a new deck, and add a note/card to it
    g1 = deck.decks.id("g1")
    f = deck.newNote()
    f['Front'] = u"1"
    f.model()['did'] = g1
    deck.addNote(f)
    c = f.cards()[0]
    assert c.did == g1
    # by default deleting the deck leaves the cards with an invalid did
    assert deck.cardCount() == 1
    deck.decks.rem(g1)
    assert deck.cardCount() == 1
    c.load()
    assert c.did == g1
    # but if we try to get it, we get the default
    assert deck.decks.name(c.did) == "[no deck]"
    # let's create another deck and explicitly set the card to it
    g2 = deck.decks.id("g2")
    c.did = g2; c.flush()
    # this time we'll delete the card/note too
    deck.decks.rem(g2, cardsToo=True)
    assert deck.cardCount() == 0
    assert deck.noteCount() == 0
Example #4
0
def test_delete():
    deck = getEmptyDeck()
    f = deck.newFact()
    f['Front'] = u'1'
    f['Back'] = u'2'
    deck.addFact(f)
    cid = f.cards()[0].id
    deck.reset()
    deck.sched.answerCard(deck.sched.getCard(), 2)
    assert deck.db.scalar("select count() from revlog") == 1
    deck.delCards([cid])
    assert deck.cardCount() == 0
    assert deck.factCount() == 0
    assert deck.db.scalar("select count() from facts") == 0
    assert deck.db.scalar("select count() from cards") == 0
    assert deck.db.scalar("select count() from fsums") == 0
    assert deck.db.scalar("select count() from revlog") == 0
    assert deck.db.scalar("select count() from graves") == 0
    # add the fact back
    deck.addFact(f)
    assert deck.cardCount() == 1
    cid = f.cards()[0].id
    # delete again, this time with syncing enabled
    deck.syncName = "abc"
    deck.lastSync = time.time()
    deck.delCards([cid])
    assert deck.cardCount() == 0
    assert deck.factCount() == 0
    assert deck.db.scalar("select count() from graves") != 0
Example #5
0
def test_genrem():
    d = getEmptyDeck()
    f = d.newNote()
    f['Front'] = u'1'
    f['Back'] = u''
    d.addNote(f)
    assert len(f.cards()) == 1
    m = d.models.current()
    mm = d.models
    # adding a new template should automatically create cards
    t = mm.newTemplate("rev")
    t['qfmt'] = '{{Front}}'
    t['afmt'] = ""
    mm.addTemplate(m, t)
    mm.save(m, templates=True)
    assert len(f.cards()) == 2
    # if the template is changed to remove cards, they'll be removed
    t['qfmt'] = "{{Back}}"
    mm.save(m, templates=True)
    d.remCards(d.emptyCids())
    assert len(f.cards()) == 1
    # if we add to the note, a card should be automatically generated
    f.load()
    f['Back'] = "1"
    f.flush()
    assert len(f.cards()) == 2
Example #6
0
def test_fieldChecksum():
    deck = getEmptyDeck()
    f = deck.newFact()
    f['Front'] = u"new"; f['Back'] = u"new2"
    deck.addFact(f)
    assert deck.db.scalar(
        "select csum from fsums") == int("22af645d", 16)
    # empty field should have no checksum
    f['Front'] = u""
    f.flush()
    assert deck.db.scalar(
        "select count() from fsums") == 0
    # changing the val should change the checksum
    f['Front'] = u"newx"
    f.flush()
    assert deck.db.scalar(
        "select csum from fsums") == int("4b0e5a4c", 16)
    # turning off unique and modifying the fact should delete the sum
    m = f.model()
    m.fields[0]['uniq'] = False
    m.flush()
    f.flush()
    assert deck.db.scalar(
        "select count() from fsums") == 0
    # and turning on both should ensure two checksums generated
    m.fields[0]['uniq'] = True
    m.fields[1]['uniq'] = True
    m.flush()
    f.flush()
    assert deck.db.scalar(
        "select count() from fsums") == 2
Example #7
0
def test_genrem():
    d = getEmptyDeck()
    f = d.newNote()
    f['Front'] = u'1'
    f['Back'] = u''
    d.addNote(f)
    assert len(f.cards()) == 1
    m = d.models.current()
    mm = d.models
    # adding a new template should automatically create cards
    t = mm.newTemplate("rev")
    t['qfmt'] = '{{Front}}'
    t['afmt'] = ""
    mm.addTemplate(m, t)
    mm.save(m, templates=True)
    assert len(f.cards()) == 2
    # if the template is changed to remove cards, they'll be removed
    t['qfmt'] = "{{Back}}"
    mm.save(m, templates=True)
    d.remCards(d.emptyCids())
    assert len(f.cards()) == 1
    # if we add to the note, a card should be automatically generated
    f.load()
    f['Back'] = "1"
    f.flush()
    assert len(f.cards()) == 2
Example #8
0
def test_suspend():
    d = getEmptyDeck()
    f = d.newFact()
    f['Front'] = u"one"
    d.addFact(f)
    c = f.cards()[0]
    # suspending
    d.reset()
    assert d.sched.getCard()
    d.sched.suspendCards([c.id])
    d.reset()
    assert not d.sched.getCard()
    # unsuspending
    d.sched.unsuspendCards([c.id])
    d.reset()
    assert d.sched.getCard()
    # should cope with rev cards being relearnt
    c.due = 0
    c.ivl = 100
    c.type = 2
    c.queue = 2
    c.flush()
    d.reset()
    c = d.sched.getCard()
    d.sched.answerCard(c, 1)
    assert c.due >= time.time()
    assert c.queue == 1
    assert c.type == 2
    d.sched.suspendCards([c.id])
    d.sched.unsuspendCards([c.id])
    c.load()
    assert c.queue == 2
    assert c.type == 2
    assert c.due == 1
Example #9
0
def test_fieldChecksum():
    deck = getEmptyDeck()
    f = deck.newFact()
    f['Front'] = u"new"
    f['Back'] = u"new2"
    deck.addFact(f)
    assert deck.db.scalar("select csum from fsums") == int("22af645d", 16)
    # empty field should have no checksum
    f['Front'] = u""
    f.flush()
    assert deck.db.scalar("select count() from fsums") == 0
    # changing the val should change the checksum
    f['Front'] = u"newx"
    f.flush()
    assert deck.db.scalar("select csum from fsums") == int("4b0e5a4c", 16)
    # turning off unique and modifying the fact should delete the sum
    m = f.model()
    m.fields[0]['uniq'] = False
    m.flush()
    f.flush()
    assert deck.db.scalar("select count() from fsums") == 0
    # and turning on both should ensure two checksums generated
    m.fields[0]['uniq'] = True
    m.fields[1]['uniq'] = True
    m.flush()
    f.flush()
    assert deck.db.scalar("select count() from fsums") == 2
Example #10
0
def test_selective():
    deck = getEmptyDeck()
    f = deck.newFact()
    f['Front'] = u"1"
    f.tags = ["one", "three"]
    deck.addFact(f)
    f = deck.newFact()
    f['Front'] = u"2"
    f.tags = ["two", "three", "four"]
    deck.addFact(f)
    f = deck.newFact()
    f['Front'] = u"3"
    f.tags = ["one", "two", "three", "four"]
    deck.addFact(f)
    assert len(deck.selTagFids(["one"], [])) == 2
    assert len(deck.selTagFids(["three"], [])) == 3
    assert len(deck.selTagFids([], ["three"])) == 0
    assert len(deck.selTagFids(["one"], ["three"])) == 0
    assert len(deck.selTagFids(["one"], ["two"])) == 1
    assert len(deck.selTagFids(["two", "three"], [])) == 3
    assert len(deck.selTagFids(["two", "three"], ["one"])) == 1
    assert len(deck.selTagFids(["one", "three"], ["two", "four"])) == 1
    deck.setGroupForTags(["three"], [], 3)
    assert deck.db.scalar("select count() from cards where gid = 3") == 3
    deck.setGroupForTags(["one"], [], 2)
    assert deck.db.scalar("select count() from cards where gid = 2") == 2
Example #11
0
def test_findDupes():
    deck = getEmptyDeck()
    f = deck.newNote()
    f['Front'] = u'foo'
    f['Back'] = u'bar'
    deck.addNote(f)
    f2 = deck.newNote()
    f2['Front'] = u'baz'
    f2['Back'] = u'bar'
    deck.addNote(f2)
    f3 = deck.newNote()
    f3['Front'] = u'quux'
    f3['Back'] = u'bar'
    deck.addNote(f3)
    f4 = deck.newNote()
    f4['Front'] = u'quuux'
    f4['Back'] = u'nope'
    deck.addNote(f4)
    r = deck.findDupes("Back")
    assert r[0][0] == "bar"
    assert len(r[0][1]) == 3
    # valid search
    r = deck.findDupes("Back", "bar")
    assert r[0][0] == "bar"
    assert len(r[0][1]) == 3
    # excludes everything
    r = deck.findDupes("Back", "invalid")
    assert not r
    # front isn't dupe
    assert deck.findDupes("Front") == []
Example #12
0
def test_chained_mods():
    d = getEmptyDeck()
    d.models.setCurrent(d.models.byName("Cloze"))
    m = d.models.current()
    mm = d.models

    #We replace the default Cloze template
    t = mm.newTemplate("ChainedCloze")
    t['qfmt'] = "{{cloze:text:Text}}"
    t['afmt'] = "{{text:cloze:Text}}"  #independent of the order of mods
    mm.addTemplate(m, t)
    mm.save(m)
    d.models.remTemplate(m, m['tmpls'][0])

    f = d.newNote()
    q1 = '<span style=\"color:red\">phrase</span>'
    a1 = '<b>sentence</b>'
    q2 = '<span style=\"color:red\">en chaine</span>'
    a2 = '<i>chained</i>'
    f['Text'] = "This {{c1::%s::%s}} demonstrates {{c1::%s::%s}} clozes." % (
        q1, a1, q2, a2)
    assert d.addNote(f) == 1
    assert "This <span class=cloze>[sentence]</span> demonstrates <span class=cloze>[chained]</span> clozes." in f.cards(
    )[0].q()
    assert "This <span class=cloze>phrase</span> demonstrates <span class=cloze>en chaine</span> clozes." in f.cards(
    )[0].a()
Example #13
0
def test_basic():
    deck = getEmptyDeck()
    # we start with a standard deck
    assert len(deck.decks.decks) == 1
    # it should have an id of 1
    assert deck.decks.name(1)
    # create a new deck
    parentId = deck.decks.id("new deck")
    assert parentId
    assert len(deck.decks.decks) == 2
    # should get the same id
    assert deck.decks.id("new deck") == parentId
    # we start with the default deck selected
    assert deck.decks.selected() == 1
    assert deck.decks.active() == [1]
    # we can select a different deck
    deck.decks.select(parentId)
    assert deck.decks.selected() == parentId
    assert deck.decks.active() == [parentId]
    # let's create a child
    childId = deck.decks.id("new deck::child")
    # it should have been added to the active list
    assert deck.decks.selected() == parentId
    assert deck.decks.active() == [parentId, childId]
    # we can select the child individually too
    deck.decks.select(childId)
    assert deck.decks.selected() == childId
    assert deck.decks.active() == [childId]
Example #14
0
def test_anki2():
    global srcNotes, srcCards
    # get the deck to import
    tmp = getUpgradeDeckPath()
    u = Upgrader()
    src = u.upgrade(tmp)
    srcpath = src.path
    srcNotes = src.noteCount()
    srcCards = src.cardCount()
    srcRev = src.db.scalar("select count() from revlog")
    # add a media file for testing
    open(os.path.join(src.media.dir(), "foo.jpg"), "w").write("foo")
    src.close()
    # create a new empty deck
    dst = getEmptyDeck()
    # import src into dst
    imp = Anki2Importer(dst, srcpath)
    imp.run()
    def check():
        assert dst.noteCount() == srcNotes
        assert dst.cardCount() == srcCards
        assert srcRev == dst.db.scalar("select count() from revlog")
        mids = [int(x) for x in dst.models.models.keys()]
        assert not dst.db.scalar(
            "select count() from notes where mid not in "+ids2str(mids))
        assert not dst.db.scalar(
            "select count() from cards where nid not in (select id from notes)")
        assert not dst.db.scalar(
            "select count() from revlog where cid not in (select id from cards)")
        assert dst.fixIntegrity()[0].startswith("Database rebuilt")
    check()
    # importing should be idempotent
    imp.run()
    check()
    assert len(os.listdir(dst.media.dir())) == 1
Example #15
0
def test_ordcycle():
    d = getEmptyDeck()
    # add two more templates and set second active
    m = d.models.current()
    mm = d.models
    t = mm.newTemplate("Reverse")
    t['qfmt'] = "{{Back}}"
    t['afmt'] = "{{Front}}"
    mm.addTemplate(m, t)
    t = mm.newTemplate("f2")
    t['qfmt'] = "{{Front}}"
    t['afmt'] = "{{Back}}"
    mm.addTemplate(m, t)
    mm.save(m)
    # create a new note; it should have 3 cards
    f = d.newNote()
    f['Front'] = "1"
    f['Back'] = "1"
    d.addNote(f)
    assert d.cardCount() == 3
    d.reset()
    # ordinals should arrive in order
    assert d.sched.getCard().ord == 0
    assert d.sched.getCard().ord == 1
    assert d.sched.getCard().ord == 2
Example #16
0
def test_anki1_diffmodels():
    # create a new empty deck
    dst = getEmptyDeck()
    # import the 1 card version of the model
    tmp = getUpgradeDeckPath("diffmodels1.anki")
    imp = Anki1Importer(dst, tmp)
    imp.run()
    before = dst.noteCount()
    # repeating the process should do nothing
    imp = Anki1Importer(dst, tmp)
    imp.run()
    assert before == dst.noteCount()
    # then the 2 card version
    tmp = getUpgradeDeckPath("diffmodels2.anki")
    imp = Anki1Importer(dst, tmp)
    imp.run()
    after = dst.noteCount()
    # as the model schemas differ, should have been imported as new model
    assert after == before + 1
    # repeating the process should do nothing
    beforeModels = len(dst.models.all())
    imp = Anki1Importer(dst, tmp)
    imp.run()
    after = dst.noteCount()
    assert after == before + 1
    assert beforeModels == len(dst.models.all())
Example #17
0
def test_findReplace():
    deck = getEmptyDeck()
    f = deck.newNote()
    f['Front'] = u'foo'
    f['Back'] = u'bar'
    deck.addNote(f)
    f2 = deck.newNote()
    f2['Front'] = u'baz'
    f2['Back'] = u'foo'
    deck.addNote(f2)
    nids = [f.id, f2.id]
    # should do nothing
    assert deck.findReplace(nids, "abc", "123") == 0
    # global replace
    assert deck.findReplace(nids, "foo", "qux") == 2
    f.load()
    assert f['Front'] == "qux"
    f2.load()
    assert f2['Back'] == "qux"
    # single field replace
    assert deck.findReplace(nids, "qux", "foo", field="Front") == 1
    f.load()
    assert f['Front'] == "foo"
    f2.load()
    assert f2['Back'] == "qux"
    # regex replace
    assert deck.findReplace(nids, "B.r", "reg") == 0
    f.load()
    assert f['Back'] != "reg"
    assert deck.findReplace(nids, "B.r", "reg", regex=True) == 1
    f.load()
    assert f['Back'] == "reg"
Example #18
0
def test_counts():
    d = getEmptyDeck()
    # add a second group
    assert d.groupId("new group") == 2
    # for each card type
    for type in range(3):
        # and each of the groups
        for gid in (1, 2):
            # create a new fact
            f = d.newFact()
            f['Front'] = u"one"
            d.addFact(f)
            c = f.cards()[0]
            # set type/gid
            c.type = type
            c.queue = type
            c.gid = gid
            c.due = 0
            c.flush()
    d.reset()
    # with the default settings, there's no count limit
    assert d.sched.counts() == (2, 2, 2)
    # check limit to one group
    d.qconf['groups'] = [1]
    d.reset()
    assert d.sched.counts() == (1, 1, 1)
    # we don't need to build the queue to get the counts
    assert d.sched.allCounts() == (2, 2, 2)
    assert d.sched.selCounts() == (1, 1, 1)
    assert d.sched.allCounts() == (2, 2, 2)
Example #19
0
def test_findDupes():
    deck = getEmptyDeck()
    f = deck.newNote()
    f['Front'] = u'foo'
    f['Back'] = u'bar'
    deck.addNote(f)
    f2 = deck.newNote()
    f2['Front'] = u'baz'
    f2['Back'] = u'bar'
    deck.addNote(f2)
    f3 = deck.newNote()
    f3['Front'] = u'quux'
    f3['Back'] = u'bar'
    deck.addNote(f3)
    f4 = deck.newNote()
    f4['Front'] = u'quuux'
    f4['Back'] = u'nope'
    deck.addNote(f4)
    r = deck.findDupes("Back")
    assert r[0][0] == "bar"
    assert len(r[0][1]) == 3
    # valid search
    r = deck.findDupes("Back", "bar")
    assert r[0][0] == "bar"
    assert len(r[0][1]) == 3
    # excludes everything
    r = deck.findDupes("Back", "invalid")
    assert not r
    # front isn't dupe
    assert deck.findDupes("Front") == []
Example #20
0
def test_gendeck():
    d = getEmptyDeck()
    cloze = d.models.byName("Cloze")
    d.models.setCurrent(cloze)
    f = d.newNote()
    f['Text'] = u'{{c1::one}}'
    d.addNote(f)
    assert d.cardCount() == 1
    assert f.cards()[0].did == 1
    # set the model to a new default deck
    newId = d.decks.id("new")
    cloze['did'] = newId
    d.models.save(cloze)
    # a newly generated card should share the first card's deck
    f['Text'] += u'{{c2::two}}'
    f.flush()
    assert f.cards()[1].did == 1
    # and same with multiple cards
    f['Text'] += u'{{c3::three}}'
    f.flush()
    assert f.cards()[2].did == 1
    # if one of the cards is in a different deck, it should revert to the
    # model default
    c = f.cards()[1]
    c.did = newId
    c.flush()
    f['Text'] += u'{{c4::four}}'
    f.flush()
    assert f.cards()[3].did == newId
Example #21
0
def test_findReplace():
    deck = getEmptyDeck()
    f = deck.newFact()
    f['Front'] = u'foo'
    f['Back'] = u'bar'
    deck.addFact(f)
    f2 = deck.newFact()
    f2['Front'] = u'baz'
    f2['Back'] = u'foo'
    deck.addFact(f2)
    fids = [f.id, f2.id]
    # should do nothing
    assert deck.findReplace(fids, "abc", "123") == 0
    # global replace
    assert deck.findReplace(fids, "foo", "qux") == 2
    f.load(); assert f['Front'] == "qux"
    f2.load(); assert f2['Back'] == "qux"
    # single field replace
    assert deck.findReplace(fids, "qux", "foo", field="Front") == 1
    f.load(); assert f['Front'] == "foo"
    f2.load(); assert f2['Back'] == "qux"
    # regex replace
    assert deck.findReplace(fids, "B.r", "reg") == 0
    f.load(); assert f['Back'] != "reg"
    assert deck.findReplace(fids, "B.r", "reg", regex=True) == 1
    f.load(); assert f['Back'] == "reg"
Example #22
0
def test_templates():
    d = getEmptyDeck()
    m = d.currentModel()
    m.templates[1]['actv'] = True
    m.flush()
    f = d.newFact()
    f['Front'] = u'1'
    f['Back'] = u'2'
    d.addFact(f)
    assert d.cardCount() == 2
    (c, c2) = f.cards()
    # first card should have first ord
    assert c.ord == 0
    assert c2.ord == 1
    # switch templates
    m.moveTemplate(c.template(), 1)
    c.load(); c2.load()
    assert c.ord == 1
    assert c2.ord == 0
    # removing a template should delete its cards
    m.delTemplate(m.templates[0])
    assert d.cardCount() == 1
    # and should have updated the other cards' ordinals
    c = f.cards()[0]
    assert c.ord == 0
    stripHTML(c.q()) == "2"
Example #23
0
def test_anki2_updates():
    # create a new empty deck
    dst = getEmptyDeck()
    tmp = getUpgradeDeckPath("update1.apkg")
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    assert imp.dupes == 0
    assert imp.added == 1
    assert imp.updated == 0
    # importing again should be idempotent
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    assert imp.dupes == 1
    assert imp.added == 0
    assert imp.updated == 0
    # importing a newer note should update
    assert dst.noteCount() == 1
    assert dst.db.scalar("select flds from notes").startswith("hello")
    tmp = getUpgradeDeckPath("update2.apkg")
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    assert imp.dupes == 1
    assert imp.added == 0
    assert imp.updated == 1
    assert dst.noteCount() == 1
    assert dst.db.scalar("select flds from notes").startswith("goodbye")
Example #24
0
def test_gendeck():
    d = getEmptyDeck()
    cloze = d.models.byName("Cloze")
    d.models.setCurrent(cloze)
    f = d.newNote()
    f['Text'] = u'{{c1::one}}'
    d.addNote(f)
    assert d.cardCount() == 1
    assert f.cards()[0].did == 1
    # set the model to a new default deck
    newId = d.decks.id("new")
    cloze['did'] = newId
    d.models.save(cloze)
    # a newly generated card should share the first card's deck
    f['Text'] += u'{{c2::two}}'
    f.flush()
    assert f.cards()[1].did == 1
    # and same with multiple cards
    f['Text'] += u'{{c3::three}}'
    f.flush()
    assert f.cards()[2].did == 1
    # if one of the cards is in a different deck, it should revert to the
    # model default
    c = f.cards()[1]
    c.did = newId
    c.flush()
    f['Text'] += u'{{c4::four}}'
    f.flush()
    assert f.cards()[3].did == newId
Example #25
0
def test_genrem():
    d = getEmptyDeck()
    f = d.newNote()
    f['Front'] = u'1'
    f['Back'] = u''
    d.addNote(f)
    assert len(f.cards()) == 1
    m = d.models.current()
    mm = d.models
    # adding a new template should automatically create cards
    t = mm.newTemplate("rev")
    t['qfmt'] = '{{Front}}'
    t['afmt'] = ""
    mm.addTemplate(m, t)
    mm.save(m, templates=True)
    assert len(f.cards()) == 2
    # if the template is changed to remove cards, they'll be removed
    t['qfmt'] = "{{Back}}"
    mm.save(m, templates=True)
    assert len(f.cards()) == 1
    # if we add to the note, a card should be automatically generated
    f.load()
    f['Back'] = "1"
    f.flush()
    assert len(f.cards()) == 2
    # deleteion calls a hook to let the user abort the delete. let's abort it:
    def abort(val, *args):
        return False
    addHook("remEmptyCards", abort)
    f['Back'] = ""
    f.flush()
    assert len(f.cards()) == 2
Example #26
0
def test_remove():
    deck = getEmptyDeck()
    # create a new deck, and add a note/card to it
    g1 = deck.decks.id("g1")
    f = deck.newNote()
    f['Front'] = u"1"
    f.model()['did'] = g1
    deck.addNote(f)
    c = f.cards()[0]
    assert c.did == g1
    # by default deleting the deck leaves the cards with an invalid did
    assert deck.cardCount() == 1
    deck.decks.rem(g1)
    assert deck.cardCount() == 1
    c.load()
    assert c.did == g1
    # but if we try to get it, we get the default
    assert deck.decks.name(c.did) == "[no deck]"
    # let's create another deck and explicitly set the card to it
    g2 = deck.decks.id("g2")
    c.did = g2; c.flush()
    # this time we'll delete the card/note too
    deck.decks.rem(g2, cardsToo=True)
    assert deck.cardCount() == 0
    assert deck.noteCount() == 0
Example #27
0
def test_ordcycle():
    d = getEmptyDeck()
    # add two more templates and set second active
    m = d.models.current()
    mm = d.models
    t = mm.newTemplate("Reverse")
    t["qfmt"] = "{{Back}}"
    t["afmt"] = "{{Front}}"
    mm.addTemplate(m, t)
    t = mm.newTemplate("f2")
    t["qfmt"] = "{{Front}}"
    t["afmt"] = "{{Back}}"
    mm.addTemplate(m, t)
    mm.save(m)
    # create a new note; it should have 3 cards
    f = d.newNote()
    f["Front"] = "1"
    f["Back"] = "1"
    d.addNote(f)
    assert d.cardCount() == 3
    d.reset()
    # ordinals should arrive in order
    assert d.sched.getCard().ord == 0
    assert d.sched.getCard().ord == 1
    assert d.sched.getCard().ord == 2
Example #28
0
def test_learn_collapsed():
    d = getEmptyDeck()
    # add 2 notes
    f = d.newNote()
    f['Front'] = u"1"
    f = d.addNote(f)
    f = d.newNote()
    f['Front'] = u"2"
    f = d.addNote(f)
    # set as a learn card and rebuild queues
    d.db.execute("update cards set queue=0, type=0")
    d.reset()
    # should get '1' first
    c = d.sched.getCard()
    assert c.q().endswith("1")
    # pass it so it's due in 10 minutes
    d.sched.answerCard(c, 2)
    # get the other card
    c = d.sched.getCard()
    assert c.q().endswith("2")
    # fail it so it's due in 1 minute
    d.sched.answerCard(c, 1)
    # we shouldn't get the same card again
    c = d.sched.getCard()
    assert not c.q().endswith("2")
Example #29
0
def test_op():
    d = getEmptyDeck()
    # should have no undo by default
    assert not d.undoName()
    # let's adjust a study option
    d.save("studyopts")
    d.conf['abc'] = 5
    # it should be listed as undoable
    assert d.undoName() == "studyopts"
    # with about 5 minutes until it's clobbered
    assert time.time() - d._lastSave < 1
    # undoing should restore the old value
    d.undo()
    assert not d.undoName()
    assert 'abc' not in d.conf
    # an (auto)save will clear the undo
    d.save("foo")
    assert d.undoName() == "foo"
    d.save()
    assert not d.undoName()
    # and a review will, too
    d.save("add")
    f = d.newNote()
    f['Front'] = u"one"
    d.addNote(f)
    d.reset()
    assert d.undoName() == "add"
    c = d.sched.getCard()
    d.sched.answerCard(c, 2)
    assert d.undoName() == "Review"
Example #30
0
def test_newLimits():
    d = getEmptyDeck()
    # add some notes
    g2 = d.decks.id("Default::foo")
    for i in range(30):
        f = d.newNote()
        f['Front'] = str(i)
        if i > 4:
            f.model()['did'] = g2
        d.addNote(f)
    # give the child deck a different configuration
    c2 = d.decks.confId("new conf")
    d.decks.setConf(d.decks.get(g2), c2)
    d.reset()
    # both confs have defaulted to a limit of 20
    assert d.sched.newCount == 20
    # first card we get comes from parent
    c = d.sched.getCard()
    assert c.did == 1
    # limit the parent to 10 cards, meaning we get 10 in total
    conf1 = d.decks.confForDid(1)
    conf1['new']['perDay'] = 10
    d.reset()
    assert d.sched.newCount == 10
    # if we limit child to 4, we should get 9
    conf2 = d.decks.confForDid(g2)
    conf2['new']['perDay'] = 4
    d.reset()
    assert d.sched.newCount == 9
Example #31
0
def test_basic():
    deck = getEmptyDeck()
    # we start with a standard deck
    assert len(deck.decks.decks) == 1
    # it should have an id of 1
    assert deck.decks.name(1)
    # create a new deck
    parentId = deck.decks.id("new deck")
    assert parentId
    assert len(deck.decks.decks) == 2
    # should get the same id
    assert deck.decks.id("new deck") == parentId
    # we start with the default deck selected
    assert deck.decks.selected() == 1
    assert deck.decks.active() == [1]
    # we can select a different deck
    deck.decks.select(parentId)
    assert deck.decks.selected() == parentId
    assert deck.decks.active() == [parentId]
    # let's create a child
    childId = deck.decks.id("new deck::child")
    # it should have been added to the active list
    assert deck.decks.selected() == parentId
    assert deck.decks.active() == [parentId, childId]
    # we can select the child individually too
    deck.decks.select(childId)
    assert deck.decks.selected() == childId
    assert deck.decks.active() == [childId]
Example #32
0
def test_timing():
    d = getEmptyDeck()
    # add a few review cards, due today
    for i in range(5):
        f = d.newNote()
        f['Front'] = "num" + str(i)
        d.addNote(f)
        c = f.cards()[0]
        c.type = 2
        c.queue = 2
        c.due = 0
        c.flush()
    # fail the first one
    d.reset()
    c = d.sched.getCard()
    # set a a fail delay of 1 second so we don't have to wait
    d.sched._cardConf(c)['lapse']['delays'][0] = 1 / 60.0
    d.sched.answerCard(c, 1)
    # the next card should be another review
    c = d.sched.getCard()
    assert c.queue == 2
    # but if we wait for a second, the failed card should come back
    time.sleep(1)
    c = d.sched.getCard()
    assert c.queue == 1
Example #33
0
def test_overdue_lapse():
    # disabled in commit 3069729776990980f34c25be66410e947e9d51a2
    return
    d = getEmptyDeck()
    # add a note
    f = d.newNote()
    f['Front'] = u"one"
    d.addNote(f)
    # simulate a review that was lapsed and is now due for its normal review
    c = f.cards()[0]
    c.type = 2
    c.queue = 1
    c.due = -1
    c.odue = -1
    c.factor = 2500
    c.left = 2002
    c.ivl = 0
    c.flush()
    d.sched._clearOverdue = False
    # checkpoint
    d.save()
    d.sched.reset()
    assert d.sched.counts() == (0, 2, 0)
    c = d.sched.getCard()
    d.sched.answerCard(c, 3)
    # it should be due tomorrow
    assert c.due == d.sched.today + 1
    # revert to before
    d.rollback()
    d.sched._clearOverdue = True
    # with the default settings, the overdue card should be removed from the
    # learning queue
    d.sched.reset()
    assert d.sched.counts() == (0, 0, 1)
Example #34
0
def test_anki2_updates():
    # create a new empty deck
    dst = getEmptyDeck()
    tmp = getUpgradeDeckPath("update1.apkg")
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    assert imp.dupes == 0
    assert imp.added == 1
    assert imp.updated == 0
    # importing again should be idempotent
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    assert imp.dupes == 1
    assert imp.added == 0
    assert imp.updated == 0
    # importing a newer note should update
    assert dst.noteCount() == 1
    assert dst.db.scalar("select flds from notes").startswith("hello")
    tmp = getUpgradeDeckPath("update2.apkg")
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    assert imp.dupes == 1
    assert imp.added == 0
    assert imp.updated == 1
    assert dst.noteCount() == 1
    assert dst.db.scalar("select flds from notes").startswith("goodbye")
Example #35
0
def test_learn_collapsed():
    d = getEmptyDeck()
    # add 2 notes
    f = d.newNote()
    f['Front'] = u"1"
    f = d.addNote(f)
    f = d.newNote()
    f['Front'] = u"2"
    f = d.addNote(f)
    # set as a learn card and rebuild queues
    d.db.execute("update cards set queue=0, type=0")
    d.reset()
    # should get '1' first
    c = d.sched.getCard()
    assert c.q().endswith("1")
    # pass it so it's due in 10 minutes
    d.sched.answerCard(c, 2)
    # get the other card
    c = d.sched.getCard()
    assert c.q().endswith("2")
    # fail it so it's due in 1 minute
    d.sched.answerCard(c, 1)
    # we shouldn't get the same card again
    c = d.sched.getCard()
    assert not c.q().endswith("2")
Example #36
0
def test_newLimits():
    d = getEmptyDeck()
    # add some notes
    g2 = d.decks.id("Default::foo")
    for i in range(30):
        f = d.newNote()
        f['Front'] = str(i)
        if i > 4:
            f.did = g2
        d.addNote(f)
    # give the child deck a different configuration
    c2 = d.decks.confId("new conf")
    d.decks.setConf(d.decks.get(g2), c2)
    d.reset()
    # both confs have defaulted to a limit of 20
    assert d.sched.newCount == 20
    # first card we get comes from parent
    c = d.sched.getCard()
    assert c.did == 1
    # limit the parent to 10 cards, meaning we get 10 in total
    conf1 = d.decks.confForDid(1)
    conf1['new']['perDay'] = 10
    d.reset()
    assert d.sched.newCount == 10
    # if we limit child to 4, we should get 9
    conf2 = d.decks.confForDid(g2)
    conf2['new']['perDay'] = 4
    d.reset()
    assert d.sched.newCount == 9
Example #37
0
def test_templates():
    d = getEmptyDeck()
    m = d.models.current(); mm = d.models
    t = mm.newTemplate("Reverse")
    t['qfmt'] = "{{Back}}"
    t['afmt'] = "{{Front}}"
    mm.addTemplate(m, t)
    mm.save(m)
    f = d.newNote()
    f['Front'] = u'1'
    f['Back'] = u'2'
    d.addNote(f)
    assert d.cardCount() == 2
    (c, c2) = f.cards()
    # first card should have first ord
    assert c.ord == 0
    assert c2.ord == 1
    # switch templates
    d.models.moveTemplate(m, c.template(), 1)
    c.load(); c2.load()
    assert c.ord == 1
    assert c2.ord == 0
    # removing a template should delete its cards
    assert d.models.remTemplate(m, m['tmpls'][0])
    assert d.cardCount() == 1
    # and should have updated the other cards' ordinals
    c = f.cards()[0]
    assert c.ord == 0
    assert stripHTML(c.q()) == "1"
    # it shouldn't be possible to orphan notes by removing templates
    t = mm.newTemplate(m)
    mm.addTemplate(m, t)
    assert not d.models.remTemplate(m, m['tmpls'][0])
Example #38
0
def test_availOrds():
    d = getEmptyDeck()
    m = d.models.current(); mm = d.models
    t = m['tmpls'][0]
    f = d.newNote()
    f['Front'] = "1"
    # simple templates
    assert mm.availOrds(m, joinFields(f.fields)) == [0]
    t['qfmt'] = "{{Back}}"
    mm.save(m, templates=True)
    assert not mm.availOrds(m, joinFields(f.fields))
    # AND
    t['qfmt'] = "{{#Front}}{{#Back}}{{Front}}{{/Back}}{{/Front}}"
    mm.save(m, templates=True)
    assert not mm.availOrds(m, joinFields(f.fields))
    t['qfmt'] = "{{#Front}}\n{{#Back}}\n{{Front}}\n{{/Back}}\n{{/Front}}"
    mm.save(m, templates=True)
    assert not mm.availOrds(m, joinFields(f.fields))
    # OR
    t['qfmt'] = "{{Front}}\n{{Back}}"
    mm.save(m, templates=True)
    assert mm.availOrds(m, joinFields(f.fields)) == [0]
    t['Front'] = ""
    t['Back'] = "1"
    assert mm.availOrds(m, joinFields(f.fields)) == [0]
Example #39
0
def test_timing():
    d = getEmptyDeck()
    # add a few review cards, due today
    for i in range(5):
        f = d.newNote()
        f['Front'] = "num"+str(i)
        d.addNote(f)
        c = f.cards()[0]
        c.type = 2
        c.queue = 2
        c.due = 0
        c.flush()
    # fail the first one
    d.reset()
    c = d.sched.getCard()
    # set a a fail delay of 1 second so we don't have to wait
    d.sched._cardConf(c)['lapse']['delays'][0] = 1/60.0
    d.sched.answerCard(c, 1)
    # the next card should be another review
    c = d.sched.getCard()
    assert c.queue == 2
    # but if we wait for a second, the failed card should come back
    time.sleep(1)
    c = d.sched.getCard()
    assert c.queue == 1
Example #40
0
def test_csv():
    deck = getEmptyDeck()
    file = unicode(os.path.join(testDir, "support/text-2fields.txt"))
    i = TextImporter(deck, file)
    i.initMapping()
    i.run()
    # four problems - too many & too few fields, a missing front, and a
    # duplicate entry
    assert len(i.log) == 5
    assert i.total == 5
    # if we run the import again, it should update instead
    i.run()
    assert len(i.log) == 10
    assert i.total == 5
    # but importing should not clobber tags if they're unmapped
    n = deck.getNote(deck.db.scalar("select id from notes"))
    n.addTag("test")
    n.flush()
    i.run()
    n.load()
    assert n.tags == ['test']
    # if add-only mode, count will be 0
    i.importMode = 1
    i.run()
    assert i.total == 0
    # and if dupes mode, will reimport everything
    assert deck.cardCount() == 5
    i.importMode = 2
    i.run()
    # includes repeated field
    assert i.total == 6
    assert deck.cardCount() == 11
    deck.close()
Example #41
0
def test_anki2_diffmodels():
    # create a new empty deck
    dst = getEmptyDeck()
    # import the 1 card version of the model
    tmp = getUpgradeDeckPath("diffmodels2-1.apkg")
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    before = dst.noteCount()
    # repeating the process should do nothing
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    assert before == dst.noteCount()
    # then the 2 card version
    tmp = getUpgradeDeckPath("diffmodels2-2.apkg")
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    after = dst.noteCount()
    # as the model schemas differ, should have been imported as new model
    assert after == before + 1
    # and the new model should have both cards
    assert dst.cardCount() == 3
    # repeating the process should do nothing
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    after = dst.noteCount()
    assert after == before + 1
    assert dst.cardCount() == 3
Example #42
0
def test_overdue_lapse():
    d = getEmptyDeck()
    # add a note
    f = d.newNote()
    f['Front'] = u"one"
    d.addNote(f)
    # simulate a review that was lapsed and is now due for its normal review
    c = f.cards()[0]
    c.type = 2
    c.queue = 1
    c.due = -1
    c.odue = -1
    c.factor = 2500
    c.left = 2
    c.ivl = 0
    c.flush()
    d.sched._clearOverdue = False
    # checkpoint
    d.save()
    d.sched.reset()
    assert d.sched.counts() == (0, 2, 0)
    c = d.sched.getCard()
    d.sched.answerCard(c, 3)
    # it should be due tomorrow
    assert c.due == d.sched.today + 1
    # revert to before
    d.rollback()
    d.sched._clearOverdue = True
    # with the default settings, the overdue card should be removed from the
    # learning queue
    d.sched.reset()
    assert d.sched.counts() == (0, 0, 1)
Example #43
0
def test_anki2():
    global srcNotes, srcCards
    # get the deck to import
    tmp = getUpgradeDeckPath()
    u = Upgrader()
    src = u.upgrade(tmp)
    srcpath = src.path
    srcNotes = src.noteCount()
    srcCards = src.cardCount()
    srcRev = src.db.scalar("select count() from revlog")
    # add a media file for testing
    open(os.path.join(src.media.dir(), "_foo.jpg"), "w").write("foo")
    src.close()
    # create a new empty deck
    dst = getEmptyDeck()
    # import src into dst
    imp = Anki2Importer(dst, srcpath)
    imp.run()
    def check():
        assert dst.noteCount() == srcNotes
        assert dst.cardCount() == srcCards
        assert srcRev == dst.db.scalar("select count() from revlog")
        mids = [int(x) for x in dst.models.models.keys()]
        assert not dst.db.scalar(
            "select count() from notes where mid not in "+ids2str(mids))
        assert not dst.db.scalar(
            "select count() from cards where nid not in (select id from notes)")
        assert not dst.db.scalar(
            "select count() from revlog where cid not in (select id from cards)")
        assert dst.fixIntegrity()[0].startswith("Database rebuilt")
    check()
    # importing should be idempotent
    imp.run()
    check()
    assert len(os.listdir(dst.media.dir())) == 1
Example #44
0
def test_availOrds():
    d = getEmptyDeck()
    m = d.models.current(); mm = d.models
    t = m['tmpls'][0]
    f = d.newNote()
    f['Front'] = "1"
    # simple templates
    assert mm.availOrds(m, joinFields(f.fields)) == [0]
    t['qfmt'] = "{{Back}}"
    mm.save(m, templates=True)
    assert not mm.availOrds(m, joinFields(f.fields))
    # AND
    t['qfmt'] = "{{#Front}}{{#Back}}{{Front}}{{/Back}}{{/Front}}"
    mm.save(m, templates=True)
    assert not mm.availOrds(m, joinFields(f.fields))
    t['qfmt'] = "{{#Front}}\n{{#Back}}\n{{Front}}\n{{/Back}}\n{{/Front}}"
    mm.save(m, templates=True)
    assert not mm.availOrds(m, joinFields(f.fields))
    # OR
    t['qfmt'] = "{{Front}}\n{{Back}}"
    mm.save(m, templates=True)
    assert mm.availOrds(m, joinFields(f.fields)) == [0]
    t['Front'] = ""
    t['Back'] = "1"
    assert mm.availOrds(m, joinFields(f.fields)) == [0]
Example #45
0
def test_templates():
    d = getEmptyDeck()
    m = d.models.current(); mm = d.models
    t = mm.newTemplate("Reverse")
    t['qfmt'] = "{{Back}}"
    t['afmt'] = "{{Front}}"
    mm.addTemplate(m, t)
    mm.save(m)
    f = d.newNote()
    f['Front'] = u'1'
    f['Back'] = u'2'
    d.addNote(f)
    assert d.cardCount() == 2
    (c, c2) = f.cards()
    # first card should have first ord
    assert c.ord == 0
    assert c2.ord == 1
    # switch templates
    d.models.moveTemplate(m, c.template(), 1)
    c.load(); c2.load()
    assert c.ord == 1
    assert c2.ord == 0
    # removing a template should delete its cards
    assert d.models.remTemplate(m, m['tmpls'][0])
    assert d.cardCount() == 1
    # and should have updated the other cards' ordinals
    c = f.cards()[0]
    assert c.ord == 0
    assert stripHTML(c.q()) == "1"
    # it shouldn't be possible to orphan notes by removing templates
    t = mm.newTemplate(m)
    mm.addTemplate(m, t)
    assert not d.models.remTemplate(m, m['tmpls'][0])
Example #46
0
def test_anki1_diffmodels():
    # create a new empty deck
    dst = getEmptyDeck()
    # import the 1 card version of the model
    tmp = getUpgradeDeckPath("diffmodels1.anki")
    imp = Anki1Importer(dst, tmp)
    imp.run()
    before = dst.noteCount()
    # repeating the process should do nothing
    imp = Anki1Importer(dst, tmp)
    imp.run()
    assert before == dst.noteCount()
    # then the 2 card version
    tmp = getUpgradeDeckPath("diffmodels2.anki")
    imp = Anki1Importer(dst, tmp)
    imp.run()
    after = dst.noteCount()
    # as the model schemas differ, should have been imported as new model
    assert after == before + 1
    # repeating the process should do nothing
    beforeModels = len(dst.models.all())
    imp = Anki1Importer(dst, tmp)
    imp.run()
    after = dst.noteCount()
    assert after == before + 1
    assert beforeModels == len(dst.models.all())
Example #47
0
def test_csv():
    deck = getEmptyDeck()
    file = unicode(os.path.join(testDir, "support/text-2fields.txt"))
    i = TextImporter(deck, file)
    i.initMapping()
    i.run()
    # four problems - too many & too few fields, a missing front, and a
    # duplicate entry
    assert len(i.log) == 5
    assert i.total == 5
    # if we run the import again, it should update instead
    i.run()
    assert len(i.log) == 5
    assert i.total == 5
    # but importing should not clobber tags if they're unmapped
    n = deck.getNote(deck.db.scalar("select id from notes"))
    n.addTag("test")
    n.flush()
    i.run()
    n.load()
    assert n.tags == ['test']
    # if add-only mode, count will be 0
    i.importMode = 1
    i.run()
    assert i.total == 0
    # and if dupes mode, will reimport everything
    assert deck.cardCount() == 5
    i.importMode = 2
    i.run()
    # includes repeated field
    assert i.total == 6
    assert deck.cardCount() == 11
    deck.close()
Example #48
0
def test_anki2_diffmodels():
    # create a new empty deck
    dst = getEmptyDeck()
    # import the 1 card version of the model
    tmp = getUpgradeDeckPath("diffmodels2-1.apkg")
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    before = dst.noteCount()
    # repeating the process should do nothing
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    assert before == dst.noteCount()
    # then the 2 card version
    tmp = getUpgradeDeckPath("diffmodels2-2.apkg")
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    after = dst.noteCount()
    # as the model schemas differ, should have been imported as new model
    assert after == before + 1
    # and the new model should have both cards
    assert dst.cardCount() == 3
    # repeating the process should do nothing
    imp = AnkiPackageImporter(dst, tmp)
    imp.run()
    after = dst.noteCount()
    assert after == before + 1
    assert dst.cardCount() == 3
Example #49
0
def test_suspend():
    d = getEmptyDeck()
    f = d.newNote()
    f['Front'] = u"one"
    d.addNote(f)
    c = f.cards()[0]
    # suspending
    d.reset()
    assert d.sched.getCard()
    d.sched.suspendCards([c.id])
    d.reset()
    assert not d.sched.getCard()
    # unsuspending
    d.sched.unsuspendCards([c.id])
    d.reset()
    assert d.sched.getCard()
    # should cope with rev cards being relearnt
    c.due = 0; c.ivl = 100; c.type = 2; c.queue = 2; c.flush()
    d.reset()
    c = d.sched.getCard()
    d.sched.answerCard(c, 1)
    assert c.due >= time.time()
    assert c.queue == 1
    assert c.type == 2
    d.sched.suspendCards([c.id])
    d.sched.unsuspendCards([c.id])
    c.load()
    assert c.queue == 2
    assert c.type == 2
    assert c.due == 1
Example #50
0
def test_suspended():
    # create a new empty deck
    dst = getEmptyDeck()
    # import the 1 card version of the model
    tmp = getUpgradeDeckPath("suspended12.anki")
    imp = Anki1Importer(dst, tmp)
    imp.run()
    assert dst.db.scalar("select due from cards") < 0
Example #51
0
def test_deckTree():
    d = getEmptyDeck()
    d.decks.id("new::b::c")
    d.decks.id("new2")
    # new should not appear twice in tree
    names = [x[0] for x in d.sched.deckDueTree()]
    names.remove("new")
    assert "new" not in names
Example #52
0
def test_deckTree():
    d = getEmptyDeck()
    d.decks.id("new::b::c")
    d.decks.id("new2")
    # new should not appear twice in tree
    names = [x[0] for x in d.sched.deckDueTree()]
    names.remove("new")
    assert "new" not in names
Example #53
0
def test_suspended():
    # create a new empty deck
    dst = getEmptyDeck()
    # import the 1 card version of the model
    tmp = getUpgradeDeckPath("suspended12.anki")
    imp = Anki1Importer(dst, tmp)
    imp.run()
    assert dst.db.scalar("select due from cards") < 0
Example #54
0
def test_text():
    d = getEmptyDeck()
    m = d.models.current()
    m['tmpls'][0]['qfmt'] = "{{text:Front}}"
    d.models.save(m)
    f = d.newNote()
    f['Front'] = u'hello<b>world'
    d.addNote(f)
    assert "helloworld" in f.cards()[0].q()
Example #55
0
def test_modelDelete():
    deck = getEmptyDeck()
    f = deck.newNote()
    f['Front'] = u'1'
    f['Back'] = u'2'
    deck.addNote(f)
    assert deck.cardCount() == 1
    deck.models.rem(deck.models.current())
    assert deck.cardCount() == 0
Example #56
0
def test_learn_day():
    d = getEmptyDeck()
    # add a note
    f = d.newNote()
    f['Front'] = u"one"
    f = d.addNote(f)
    d.sched.reset()
    c = d.sched.getCard()
    d.sched._cardConf(c)['new']['delays'] = [1, 10, 1440, 2880]
    # pass it
    d.sched.answerCard(c, 2)
    # two reps to graduate, 1 more today
    assert c.left % 1000 == 3
    assert c.left / 1000 == 1
    assert d.sched.counts() == (0, 1, 0)
    c = d.sched.getCard()
    ni = d.sched.nextIvl
    assert ni(c, 2) == 86400
    # answering it will place it in queue 3
    d.sched.answerCard(c, 2)
    assert c.due == d.sched.today + 1
    assert c.queue == 3
    assert not d.sched.getCard()
    # for testing, move it back a day
    c.due -= 1
    c.flush()
    d.reset()
    assert d.sched.counts() == (0, 1, 0)
    c = d.sched.getCard()
    # nextIvl should work
    assert ni(c, 2) == 86400 * 2
    # if we fail it, it should be back in the correct queue
    d.sched.answerCard(c, 1)
    assert c.queue == 1
    d.undo()
    d.reset()
    c = d.sched.getCard()
    d.sched.answerCard(c, 2)
    # simulate the passing of another two days
    c.due -= 2
    c.flush()
    d.reset()
    # the last pass should graduate it into a review card
    assert ni(c, 2) == 86400
    d.sched.answerCard(c, 2)
    assert c.queue == c.type == 2
    # if the lapse step is tomorrow, failing it should handle the counts
    # correctly
    c.due = 0
    c.flush()
    d.reset()
    assert d.sched.counts() == (0, 0, 1)
    d.sched._cardConf(c)['lapse']['delays'] = [1440]
    c = d.sched.getCard()
    d.sched.answerCard(c, 1)
    assert c.queue == 3
    assert d.sched.counts() == (0, 0, 0)
Example #57
0
def test_mnemo():
    deck = getEmptyDeck()
    file = unicode(os.path.join(testDir, "support/mnemo.db"))
    i = MnemosyneImporter(deck, file)
    i.run()
    assert deck.cardCount() == 7
    assert "a_longer_tag" in deck.tags.all()
    assert deck.db.scalar("select count() from cards where type = 0") == 1
    deck.close()
Example #58
0
def test_modelDelete():
    deck = getEmptyDeck()
    f = deck.newNote()
    f['Front'] = u'1'
    f['Back'] = u'2'
    deck.addNote(f)
    assert deck.cardCount() == 1
    deck.models.rem(deck.models.current())
    assert deck.cardCount() == 0