コード例 #1
0
    def _checkDeckTree(self):
        decks = self.col.decks.all()
        decks.sort(key=operator.itemgetter('name'))
        names = set()

        for deck in decks:
            # two decks with the same name?
            if deck['name'] in names:
                self.col.log("fix duplicate deck name", deck['name'])
                deck['name'] += "%d" % intTime(1000)
                self.save(deck)

            # ensure no sections are blank
            if not all(deck['name'].split("::")):
                self.col.log("fix deck with missing sections", deck['name'])
                deck['name'] = "recovered%d" % intTime(1000)
                self.save(deck)

            # immediate parent must exist
            if "::" in deck['name']:
                immediateParent = "::".join(deck['name'].split("::")[:-1])
                if immediateParent not in names:
                    self.col.log("fix deck with missing parent", deck['name'])
                    self._ensureParents(deck['name'])
                    names.add(immediateParent)

            names.add(deck['name'])
コード例 #2
0
ファイル: noteimp.py プロジェクト: SzymonPajzert/anki_lib
 def updateData(self, n, id, sflds):
     self._ids.append(id)
     if not self.processFields(n, sflds):
         return
     if self._tagsMapped:
         self.col.tags.register(n.tags)
         tags = self.col.tags.join(n.tags)
         return [
             intTime(),
             self.col.usn(), n.fieldsStr, tags, id, n.fieldsStr, tags
         ]
     else:
         return [intTime(), self.col.usn(), n.fieldsStr, id, n.fieldsStr]
コード例 #3
0
ファイル: sched.py プロジェクト: SzymonPajzert/anki_lib
 def unsuspendCards(self, ids):
     "Unsuspend cards."
     self.col.log(ids)
     self.col.db.execute(
         "update cards set queue=type,mod=?,usn=? "
         "where queue = -1 and id in " + ids2str(ids), intTime(),
         self.col.usn())
コード例 #4
0
ファイル: collection.py プロジェクト: SzymonPajzert/anki_lib
    def flush(self, mod=None):
        "Flush state to DB, updating mod time."
        self.mod = intTime(1000) if mod is None else mod
        self.db.execute(
            """update col set
crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""", self.crt, self.mod,
            self.scm, self.dty, self._usn, self.ls, json.dumps(self.conf))
コード例 #5
0
    def remTemplate(self, m, template):
        "False if removing template would leave orphan notes."
        assert len(m['tmpls']) > 1
        # find cards using this template
        ord = m['tmpls'].index(template)
        cids = self.col.db.list("""
select c.id from cards c, notes f where c.nid=f.id and mid = ? and ord = ?""",
                                 m['id'], ord)
        # all notes with this template must have at least two cards, or we
        # could end up creating orphaned notes
        if self.col.db.scalar("""
select nid, count() from cards where
nid in (select nid from cards where id in %s)
group by nid
having count() < 2
limit 1""" % ids2str(cids)):
            return False
        # ok to proceed; remove cards
        self.col.modSchema(check=True)
        self.col.remCards(cids)
        # shift ordinals
        self.col.db.execute("""
update cards set ord = ord - 1, usn = ?, mod = ?
 where nid in (select id from notes where mid = ?) and ord > ?""",
                             self.col.usn(), intTime(), m['id'], ord)
        m['tmpls'].remove(template)
        self._updateTemplOrds(m)
        self.save(m)
        return True
コード例 #6
0
 def id(self, name, create=True, type=None):
     "Add a deck with NAME. Reuse deck if already exists. Return id as int."
     if type is None:
         type = defaultDeck
     name = name.replace('"', '')
     name = unicodedata.normalize("NFC", name)
     for id, g in list(self.decks.items()):
         if unicodedata.normalize("NFC", g['name'].lower()) == name.lower():
             return int(id)
     if not create:
         return None
     g = copy.deepcopy(type)
     if "::" in name:
         # not top level; ensure all parents exist
         name = self._ensureParents(name)
     g['name'] = name
     while 1:
         id = intTime(1000)
         if str(id) not in self.decks:
             break
     g['id'] = id
     self.decks[str(id)] = g
     self.save(g)
     self.maybeAddToActive()
     runHook("newDeck")
     return int(id)
コード例 #7
0
ファイル: collection.py プロジェクト: SzymonPajzert/anki_lib
 def _undoReview(self):
     data = self._undo[2]
     wasLeech = self._undo[3]
     c = data.pop()
     if not data:
         self.clearUndo()
     # remove leech tag if it didn't have it before
     if not wasLeech and c.note().hasTag("leech"):
         c.note().delTag("leech")
         c.note().flush()
     # write old data
     c.flush()
     # and delete revlog entry
     last = self.db.scalar(
         "select id from revlog where cid = ? "
         "order by id desc limit 1", c.id)
     self.db.execute("delete from revlog where id = ?", last)
     # restore any siblings
     self.db.execute(
         "update cards set queue=type,mod=?,usn=? where queue=-2 and nid=?",
         intTime(), self.usn(), c.nid)
     # and finally, update daily counts
     n = 1 if c.queue == 3 else c.queue
     type = ("new", "lrn", "rev")[n]
     self.sched._updateStats(c, type, -1)
     self.sched.reps -= 1
     return c.id
コード例 #8
0
ファイル: collection.py プロジェクト: SzymonPajzert/anki_lib
 def modSchema(self, check):
     "Mark schema modified. Call this first so user can abort if necessary."
     if not self.schemaChanged():
         if check and not runFilter("modSchema", True):
             raise AnkiError("abortSchemaMod")
     self.scm = intTime(1000)
     self.setMod()
コード例 #9
0
 def _changeCards(self, nids, oldModel, newModel, map):
     d = []
     deleted = []
     for (cid, ord) in self.col.db.execute(
         "select id, ord from cards where nid in "+ids2str(nids)):
         # if the src model is a cloze, we ignore the map, as the gui
         # doesn't currently support mapping them
         if oldModel['type'] == MODEL_CLOZE:
             new = ord
             if newModel['type'] != MODEL_CLOZE:
                 # if we're mapping to a regular note, we need to check if
                 # the destination ord is valid
                 if len(newModel['tmpls']) <= ord:
                     new = None
         else:
             # mapping from a regular note, so the map should be valid
             new = map[ord]
         if new is not None:
             d.append(dict(
                 cid=cid,new=new,u=self.col.usn(),m=intTime()))
         else:
             deleted.append(cid)
     self.col.db.executemany(
         "update cards set ord=:new,usn=:u,mod=:m where id=:cid",
         d)
     self.col.remCards(deleted)
コード例 #10
0
ファイル: sched.py プロジェクト: SzymonPajzert/anki_lib
    def _burySiblings(self, card):
        toBury = []
        nconf = self._newConf(card)
        buryNew = nconf.get("bury", True)
        rconf = self._revConf(card)
        buryRev = rconf.get("bury", True)
        # loop through and remove from queues
        for cid, queue in self.col.db.execute(
                """
select id, queue from cards where nid=? and id!=?
and (queue=0 or (queue=2 and due<=?))""", card.nid, card.id, self.today):
            if queue == 2:
                if buryRev:
                    toBury.append(cid)
                # if bury disabled, we still discard to give same-day spacing
                try:
                    self._revQueue.remove(cid)
                except ValueError:
                    pass
            else:
                # if bury disabled, we still discard to give same-day spacing
                if buryNew:
                    toBury.append(cid)
                try:
                    self._newQueue.remove(cid)
                except ValueError:
                    pass
        # then bury
        if toBury:
            self.col.db.execute(
                "update cards set queue=-2,mod=?,usn=? where id in " +
                ids2str(toBury), intTime(), self.col.usn())
            self.col.log(toBury)
コード例 #11
0
 def fix(row):
     nids.append(row[0])
     return {
         'id': row[0],
         't': fn(tags, row[1]),
         'n': intTime(),
         'u': self.col.usn()
     }
コード例 #12
0
ファイル: sched.py プロジェクト: SzymonPajzert/anki_lib
    def buryCards(self, cids):
        self.col.log(cids)
        self.remFromDyn(cids)
        self.removeLrn(cids)
        self.col.db.execute(
            """
update cards set queue=-2,mod=?,usn=? where id in """ + ids2str(cids),
            intTime(), self.col.usn())
コード例 #13
0
ファイル: sched.py プロジェクト: SzymonPajzert/anki_lib
 def unburyCardsForDeck(self):
     sids = ids2str(self.col.decks.active())
     self.col.log(
         self.col.db.list(
             "select id from cards where queue = -2 and did in %s" % sids))
     self.col.db.execute(
         "update cards set mod=?,usn=?,queue=type where queue = -2 and did in %s"
         % sids, intTime(), self.col.usn())
コード例 #14
0
ファイル: sched.py プロジェクト: SzymonPajzert/anki_lib
 def suspendCards(self, ids):
     "Suspend cards."
     self.col.log(ids)
     self.remFromDyn(ids)
     self.removeLrn(ids)
     self.col.db.execute(
         "update cards set queue=-1,mod=?,usn=? where id in " +
         ids2str(ids), intTime(), self.col.usn())
コード例 #15
0
ファイル: sync.py プロジェクト: SzymonPajzert/anki_lib
 def meta(self):
     return dict(
         mod=self.col.mod,
         scm=self.col.scm,
         usn=self.col._usn,
         ts=intTime(),
         musn=0,
         msg="",
         cont=True
     )
コード例 #16
0
 def save(self, m=None, templates=False):
     "Mark M modified if provided, and schedule registry flush."
     if m and m['id']:
         m['mod'] = intTime()
         m['usn'] = self.col.usn()
         self._updateRequired(m)
         if templates:
             self._syncTemplates(m)
     self.changed = True
     runHook("newModel")
コード例 #17
0
 def _transformFields(self, m, fn):
     # model hasn't been added yet?
     if not m['id']:
         return
     r = []
     for (id, flds) in self.col.db.execute(
         "select id, flds from notes where mid = ?", m['id']):
         r.append((joinFields(fn(splitFields(flds))),
                   intTime(), self.col.usn(), id))
     self.col.db.executemany(
         "update notes set flds=?,mod=?,usn=? where id = ?", r)
コード例 #18
0
 def new(self, name):
     "Create a new model, save it in the registry, and return it."
     # caller should call save() after modifying
     m = defaultModel.copy()
     m['name'] = name
     m['mod'] = intTime()
     m['flds'] = []
     m['tmpls'] = []
     m['tags'] = []
     m['id'] = None
     return m
コード例 #19
0
def _getColVars(db):
    import anki_lib.collection
    import anki_lib.decks
    g = copy.deepcopy(anki_lib.decks.defaultDeck)
    g['id'] = 1
    g['name'] = _("Default")
    g['conf'] = 1
    g['mod'] = intTime()
    gc = copy.deepcopy(anki_lib.decks.defaultConf)
    gc['id'] = 1
    return g, gc, anki_lib.collection.defaultConf.copy()
コード例 #20
0
ファイル: sched.py プロジェクト: SzymonPajzert/anki_lib
    def _lrnForDeck(self, did):
        cnt = self.col.db.scalar(
            """
select sum(left/1000) from
(select left from cards where did = ? and queue = 1 and due < ? limit ?)""",
            did,
            intTime() + self.col.conf['collapseTime'], self.reportLimit) or 0
        return cnt + self.col.db.scalar(
            """
select count() from
(select 1 from cards where did = ? and queue = 3
and due <= ? limit ?)""", did, self.today, self.reportLimit)
コード例 #21
0
ファイル: sched.py プロジェクト: SzymonPajzert/anki_lib
 def _leftToday(self, delays, left, now=None):
     "The number of steps that can be completed by the day cutoff."
     if not now:
         now = intTime()
     delays = delays[-left:]
     ok = 0
     for i in range(len(delays)):
         now += delays[i] * 60
         if now > self.dayCutoff:
             break
         ok = i
     return ok + 1
コード例 #22
0
ファイル: find.py プロジェクト: SzymonPajzert/anki_lib
def findReplace(col, nids, src, dst, regex=False, field=None, fold=True):
    "Find and replace fields in a note."
    mmap = {}
    if field:
        for m in col.models.all():
            for f in m['flds']:
                if f['name'].lower() == field.lower():
                    mmap[str(m['id'])] = f['ord']
        if not mmap:
            return 0
    # find and gather replacements
    if not regex:
        src = re.escape(src)
    if fold:
        src = "(?i)" + src
    regex = re.compile(src)

    def repl(str):
        return re.sub(regex, dst, str)

    d = []
    snids = ids2str(nids)
    nids = []
    for nid, mid, flds in col.db.execute(
            "select id, mid, flds from notes where id in " + snids):
        origFlds = flds
        # does it match?
        sflds = splitFields(flds)
        if field:
            try:
                ord = mmap[str(mid)]
                sflds[ord] = repl(sflds[ord])
            except KeyError:
                # note doesn't have that field
                continue
        else:
            for c in range(len(sflds)):
                sflds[c] = repl(sflds[c])
        flds = joinFields(sflds)
        if flds != origFlds:
            nids.append(nid)
            d.append(dict(nid=nid, flds=flds, u=col.usn(), m=intTime()))
    if not d:
        return 0
    # replace
    col.db.executemany(
        "update notes set flds=:flds,mod=:m,usn=:u where id=:nid", d)
    col.updateFieldCache(nids)
    col.genCards(nids)
    return len(d)
コード例 #23
0
 def confId(self, name, cloneFrom=None):
     "Create a new configuration and return id."
     if cloneFrom is None:
         cloneFrom = defaultConf
     c = copy.deepcopy(cloneFrom)
     while 1:
         id = intTime(1000)
         if str(id) not in self.dconf:
             break
     c['id'] = id
     c['name'] = name
     self.dconf[str(id)] = c
     self.save(c)
     return id
コード例 #24
0
ファイル: cards.py プロジェクト: SzymonPajzert/anki_lib
    def flushSched(self):
        self.mod = intTime()
        self.usn = self.col.usn()
        # bug checks
        if self.queue == 2 and self.odue and not self.col.decks.isDyn(
                self.did):
            runHook("odueInvalid")
        assert self.due < 4294967296
        self.col.db.execute(
            """update cards set
mod=?, usn=?, type=?, queue=?, due=?, ivl=?, factor=?, reps=?,
lapses=?, left=?, odue=?, odid=?, did=? where id = ?""", self.mod, self.usn,
            self.type, self.queue, self.due, self.ivl, self.factor, self.reps,
            self.lapses, self.left, self.odue, self.odid, self.did, self.id)
        self.col.log(self)
コード例 #25
0
ファイル: cards.py プロジェクト: SzymonPajzert/anki_lib
    def flush(self):
        self.mod = intTime()
        self.usn = self.col.usn()
        # bug check
        if self.queue == 2 and self.odue and not self.col.decks.isDyn(
                self.did):
            runHook("odueInvalid")
        assert self.due < 4294967296
        self.col.db.execute(
            """
insert or replace into cards values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", self.id, self.nid,
            self.did, self.ord, self.mod, self.usn, self.type, self.queue,
            self.due, self.ivl, self.factor, self.reps, self.lapses, self.left,
            self.odue, self.odid, self.flags, self.data)
        self.col.log(self)
コード例 #26
0
ファイル: collection.py プロジェクト: SzymonPajzert/anki_lib
    def log(self, *args, **kwargs):
        if not self._debugLog:
            return

        def customRepr(x):
            if isinstance(x, str):
                return x
            return pprint.pformat(x)

        path, num, fn, y = traceback.extract_stack(limit=2 +
                                                   kwargs.get("stack", 0))[0]
        buf = "[%s] %s:%s(): %s" % (intTime(), os.path.basename(path), fn,
                                    ", ".join([customRepr(x) for x in args]))
        self._logHnd.write(buf + "\n")
        if devMode:
            print(buf)
コード例 #27
0
ファイル: noteimp.py プロジェクト: SzymonPajzert/anki_lib
 def newData(self, n):
     id = self._nextID
     self._nextID += 1
     self._ids.append(id)
     if not self.processFields(n):
         return
     # note id for card updates later
     for ord, c in list(n.cards.items()):
         self._cards.append((id, ord, c))
     self.col.tags.register(n.tags)
     return [
         id,
         guid64(), self.model['id'],
         intTime(),
         self.col.usn(),
         self.col.tags.join(n.tags), n.fieldsStr, "", "", 0, ""
     ]
コード例 #28
0
 def _changeNotes(self, nids, newModel, map):
     d = []
     nfields = len(newModel['flds'])
     for (nid, flds) in self.col.db.execute(
         "select id, flds from notes where id in "+ids2str(nids)):
         newflds = {}
         flds = splitFields(flds)
         for old, new in list(map.items()):
             newflds[new] = flds[old]
         flds = []
         for c in range(nfields):
             flds.append(newflds.get(c, ""))
         flds = joinFields(flds)
         d.append(dict(nid=nid, flds=flds, mid=newModel['id'],
                   m=intTime(),u=self.col.usn()))
     self.col.db.executemany(
         "update notes set flds=:flds,mid=:mid,mod=:m,usn=:u where id = :nid", d)
     self.col.updateFieldCache(nids)
コード例 #29
0
    def moveTemplate(self, m, template, idx):
        oldidx = m['tmpls'].index(template)
        if oldidx == idx:
            return
        oldidxs = dict((id(t), t['ord']) for t in m['tmpls'])
        m['tmpls'].remove(template)
        m['tmpls'].insert(idx, template)
        self._updateTemplOrds(m)
        # generate change map
        map = []
        for t in m['tmpls']:
            map.append("when ord = %d then %d" % (oldidxs[id(t)], t['ord']))
        # apply
        self.save(m)
        self.col.db.execute("""
update cards set ord = (case %s end),usn=?,mod=? where nid in (
select id from notes where mid = ?)""" % " ".join(map),
                             self.col.usn(), intTime(), m['id'])
コード例 #30
0
ファイル: sched.py プロジェクト: SzymonPajzert/anki_lib
    def removeLrn(self, ids=None):
        "Remove cards from the learning queues."
        if ids:
            extra = " and id in " + ids2str(ids)
        else:
            # benchmarks indicate it's about 10x faster to search all decks
            # with the index than scan the table
            extra = " and did in " + ids2str(self.col.decks.allIds())
        # review cards in relearning
        self.col.db.execute("""
update cards set
due = odue, queue = 2, mod = %d, usn = %d, odue = 0
where queue in (1,3) and type = 2
%s
""" % (intTime(), self.col.usn(), extra))
        # new cards in learning
        self.forgetCards(
            self.col.db.list("select id from cards where queue in (1,3) %s" %
                             extra))