Пример #1
0
Файл: notes.py Проект: dae/anki
 def dupeOrEmpty(self):
     "1 if first is empty; 2 if first is a duplicate, False otherwise."
     val = self.fields[0]
     if not val.strip():
         return 1
     csum = fieldChecksum(val)
     # find any matching csums and compare
     for flds in self.col.db.list(
         "select flds from notes where csum = ? and id != ? and mid = ?", csum, self.id or 0, self.mid
     ):
         if stripHTMLMedia(splitFields(flds)[0]) == stripHTMLMedia(self.fields[0]):
             return 2
     return False
Пример #2
0
 def dupeOrEmpty(self):
     "1 if first is empty; 2 if first is a duplicate, False otherwise."
     val = self.fields[0]
     if not val.strip():
         return 1
     csum = fieldChecksum(val)
     # find any matching csums and compare
     for flds in self.col.db.list(
             "select flds from notes where csum = ? and id != ? and mid = ?",
             csum, self.id or 0, self.mid):
         if stripHTMLMedia(splitFields(flds)[0]) == stripHTMLMedia(
                 self.fields[0]):
             return 2
     return False
Пример #3
0
 def showDupes(self):
     contents = stripHTMLMedia(self.note.fields[0])
     browser = aqt.dialogs.open("Browser", self.mw)
     browser.form.searchEdit.lineEdit().setText(
         '"dupe:%s,%s"' % (self.note.model()['id'],
                           contents))
     browser.onSearch()
Пример #4
0
def findDupes(col, fieldName, search=""):
    # limit search to notes with applicable field name
    if search:
        search = "("+search+") "
    search += "'%s:*'" % fieldName
    # go through notes
    vals = {}
    dupes = []
    fields = {}
    def ordForMid(mid):
        if mid not in fields:
            model = col.models.get(mid)
            for c, f in enumerate(model['flds']):
                if f['name'].lower() == fieldName.lower():
                    fields[mid] = c
                    break
        return fields[mid]
    for nid, mid, flds in col.db.all(
        "select id, mid, flds from notes where id in "+ids2str(
            col.findNotes(search))):
        flds = splitFields(flds)
        ord = ordForMid(mid)
        if ord is None:
            continue
        val = flds[ord]
        val = stripHTMLMedia(val)
        # empty does not count as duplicate
        if not val:
            continue
        if val not in vals:
            vals[val] = []
        vals[val].append(nid)
        if len(vals[val]) == 2:
            dupes.append((val, vals[val]))
    return dupes
Пример #5
0
    def findDupes(self,
                  fieldName: str,
                  search: str = "") -> List[Tuple[Any, list]]:
        nids = self.findNotes(search, SearchNode(field_name=fieldName))
        # go through notes
        vals: Dict[str, List[int]] = {}
        dupes = []
        fields: Dict[int, int] = {}

        def ordForMid(mid: int) -> int:
            if mid not in fields:
                model = self.models.get(mid)
                for c, f in enumerate(model["flds"]):
                    if f["name"].lower() == fieldName.lower():
                        fields[mid] = c
                        break
            return fields[mid]

        for nid, mid, flds in self.db.all(
                f"select id, mid, flds from notes where id in {ids2str(nids)}"
        ):
            flds = splitFields(flds)
            ord = ordForMid(mid)
            if ord is None:
                continue
            val = flds[ord]
            val = stripHTMLMedia(val)
            # empty does not count as duplicate
            if not val:
                continue
            vals.setdefault(val, []).append(nid)
            if len(vals[val]) == 2:
                dupes.append((val, vals[val]))
        return dupes
Пример #6
0
 def showDupes(self):
     contents = stripHTMLMedia(self.note.fields[0])
     browser = aqt.dialogs.open("Browser", self.mw)
     browser.form.searchEdit.lineEdit().setText(
         '"dupe:%s,%s"' % (self.note.model()['id'],
                           contents))
     browser.onSearchActivated()
Пример #7
0
    def flush(self, mod=None):
        """If fields or tags have changed, write changes to disk.


        If there exists a note with same id, tags and fields, and mod is not set, do nothing.
        Change the mod to given argument or current time
        Change the USNk
        If the not is not new, according to _preFlush, generate the cards
        Add its tag to the collection
        Add the note in the db

        Keyword arguments:
        mod -- A modification timestamp"""
        assert self.scm == self.col.scm
        self._preFlush()
        sfld = stripHTMLMedia(self.fields[self.col.models.sortIdx(self._model)])
        tags = self.stringTags()
        fields = self.joinedFields()
        if not mod and self.col.db.scalar(
            "select 1 from notes where id = ? and tags = ? and flds = ?",
            self.id, tags, fields):
            return
        csum = fieldChecksum(self.fields[0])
        self.mod = mod if mod else intTime()
        self.usn = self.col.usn()
        res = self.col.db.execute("""
insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)""",
                            self.id, self.guid, self.mid,
                            self.mod, self.usn, tags,
                            fields, sfld, csum, self.flags,
                            self.data)
        self.col.tags.register(self.tags)
        self._postFlush()
Пример #8
0
    def render_sections(self, template, context):
        """Expands sections."""
        while 1:
            match = self.section_re.search(template)
            if match is None:
                break

            section, section_name, inner = match.group(0, 1, 2)
            section_name = section_name.strip()

            # check for cloze
            val = None
            m = re.match(r"c[qa]:(\d+):(.+)", section_name)
            if m:
                # get full field text
                txt = get_or_attr(context, m.group(2), None)
                m = re.search(clozeReg%m.group(1), txt)
                if m:
                    val = m.group(1)
            else:
                val = get_or_attr(context, section_name, None)

            replacer = ''
            inverted = section[2] == "^"
            if val:
                val = stripHTMLMedia(val).strip()
            if (val and not inverted) or (not val and inverted):
                replacer = inner

            template = template.replace(section, replacer)

        return template
Пример #9
0
    def render_sections(self, template, context) -> str:
        """Expands sections."""
        while 1:
            match = self.section_re.search(template)
            if match is None:
                break

            section, section_name, inner = match.group(0, 1, 2)
            section_name = section_name.strip()

            # check for cloze
            val = None
            m = re.match(r"c[qa]:(\d+):(.+)", section_name)
            if m:
                # get full field text
                txt = get_or_attr(context, m.group(2), None)
                m = re.search(clozeReg % m.group(1), txt)
                if m:
                    val = m.group(CLOZE_REGEX_MATCH_GROUP_TAG)
            else:
                val = get_or_attr(context, section_name, None)

            replacer = ""
            inverted = section[2] == "^"
            if val:
                val = stripHTMLMedia(val).strip()
            if (val and not inverted) or (not val and inverted):
                replacer = inner

            template = template.replace(section, replacer)

        return template
Пример #10
0
Файл: notes.py Проект: dae/anki
    def flush(self, mod=None):
        "If fields or tags have changed, write changes to disk."
        assert self.scm == self.col.scm
        self._preFlush()
        sfld = stripHTMLMedia(self.fields[self.col.models.sortIdx(self._model)])
        tags = self.stringTags()
        fields = self.joinedFields()
        if not mod and self.col.db.scalar(
            "select 1 from notes where id = ? and tags = ? and flds = ?", self.id, tags, fields
        ):
            return
        csum = fieldChecksum(self.fields[0])
        self.mod = mod if mod else intTime()
        self.usn = self.col.usn()
        res = self.col.db.execute(
            """
insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)""",
            self.id,
            self.guid,
            self.mid,
            self.mod,
            self.usn,
            tags,
            fields,
            sfld,
            csum,
            self.flags,
            self.data,
        )
        self.col.tags.register(self.tags)
        self._postFlush()
Пример #11
0
def findDupes(col, fieldName, search="") -> List[Tuple[Any, List]]:
    # limit search to notes with applicable field name
    if search:
        search = "(" + search + ") "
    search += "'%s:*'" % fieldName
    # go through notes
    vals: Dict[str, List[int]] = {}
    dupes = []
    fields: Dict[int, int] = {}

    def ordForMid(mid):
        if mid not in fields:
            model = col.models.get(mid)
            for c, f in enumerate(model["flds"]):
                if f["name"].lower() == fieldName.lower():
                    fields[mid] = c
                    break
        return fields[mid]

    for nid, mid, flds in col.db.all(
        "select id, mid, flds from notes where id in " + ids2str(col.findNotes(search))
    ):
        flds = splitFields(flds)
        ord = ordForMid(mid)
        if ord is None:
            continue
        val = flds[ord]
        val = stripHTMLMedia(val)
        # empty does not count as duplicate
        if not val:
            continue
        vals.setdefault(val, []).append(nid)
        if len(vals[val]) == 2:
            dupes.append((val, vals[val]))
    return dupes
Пример #12
0
 def showDupes(self):
     contents = html.escape(stripHTMLMedia(self.note.fields[0]),
                            quote=False).replace('"', r"\"")
     browser = aqt.dialogs.open("Browser", self.mw)
     browser.form.searchEdit.lineEdit().setText(
         '"dupe:%s,%s"' % (self.note.model()["id"], contents))
     browser.onSearchActivated()
Пример #13
0
    def flush(self, mod: Optional[int] = None) -> None:
        "If fields or tags have changed, write changes to disk."
        assert self.scm == self.col.scm
        self._preFlush()
        sfld = stripHTMLMedia(self.fields[self.col.models.sortIdx(
            self._model)])
        tags = self.stringTags()
        fields = self.joinedFields()
        if not mod and self.col.db.scalar(
                "select 1 from notes where id = ? and tags = ? and flds = ?",
                self.id,
                tags,
                fields,
        ):
            return
        csum = fieldChecksum(self.fields[0])
        self.mod = mod if mod else intTime()
        self.usn = self.col.usn()
        res = self.col.db.execute(
            """
insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)""",
            self.id,
            self.guid,
            self.mid,
            self.mod,
            self.usn,
            tags,
            fields,
            sfld,
            csum,
            self.flags,
            self.data,
        )
        self.col.tags.register(self.tags)
        self._postFlush()
Пример #14
0
def sub_section(self, match, context):
    section, section_name, inner = match.group(0, 1, 2)
    section_name = section_name.strip()

    # val will contain the content of the field considered
    # right now
    val = None
    m = re.match("c[qa]:(\d+):(.+)", section_name)
    if m:
        # get full field text
        txt = get_or_attr(context, m.group(2), None)
        m = re.search(clozeReg % m.group(1), txt)
        if m:
            val = m.group(1)
    else:
        val = get_or_attr(context, section_name, None)
    replacer = ''
    # Whether it's {{^
    inverted = section[2] == "^"
    # Ensuring we don't consider whitespace in wval
    if val:
        val = stripHTMLMedia(val).strip()
    if (val and not inverted) or (not val and inverted):
        replacer = inner
    return replacer
Пример #15
0
    def flush(self, mod=None):
        "If fields or tags have changed, write changes to disk."
        assert self.scm == self.col.scm
        self.newlyAdded = (self.id is None)

        sfld = stripHTMLMedia(self.fields[self.col.models.sortIdx(
            self._model)])
        tags = self.stringTags()
        fields = self.joinedFields()
        if not mod and self.col.db.scalar(
                "select 1 from notes where id = ? and tags = ? and flds = ?",
                self.id, tags, fields):
            return
        csum = fieldChecksum(self.fields[0])
        self.mod = mod if mod else intTime()
        self.usn = self.col.usn()

        if self.id is None:
            self.id = timestampID(self.col.db, "notes")
            self.col.db.execute(
                """insert into notes values (?,?,?,?,?,?,?,?,?,?,?)""",
                self.id, self.guid, self.mid, self.mod, self.usn, tags, fields,
                sfld, csum, self.flags, self.data)
        else:
            self.col.db.execute(
                """update notes set guid=?, mid=?, mod=?, usn=?, tags=?, flds=?, sfld=?, csum=?, flags=?, data=?
                 where id = ?""", self.guid, self.mid, self.mod, self.usn,
                tags, fields, sfld, csum, self.flags, self.data, self.id)

        self.col.tags.register(self.tags)
        self._postFlush()
Пример #16
0
 def showDupes(self):
     contents = stripHTMLMedia(self.note.fields[0])
     browser = aqt.dialogs.open("Browser", self.mw)
     browser.form.searchEdit.lineEdit().setText(
         "'note:%s' '%s:%s'" %
         (self.note.model()['name'], self.note.model()['flds'][0]['name'],
          contents))
     browser.onSearch()
Пример #17
0
 def formatQA(self, txt):
     s = txt.replace("<br>", u" ")
     s = s.replace("<br />", u" ")
     s = s.replace("\n", u" ")
     s = re.sub("\[sound:[^]]+\]", "", s)
     s = stripHTMLMedia(s)
     s = s.strip()
     return s
Пример #18
0
 def formatQA(self, txt):
     s = txt.replace("<br>", u" ")
     s = s.replace("<br />", u" ")
     s = s.replace("\n", u" ")
     s = re.sub("\[sound:[^]]+\]", "", s)
     s = stripHTMLMedia(s)
     s = s.strip()
     return s
Пример #19
0
 def showDupes(self):
     contents = stripHTMLMedia(self.note.fields[0])
     browser = aqt.dialogs.open("Browser", self.mw)
     browser.form.searchEdit.lineEdit().setText(
         "'note:%s' '%s:%s'" % (
             self.note.model()['name'],
             self.note.model()['flds'][0]['name'],
             contents))
     browser.onSearch()
Пример #20
0
def onGetSoundFile(editor):
    datasource.setConfig(get_config()['profiles'][mw.pm.name])
    word = stripHTMLMedia(getWordToLookup(editor))
    filename = datasource.lookup(word)
    if filename:
        if datasource.use_text_selection and editor.web.hasSelection():
            editor.web.triggerPageAction(QWebEnginePage.Unselect)
        editor.addMedia(filename)
    else:
        showWarning('{0}: no sound data found'.format(word, title='Sound Files'))
Пример #21
0
def htmlToTextLine(s):
    s = s.replace("<br>", " ")
    s = s.replace("<br />", " ")
    s = s.replace("<div>", " ")
    s = s.replace("\n", " ")
    s = re.sub("\[sound:[^]]+\]", "", s)
    s = re.sub("\[\[type:[^]]+\]\]", "", s)
    s = stripHTMLMedia(s)
    s = s.strip()
    return s
Пример #22
0
 def from_anki(cls, local_id):
     if is_remote_note(local_id):
         remote_id = get_remote_note_id(local_id)
     else:
         remote_id = None
     anki_note = mw.col.getNote(local_id)
     tags = anki_note.tags
     fields = anki_note.joinedFields()
     model_id = anki_note.mid
     sfld = stripHTMLMedia(anki_note.fields[anki_note.col.models.sortIdx(anki_note._model)])
     return cls(tags, fields, model_id, sfld, remote_id, local_id)
Пример #23
0
 def setModified(self, textChanged=False):
     "Mark modified and update cards."
     self.modified = time.time()
     if textChanged:
         d = {}
         for f in self.model.fieldModels:
             d[f.name] = (f.id, self[f.name])
         self.spaceUntil = stripHTMLMedia(u" ".join([x[1] for x in d.values()]))
         for card in self.cards:
             qa = formatQA(None, self.modelId, d, card.splitTags(), card.cardModel)
             card.question = qa['question']
             card.answer = qa['answer']
             card.setModified()
Пример #24
0
 def onHistory(self):
     m = QMenu(self)
     for nid in self.history:
         if self.mw.col.findNotes("nid:%s" % nid):
             fields = self.mw.col.getNote(nid).fields
             txt = stripHTMLMedia(",".join(fields))[:30]
             a = m.addAction(_("Edit %s") % txt)
             a.triggered.connect(lambda b, nid=nid: self.editHistory(nid))
         else:
             a = m.addAction(_("(Note deleted)"))
             a.setEnabled(False)
     runHook("AddCards.onHistory", self, m)
     m.exec_(self.historyButton.mapToGlobal(QPoint(0, 0)))
Пример #25
0
def exportDeck():
    deckId = chooseDeck(prompt="Choose deck to export scheduling from")
    if deckId == 0:
        return
    cids = mw.col.decks.cids(deckId, children=False)
    cards = {}

    for cid in cids:
        card = mw.col.getCard(cid)
        note = mw.col.getNote(card.nid)
        sfld = stripHTMLMedia(note.fields[note.col.models.sortIdx(
            note._model)])
        # Skip new cards
        if card.queue == 0:
            continue
        if sfld in cards:
            showText("Card with duplicated field found; aborting.")
            return

        revlogKeys = ["id", "ease", "ivl", "lastIvl", "factor", "time", "type"]
        revlogsArray = mw.col.db.all(
            "select " + ", ".join(revlogKeys) + " from revlog " +
            "where cid = ?", cid)
        revlogsDict = list(
            map(
                lambda row:
                {revlogKeys[i]: row[i]
                 for i in range(0, len(revlogKeys))}, revlogsArray))

        cards[sfld] = dict(due=card.due,
                           queue=card.queue,
                           ivl=card.ivl,
                           factor=card.factor,
                           left=card.left,
                           type=card.type,
                           lapses=card.lapses,
                           reps=card.reps,
                           flags=card.flags,
                           revlogs=revlogsDict)

    file = getSaveFile(
        mw, "Export scheduling info to file", "anki-times", "JSON", ".json",
        re.sub('[\\\\/?<>:*|"^]', '_', mw.col.decks.name(deckId)) +
        "-scheduling")
    if not file:
        return

    output = {"meta": {"crt": mw.col.crt}, "cards": cards}

    with open(file, "w") as f:
        json.dump(output, f, ensure_ascii=False)
Пример #26
0
 def updateFieldCache(self, nids):
     "Update field checksums and sort cache, after find&replace, etc."
     snids = ids2str(nids)
     r = []
     for (nid, mid, flds) in self._fieldData(snids):
         fields = splitFields(flds)
         model = self.models.get(mid)
         if not model:
             # note points to invalid model
             continue
         r.append((stripHTMLMedia(fields[self.models.sortIdx(model)]),
                   fieldChecksum(fields[0]), nid))
     # apply, relying on calling code to bump usn+mod
     self.db.executemany("update notes set sfld=?, csum=? where id=?", r)
Пример #27
0
 def _findDupes(self, val):
     # caller must call stripHTMLMedia on passed val
     try:
         mid, val = val.split(",", 1)
     except OSError:
         return
     csum = fieldChecksum(val)
     nids = []
     for nid, flds in self.col.db.execute(
             "select id, flds from notes where mid=? and csum=?", mid,
             csum):
         if stripHTMLMedia(splitFields(flds)[0]) == val:
             nids.append(nid)
     return "n.id in %s" % ids2str(nids)
Пример #28
0
 def _findDupes(self, val):
     # caller must call stripHTMLMedia on passed val
     try:
         mid, val = val.split(",", 1)
     except OSError:
         return
     csum = fieldChecksum(val)
     nids = []
     for nid, flds in self.col.db.execute(
             "select id, flds from notes where mid=? and csum=?",
             mid, csum):
         if stripHTMLMedia(splitFields(flds)[0]) == val:
             nids.append(nid)
     return "n.id in %s" % ids2str(nids)
Пример #29
0
def dupeOrEmptyWithOrds(self):
    """
    Returns a tuple. The contents of each element are as follows:

    1) 1 if first is empty; 2 if first is a duplicate, False otherwise.
    2) For a duplicate (2), this returns the list of ordinals that make up the key.
       Otherwise this is None.
    """

    val = self.fields[0]
    if not val.strip():
        return 1, None
    csum = fieldChecksum(val)
    # find any matching csums and compare
    for flds in self.col.db.list(
            "select flds from notes where csum = ? and id != ? and mid = ?",
            csum, self.id or 0, self.mid):

        model = self.model()
        field_ords = [0]
        for fld in model["flds"]:
            if fld["ord"] == 0:
                continue
            elif fld["name"].endswith(KEY_SUFFIX):
                field_ords.append(fld["ord"])

        all_fields_equal = True
        fields_split = splitFields(flds)
        for field_ord in field_ords:
            if stripHTMLMedia(fields_split[field_ord]) != stripHTMLMedia(
                    self.fields[field_ord]):
                all_fields_equal = False

        if all_fields_equal:
            return 2, field_ords

    return False, None
Пример #30
0
def showDupes(self):
    """
    Shows the duplicates for the current note in the editor by conducting a search in the browser.

    This basically performs the normal dupes search that Anki does but appends additional search
    terms for other keys that have the _pk suffix.
    """

    contents = stripHTMLMedia(self.note.fields[0])
    browser = aqt.dialogs.open("Browser", self.mw)

    model = self.note.model()

    # Find other notes with the same content for the first field.
    search_cmds = ['"dupe:%s,%s"' % (model['id'], contents)]

    # If any other field names end in the special suffix, then they are considered part of the "key"
    # that uniquely identifies a note.  Search for notes that have the same content for these fields,
    # in addition to having the first field match.
    for fld in model["flds"]:
        # First field is already filtered on by the dupe check.
        if fld["ord"] == 0:
            continue
        elif fld["name"].endswith(KEY_SUFFIX):
            term = stripHTMLMedia(self.note.fields[fld["ord"]])
            cmd_args = (fld["name"], term)
            if '"' in term and "'" in term:
                # ignore, unfortunately we can't search for it
                pass
            elif '"' in term:
                search_cmds.append("%s:'%s'" % cmd_args)
            else:
                search_cmds.append("%s:\"%s\"" % cmd_args)

    browser.form.searchEdit.lineEdit().setText(" ".join(search_cmds))
    browser.onSearchActivated()
Пример #31
0
 def updateFieldCache(self, nids):
     "Update field checksums and sort cache, after find&replace, etc."
     snids = ids2str(nids)
     r = []
     for (nid, mid, flds) in self._fieldData(snids):
         fields = splitFields(flds)
         model = self.models.get(mid)
         if not model:
             # note points to invalid model
             continue
         r.append((stripHTMLMedia(fields[self.models.sortIdx(model)]),
                   fieldChecksum(fields[0]),
                   nid))
     # apply, relying on calling code to bump usn+mod
     self.db.executemany("update notes set sfld=?, csum=? where id=?", r)
Пример #32
0
    def flush(self, mod=None):
        assert self.scm == self.col.scm
        self._preFlush()
        self.mod = mod if mod else intTime()
        self.usn = self.col.usn()
        sfld = stripHTMLMedia(self.fields[self.col.models.sortIdx(self._model)])
        tags = self.stringTags()
        csum = fieldChecksum(self.fields[0])
        res = self.col.db.execute("""
insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)""",
                            self.id, self.guid, self.mid,
                            self.mod, self.usn, tags,
                            self.joinedFields(), sfld, csum, self.flags,
                            self.data)
        self.col.tags.register(self.tags)
        self._postFlush()
Пример #33
0
 def setModified(self, textChanged=False, deck=None, media=True):
     "Mark modified and update cards."
     self.modified = time.time()
     if textChanged:
         if not deck:
             # FIXME: compat code
             import ankiqt
             if not getattr(ankiqt, 'setModWarningShown', None):
                 import sys; sys.stderr.write(
                     "plugin needs to pass deck to fact.setModified()")
                 ankiqt.setModWarningShown = True
             deck = ankiqt.mw.deck
         assert deck
         self.spaceUntil = stripHTMLMedia(u" ".join(
             self.values()))
         for card in self.cards:
             card.rebuildQA(deck)
Пример #34
0
def swap_meaning_and_extra_info():
    note = current_note_in_review()
    if not note or MEANING not in note or EXTRA_INFO not in note:
        return
    meaning = note[MEANING]
    extra_info = note[EXTRA_INFO]

    if levenshtein(stripHTMLMedia(meaning), PRACTICE_SENTENCE) < 5:
        # PRACTICE SENTENCE!!!!
        note[MEANING] = extra_info
        note[EXTRA_INFO] = ""
    else:
        note[MEANING] = extra_info
        note[EXTRA_INFO] = meaning

    note.flush()
    reshow_card()
Пример #35
0
def allSearch(note):
    model = note.model()
    sortIdx = note.col.models.sortIdx(model)
    fields = note.fields
    allfields = [stripHTMLMedia(field) for field in fields]
    sfields = [
        allfields[idx] for idx in range(len(fields))
        if idx != sortIdx and allfields[idx] != fields[idx]
    ]
    if allfields[sortIdx] != fields[sortIdx] or getUserOption(
            "sort field", True):
        firstField = [allfields[sortIdx]]
    else:
        firstField = []
    sfields = firstField + sfields
    sfield = " ".join(sfields)
    changeSfield(note, sfield)
Пример #36
0
 def setModified(self, textChanged=False, deck=None, media=True):
     "Mark modified and update cards."
     self.modified = time.time()
     if textChanged:
         if not deck:
             # FIXME: compat code
             import ankiqt
             if not getattr(ankiqt, 'setModWarningShown', None):
                 import sys
                 sys.stderr.write(
                     "plugin needs to pass deck to fact.setModified()")
                 ankiqt.setModWarningShown = True
             deck = ankiqt.mw.deck
         assert deck
         self.spaceUntil = stripHTMLMedia(u" ".join(self.values()))
         for card in self.cards:
             card.rebuildQA(deck)
Пример #37
0
    def extract_field(model_id, field_name) -> Iterable[Tuple[int, str]]:
        # type works better in future anki
        model = self.models.get(model_id)
        assert model  # type is optional, but None should never come back

        note_ids = self.findNotes(" ".join(search_filters +
                                           [f'note:{model["name"]}']))

        field_ord: int = next(field["ord"] for field in model["flds"]
                              if field["name"] == field_name)

        assert self.db

        for note_id, fields in self.db.all(
                "select id, flds from notes where id in " + ids2str(note_ids)):
            value = splitFields(fields)[field_ord]
            yield note_id, stripHTMLMedia(value)
Пример #38
0
    def render_sections(self, template, context):
        """Expands sections."""
        while 1:
            match = self.section_re.search(template)
            if match is None:
                break

            section, section_name, inner = match.group(0, 1, 2)
            section_name = section_name.strip()

            # check for cloze
            m = re.match("c[qa]:(\d+):(.+)", section_name)
            if m:
                # get full field text
                txt = get_or_attr(context, m.group(2), None)
                m = re.search(clozeReg % m.group(1), txt)
                if m:
                    it = m.group(1)
                else:
                    it = None
            else:
                it = get_or_attr(context, section_name, None)

            replacer = ''
            # if it and isinstance(it, collections.Callable):
            #     replacer = it(inner)
            if isinstance(it, basestring):
                it = stripHTMLMedia(it).strip()
            if it and not hasattr(it, '__iter__'):
                if section[2] != '^':
                    replacer = inner
            elif it and hasattr(it, 'keys') and hasattr(it, '__getitem__'):
                if section[2] != '^':
                    replacer = self.render(inner, it)
            elif it:
                insides = []
                for item in it:
                    insides.append(self.render(inner, item))
                replacer = ''.join(insides)
            elif not it and section[2] == '^':
                replacer = inner

            template = template.replace(section, replacer)

        return template
Пример #39
0
    def render_sections(self, template, context):
        """Expands sections."""
        while 1:
            match = self.section_re.search(template)
            if match is None:
                break

            section, section_name, inner = match.group(0, 1, 2)
            section_name = section_name.strip()

            # check for cloze
            m = re.match("c[qa]:(\d+):(.+)", section_name)
            if m:
                # get full field text
                txt = get_or_attr(context, m.group(2), None)
                m = re.search(clozeReg%m.group(1), txt)
                if m:
                    it = m.group(1)
                else:
                    it = None
            else:
                it = get_or_attr(context, section_name, None)

            replacer = ''
            # if it and isinstance(it, collections.Callable):
            #     replacer = it(inner)
            if isinstance(it, basestring):
                it = stripHTMLMedia(it).strip()
            if it and not hasattr(it, '__iter__'):
                if section[2] != '^':
                    replacer = inner
            elif it and hasattr(it, 'keys') and hasattr(it, '__getitem__'):
                if section[2] != '^':
                    replacer = self.render(inner, it)
            elif it:
                insides = []
                for item in it:
                    insides.append(self.render(inner, item))
                replacer = ''.join(insides)
            elif not it and section[2] == '^':
                replacer = inner

            template = template.replace(section, replacer)

        return template
Пример #40
0
def createNote(deck, model, fields):
    m = _find_model(model)
    m['did'] = _find_deck(deck)
    
    note = Note(mw.col, m)
    _set_fields(note, fields)
    mw.col.addNote(note)
    
    duplicateOrEmpty = note.dupeOrEmpty()
    if duplicateOrEmpty == 1:
        raise Exception('cannot create note because it is empty')
    elif duplicateOrEmpty == 2:
        key = m['flds'][0]['name']
        value = stripHTMLMedia(fields[key]) if key in fields else ''
        raise DuplicateException('"{0}" note already exists for "{1}"'.format(model, value))
    elif duplicateOrEmpty == False:
        return
    else:
        raise Exception('cannot create note for unknown reason')
Пример #41
0
def get_data(editor):
    ""

    ""

    # get word from current field
    word = stripHTMLMedia(editor.note.fields[editor.currentField])

    clean_word = _normalize_word(word)

    audio_file = save_audio(clean_word)
    if (audio_file == None):
        showWarning(
            'Vocabolaudio: no information found for the word: {}'.format(word))
        return

    editor.addMedia(audio_file)

    for field in editor.note.keys():
        if field == 'Audio' and audio_file != None:
            editor.note[field] = str('[sound:{}.{}]'.format(clean_word, 'mp3'))
            editor.note.flush()

    mw.reset()
Пример #42
0
    def render_sections(self, template, context):
        """replace {{#foo}}bar{{/foo}} and {{^foo}}bar{{/foo}} by
        their normal value."""
        while 1:
            match = self.section_re.search(template)
            if match is None:
                break

            section, section_name, inner = match.group(0, 1, 2)
            section_name = section_name.strip()

            # val will contain the content of the field considered
            # right now
            val = None
            m = re.match(r"c[qa]:(\d+):(.+)", section_name)
            if m:
                # get full field text
                txt = get_or_attr(context, m.group(2), None)
                m = re.search(clozeReg % m.group(1), txt)
                if m:
                    val = m.group(1)
            else:
                val = get_or_attr(context, section_name, None)

            replacer = ''
            # Whether it's {{^
            inverted = section[2] == "^"
            # Ensuring we don't consider whitespace in wval
            if val:
                val = stripHTMLMedia(val).strip()
            if (val and not inverted) or (not val and inverted):
                replacer = inner

            template = template.replace(section, replacer)

        return template
Пример #43
0
            except sre_constants.error:
                return
        if not nids:
            return "0"
        return "n.id in %s" % ids2str(nids)

    def _findDupes(self, (val, args)):
        # caller must call stripHTMLMedia on passed val
        try:
            mid, val = val.split(",", 1)
        except OSError:
            return
        csum = fieldChecksum(val)
        nids = []
        for nid, flds in self.col.db.execute("select id, flds from notes where mid=? and csum=?", mid, csum):
            if stripHTMLMedia(splitFields(flds)[0]) == val:
                nids.append(nid)
        return "n.id in %s" % ids2str(nids)


# Find and replace
##########################################################################


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:
Пример #44
0
 def addHistory(self, note):
     txt = stripHTMLMedia(",".join(note.fields))[:30]
     self.history.append((note.id, txt))
     self.history = self.history[-15:]
     self.historyButton.setEnabled(True)
Пример #45
0
def _copyScheduling(deckFrom, deckTo):
    now = intTime()
    logs = []
    cids = mw.col.decks.cids(deckTo["id"], children=False)
    copiedN = 0
    updates = []
    
    for cid in cids:
        card = mw.col.getCard(cid)
        note = mw.col.getNote(card.nid)
        sfld = stripHTMLMedia(note.fields[note.col.models.sortIdx(note._model)])
        sourceCids = mw.col.db.list(
            "select distinct(c.id) from cards c, notes n where c.nid=n.id and c.did=? and n.sfld=?",
            deckFrom["id"],
            sfld
        )
        
        # If there are no source cards, skip
        if not sourceCids:
            continue
        if len(sourceCids) > 1:
            logs.append("Multiple source cards not supported. Matched field={0}".format(sfld))
            continue
        sourceCid = sourceCids[0]
        sourceCard = mw.col.getCard(sourceCid)
        # Skip new cards
        if sourceCard.queue == 0:
            continue

        logs.append("Matched card {0}".format(sfld))
        
        updates.append(dict(
            cid=cid,
            due=sourceCard.due,
            queue=sourceCard.queue,
            ivl=sourceCard.ivl,
            factor=sourceCard.factor,
            left=sourceCard.left,
            type=sourceCard.type,
            now=now,
            usn=mw.col.usn()
        ))

        def copyRevlog(offset):
            mw.col.db.execute(
                "insert into revlog "
                "select r.id + :offset, :newcid, :usn, r.ease, r.ivl, r.lastIvl, r.factor, r.time, r.type "
                "from revlog as r "
                "where cid=:oldcid",
                offset=offset,
                oldcid=sourceCid,
                newcid=cid,
                usn=mw.col.usn()
            )
        for i in range(0, MAX_RETRIES + 1):
            try:
                copyRevlog(2 << i)
                break
            except:
                if i == MAX_RETRIES:
                    raise

        copiedN += 1

    #logs.append("updates {0}".format(updates))

    mw.col.db.executemany(
        "update cards set "
        "due=:due, mod=:now, usn=:usn, queue=:queue, "
        "ivl=:ivl, factor=:factor, left=:left, type=:type "
        "where id=:cid",
        updates
    )
    
    logs.append("Copied {0} cards".format(copiedN))

    showText("\n".join(logs), title="Copy scheduling log")
    mw.reset()
Пример #46
0
 def addHistory(self, note):
     txt = stripHTMLMedia(",".join(note.fields))[:30]
     self.history.insert(0, (note.id, txt))
     self.history = self.history[:15]
     self.historyButton.setEnabled(True)
Пример #47
0
    def deck_notes(self, did):
        import re, os
        from anki import utils

        #afx = AnkiFx(self.col)
        #cids = afx.did2cids(did=did)
        cids = self.cids(did, utils)

        components = self.col.split(os.sep)
        mediadir = ('/'.join(components[:-1]) + '/collection.media')

        nids = []
        for cid in cids:
            card = self.card_spec('nid', cid=cid)
            nids.append(card[0])

        notes = []
        for nid in nids:

            ndict = {
                'nid': None,
                'flds': None,
                'tags': None,
                'mid': None,
                'mname': None,
                'img': None
            }

            # note info for each note id
            note = self.note_info(nid=nid)

            # readable note fields, strip HTML
            flds = filter(None, note['flds'].split('\x1f'))
            m = lambda i: [
                re.findall('src="([^"]+)"', f, re.DOTALL) for f in i
            ]
            img = [x[0] for x in m(flds) if x]
            f = lambda x: [utils.stripHTMLMedia(i) for i in x]
            flds = f(flds)

            #f = lambda x: [utils.stripHTMLMedia(i) for i in x]
            #flds = f(filter(None, note['flds'].split('\x1f')))

            # from note model id, retrieve model fields
            mid = str(note['mid'])

            #model = afx.did2cids(mid=mid)
            model = self.model_info(mid=mid)

            # get field names for model
            mflds = [mfld['name'] for mfld in model['flds']]

            ndict['nid'] = nid
            #ndict['flds']  = (' '.join(flds)).encode('utf-8')
            ndict['flds'] = flds
            ndict['tags'] = note['tags']
            ndict['mid'] = mid
            ndict['mname'] = model['name']
            if img:
                ndict['img'] = '{}/{}'.format(mediadir, img[0])

            notes.append(ndict)

        return notes
Пример #48
0
def importMedia(self, mime, _old):
    """import audios and images from goldendict"""

    # find out where we are
    if dialogs._dialogs['AddCards'][1]:
        # we are adding cards
        window = dialogs._dialogs['AddCards'][1]
    elif dialogs._dialogs['Browser'][1]:
        # we are browsing cards
        window = dialogs._dialogs['Browser'][1]
    elif dialogs._dialogs['EditCurrent'][1]:
        # we are editing cards
        window = dialogs._dialogs['EditCurrent'][1]
    else:
        # I don't know where we are, just exit
        return _old(self, mime) 

    html = mime.html()
    soup = BeautifulSoup(html)
    newMime = QMimeData()
    addressMap = Setup.config['addressMap']

    # sound
    links = [link for link in soup.findAll('a') if 'gdau' in link['href']]

    # images
    links += [link for link in soup.findAll('img') if 'bres' in link['src']]


    for link in links:
        if link.get('href'):
            # audio
            attr = 'href'
        elif link.get('src'):
            # image
            attr = 'src'
        else:
            # something else, I don't know, at least not 
            # something we're looking for, skip
            continue

        goldenPath = link.get(attr)
        matchObj = re.search(r'(?<=(gdau|bres)://)[^/\\]*', goldenPath)
        if not matchObj:
            continue
        code = matchObj.group(0)
        if code not in addressMap:
            # new media
            filename = os.path.basename(goldenPath)
            res = addNewMedia(code, filename)
            if not res:
                # media import failed, continue to
                # process the next link
                continue

        # get the full path of the media file
        prefix = re.search(r'^(gdau|bres)://[^/\\]*', goldenPath).group(0)
        filePath = link[attr].replace(prefix, addressMap[code])

        # import the file to anki
        ankiMedia = window.editor._addMedia(filePath, canDelete=True)
        # sound
        if attr == 'href': 
            span = link.parent
            # delete the original link, 
            # because we don't need it any more
            del link
            # append ankiMedia
            span.string = ankiMedia

        # images
        else:
            img = BeautifulSoup(ankiMedia)
            link.replaceWith(img)

    html = str(soup).decode('utf8')

    # assign the modified html to new Mime
    newMime = QMimeData()
    newMime.setHtml(html)

    # set text so the addon is able to work even when StripHTML is on
    newMime.setText(stripHTMLMedia(html))

    # default _processHtml method
    return _old(self, newMime)