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))
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)
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
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)
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
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""
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)
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]
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
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
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)))))
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
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()
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)
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()
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
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])
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
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()
def hasTag(self, tag): return findTag(tag, parseTags(self.allTags()))
def tags(self, id): if self.includeTags: return "\t" + ", ".join(parseTags(self.cardTags[id])) return ""
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
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()
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)
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:
def hasTag(self, tag): alltags = parseTags(self.tags + "," + self.fact.tags + "," + self.cardModel.name + "," + self.fact.model.tags) return findTag(tag, alltags)