Example #1
0
File: deck.py Project: ChYi/libanki
    def addTags(self, ids, tags, add=True):
        "Add tags in bulk. TAGS is space-separated."
        newTags = parseTags(tags)
        if not newTags:
            return
        # cache tag names
        self.registerTags(newTags)
        # find facts missing the tags
        if add:
            l = "tags not "
            fn = addTags
        else:
            l = "tags "
            fn = delTags
        lim = " or ".join(
            [l+"like :_%d" % c for c, t in enumerate(newTags)])
        res = self.db.all(
            "select id, tags from facts where id in %s and %s" % (
                ids2str(ids), lim),
            **dict([("_%d" % x, '%% %s %%' % y) for x, y in enumerate(newTags)]))
        # update tags
        fids = []
        def fix(row):
            fids.append(row[0])
            return {'id': row[0], 't': fn(tags, row[1]), 'n':intTime()}
        self.db.executemany("""
update facts set tags = :t, mod = :n where id = :id""", [fix(row) for row in res])
        # update q/a cache
        self.registerTags(parseTags(tags))
Example #2
0
 def rebuildTagList(self):
     usertags = self.deck.allTags()
     self.items = []
     self.suspended = {}
     yes = parseTags(self.deck.getVar(self.active))
     no = parseTags(self.deck.getVar(self.inactive))
     yesHash = {}
     noHash = {}
     for y in yes:
         yesHash[y] = True
     for n in no:
         noHash[n] = True
     groupedTags = []
     usertags.sort()
     # render models and templates
     for (type, sql, icon) in (("models", "select tags from models",
                                "contents.png"),
                               ("cms", "select name from cardModels",
                                "Anki_Card.png")):
         d = {}
         tagss = self.deck.db.column0(sql)
         for tags in tagss:
             for tag in parseTags(tags):
                 d[tag] = 1
         sortedtags = sorted(d.keys())
         icon = QIcon(":/icons/" + icon)
         groupedTags.append([icon, sortedtags])
     # remove from user tags
     for tag in groupedTags[0][1] + groupedTags[1][1]:
         try:
             usertags.remove(tag)
         except:
             pass
     # user tags
     icon = QIcon(":/icons/Anki_Fact.png")
     groupedTags.append([icon, usertags])
     self.tags = []
     for (icon, tags) in groupedTags:
         for t in tags:
             self.tags.append(t)
             item = QListWidgetItem(icon, t.replace("_", " "))
             self.dialog.activeList.addItem(item)
             if t in yesHash:
                 mode = QItemSelectionModel.Select
                 self.dialog.activeCheck.setChecked(True)
             else:
                 mode = QItemSelectionModel.Deselect
             idx = self.dialog.activeList.indexFromItem(item)
             self.dialog.activeList.selectionModel().select(idx, mode)
             # inactive
             item = QListWidgetItem(icon, t.replace("_", " "))
             self.dialog.inactiveList.addItem(item)
             if t in noHash:
                 mode = QItemSelectionModel.Select
                 self.dialog.inactiveCheck.setChecked(True)
             else:
                 mode = QItemSelectionModel.Deselect
             idx = self.dialog.inactiveList.indexFromItem(item)
             self.dialog.inactiveList.selectionModel().select(idx, mode)
Example #3
0
 def rebuildTagList(self):
     self.tags = self.deck.allTags()
     self.items = []
     self.suspended = {}
     alltags = []
     # get list of currently suspended
     for t in parseTags(self.deck.suspended):
         if t == "Suspended":
             continue
         self.suspended[t] = 1
         if t not in self.tags:
             self.tags.append(t)
     # sort and remove special 'Suspended' tag
     self.tags.sort()
     try:
         self.tags.remove("Suspended")
     except ValueError:
         pass
     # render models and templates
     for (type, sql, icon) in (
         ("models", "select tags from models", "contents.png"),
         ("cms", "select name from cardModels", "Anki_Card.png")):
         d = {}
         tagss = self.deck.s.column0(sql)
         for tags in tagss:
             for tag in parseTags(tags):
                 d[tag] = 1
         sortedtags = sorted(d.keys())
         alltags.extend(sortedtags)
         icon = QIcon(":/icons/" + icon)
         for t in sortedtags:
             item = QListWidgetItem(icon, t.replace("_", " "))
             self.dialog.list.addItem(item)
             self.items.append(item)
             idx = self.dialog.list.indexFromItem(item)
             if t in self.suspended:
                 mode = QItemSelectionModel.Select
             else:
                 mode = QItemSelectionModel.Deselect
             self.dialog.list.selectionModel().select(idx, mode)
     # remove from user tags
     for tag in alltags + ["Suspended"]:
         try:
             self.tags.remove(tag)
         except:
             pass
     # user tags
     icon = QIcon(":/icons/Anki_Fact.png")
     for t in self.tags:
         item = QListWidgetItem(icon, t.replace("_", " "))
         self.dialog.list.addItem(item)
         self.items.append(item)
         idx = self.dialog.list.indexFromItem(item)
         if t in self.suspended:
             mode = QItemSelectionModel.Select
         else:
             mode = QItemSelectionModel.Deselect
         self.dialog.list.selectionModel().select(idx, mode)
     self.tags = alltags + self.tags
Example #4
0
 def rebuildTagList(self):
     usertags = self.deck.allTags()
     self.items = []
     self.suspended = {}
     yes = parseTags(self.deck.getVar(self.active))
     no = parseTags(self.deck.getVar(self.inactive))
     yesHash = {}
     noHash = {}
     for y in yes:
         yesHash[y] = True
     for n in no:
         noHash[n] = True
     groupedTags = []
     usertags.sort()
     # render models and templates
     for (type, sql, icon) in (
         ("models", "select tags from models", "contents.png"),
         ("cms", "select name from cardModels", "Anki_Card.png")):
         d = {}
         tagss = self.deck.db.column0(sql)
         for tags in tagss:
             for tag in parseTags(tags):
                 d[tag] = 1
         sortedtags = sorted(d.keys())
         icon = QIcon(":/icons/" + icon)
         groupedTags.append([icon, sortedtags])
     # remove from user tags
     for tag in groupedTags[0][1] + groupedTags[1][1]:
         try:
             usertags.remove(tag)
         except:
             pass
     # user tags
     icon = QIcon(":/icons/Anki_Fact.png")
     groupedTags.append([icon, usertags])
     self.tags = []
     for (icon, tags) in groupedTags:
         for t in tags:
             self.tags.append(t)
             item = QListWidgetItem(icon, t.replace("_", " "))
             self.dialog.activeList.addItem(item)
             if t in yesHash:
                 mode = QItemSelectionModel.Select
                 self.dialog.activeCheck.setChecked(True)
             else:
                 mode = QItemSelectionModel.Deselect
             idx = self.dialog.activeList.indexFromItem(item)
             self.dialog.activeList.selectionModel().select(idx, mode)
             # inactive
             item = QListWidgetItem(icon, t.replace("_", " "))
             self.dialog.inactiveList.addItem(item)
             if t in noHash:
                 mode = QItemSelectionModel.Select
                 self.dialog.inactiveCheck.setChecked(True)
             else:
                 mode = QItemSelectionModel.Deselect
             idx = self.dialog.inactiveList.indexFromItem(item)
             self.dialog.inactiveList.selectionModel().select(idx, mode)
Example #5
0
 def onKeyPress(self, fact, field, value):
     if findTag("Reading source", parseTags(field.fieldModel.features)):
         dst = None
         for field in fact.fields:
             if findTag("Reading destination",
                        parseTags(field.fieldModel.features)):
                 dst = field
                 break
         if not dst:
             return
         self.lazyInit()
         reading = self.unihan.reading(value)
         fact[dst.name] = reading
Example #6
0
 def onKeyPress(self, fact, field, value):
     if self.kakasi and findTag("Reading source",
                                parseTags(field.fieldModel.features)):
         reading = self.kakasi.toFurigana(value)
         dst = None
         for field in fact.fields:
             if findTag("Reading destination", parseTags(
                 field.fieldModel.features)):
                 dst = field
                 break
         if dst:
             if self.kakasi.formatForKakasi(value) != reading:
                 fact[dst.name] = reading
             else:
                 fact[dst.name] = u""
Example #7
0
 def accept(self):
     if isinstance(self.exporter, PackagedAnkiExporter):
         self.parent.onShare(parseTags(unicode(self.tags.text())))
         return QDialog.accept(self)
     file = ui.utils.getSaveFile(self, _("Choose file to export to"), "export",
                                 self.exporter.key, self.exporter.ext)
     self.hide()
     if file:
         self.exporter.includeSchedulingInfo = (
             self.dialog.includeScheduling.isChecked())
         self.exporter.includeTags = (
             self.dialog.includeTags.isChecked())
         self.exporter.limitTags = parseTags(unicode(self.tags.text()))
         self.exporter.exportInto(file)
         self.parent.setStatus(_("%d exported.") % self.exporter.count)
     QDialog.accept(self)
def unblock(string):
    query = """select cards.Id from cardTags, tags, cards
                 where cardTags.tagId = tags.id and cards.Id= cardTags.cardId and cards.type = 2 
                 and tags.tag in ('%(tags)s') group by cardTags.cardId
                 """ % {'tags':"','".join(parseTags(myDict[string]))}
    cards = list(mw.deck.s.column0(query))
    mw.deck.unsuspendCards(cards)
Example #9
0
 def splitPath(self, str):
     str = unicode(str).strip()
     str = re.sub("  +", " ", str)
     self.tags = parseTags(str)
     self.tags.append(u"")
     p = self.edit.cursorPosition()
     self.cursor = str.count(" ", 0, p)
     return self.tags[self.cursor]
Example #10
0
def updateMenu():
    try:
        mw.tagsMenu.clear()
        for t in parseTags(mw.deck.suspended):
            mw.tagsMenu.addAction(t, setTagCallable(t))
    except AttributeError:
        # mw.deck doesn't exist yet. wait for the menu to be invoked.
        pass
Example #11
0
 def run(tagstr, cmd, *args):
     "Run CMD on all matching features in DLIST."
     tags = parseTags(tagstr)
     for (name, feature) in FeatureManager.features.items():
         for tag in tags:
             if findTag(tag, feature.tags):
                 feature.run(cmd, *args)
                 break
Example #12
0
File: deck.py Project: ChYi/libanki
 def updateFactTags(self, fids=None):
     "Add any missing tags to the tags list."
     if fids:
         lim = " where id in " + ids2str(fids)
     else:
         lim = ""
     self.registerTags(set(parseTags(
         " ".join(self.db.list("select distinct tags from facts"+lim)))))
Example #13
0
 def hasTags(self, tags):
     tags = parseTags(tags)
     if not self.limitTags:
         return True
     for tag in self.limitTags:
         if findTag(tag, tags):
             return True
     return False
Example #14
0
    def load(self):
        (self.mid, self.gid, self.crt, self.mod, self.tags, self.fields,
         self.data) = self.deck.db.first(
             """
select mid, gid, crt, mod, tags, flds, data from facts where id = ?""",
             self.id)
        self.fields = splitFields(self.fields)
        self.tags = parseTags(self.tags)
        self._model = self.deck.getModel(self.mid)
        self._fmap = self._model.fieldMap()
Example #15
0
 def saveTagsAndGroup(self):
     if not self.fact:
         return
     self.fact.tags = parseTags(unicode(self.tags.text()))
     if self.addMode:
         # save group and tags to model
         self.fact.gid = self.mw.deck.groupId(unicode(self.group.text()))
         m = self.fact.model()
         m.conf['gid'] = self.fact.gid
         m.conf['tags'] = self.fact.tags
         m.flush()
     self.fact.flush()
     runHook("tagsAndGroupUpdated", self.fact)
Example #16
0
 def saveTagsAndGroup(self):
     if not self.fact:
         return
     self.fact.tags = parseTags(unicode(self.tags.text()))
     if self.addMode:
         # save group and tags to model
         self.fact.gid = self.mw.deck.groupId(unicode(self.group.text()))
         m = self.fact.model()
         m.conf['gid'] = self.fact.gid
         m.conf['tags'] = self.fact.tags
         m.flush()
     self.fact.flush()
     runHook("tagsAndGroupUpdated", self.fact)
Example #17
0
    def load(self):
        (self.mid,
         self.gid,
         self.crt,
         self.mod,
         self.tags,
         self.fields,
         self.data) = self.deck.db.first("""
select mid, gid, crt, mod, tags, flds, data from facts where id = ?""", self.id)
        self.fields = splitFields(self.fields)
        self.tags = parseTags(self.tags)
        self._model = self.deck.getModel(self.mid)
        self._fmap = self._model.fieldMap()
Example #18
0
 def drawTags(self):
     self.dialog.tagList.view().setFixedWidth(200)
     self.dialog.tagList.setMaxVisibleItems(30)
     self.dialog.tagList.setFixedWidth(130)
     self.dialog.tagList.clear()
     alltags = [None, "Marked", None, None, "Leech", None, None]
     # system tags
     self.dialog.tagList.addItem(_("<Filter>"))
     self.dialog.tagList.addItem(QIcon(":/icons/rating.png"),
                                 _('Marked'))
     self.dialog.tagList.addItem(QIcon(":/icons/media-playback-pause.png"),
                                 _('Suspended'))
     self.dialog.tagList.addItem(QIcon(":/icons/chronometer.png"),
                                 _('Due'))
     self.dialog.tagList.addItem(QIcon(":/icons/emblem-important.png"),
                                 _('Leech'))
     self.dialog.tagList.addItem(QIcon(":/icons/editclear.png"),
                                 _('No fact tags'))
     self.dialog.tagList.insertSeparator(
         self.dialog.tagList.count())
     # model and card templates
     for (type, sql, icon) in (
         ("models", "select tags from models", "contents.png"),
         ("cms", "select name from cardModels", "Anki_Card.png")):
         d = {}
         tagss = self.deck.s.column0(sql)
         for tags in tagss:
             for tag in parseTags(tags):
                 d[tag] = 1
         sortedtags = sorted(d.keys())
         alltags.extend(sortedtags)
         icon = QIcon(":/icons/" + icon)
         for t in sortedtags:
             self.dialog.tagList.addItem(icon, t.replace("_", " "))
         if sortedtags:
             self.dialog.tagList.insertSeparator(
                 self.dialog.tagList.count())
             alltags.append(None)
     # fact tags
     alluser = sorted(self.deck.allTags())
     for tag in alltags:
         try:
             alluser.remove(tag)
         except:
             pass
     icon = QIcon(":/icons/Anki_Fact.png")
     for t in alluser:
         t = t.replace("_", " ")
         self.dialog.tagList.addItem(icon, t)
     alltags.extend(alluser)
     self.alltags = alltags
Example #19
0
 def accept(self):
     if isinstance(self.exporter, PackagedAnkiExporter):
         self.parent.onShare(unicode(self.tags.text()))
         return QDialog.accept(self)
     file = ui.utils.getSaveFile(self, _("Choose file to export to"), "export",
                                 self.exporter.key, self.exporter.ext)
     self.hide()
     if file:
         self.exporter.includeSchedulingInfo = (
             self.dialog.includeScheduling.isChecked())
         self.exporter.includeTags = (
             self.dialog.includeTags.isChecked())
         self.exporter.limitTags = parseTags(unicode(self.tags.text()))
         self.exporter.exportInto(file)
         self.parent.setStatus(_("%d exported.") % self.exporter.count)
     QDialog.accept(self)
def update_card(card):
  candidate_lesson_nos = []
  for tag in parseTags(card.allTags()):
    if tag.startswith(LESSON_TAG_PREFIX):
      suffix = tag.lstrip(LESSON_TAG_PREFIX)
      
      try:
        candidate_lesson_nos.append(int(suffix))
      except ValueError:
        continue
  
  if len(candidate_lesson_nos) != 1:
    print "Too many or too few lesson numbers, skipping"
    return
  
  card.fact[LESSON_FIELD_NAME] = unicode(candidate_lesson_nos[0])
Example #21
0
 def renderQA(self, card, fact, type, format="text"):
     "Render fact into card based on card model."
     if type == "question": field = self.qformat
     elif type == "answer": field = self.aformat
     htmlFields = {}
     htmlFields.update(fact)
     alltags = parseTags(card.tags + "," +
                         card.fact.tags + "," +
                         card.cardModel.name + "," +
                         card.fact.model.tags)
     htmlFields['tags'] = ", ".join(alltags)
     textFields = {}
     textFields.update(htmlFields)
     # add per-field formatting
     for (k, v) in htmlFields.items():
         # generate pure text entries
         htmlFields["text:"+k] = v
         textFields["text:"+k] = v
         if v:
             # convert newlines to html & add spans to fields
             v = v.replace("\n", "<br>")
             htmlFields[k] = '<span class="%s">%s</span>' % (k.replace(" ",""), v)
     try:
         html = field % htmlFields
         text = field % textFields
     except (KeyError, TypeError, ValueError):
         return _("[invalid format; see model properties]")
     if not html:
         html = _("[empty]")
         text = _("[empty]")
     if format == "text":
         return text
     # add outer div & alignment (with tables due to qt's html handling)
     html = '<div class="%s">%s</div>' % (type, html)
     attr = type + 'Align'
     if getattr(self, attr) == 0:
         align = "center"
     elif getattr(self, attr) == 1:
         align = "left"
     else:
         align = "right"
     html = (("<center><table width=95%%><tr><td align=%s>" % align) +
                html + "</td></tr></table></center>")
     return html
Example #22
0
    def addCards(self):
        # make sure updated
        self.editor.saveFieldsNow()
        fact = self.editor.fact
        n = _("Add")
        self.parent.deck.setUndoStart(n)
        try:
            fact = self.parent.deck.addFact(fact)
        except FactInvalidError:
            ui.utils.showInfo(_(
                "Some fields are missing or not unique."),
                              parent=self, help="AddItems#AddError")
            return
        if not fact:
            ui.utils.showWarning(_("""\
The input you have provided would make an empty
question or answer on all cards."""), parent=self)
            return
        self.dialog.status.append(
            _("Added %(num)d card(s) for <a href=\"%(id)d\">"
              "%(str)s</a>.") % {
            "num": len(fact.cards),
            "id": fact.id,
            # we're guaranteed that all fields will exist now
            "str": stripHTML(fact[fact.fields[0].name]),
            })
        # stop anything playing
        clearAudioQueue()
        self.parent.deck.setUndoEnd(n)
        self.parent.updateTitleBar()
        # start a new fact
        f = self.parent.deck.newFact()
        f.tags = self.parent.deck.lastTags
        self.editor.setFact(f, check=True, scroll=True)
        # let completer know our extra tags
        self.editor.tags.addTags(parseTags(self.parent.deck.lastTags))
        self.maybeSave()
Example #23
0
 def hasTag(self, tag):
     return findTag(tag, parseTags(self.allTags()))
Example #24
0
 def tags(self, id):
     if self.includeTags:
         return "\t" + ", ".join(parseTags(self.cardTags[id]))
     return ""
Example #25
0
 def clearOldFact(self, old_fact):
     f = self.initializeNewFact(old_fact)
     self.editor.setFact(f, check=True, scroll=True)
     # let completer know our extra tags
     self.editor.tags.addTags(parseTags(self.parent.deck.lastTags))
     return f
Example #26
0
 def clearOldFact(self, old_fact):
     f = self.initializeNewFact(old_fact)
     self.editor.setFact(f, check=True, scroll=True)
     # let completer know our extra tags
     self.editor.tags.addTags(parseTags(self.parent.deck.lastTags))
     return f
Example #27
0
 def tags(self, id):
     if self.includeTags:
         return "\t" + ", ".join(parseTags(self.cardTags[id]))
     return ""
Example #28
0
    def opbutclick(self, widget, cmd):
        if cmd == 'save':
            if self.deck:
                self.deck_save()
                self.set_question()
                self.set_stats()
        elif cmd == 'answer':
            self.set_q_a()
            self.set_stats()
        elif cmd == 'close':
            if not self.deck:
                return
            
            if self.deck.modifiedSinceSave() and \
                    self.yesno_dlg(gtk.MESSAGE_QUESTION, "Save the current deck first?"):
                self.deck_save()                
            self.deck.close()
            self.deck = None
            self.update_recent_menu(self.DECK_PATH)
            self.DECK_PATH = ""
            self.conf_client.set_string("/apps/anki/general/deck_path", self.DECK_PATH)
            self.opbuttonsbox.hide()
            self.answerbuttonbox.hide()
            self.resultbuttonbox.hide()
            self.set_html_doc('<center><div class="a"><br/><br/><br/>%s %s</div></center>' % 
                              (appname, appversion))
            self.statslabel.set_markup("T: 0/0 (0.0%) A: <b>0.0%</b>. ETA: <b>Unknown</b>")
            self.missinglabel.set_markup('<span foreground="red">0</span>+0+<span foreground="blue">0</span>')

        elif cmd == 'replay':
            self.prepareMedia(self.currentCard.question)
            self.prepareMedia(self.currentCard.answer)
        elif cmd == 'sync':
            if not self.deck:
                return

            self.opbuttonsbox.hide()
            self.answerbuttonbox.hide()
            self.resultbuttonbox.hide()
            self.do_sync()
            self.set_question()
            self.set_stats()
        elif cmd == 'mark':
            if "marked" in self.currentCard.tags.lower():
                t = parseTags(self.currentCard.tags)
                t.remove("Marked")
                self.currentCard.tags = joinTags(t)
            else:
                self.currentCard.tags = joinTags(parseTags(self.currentCard.tags) + ["Marked"])
            self.currentCard.toDB(self.deck.s)
            if self.currentCard and "marked" in self.currentCard.tags.lower():
                self.markbuttonlabel.set_markup('<span color="red">mark</span>')
            else:
                self.markbuttonlabel.set_markup('mark')
        elif cmd == 'learnmore':
            self.deck.extraNewCards += 5

            self.deck.refresh()
            self.deck.updateAllPriorities()
            self.deck.rebuildCounts()
            self.deck.rebuildQueue()
            self.set_question()
            self.set_stats()

        elif cmd == 'reviewearly':
            self.deck.reviewEarly = True
            self.deck.refresh()
            self.deck.updateAllPriorities()
            self.deck.rebuildCounts()
            self.deck.rebuildQueue()
            self.set_question()
            self.set_stats()
Example #29
0
            else:
                self.path = "/"

        if self.path == "/":
            # refresh
            if deck:
                deck.rebuildQueue()
            self.flushWrite(self._outer())
        elif deck and self.path.startswith("/save"):
            deck.save()
            self.path = "/question#inner_top"
        elif deck and self.path.startswith("/mark"):
            if currentCard:
                f = deck.s.query(Fact).get(currentCard.factId)
                if "marked" in f.tags.lower():
                    t = parseTags(f.tags)
                    t.remove("Marked")
                    f.tags = joinTags(t)
                else:
                    f.tags = joinTags(parseTags(f.tags) + ["Marked"])
                f.setModified(textChanged=True)
                deck.updateFactTags([f.id])
                f.setModified()
                deck.flushMod()
                deck.s.flush()
                deck.s.expunge(f)
                history.pop()
            self.path = "/question#inner_top"
        elif deck and self.path.startswith("/replay"):
            if currentCard:
                self.prepareMedia(currentCard.question)
Example #30
0
            else:
                self.path="/"

        if self.path == "/":
            # refresh
            if deck:
                deck.reset()
            self.flushWrite(self._outer())
        elif deck and self.path.startswith("/save"):
            deck.save()
            self.path = "/question#inner_top"
        elif deck and self.path.startswith("/mark"):
            if currentCard:
                f = deck.s.query(Fact).get(currentCard.factId)
                if "marked" in f.tags.lower():
                    t = parseTags(f.tags)
                    t.remove("Marked")
                    f.tags = joinTags(t)
                else:
                    f.tags = joinTags(parseTags(
                        f.tags) + ["Marked"])
                f.setModified(textChanged=True, deck=deck)
                deck.updateFactTags([f.id])
                f.setModified()
                deck.flushMod()
                deck.s.flush()
                deck.s.expunge(f)
                history.pop()
            self.path = "/question#inner_top"
        elif deck and self.path.startswith("/replay"):
            if currentCard:
Example #31
0
 def hasTag(self, tag):
     alltags = parseTags(self.tags + "," +
                         self.fact.tags + "," +
                         self.cardModel.name + "," +
                         self.fact.model.tags)
     return findTag(tag, alltags)
Example #32
0
 def hasTag(self, tag):
     return findTag(tag, parseTags(self.allTags()))