Exemplo n.º 1
0
 def _reqForTemplate(self, m, flds, t):
     a = []
     b = []
     cloze = "cloze" in t['qfmt']
     reqstrs = []
     if cloze:
         # need a cloze-specific filler
         cloze = ""
         nums = re.findall("\{\{cloze:(\d+):", t['qfmt'])
         for n in nums:
             n = int(n)
             cloze += "{{c%d::foo}}" % n
             # record that we require a specific string for generation
             reqstrs.append("{{c%d::" % n)
         return 'all', [], reqstrs
     for f in flds:
         a.append(cloze if cloze else "1")
         b.append("")
     data = [1, 1, m['id'], 1, t['ord'], "", joinFields(a)]
     full = self.col._renderQA(data)['q']
     data = [1, 1, m['id'], 1, t['ord'], "", joinFields(b)]
     empty = self.col._renderQA(data)['q']
     # if full and empty are the same, the template is invalid and there is
     # no way to satisfy it
     if full == empty:
         return "none", [], []
     type = 'all'
     req = []
     for i in range(len(flds)):
         tmp = a[:]
         tmp[i] = ""
         data[6] = joinFields(tmp)
         # if the result is same as empty, field is required
         if self.col._renderQA(data)['q'] == empty:
             req.append(i)
     if req:
         return type, req, reqstrs
     # if there are no required fields, switch to any mode
     type = 'any'
     req = []
     for i in range(len(flds)):
         tmp = b[:]
         tmp[i] = "1"
         data[6] = joinFields(tmp)
         # if not the same as empty, this field can make the card non-blank
         if self.col._renderQA(data)['q'] != empty:
             req.append(i)
     return type, req, reqstrs
Exemplo n.º 2
0
 def _reqForTemplate(self, m, flds, t):
     a = []
     b = []
     cloze = "cloze" in t['qfmt']
     reqstrs = []
     if cloze:
         # need a cloze-specific filler
         cloze = ""
         nums = re.findall("\{\{cloze:(\d+):", t['qfmt'])
         for n in nums:
             n = int(n)
             cloze += "{{c%d::foo}}" % n
             # record that we require a specific string for generation
             reqstrs.append("{{c%d::" % n)
         return 'all', [], reqstrs
     for f in flds:
         a.append(cloze if cloze else "1")
         b.append("")
     data = [1, 1, m['id'], 1, t['ord'], "", joinFields(a)]
     full = self.col._renderQA(data)['q']
     data = [1, 1, m['id'], 1, t['ord'], "", joinFields(b)]
     empty = self.col._renderQA(data)['q']
     # if full and empty are the same, the template is invalid and there is
     # no way to satisfy it
     if full == empty:
         return "none", [], []
     type = 'all'
     req = []
     for i in range(len(flds)):
         tmp = a[:]
         tmp[i] = ""
         data[6] = joinFields(tmp)
         # if the result is same as empty, field is required
         if self.col._renderQA(data)['q'] == empty:
             req.append(i)
     if req:
         return type, req, reqstrs
     # if there are no required fields, switch to any mode
     type = 'any'
     req = []
     for i in range(len(flds)):
         tmp = b[:]
         tmp[i] = "1"
         data[6] = joinFields(tmp)
         # if not the same as empty, this field can make the card non-blank
         if self.col._renderQA(data)['q'] != empty:
             req.append(i)
     return type, req, reqstrs
Exemplo n.º 3
0
 def _transformFields(self, fn):
     self.deck.modSchema()
     r = []
     for (id, flds) in self.deck.db.execute(
         "select id, flds from facts where mid = ?", self.id):
         r.append((joinFields(fn(splitFields(flds))), id))
     self.deck.db.executemany("update facts set flds = ? where id = ?", r)
Exemplo n.º 4
0
    def _changeNotes(self, nids, newModel, map):
        """Change the note whose ids are nid to the model newModel, reorder
        fields according to map. Write the change in the database

        Note that if a field is mapped to nothing, it is lost

        keyword arguments:
        nids -- the list of id of notes to change
        newmodel -- the model of destination of the note
        map -- the dictionnary sending to each fields'ord of the old model a field'ord of the new model
        """
        d = [
        ]  #The list of dictionnaries, containing the information relating to the new cards
        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)
Exemplo n.º 5
0
    def _mungeMedia(self, mid: NotetypeId, fieldsStr: str) -> str:
        fields = splitFields(fieldsStr)

        def repl(match):
            fname = match.group("fname")
            srcData = self._srcMediaData(fname)
            dstData = self._dstMediaData(fname)
            if not srcData:
                # file was not in source, ignore
                return match.group(0)
            # if model-local file exists from a previous import, use that
            name, ext = os.path.splitext(fname)
            lname = f"{name}_{mid}{ext}"
            if self.dst.media.have(lname):
                return match.group(0).replace(fname, lname)
            # if missing or the same, pass unmodified
            elif not dstData or srcData == dstData:
                # need to copy?
                if not dstData:
                    self._writeDstMedia(fname, srcData)
                return match.group(0)
            # exists but does not match, so we need to dedupe
            self._writeDstMedia(lname, srcData)
            return match.group(0).replace(fname, lname)

        for idx, field in enumerate(fields):
            fields[idx] = self.dst.media.transformNames(field, repl)
        return joinFields(fields)
Exemplo n.º 6
0
 def _mungeMedia(self, mid, fields):
     fields = splitFields(fields)
     def repl(match):
         fname = match.group("fname")
         srcData = self._srcMediaData(fname)
         dstData = self._dstMediaData(fname)
         if not srcData:
             # file was not in source, ignore
             return match.group(0)
         # if model-local file exists from a previous import, use that
         name, ext = os.path.splitext(fname)
         lname = "%s_%s%s" % (name, mid, ext)
         if self.dst.media.have(lname):
             return match.group(0).replace(fname, lname)
         # if missing or the same, pass unmodified
         elif not dstData or srcData == dstData:
             # need to copy?
             if not dstData:
                 self._writeDstMedia(fname, srcData)
             return match.group(0)
         # exists but does not match, so we need to dedupe
         self._writeDstMedia(lname, srcData)
         return match.group(0).replace(fname, lname)
     for i in range(len(fields)):
         fields[i] = self.dst.media.transformNames(fields[i], repl)
     return joinFields(fields)
Exemplo n.º 7
0
    def _mungeMedia(self, mid, fields):
        fields = splitFields(fields)

        def repl(match):
            fname = match.group("fname")
            srcData = self._srcMediaData(fname)
            dstData = self._dstMediaData(fname)
            if not srcData:
                # file was not in source, ignore
                return match.group(0)
            # if model-local file exists from a previous import, use that
            name, ext = os.path.splitext(fname)
            lname = "%s_%s%s" % (name, mid, ext)
            if self.dst.media.have(lname):
                return match.group(0).replace(fname, lname)
            # if missing or the same, pass unmodified
            elif not dstData or srcData == dstData:
                # need to copy?
                if not dstData:
                    self._writeDstMedia(fname, srcData)
                return match.group(0)
            # exists but does not match, so we need to dedupe
            self._writeDstMedia(lname, srcData)
            return match.group(0).replace(fname, lname)

        for i in range(len(fields)):
            fields[i] = self.dst.media.transformNames(fields[i], repl)
        return joinFields(fields)
Exemplo n.º 8
0
 def _changeNotes(
     self, nids: List[int], newModel: NoteType, map: Dict[int, Union[None, int]]
 ) -> None:
     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)
Exemplo n.º 9
0
 def _transformFields(self, fn):
     self.deck.modSchema()
     r = []
     for (id, flds) in self.deck.db.execute(
             "select id, flds from facts where mid = ?", self.id):
         r.append((joinFields(fn(splitFields(flds))), id))
     self.deck.db.executemany("update facts set flds = ? where id = ?", r)
Exemplo n.º 10
0
 def _changeNotes(
     self,
     nids: List[anki.notes.NoteId],
     newModel: NotetypeDict,
     map: Dict[int, Union[None, int]],
 ) -> None:
     d = []
     nfields = len(newModel["flds"])
     for (nid, flds) in self.col.db.execute(
             f"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((
             flds,
             newModel["id"],
             intTime(),
             self.col.usn(),
             nid,
         ))
     self.col.db.executemany(
         "update notes set flds=?,mid=?,mod=?,usn=? where id = ?", d)
Exemplo n.º 11
0
def findReplace(
    col: _Collection,
    nids: List[int],
    src: str,
    dst: str,
    regex: bool = False,
    field: Optional[str] = None,
    fold: bool = True,
) -> int:
    "Find and replace fields in a note."
    mmap: Dict[str, Any] = {}
    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)
        dst = dst.replace("\\", "\\\\")
    if fold:
        src = "(?i)" + src
    compiled_re = re.compile(src)

    def repl(s: str):
        return compiled_re.sub(dst, s)

    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)
Exemplo n.º 12
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)
Exemplo n.º 13
0
    def _reqForTemplate(self, m, flds, t):
        """A rule which is supposed to determine whether a card should be
        generated or not according to its fields.

        See ../documentation/templates_generation_rules.md

        """
        a = []
        b = []
        for f in flds:
            a.append("ankiflag")
            b.append("")
        data = [1, 1, m['id'], 1, t['ord'], "", joinFields(a), 0]
        # The html of the card at position ord where each field's content is "ankiflag"
        full = self.col._renderQA(data)['q']
        data = [1, 1, m['id'], 1, t['ord'], "", joinFields(b), 0]
        # The html of the card at position ord where each field's content is the empty string ""
        empty = self.col._renderQA(data)['q']

        # if full and empty are the same, the template is invalid and there is
        # no way to satisfy it
        if full == empty:
            return "none", [], []
        type = 'all'
        req = []
        for i in range(len(flds)):
            tmp = a[:]
            tmp[i] = ""
            data[6] = joinFields(tmp)
            # if no field content appeared, field is required
            if "ankiflag" not in self.col._renderQA(data)['q']:
                req.append(i)
        if req:
            return type, req
        # if there are no required fields, switch to any mode
        type = 'any'
        req = []
        for i in range(len(flds)):
            tmp = b[:]
            tmp[i] = "1"
            data[6] = joinFields(tmp)
            # if not the same as empty, this field can make the card non-blank
            if self.col._renderQA(data)['q'] != empty:
                req.append(i)
        return type, req
Exemplo n.º 14
0
 def findTemplates(self, note):
     "Return (active), non-empty templates."
     model = note.model()
     avail = self.models.availOrds(model, joinFields(note.fields))
     ok = []
     for t in model['tmpls']:
         if t['ord'] in avail:
             ok.append(t)
     return ok
Exemplo n.º 15
0
def findReplace(col,
                nids,
                src,
                dst,
                regex=False,
                field=None,
                fold=True) -> int:
    "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)
        dst = dst.replace("\\", "\\\\")
    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)
Exemplo n.º 16
0
 def findTemplates(self, note):
     "Return (active), non-empty templates."
     ok = []
     model = note.model()
     avail = self.models.availOrds(model, joinFields(note.fields))
     ok = []
     for t in model['tmpls']:
         if t['ord'] in avail:
             ok.append(t)
     return ok
Exemplo n.º 17
0
 def _transformFields(self, m: NoteType, fn: Callable) -> None:
     # 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)
Exemplo n.º 18
0
 def addTab(self, t):
     c = self.connect
     w = QWidget()
     l = QHBoxLayout()
     l.setMargin(0)
     l.setSpacing(3)
     left = QWidget()
     # template area
     tform = aqt.forms.template.Ui_Form()
     tform.setupUi(left)
     tform.label1.setText(u" →")
     tform.label2.setText(u" →")
     tform.labelc1.setText(u" ↗")
     tform.labelc2.setText(u" ↘")
     if self.style().objectName() == "gtk+":
         # gtk+ requires margins in inner layout
         tform.tlayout1.setContentsMargins(0, 11, 0, 0)
         tform.tlayout2.setContentsMargins(0, 11, 0, 0)
         tform.tlayout3.setContentsMargins(0, 11, 0, 0)
     if len(self.cards) > 1:
         tform.groupBox_3.setTitle(_(
             "Styling (shared between cards)"))
     c(tform.front, SIGNAL("textChanged()"), self.saveCard)
     c(tform.css, SIGNAL("textChanged()"), self.saveCard)
     c(tform.back, SIGNAL("textChanged()"), self.saveCard)
     l.addWidget(left, 5)
     # preview area
     right = QWidget()
     pform = aqt.forms.preview.Ui_Form()
     pform.setupUi(right)
     if self.style().objectName() == "gtk+":
         # gtk+ requires margins in inner layout
         pform.frontPrevBox.setContentsMargins(0, 11, 0, 0)
         pform.backPrevBox.setContentsMargins(0, 11, 0, 0)
     # for cloze notes, show that it's one of n cards
     if self.model['type'] == MODEL_CLOZE:
         cnt = len(self.mm.availOrds(
             self.model, joinFields(self.note.fields)))
         for g in pform.groupBox, pform.groupBox_2:
             g.setTitle(g.title() + _(" (1 of %d)") % max(cnt, 1))
     pform.frontWeb = AnkiWebView()
     pform.frontPrevBox.addWidget(pform.frontWeb)
     pform.backWeb = AnkiWebView()
     pform.backPrevBox.addWidget(pform.backWeb)
     def linkClicked(url):
         openLink(url)
     for wig in pform.frontWeb, pform.backWeb:
         wig.page().setLinkDelegationPolicy(
             QWebPage.DelegateExternalLinks)
         c(wig, SIGNAL("linkClicked(QUrl)"), linkClicked)
     l.addWidget(right, 5)
     w.setLayout(l)
     self.forms.append({'tform': tform, 'pform': pform})
     self.tabs.addTab(w, t['name'])
Exemplo n.º 19
0
    def addTab(self, t):
        c = self.connect
        w = QWidget()
        l = QHBoxLayout()
        l.setMargin(0)
        l.setSpacing(3)
        left = QWidget()
        # template area
        tform = aqt.forms.template.Ui_Form()
        tform.setupUi(left)
        tform.label1.setText(u" →")
        tform.label2.setText(u" →")
        tform.labelc1.setText(u" ↗")
        tform.labelc2.setText(u" ↘")
        if self.style().objectName() == "gtk+":
            # gtk+ requires margins in inner layout
            tform.tlayout1.setContentsMargins(0, 11, 0, 0)
            tform.tlayout2.setContentsMargins(0, 11, 0, 0)
            tform.tlayout3.setContentsMargins(0, 11, 0, 0)
        if len(self.cards) > 1:
            tform.groupBox_3.setTitle(_("Styling (shared between cards)"))
        c(tform.front, SIGNAL("textChanged()"), self.saveCard)
        c(tform.css, SIGNAL("textChanged()"), self.saveCard)
        c(tform.back, SIGNAL("textChanged()"), self.saveCard)
        l.addWidget(left, 5)
        # preview area
        right = QWidget()
        pform = aqt.forms.preview.Ui_Form()
        pform.setupUi(right)
        if self.style().objectName() == "gtk+":
            # gtk+ requires margins in inner layout
            pform.frontPrevBox.setContentsMargins(0, 11, 0, 0)
            pform.backPrevBox.setContentsMargins(0, 11, 0, 0)
        # for cloze notes, show that it's one of n cards
        if self.model['type'] == MODEL_CLOZE:
            cnt = len(
                self.mm.availOrds(self.model, joinFields(self.note.fields)))
            for g in pform.groupBox, pform.groupBox_2:
                g.setTitle(g.title() + _(" (1 of %d)") % max(cnt, 1))
        pform.frontWeb = AnkiWebView()
        pform.frontPrevBox.addWidget(pform.frontWeb)
        pform.backWeb = AnkiWebView()
        pform.backPrevBox.addWidget(pform.backWeb)

        def linkClicked(url):
            openLink(url)

        for wig in pform.frontWeb, pform.backWeb:
            wig.page().setLinkDelegationPolicy(QWebPage.DelegateExternalLinks)
            c(wig, SIGNAL("linkClicked(QUrl)"), linkClicked)
        l.addWidget(right, 5)
        w.setLayout(l)
        self.forms.append({'tform': tform, 'pform': pform})
        self.tabs.addTab(w, t['name'])
Exemplo n.º 20
0
 def _transformFields(self, m, fn):
     """TODO"""
     # 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)
Exemplo n.º 21
0
def test_availOrds():
    d = getEmptyCol()
    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]
Exemplo n.º 22
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]
Exemplo n.º 23
0
 def processFields(self, note, fields=None):
     if not fields:
         fields = [""]*len(self.model['flds'])
     for c, f in enumerate(self.mapping):
         if not f:
             continue
         elif f == "_tags":
             note.tags.extend(self.col.tags.split(note.fields[c]))
         else:
             sidx = self._fmap[f][0]
             fields[sidx] = note.fields[c]
     note.fieldsStr = joinFields(fields)
     return self.col.models.availOrds(self.model, note.fieldsStr)
Exemplo n.º 24
0
 def processFields(self, note, fields=None):
     if not fields:
         fields = [""] * len(self.model['flds'])
     for c, f in enumerate(self.mapping):
         if not f:
             continue
         elif f == "_tags":
             note.tags.extend(self.col.tags.split(note.fields[c]))
         else:
             sidx = self._fmap[f][0]
             fields[sidx] = note.fields[c]
     note.fieldsStr = joinFields(fields)
     return self.col.models.availOrds(self.model, note.fieldsStr)
Exemplo n.º 25
0
 def _reqForTemplate(
         self, m: Dict[str, Any], flds: List[str],
         t: Dict[str, Any]) -> Tuple[Union[str, List[int]], ...]:
     a = []
     b = []
     for f in flds:
         a.append("ankiflag")
         b.append("")
     data = [1, 1, m['id'], 1, t['ord'], "", joinFields(a), 0]
     full = self.col._renderQA(data)['q']
     data = [1, 1, m['id'], 1, t['ord'], "", joinFields(b), 0]
     empty = self.col._renderQA(data)['q']
     # if full and empty are the same, the template is invalid and there is
     # no way to satisfy it
     if full == empty:
         return "none", [], []
     type = 'all'
     req = []
     for i in range(len(flds)):
         tmp = a[:]
         tmp[i] = ""
         data[6] = joinFields(tmp)
         # if no field content appeared, field is required
         if "ankiflag" not in self.col._renderQA(data)['q']:
             req.append(i)
     if req:
         return type, req
     # if there are no required fields, switch to any mode
     type = 'any'
     req = []
     for i in range(len(flds)):
         tmp = b[:]
         tmp[i] = "1"
         data[6] = joinFields(tmp)
         # if not the same as empty, this field can make the card non-blank
         if self.col._renderQA(data)['q'] != empty:
             req.append(i)
     return type, req
Exemplo n.º 26
0
 def processFields(self,
                   note: ForeignNote,
                   fields: Optional[List[str]] = None) -> None:
     if not fields:
         fields = [""] * len(self.model["flds"])
     for c, f in enumerate(self.mapping):
         if not f:
             continue
         elif f == "_tags":
             note.tags.extend(self.col.tags.split(note.fields[c]))
         else:
             sidx = self._fmap[f][0]
             fields[sidx] = note.fields[c]
     note.fieldsStr = joinFields(fields)
Exemplo n.º 27
0
    def _transformFields(self, m, fn):
        """For each note of the model m, apply m to the set of field's value,
        and save the note modified.

        fn -- a function taking and returning a list of field."""
        # 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)
Exemplo n.º 28
0
Arquivo: models.py Projeto: Stvad/anki
 def _reqForTemplate(self, m, flds, t):
     a = []
     b = []
     for f in flds:
         a.append("ankiflag")
         b.append("")
     data = [1, 1, m['id'], 1, t['ord'], "", joinFields(a)]
     full = self.col._renderQA(data)['q']
     data = [1, 1, m['id'], 1, t['ord'], "", joinFields(b)]
     empty = self.col._renderQA(data)['q']
     # if full and empty are the same, the template is invalid and there is
     # no way to satisfy it
     if full == empty:
         return "none", [], []
     type = 'all'
     req = []
     for i in range(len(flds)):
         tmp = a[:]
         tmp[i] = ""
         data[6] = joinFields(tmp)
         # if no field content appeared, field is required
         if "ankiflag" not in self.col._renderQA(data)['q']:
             req.append(i)
     if req:
         return type, req
     # if there are no required fields, switch to any mode
     type = 'any'
     req = []
     for i in range(len(flds)):
         tmp = b[:]
         tmp[i] = "1"
         data[6] = joinFields(tmp)
         # if not the same as empty, this field can make the card non-blank
         if self.col._renderQA(data)['q'] != empty:
             req.append(i)
     return type, req
Exemplo n.º 29
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 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)
Exemplo n.º 30
0
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'] == field:
                    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)
Exemplo n.º 31
0
 def addTab(self, t):
     w = QWidget()
     l = QHBoxLayout()
     l.setContentsMargins(0,0,0,0)
     l.setSpacing(3)
     left = QWidget()
     # template area
     tform = aqt.forms.template.Ui_Form()
     tform.setupUi(left)
     tform.label1.setText(" →")
     tform.label2.setText(" →")
     tform.labelc1.setText(" ↗")
     tform.labelc2.setText(" ↘")
     if self.style().objectName() == "gtk+":
         # gtk+ requires margins in inner layout
         tform.tlayout1.setContentsMargins(0, 11, 0, 0)
         tform.tlayout2.setContentsMargins(0, 11, 0, 0)
         tform.tlayout3.setContentsMargins(0, 11, 0, 0)
     if len(self.cards) > 1:
         tform.groupBox_3.setTitle(_(
             "Styling (shared between cards)"))
     tform.front.textChanged.connect(self.saveCard)
     tform.css.textChanged.connect(self.saveCard)
     tform.back.textChanged.connect(self.saveCard)
     l.addWidget(left, 5)
     # preview area
     right = QWidget()
     pform = aqt.forms.preview.Ui_Form()
     pform.setupUi(right)
     if self.style().objectName() == "gtk+":
         # gtk+ requires margins in inner layout
         pform.frontPrevBox.setContentsMargins(0, 11, 0, 0)
         pform.backPrevBox.setContentsMargins(0, 11, 0, 0)
     # for cloze notes, show that it's one of n cards
     if self.model['type'] == MODEL_CLOZE:
         cnt = len(self.mm.availOrds(
             self.model, joinFields(self.note.fields)))
         for g in pform.groupBox, pform.groupBox_2:
             g.setTitle(g.title() + _(" (1 of %d)") % max(cnt, 1))
     pform.frontWeb = AnkiWebView()
     pform.frontPrevBox.addWidget(pform.frontWeb)
     pform.backWeb = AnkiWebView()
     pform.backPrevBox.addWidget(pform.backWeb)
     l.addWidget(right, 5)
     w.setLayout(l)
     self.forms.append({'tform': tform, 'pform': pform})
     self.tabs.addTab(w, t['name'])
Exemplo n.º 32
0
 def addTab(self, t):
     w = QWidget()
     l = QHBoxLayout()
     l.setContentsMargins(0,0,0,0)
     l.setSpacing(3)
     left = QWidget()
     # template area
     tform = aqt.forms.template.Ui_Form()
     tform.setupUi(left)
     tform.label1.setText(" →")
     tform.label2.setText(" →")
     tform.labelc1.setText(" ↗")
     tform.labelc2.setText(" ↘")
     if self.style().objectName() == "gtk+":
         # gtk+ requires margins in inner layout
         tform.tlayout1.setContentsMargins(0, 11, 0, 0)
         tform.tlayout2.setContentsMargins(0, 11, 0, 0)
         tform.tlayout3.setContentsMargins(0, 11, 0, 0)
     if len(self.cards) > 1:
         tform.groupBox_3.setTitle(_(
             "Styling (shared between cards)"))
     tform.front.textChanged.connect(self.saveCard)
     tform.css.textChanged.connect(self.saveCard)
     tform.back.textChanged.connect(self.saveCard)
     l.addWidget(left, 5)
     # preview area
     right = QWidget()
     pform = aqt.forms.preview.Ui_Form()
     pform.setupUi(right)
     if self.style().objectName() == "gtk+":
         # gtk+ requires margins in inner layout
         pform.frontPrevBox.setContentsMargins(0, 11, 0, 0)
         pform.backPrevBox.setContentsMargins(0, 11, 0, 0)
     # for cloze notes, show that it's one of n cards
     if self.model['type'] == MODEL_CLOZE:
         cnt = len(self.mm.availOrds(
             self.model, joinFields(self.note.fields)))
         for g in pform.groupBox, pform.groupBox_2:
             g.setTitle(g.title() + _(" (1 of %d)") % max(cnt, 1))
     pform.frontWeb = AnkiWebView()
     pform.frontPrevBox.addWidget(pform.frontWeb)
     pform.backWeb = AnkiWebView()
     pform.backPrevBox.addWidget(pform.backWeb)
     l.addWidget(right, 5)
     w.setLayout(l)
     self.forms.append({'tform': tform, 'pform': pform})
     self.tabs.addTab(w, t['name'])
Exemplo n.º 33
0
 def _changeFacts(self, fids, newModel, map):
     d = []
     nfields = len(newModel.fields)
     for (fid, flds) in self.deck.db.execute(
         "select id, flds from facts where id in "+ids2str(fids)):
         newflds = {}
         flds = splitFields(flds)
         for old, new in map.items():
             newflds[new] = flds[old]
         flds = []
         for c in range(nfields):
             flds.append(newflds.get(c, ""))
         flds = joinFields(flds)
         d.append(dict(fid=fid, flds=flds, mid=newModel.id))
     self.deck.db.executemany(
         "update facts set flds=:flds, mid=:mid where id = :fid", d)
     self.deck.updateFieldCache(fids)
Exemplo n.º 34
0
    def getNoteData(self, sortId, question, answerDicts, ref, siblings,
                    connections):
        """returns a list of all content needed to create the a new note and
        the media contained in that note in a list"""

        noteList = []
        media = []

        # Set field Reference
        noteList.append('<ul>%s</ul>' % ref)

        # Set field Question
        qtContent, qtMedia = getNodeContent(tagList=self.tagList, tag=question)
        noteList.append(qtContent)
        media.append(qtMedia)

        # Set Answer fields
        aId = 0
        for answerDict in answerDicts:
            if answerDict['isAnswer']:
                aId += 1
                # noinspection PyTypeChecker
                anContent, anMedia = getNodeContent(tagList=self.tagList,
                                                    tag=answerDict['nodeTag'])
                noteList.append(anContent)
                media.append(anMedia)
                answerDict['aId'] = str(aId)

        # noinspection PyShadowingNames
        for i in range(aId, X_MAX_ANSWERS):
            noteList.append('')

        # set field ID
        noteList.append(sortId)

        # set field Meta
        meta = self.getXMindMeta(question=question, answerDicts=answerDicts,
                                 siblings=siblings, connections=connections)
        noteList.append(meta)

        nId = timestampID(self.col.db, "notes")
        noteData = [nId, guid64(), self.model['id'], intTime(), self.col.usn(),
                    self.currentSheetImport['tag'], joinFields(noteList), "",
                    "", 0, ""]

        return noteData, media
Exemplo n.º 35
0
 def maybeSync(self, sheetId, noteList):
     if self.repair:
         existingNotes = list(self.col.db.execute(
             "select id, flds from notes where tags like '%" +
             self.currentSheetImport['tag'].replace(" ", "") + "%'"))
     else:
         existingNotes = getNotesFromSheet(sheetId=sheetId, col=self.col)
     if existingNotes:
         notesToAdd = []
         notesToUpdate = []
         oldQIdList = list(map(lambda n: json.loads(
             splitFields(n[1])[list(X_FLDS.keys()).index('mt')])[
             'questionId'], existingNotes))
         for newNote in noteList:
             newFields = splitFields(newNote[6])
             newMeta = json.loads(newFields[list(X_FLDS.keys()).index('mt')])
             newQId = newMeta['questionId']
             try:
                 if self.repair:
                     print('')
                     newQtxAw = joinFields(newFields[1:22])
                     oldTpl = tuple(
                         filter(lambda n: newQtxAw in n[1], existingNotes))[
                         0]
                     noteId = existingNotes.index(oldTpl)
                 else:
                     noteId = oldQIdList.index(newQId)
                     # if the fields are different, add it to notes to be updated
                 if not existingNotes[noteId][1] == newNote[6]:
                     notesToUpdate.append(
                         [existingNotes[noteId], newNote])
                 del existingNotes[noteId]
                 del oldQIdList[noteId]
             except (ValueError, IndexError):
                 notesToAdd.append(newNote)
         self.addNew(notesToAdd)
         self.log[0][1] += len(notesToAdd)
         self.addUpdates(notesToUpdate)
         self.log[1][1] += len(notesToUpdate)
         self.removeOld(existingNotes)
         self.log[2][1] += len(existingNotes)
         self.col.save()
     else:
         notesToAdd = noteList
         self.addNew(notesToAdd)
         self.log[0][1] += len(notesToAdd)
Exemplo n.º 36
0
 def _changeFacts(self, fids, newModel, map):
     d = []
     nfields = len(newModel.fields)
     for (fid, flds) in self.deck.db.execute(
             "select id, flds from facts where id in " + ids2str(fids)):
         newflds = {}
         flds = splitFields(flds)
         for old, new in map.items():
             newflds[new] = flds[old]
         flds = []
         for c in range(nfields):
             flds.append(newflds.get(c, ""))
         flds = joinFields(flds)
         d.append(dict(fid=fid, flds=flds, mid=newModel.id))
     self.deck.db.executemany(
         "update facts set flds=:flds, mid=:mid where id = :fid", d)
     self.deck.updateFieldCache(fids)
def dump_collection(colpath, includetags, includedecknames):
    sql_attach = 'ATTACH DATABASE "%s" AS db2' % colpath
    mw.col.db.execute(sql_attach)
    sql_names_of_tables = """SELECT name FROM sqlite_master WHERE type='table'"""
    tables = mw.col.db.all(sql_names_of_tables)
    tags_anon = {}
    for t in tables:
        tabname = t[0]
        sql_copy_over = """INSERT OR REPLACE INTO db2.{0} SELECT * FROM {0};""".format(tabname)
        try:
            mw.col.db.execute(sql_copy_over)
        # e.g. the add-on "symbols as you type" inserts another table into the database, which was 
        # recommended in https://apps.ankiweb.net/docs/addons20.html
        except:
            continue  
        else:
            if tabname == "notes":
                for nid, flds, tags in mw.col.db.all("""SELECT id, flds, tags from db2.notes"""):
                    flist = splitFields(flds)
                    for i, v in enumerate(flist):
                        flist[i] = "..."  # len(v) 
                    joined = joinFields(flist)
                    if includetags:
                        tags = ""
                    else:
                        t = tags.strip().split()
                        alist = []
                        for i in t:
                            if i not in tags_anon:
                                tags_anon[i] = randstr(12)
                            alist.append(tags_anon[i])
                        tags = ', tags="' + mw.col.tags.join(mw.col.tags.canonify(alist)) + '"'
                    attrs = 'flds = "{}", sfld = ""{}'.format(joined, tags)
                    mw.col.db.execute("UPDATE db2.notes SET {} WHERE id = {}".format(attrs, nid))
    if not includedecknames:
        decks = mw.col.db.first("select decks from db2.col")[0]
        deckdict = json.loads(decks)
        old_to_new = decknames_to_anon_dict()
        for props in deckdict.values():
            props['name'] = old_to_new[props['name']]
        mw.col.db.execute("update db2.col set decks=?", json.dumps(deckdict))
    if not includetags:
        mw.col.db.execute('UPDATE db2.col SET tags = "{}"')  # clear tag cache
    mw.col.db.commit()
    mw.col.db.execute('DETACH DATABASE db2')
Exemplo n.º 38
0
 def processFields(self,
                   note: ForeignNote,
                   fields: Optional[List[str]] = None) -> None:
     if not fields:
         fields = [""] * len(self.model["flds"])
     for c, f in enumerate(self.mapping):
         if not f:
             continue
         elif f == "_tags":
             note.tags.extend(self.col.tags.split(note.fields[c]))
         else:
             sidx = self._fmap[f][0]
             fields[sidx] = note.fields[c]
     note.fieldsStr = joinFields(fields)
     # temporary fix for the following issue until we can update the code:
     # https://forums.ankiweb.net/t/python-checksum-rust-checksum/8195/16
     if self.col.get_config_bool(Config.Bool.NORMALIZE_NOTE_TEXT):
         note.fieldsStr = unicodedata.normalize("NFC", note.fieldsStr)
Exemplo n.º 39
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 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)
Exemplo n.º 40
0
 def processFields(self,
                   note: ForeignNote,
                   fields: Optional[List[str]] = None) -> Any:
     if not fields:
         fields = [""] * len(self.model['flds'])
     for c, f in enumerate(self.mapping):
         if not f:
             continue
         elif f == "_tags":
             note.tags.extend(self.col.tags.split(note.fields[c]))
         else:
             sidx = self._fmap[f][0]
             fields[sidx] = note.fields[c]
     note.fieldsStr = joinFields(fields)
     ords = self.col.models.availOrds(self.model, note.fieldsStr)
     if not ords:
         self._emptyNotes = True
     return ords
Exemplo n.º 41
0
	def isDuplicate(self, card):
		#query the db directly since Anki's search will not match ':' and '(', eg

		fields = joinFields([card.front, card.back])
		q = 'select id from notes where flds = ?'
		noteIds = mw.col.db.list(q, fields)
		
		if len(noteIds) == 0:
			# no notes match, we are done
			return False
		else:
			# notes match, but are they in the current deck?
			deckId = mw.col.conf['curDeck']
			q = 'select id from cards where nid = ? and did = ?'
			for noteId in noteIds:
				cardIds = mw.col.db.list(q, noteId, deckId)
				if len(cardIds) > 0:
					return True
			return False # matches were found, but not in the current deck
Exemplo n.º 42
0
Arquivo: find.py Projeto: ChYi/libanki
def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
    "Find and replace fields in a fact."
    mmap = {}
    if field:
        for m in deck.models().values():
            for f in m.fields:
                if f['name'] == field:
                    mmap[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 = []
    for fid, mid, flds in deck.db.execute(
            "select id, mid, flds from facts where id in " + ids2str(fids)):
        origFlds = flds
        # does it match?
        sflds = splitFields(flds)
        if field:
            ord = mmap[mid]
            sflds[ord] = repl(sflds[ord])
        else:
            for c in range(len(sflds)):
                sflds[c] = repl(sflds[c])
        flds = joinFields(sflds)
        if flds != origFlds:
            d.append(dict(fid=fid, flds=flds))
    if not d:
        return 0
    # replace
    deck.db.executemany("update facts set flds = :flds where id=:fid", d)
    deck.updateFieldCache(fids)
    return len(d)
Exemplo n.º 43
0
Arquivo: find.py Projeto: ChYi/libanki
def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
    "Find and replace fields in a fact."
    mmap = {}
    if field:
        for m in deck.models().values():
            for f in m.fields:
                if f['name'] == field:
                    mmap[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 = []
    for fid, mid, flds in deck.db.execute(
        "select id, mid, flds from facts where id in "+ids2str(fids)):
        origFlds = flds
        # does it match?
        sflds = splitFields(flds)
        if field:
            ord = mmap[mid]
            sflds[ord] = repl(sflds[ord])
        else:
            for c in range(len(sflds)):
                sflds[c] = repl(sflds[c])
        flds = joinFields(sflds)
        if flds != origFlds:
            d.append(dict(fid=fid, flds=flds))
    if not d:
        return 0
    # replace
    deck.db.executemany("update facts set flds = :flds where id=:fid", d)
    deck.updateFieldCache(fids)
    return len(d)
Exemplo n.º 44
0
def mainBackend(modelName,tag,destField,expr):
# Main backend code called by the GUI to do the replace all/field fill operation 
  # Assign collection to col
  col=mw.col
  # Get the model id corresponding to modelName
  model=findModel(col,modelName)
  # Get the index belonging to destination field
  destFieldIdx=findFieldIdx(model,destField)
  # Run a database query and return list of noteid,fields of notes with specified model ID and tag
  noteid,tags,flds=fetchData(col,model,tag)
  # Render exp for each note
  newflds=[]
  for idx in range(len(flds)):
    flist=splitFields(flds[idx])
    fdic=makeFieldDic(col,model,flist)
    fdic['Tags'] = tags [idx]
    fdic['Type'] = model['name']
    flist[destFieldIdx]=template.render(expr,fdic)
    newflds.append([joinFields(flist),noteid[idx]])      
  # Write the new data to the database
  col.db.executemany("update notes set flds=? where id=?",newflds)
  # Return the number of notes which were updated
  return len(noteid)  
Exemplo n.º 45
0
def mainBackend(modelName, tag, destField, expr):
    # Main backend code called by the GUI to do the replace all/field fill operation
    # Assign collection to col
    col = mw.col
    # Get the model id corresponding to modelName
    model = findModel(col, modelName)
    # Get the index belonging to destination field
    destFieldIdx = findFieldIdx(model, destField)
    # Run a database query and return list of noteid,fields of notes with specified model ID and tag
    noteid, tags, flds = fetchData(col, model, tag)
    # Render exp for each note
    newflds = []
    for idx in range(len(flds)):
        flist = splitFields(flds[idx])
        fdic = makeFieldDic(col, model, flist)
        fdic['Tags'] = tags[idx]
        fdic['Type'] = model['name']
        flist[destFieldIdx] = template.render(expr, fdic)
        newflds.append([joinFields(flist), noteid[idx]])
    # Write the new data to the database
    col.db.executemany("update notes set flds=? where id=?", newflds)
    # Return the number of notes which were updated
    return len(noteid)
Exemplo n.º 46
0
    def updateCompleteDeck(self, data):
        self.startEditing()
        did = self.decks().id(data['deck'])
        self.decks().flush()
        model_manager = self.collection().models
        for _, card in data['cards'].items():
            self.database().execute(
                'replace into cards (id, nid, did, ord, type, queue, due, ivl, factor, reps, lapses, left, '
                'mod, usn, odue, odid, flags, data) '
                'values (' + '?,' * (12 + 6 - 1) + '?)',
                card['id'], card['nid'], did, card['ord'], card['type'], card['queue'], card['due'],
                card['ivl'], card['factor'], card['reps'], card['lapses'], card['left'],
                intTime(), -1, 0, 0, 0, 0
            )
            note = data['notes'][str(card['nid'])]
            tags = self.collection().tags.join(self.collection().tags.canonify(note['tags']))
            self.database().execute(
                'replace into notes(id, mid, tags, flds,'
                'guid, mod, usn, flags, data, sfld, csum) values (' + '?,' * (4 + 7 - 1) + '?)',
                note['id'], note['mid'], tags, joinFields(note['fields']),
                guid64(), intTime(), -1, 0, 0, '', fieldChecksum(note['fields'][0])
            )
            model = data['models'][str(note['mid'])]
            if not model_manager.get(model['id']):
                model_o = model_manager.new(model['name'])
                for field_name in model['fields']:
                    field = model_manager.newField(field_name)
                    model_manager.addField(model_o, field)
                for template_name in model['templateNames']:
                    template = model_manager.newTemplate(template_name)
                    model_manager.addTemplate(model_o, template)
                model_o['id'] = model['id']
                model_manager.update(model_o)
                model_manager.flush()

        self.stopEditing()
Exemplo n.º 47
0
 def joinedFields(self):
     return joinFields(self.fields)
Exemplo n.º 48
0
def updateNotes( allDb ):
    t_0, now, db, TAG   = time.time(), intTime(), mw.col.db, mw.col.tags
    ds, nid2mmi         = [], {}
    N_notes             = db.scalar( 'select count() from notes' )
    mw.progress.start( label='Updating data', max=N_notes, immediate=True )
    fidDb   = allDb.fidDb()
    locDb   = allDb.locDb( recalc=False ) # fidDb() already forces locDb recalc

    # handle secondary databases
    mw.progress.update( label='Creating seen/known/mature from all.db' )
    seenDb      = filterDbByMat( allDb, cfg1('threshold_seen') )
    knownDb     = filterDbByMat( allDb, cfg1('threshold_known') )
    matureDb    = filterDbByMat( allDb, cfg1('threshold_mature') )
    mw.progress.update( label='Loading priority.db' )
    priorityDb  = MorphDb( cfg1('path_priority'), ignoreErrors=True ).db

    if cfg1('saveDbs'):
        mw.progress.update( label='Saving seen/known/mature dbs' )
        seenDb.save( cfg1('path_seen') )
        knownDb.save( cfg1('path_known') )
        matureDb.save( cfg1('path_mature') )

    mw.progress.update( label='Calculating frequency information' )
    pops = [ len( locs ) for locs in allDb.db.values() ]
    pops = [ n for n in pops if n > 1 ]

    mw.progress.update( label='Updating notes' )
    for i,( nid, mid, flds, guid, tags ) in enumerate( db.execute( 'select id, mid, flds, guid, tags from notes' ) ):
        if i % 500 == 0:    mw.progress.update( value=i )
        C = partial( cfg, mid, None )
        if not C('enabled'): continue
        # Get all morphemes for note
        ms = set()
        for fieldName in C('morph_fields'):
            try:
                loc = fidDb[ ( nid, guid, fieldName ) ]
                ms.update( locDb[ loc ] )
            except KeyError: continue
        ms = [ m for m in ms if m.pos not in C('morph_blacklist') ]

        # Determine un-seen/known/mature and i+N
        unseens, unknowns, unmatures, newKnowns = set(), set(), set(), set()
        for m in ms:
            if m not in seenDb.db:      unseens.add( m )
            if m not in knownDb.db:     unknowns.add( m )
            if m not in matureDb.db:    unmatures.add( m )
            if m not in matureDb.db and m in knownDb.db:
                newKnowns.add( m )

        # Determine MMI - Morph Man Index
        N, N_s, N_k, N_m = len( ms ), len( unseens ), len( unknowns ), len( unmatures )

        # Bail early for lite update
        if N_k > 2 and C('only update k+2 and below'): continue

            # average frequency of unknowns (ie. how common the word is within your collection)
        F_k = 0
        for focusMorph in unknowns: # focusMorph used outside loop
            F_k += len( allDb.db[ focusMorph ] )
        F_k_avg = F_k / N_k if N_k > 0 else F_k
        usefulness = F_k_avg

            # add bonus for morphs in priority.db
        isPriority = False
        for focusMorph in unknowns:
            if focusMorph in priorityDb:
                isPriority = True
                usefulness += C('priority.db weight')

            # add bonus for studying recent learned knowns (reinforce)
        for m in newKnowns:
            locs = allDb.db[ m ]
            if locs:
                ivl = min( 1, max( loc.maturity for loc in locs ) )
                usefulness += C('reinforce new vocab weight') / ivl #TODO: maybe average this so it doesnt favor long sentences

        if any( m.pos == u'動詞' for m in unknowns ): #FIXME: this isn't working???
            usefulness += C('verb bonus')

        usefulness = 999 - min( 999, usefulness )

            # difference from optimal length (too little context vs long sentence)
        lenDiff = max( 0, min( 9, abs( C('optimal sentence length') - N ) -2 ) )

            # calculate mmi
        mmi = 10000*N_k + 1000*lenDiff + usefulness
        if C('set due based on mmi'):
            nid2mmi[ nid ] = mmi

        # Fill in various fields/tags on the note based on cfg
        ts, fs = TAG.split( tags ), splitFields( flds )
            # determine card type
        compTag, vocabTag, notReadyTag, alreadyKnownTag, priorityTag = tagNames = C('tag_comprehension'), C('tag_vocab'), C('tag_notReady'), C('tag_alreadyKnown'), C('tag_priority')
        if N_m == 0:    # sentence comprehension card, m+0
            ts = [ compTag ] + [ t for t in ts if t not in [ vocabTag, notReadyTag ] ]
            setField( mid, fs, C('focusMorph'), u'' )
        elif N_k == 1:  # new vocab card, k+1
            ts = [ vocabTag ] + [ t for t in ts if t not in [ compTag, notReadyTag ] ]
            setField( mid, fs, C('focusMorph'), u'%s' % focusMorph.base )
        elif N_k > 1:   # M+1+ and K+2+
            ts = [ notReadyTag ] + [ t for t in ts if t not in [ compTag, vocabTag ] ]

            # set type agnostic fields
        setField( mid, fs, C('k+N'), u'%d' % N_k )
        setField( mid, fs, C('m+N'), u'%d' % N_m )
        setField( mid, fs, C('morphManIndex'), u'%d' % mmi )
        setField( mid, fs, C('unknowns'), u', '.join( u.base for u in unknowns ) )
        setField( mid, fs, C('unmatures'), u', '.join( u.base for u in unmatures ) )
        setField( mid, fs, C('unknownFreq'), u'%d' % F_k_avg )

            # other tags
        if priorityTag in ts:   ts.remove( priorityTag )
        if isPriority:          ts.append( priorityTag )

            # update sql db
        tags_ = TAG.join( TAG.canonify( ts ) )
        flds_ = joinFields( fs )
        if flds != flds_ or tags != tags_:  # only update notes that have changed
            csum = fieldChecksum( fs[0] )
            sfld = stripHTML( fs[ getSortFieldIndex( mid ) ] )
            ds.append( { 'now':now, 'tags':tags_, 'flds':flds_, 'sfld':sfld, 'csum':csum, 'usn':mw.col.usn(), 'nid':nid } )

    mw.progress.update( value=i, label='Updating anki database...' )
    mw.col.db.executemany( 'update notes set tags=:tags, flds=:flds, sfld=:sfld, csum=:csum, mod=:now, usn=:usn where id=:nid', ds )
    TAG.register( tagNames )

    # Now reorder new cards based on MMI
    mw.progress.update( value=i, label='Updating new card ordering...' )
    ds = []
    for ( cid, nid, due ) in db.execute( 'select id, nid, due from cards where type = 0' ):
        if nid in nid2mmi: # owise it was disabled
            due_ = nid2mmi[ nid ]
            if due != due_: # only update cards that have changed
                ds.append( { 'now':now, 'due':due_, 'usn':mw.col.usn(), 'cid':cid } )
    mw.col.db.executemany( 'update cards set due=:due, mod=:now, usn=:usn where id=:cid', ds )
    mw.reset()

    printf( 'Updated notes in %f sec' % ( time.time() - t_0 ) )
    mw.progress.finish()
Exemplo n.º 49
0
 def updateMainArea(self):
     if self._isCloze():
         cnt = len(self.mm.availOrds(
             self.model, joinFields(self.note.fields)))
         for g in self.pform.groupBox, self.pform.groupBox_2:
             g.setTitle(g.title() + _(" (1 of %d)") % max(cnt, 1))
Exemplo n.º 50
0
 def findTemplates(self, note):
     "Return (active), non-empty templates."
     model = note.model()
     avail = self.models.availOrds(model, joinFields(note.fields))
     return self._tmplsFromOrds(model, avail)
Exemplo n.º 51
0
def updateNotes( allDb ):
    t_0, now, db, TAG   = time.time(), intTime(), mw.col.db, mw.col.tags
    ds, nid2mmi         = [], {}
    N_notes             = db.scalar( 'select count() from notes' )
    mw.progress.start( label='Updating data', max=N_notes, immediate=True )
    fidDb   = allDb.fidDb()
    locDb   = allDb.locDb( recalc=False ) # fidDb() already forces locDb recalc

    # read tag names
    compTag, vocabTag, freshTag, notReadyTag, alreadyKnownTag, priorityTag, tooShortTag, tooLongTag = tagNames = jcfg('Tag_Comprehension'), jcfg('Tag_Vocab'), jcfg('Tag_Fresh'), jcfg('Tag_NotReady'), jcfg('Tag_AlreadyKnown'), jcfg('Tag_Priority'), jcfg('Tag_TooShort'), jcfg('Tag_TooLong')
    TAG.register( tagNames )
    badLengthTag = jcfg2().get('Tag_BadLength')

    # handle secondary databases
    mw.progress.update( label='Creating seen/known/mature from all.db' )
    seenDb      = filterDbByMat( allDb, cfg1('threshold_seen') )
    knownDb     = filterDbByMat( allDb, cfg1('threshold_known') )
    matureDb    = filterDbByMat( allDb, cfg1('threshold_mature') )
    mw.progress.update( label='Loading priority.db' )
    priorityDb  = MorphDb( cfg1('path_priority'), ignoreErrors=True ).db

    if cfg1('saveDbs'):
        mw.progress.update( label='Saving seen/known/mature dbs' )
        seenDb.save( cfg1('path_seen') )
        knownDb.save( cfg1('path_known') )
        matureDb.save( cfg1('path_mature') )

    mw.progress.update( label='Updating notes' )
    for i,( nid, mid, flds, guid, tags ) in enumerate( db.execute( 'select id, mid, flds, guid, tags from notes' ) ):
        if i % 500 == 0:    mw.progress.update( value=i )
        C = partial( cfg, mid, None )

        note = mw.col.getNote(nid)
        notecfg = getFilter(note)
        if notecfg is None or not notecfg['Modify']: continue

        # Get all morphemes for note
        morphemes = set()
        for fieldName in notecfg['Fields']:
            try:
                loc = fidDb[ ( nid, guid, fieldName ) ]
                morphemes.update( locDb[ loc ] )
            except KeyError: continue

        # Determine un-seen/known/mature and i+N
        unseens, unknowns, unmatures, newKnowns = set(), set(), set(), set()
        for morpheme in morphemes:
            if morpheme not in seenDb.db:      unseens.add( morpheme )
            if morpheme not in knownDb.db:     unknowns.add( morpheme )
            if morpheme not in matureDb.db:    unmatures.add( morpheme )
            if morpheme not in matureDb.db and morpheme in knownDb.db:
                newKnowns.add( morpheme )

        # Determine MMI - Morph Man Index
        N, N_s, N_k, N_m = len( morphemes ), len( unseens ), len( unknowns ), len( unmatures )

        # Bail early for lite update
        if N_k > 2 and C('only update k+2 and below'): continue

            # average frequency of unknowns (ie. how common the word is within your collection)
        F_k = 0
        for focusMorph in unknowns: # focusMorph used outside loop
            F_k += allDb.frequency(focusMorph)
        F_k_avg = F_k // N_k if N_k > 0 else F_k
        usefulness = F_k_avg

            # add bonus for morphs in priority.db
        isPriority = False
        for focusMorph in unknowns:
            if focusMorph in priorityDb:
                isPriority = True
                usefulness += C('priority.db weight')

            # add bonus for studying recent learned knowns (reinforce)
        for morpheme in newKnowns:
            locs = allDb.db[ morpheme ]
            if locs:
                ivl = min( 1, max( loc.maturity for loc in locs ) )
                usefulness += C('reinforce new vocab weight') // ivl #TODO: maybe average this so it doesnt favor long sentences

        if any( morpheme.pos == u'動詞' for morpheme in unknowns ): #FIXME: this isn't working???
            usefulness += C('verb bonus')

        usefulness = 999 - min( 999, usefulness )

        # difference from optimal length range (too little context vs long sentence)
        lenDiffRaw = min(N - C('min good sentence length'),
                         max(0, N - C('max good sentence length')))
        lenDiff = min(9, abs(lenDiffRaw))

            # calculate mmi
        mmi = 10000*N_k + 1000*lenDiff + usefulness
        if C('set due based on mmi'):
            nid2mmi[ nid ] = mmi

        # Fill in various fields/tags on the note based on cfg
        ts, fs = TAG.split( tags ), splitFields( flds )

        # clear any 'special' tags, the appropriate will be set in the next few lines
        ts = [ t for t in ts if t not in [ notReadyTag, compTag, vocabTag, freshTag ] ]

        # determine card type
        if N_m == 0:    # sentence comprehension card, m+0
            ts = ts + [ compTag ]
            setField( mid, fs, jcfg('Field_FocusMorph'), u'' )
        elif N_k == 1:  # new vocab card, k+1
            ts = ts + [ vocabTag ]
            setField( mid, fs, jcfg('Field_FocusMorph'), u'%s' % focusMorph.base )
        elif N_k > 1:   # M+1+ and K+2+
            ts = ts + [ notReadyTag ]
            setField( mid, fs, jcfg('Field_FocusMorph'), u'')
        elif N_m == 1: # we have k+0, and m+1, so this card does not introduce a new vocabulary -> card for newly learned morpheme
            ts = ts + [ freshTag ]
            setField( mid, fs, jcfg('Field_FocusMorph'), u'%s' % list(unmatures)[0].base)
        else: # only case left: we have k+0, but m+2 or higher, so this card does not introduce a new vocabulary -> card for newly learned morpheme
            ts = ts + [ freshTag ]
            setField( mid, fs, jcfg('Field_FocusMorph'), u'')


            # set type agnostic fields
        setField( mid, fs, jcfg('Field_UnknownMorphCount'), u'%d' % N_k )
        setField( mid, fs, jcfg('Field_UnmatureMorphCount'), u'%d' % N_m )
        setField( mid, fs, jcfg('Field_MorphManIndex'), u'%d' % mmi )
        setField( mid, fs, jcfg('Field_Unknowns'), u', '.join( u.base for u in unknowns ) )
        setField( mid, fs, jcfg('Field_Unmatures'), u', '.join( u.base for u in unmatures ) )
        setField( mid, fs, jcfg('Field_UnknownFreq'), u'%d' % F_k_avg )

            # remove deprecated tag
        if badLengthTag is not None and badLengthTag in ts:
            ts.remove( badLengthTag )

            # other tags
        if priorityTag in ts:   ts.remove( priorityTag )
        if isPriority:          ts.append( priorityTag )

        if tooShortTag in ts:   ts.remove( tooShortTag )
        if lenDiffRaw < 0:      ts.append( tooShortTag )

        if tooLongTag in ts:    ts.remove( tooLongTag )
        if lenDiffRaw > 0:      ts.append( tooLongTag )

        # remove unnecessary tags
        if not jcfg('Option_SetNotRequiredTags'):
            unnecessary = [priorityTag, tooShortTag, tooLongTag]
            ts = [tag for tag in ts if tag not in unnecessary]

            # update sql db
        tags_ = TAG.join( TAG.canonify( ts ) )
        flds_ = joinFields( fs )
        if flds != flds_ or tags != tags_:  # only update notes that have changed
            csum = fieldChecksum( fs[0] )
            sfld = stripHTML( fs[ getSortFieldIndex( mid ) ] )
            ds.append( { 'now':now, 'tags':tags_, 'flds':flds_, 'sfld':sfld, 'csum':csum, 'usn':mw.col.usn(), 'nid':nid } )

    mw.progress.update( value=i, label='Updating anki database...' )
    mw.col.db.executemany( 'update notes set tags=:tags, flds=:flds, sfld=:sfld, csum=:csum, mod=:now, usn=:usn where id=:nid', ds )

    # Now reorder new cards based on MMI
    mw.progress.update( value=i, label='Updating new card ordering...' )
    ds = []

    # "type = 0": new cards
    # "type = 1": learning cards [is supposed to be learning: in my case no learning card had this type]
    # "type = 2": review cards
    for ( cid, nid, due ) in db.execute( 'select id, nid, due from cards where type = 0' ):
        if nid in nid2mmi: # owise it was disabled
            due_ = nid2mmi[ nid ]
            if due != due_: # only update cards that have changed
                ds.append( { 'now':now, 'due':due_, 'usn':mw.col.usn(), 'cid':cid } )
    mw.col.db.executemany( 'update cards set due=:due, mod=:now, usn=:usn where id=:cid', ds )
    mw.reset()

    printf( 'Updated notes in %f sec' % ( time.time() - t_0 ) )
    mw.progress.finish()
    return knownDb
Exemplo n.º 52
0
 def isEmpty(self):
     ords = self.col.models.availOrds(
         self.model(), joinFields(self.note().fields))
     if self.ord not in ords:
         return True
Exemplo n.º 53
0
    def run(self):

        tree = Tree(self.file[:-4])
        queue = tree.parse()
        deck_name = tree.name

        col = self.col
        # Setup a deck
        deck = col.decks.id(deck_name)
        col.decks.select(deck)

        # Make new models
        new_models = []
        old_models = []
        old_model_ids = []
        for note in queue:
            m = col.models.byName(note["ffsModel"]["name"])
            if not m:
                unique = True
                for n in new_models:
                    if note["ffsModel"]["name"] == n["ffsModel"]["name"]:
                        unique = False
                        break
                if unique:
                    new_models.append(note)
            else:
                unique = True
                for n in old_models:
                    if note["ffsModel"]["name"] == n["ffsModel"]["name"]:
                        unique = False
                        break
                if unique:
                    old_models.append(note)
                note["ffsModel"]["id"] = m["id"]
        self.handle_models(new_models, True)

        # Check for updates
        nids = []
        nids_decks = {}
        update = []
        update_nids = []
        add = []
        delete = []
        tags_to_register = ["ffsi:added", "ffsi:changed", "ffsi:owned"]
        addedc = 0
        changedc = 0
        deletedc = 0
        for note in col.db.execute("select * from notes"):
            note = list(note)
            match = None
            deletable = False
            m = col.models.get(note[2])
            tags = col.tags.split(note[5])
            field_map = col.models.fieldMap(m)
            fields = splitFields(note[6])
            if "Filename" not in field_map:
                if col.tags.inList("ffsi:owned", tags):
                    raise ValueError("ffs owned note tampered with.")
                continue
            filename = fields[field_map["Filename"][0]]

            deck_check = filename.split("/")[0]
            if col.tags.inList("ffsi:owned", tags) and deck_name == deck_check:
                deletable = True
            for n in queue:
                om = col.models.byName(n["ffsModel"]["name"])
                if n["Filename"] == filename:
                    flds = []
                    changed = False
                    if "fields" in n["ffsModel"]:
                        flds.append(n["Filename"])
                        for field in n["ffsModel"]["fields"].split():
                            if field not in n:
                                raise ValueError("Note missing model field," + " in file: {0}".format(n["Filename"]))
                            flds.append(n[field])
                    else:
                        for field in col.models.fieldNames(om):
                            flds.append(n[field])
                    if int(note[2]) != int(om["id"]):
                        changed = True
                        old_model_ids.append(note[2])
                        note[2] = int(om["id"])
                    if note[6] != joinFields(flds):
                        changed = True
                        note[6] = joinFields(flds)

                    new_tags = col.tags.addToStr("ffsi:owned", "")
                    note[5] = col.tags.remFromStr("ffsi:added", note[5])
                    note[5] = col.tags.remFromStr("ffsi:changed", note[5])
                    if "tags" in n:
                        for t in n["tags"].split():
                            new_tags = col.tags.addToStr(t, new_tags)
                        tags_to_register.extend(col.tags.split(new_tags))
                    else:
                        new_tags = note[5]
                    if note[5] != new_tags:
                        changed = True

                    if changed:
                        new_tags = col.tags.addToStr("ffsi:changed", new_tags)
                        changedc = changedc + 1
                    note[5] = new_tags
                    update.append(note)
                    update_nids.append(note[0])
                    match = n
            if match:
                queue.remove(match)
            elif deletable:
                delete.append(note[0])
                deletedc = deletedc + 1

        self.handle_models(old_models)

        for note in queue:
            add.append(note)

        col.remNotes(delete)
        col.db.executemany("insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)", update)
        col.genCards(update_nids)
        col.updateFieldCache(update_nids)
        col.tags.registerNotes(update_nids)

        for textfile in add:
            m = col.models.byName(textfile["ffsModel"]["name"])
            col.models.setCurrent(m)
            note = col.newNote()

            new_tags = ["ffsi:owned", "ffsi:added"]
            if "tags" in textfile:
                custom_tags = textfile["tags"].split()
                new_tags.extend(custom_tags)
                tags_to_register.extend(custom_tags)
            [note.addTag(tag) for tag in list(set(new_tags))]

            fields = col.models.fieldNames(m)
            for field in fields:
                note[field] = textfile[field]
            col.addNote(note)
            nids.append(note.id)
            nids_decks[note.id] = textfile["ffsDeckname"]
            addedc = addedc + 1
        col.genCards(nids)

        added_cards = []
        for card in col.db.execute("select * from cards"):
            card = list(card)
            if card[1] in nids:
                added_cards.append(card)
        totalc = addedc + changedc + deletedc
        self.log.append(
            ngettext(
                "{0} card changed, " + "({1}, {2}, {3}) note (added, changed, or deleted).",
                "{0} cards changed. " + "({1}, {2}, {3}) notes (added, changed, or deleted).",
                totalc,
            ).format(len(added_cards), addedc, changedc, deletedc)
        )
        # TODO improve this loop
        for card in added_cards:
            deck_name = nids_decks[card[1]]
            deck = col.decks.id(deck_name)
            col.decks.setDeck([card[0]], deck)

        # Set the default model to something standard
        basic = col.models.byName("Basic")
        if basic:
            col.models.setCurrent(basic)

        # Register all of our tags
        col.tags.register(list(set(tags_to_register)))

        # Cleanup empty decks
        children_decks = col.decks.children(deck)
        for child in children_decks:
            cids = col.decks.cids(child[1], True)
            if len(cids) == 0:
                col.decks.rem(child[1])

        # Cleanup models
        for note in old_models:
            m = col.models.byName(note["ffsModel"]["name"])
            if m:
                nids = col.models.nids(m)
                if len(nids) == 0:
                    col.models.rem(m)
        for i in old_model_ids:
            m = col.models.get(i)
            if m:
                nids = col.models.nids(m)
                if len(nids) == 0:
                    col.models.rem(m)

        col.conf["nextPos"] = col.db.scalar("select max(due)+1 from cards where type = 0") or 0
        col.save()
        col.db.execute("vacuum")
        col.db.execute("analyze")
        col.fixIntegrity()