def test_templates(): d = getEmptyDeck() m = d.models.current(); mm = d.models t = mm.newTemplate("Reverse") t['qfmt'] = "{{Back}}" t['afmt'] = "{{Front}}" mm.addTemplate(m, t) mm.save(m) f = d.newNote() f['Front'] = u'1' f['Back'] = u'2' d.addNote(f) assert d.cardCount() == 2 (c, c2) = f.cards() # first card should have first ord assert c.ord == 0 assert c2.ord == 1 # switch templates d.models.moveTemplate(m, c.template(), 1) c.load(); c2.load() assert c.ord == 1 assert c2.ord == 0 # removing a template should delete its cards assert d.models.remTemplate(m, m['tmpls'][0]) assert d.cardCount() == 1 # and should have updated the other cards' ordinals c = f.cards()[0] assert c.ord == 0 stripHTML(c.q()) == "2" # it shouldn't be possible to orphan notes by removing templates assert not d.models.remTemplate(m, m['tmpls'][0])
def test_templates(): d = getEmptyDeck() m = d.models.current() mm = d.models t = mm.newTemplate("Reverse") t['qfmt'] = "{{Back}}" t['afmt'] = "{{Front}}" mm.addTemplate(m, t) mm.save(m) f = d.newNote() f['Front'] = u'1' f['Back'] = u'2' d.addNote(f) assert d.cardCount() == 2 (c, c2) = f.cards() # first card should have first ord assert c.ord == 0 assert c2.ord == 1 # switch templates d.models.moveTemplate(m, c.template(), 1) c.load() c2.load() assert c.ord == 1 assert c2.ord == 0 # removing a template should delete its cards assert d.models.remTemplate(m, m['tmpls'][0]) assert d.cardCount() == 1 # and should have updated the other cards' ordinals c = f.cards()[0] assert c.ord == 0 stripHTML(c.q()) == "2" # it shouldn't be possible to orphan notes by removing templates assert not d.models.remTemplate(m, m['tmpls'][0])
def drawLastCard(self): "Show the last card if not the current one, and next time." if self.main.lastCard: if self.main.config["showLastCardContent"]: if self.state == "deckFinished" or self.main.currentCard.id != self.main.lastCard.id: q = self.main.lastCard.question.replace("<br>", " ") q = stripHTML(q) if len(q) > 50: q = q[:50] + "..." a = self.main.lastCard.answer.replace("<br>", " ") a = stripHTML(a) if len(a) > 50: a = a[:50] + "..." s = "%s<br>%s" % (q, a) s = stripLatex(s) self.write('<span class="lastCard">%s</span><br>' % s) if self.main.config["showLastCardInterval"]: if self.main.lastQuality > 1: msg = _("Well done! This card will appear again in " "<b>%(next)s</b>.") % { "next": self.main.lastScheduledTime } else: msg = _("This card will appear again later.") self.write(msg) self.write("<br>")
def new_name_base(old_base): """ Get the base of a new file name Look at the information on the card and use the data to create a base new name. """ # Several tries. First, look through the list. name, value = find_field(note, old_base) if value and not old_base in value: return value # Still here, next try the sort field. name, value = note.items()[mw.col.models.sortIdx(note.model())] value = stripHTML(value) if value and not old_base in value: return value for name, value in note.items(): # Last resort: go through the fields and grab the first # non-empty one, except the one with the file. value = stripHTML(value) if value and not old_base in value: return value # Well, shoot. Looks like the only field with anything interesting # is the one with the file. (Almost reasonable. One-side cards to # just listen to something and decide without further info if you # recoginze that.) raise ValueError(_(u'No data for new name found'))
def external_file_link(card, model): field_for_filename = "" field_for_page = "" # import user settings for field names from other add-on try: field_for_filename = __import__( "1994996371").open_in_external.field_for_filename field_for_page = __import__( "1994996371").open_in_external.field_for_page except: return "" if all([field_for_filename, field_for_page]): note = mw.col.getNote(card.nid) for i, f in enumerate(note.model()['flds']): if f['name'] == field_for_filename: file = note.fields[i] if f['name'] == field_for_page: page = note.fields[i] try: file page except: return "" f = stripHTML(file) p = stripHTML(page) pycmd = f"open_external_filesüöäüöä{f}üöäüöä{p}" if p: text = f"{f} , {p}" else: text = f"{f}" out = f"""<a href='javascript:pycmd("{pycmd}");'>{text}</a>""" return out
def JxAffectFields(Card,Types): # we now try to affect relevant fields for each type (first try fields with the name similar to the type) List=[] for (Type,TypeList) in JxType: if Type in Types: for Field in Card.fact.model.fieldModels: if Field.name in TypeList: List.append((Type,Field.name,stripHTML(Card.fact[Field.name]))) break if len(List)<len(Types): # there are still missing fields for the types, we could try the "Expression" field next and update the List if len(List)>0: (Done,Field) = zip(*List) else : Done=[] TempList=[] for (Type,TypeList) in JxType: if Type in Types and Type in Done: TempList.append(List.pop(0)) elif Type in Types: for Field in Card.fact.model.fieldModels: if Field.name == u"Expression": TempList.append((Type,Field.name,stripHTML(Card.fact[Field.name]))) break List = TempList if len(List)<len(Types): # field names and "Expression" have failed, we could still try to guess with the fields content # but for the time being, we will pass because I don't know how to choose between two fields that might only have kanas (maybee query other cards to decide between the fields, might be doable for sentences (ponctuation helps) and Kanji (only 1 Kanji character)) pass return List
def test_templates(): d = getEmptyDeck() m = d.currentModel() m.templates[1]['actv'] = True m.flush() f = d.newFact() f['Front'] = u'1' f['Back'] = u'2' d.addFact(f) assert d.cardCount() == 2 (c, c2) = f.cards() # first card should have first ord assert c.ord == 0 assert c2.ord == 1 # switch templates m.moveTemplate(c.template(), 1) c.load(); c2.load() assert c.ord == 1 assert c2.ord == 0 # removing a template should delete its cards m.delTemplate(m.templates[0]) assert d.cardCount() == 1 # and should have updated the other cards' ordinals c = f.cards()[0] assert c.ord == 0 stripHTML(c.q()) == "2"
def _processHtml(self, mime): html = mime.html() newMime = QMimeData() if self.strip and not html.startswith("<!--anki-->"): # special case for google images: if after stripping there's no text # and there are image links, we'll paste those as html instead if not stripHTML(html).strip(): newHtml = "" mid = self.editor.note.mid for url in self.editor.mw.col.media.filesInStr( mid, html, includeRemote=True): newHtml += self.editor.urlToLink(url) if not newHtml and mime.hasImage(): return self._processImage(mime) newMime.setHtml(newHtml) else: # use .text() if available so newlines are preserved; otherwise strip if mime.hasText(): return self._processText(mime) else: newMime.setText(stripHTML(mime.text())) else: if html.startswith("<!--anki-->"): html = html[11:] # no html stripping html = self.editor._filterHTML(html, localize=True) newMime.setHtml(html) return newMime
def test_modelChange(): deck = DeckStorage.Deck() m = Model(u"Japanese") m1 = m f = FieldModel(u'Expression', True, True) m.addFieldModel(f) m.addFieldModel(FieldModel(u'Meaning', False, False)) f = FieldModel(u'Reading', False, False) m.addFieldModel(f) m.addCardModel( CardModel(u"Recognition", u"%(Expression)s", u"%(Reading)s<br>%(Meaning)s")) m.addCardModel( CardModel(u"Recall", u"%(Meaning)s", u"%(Expression)s<br>%(Reading)s", active=False)) m.tags = u"Japanese" m1.cardModels[1].active = True deck.addModel(m1) f = deck.newFact() f['Expression'] = u'e' f['Meaning'] = u'm' f['Reading'] = u'r' f = deck.addFact(f) f2 = deck.newFact() f2['Expression'] = u'e2' f2['Meaning'] = u'm2' f2['Reading'] = u'r2' deck.addFact(f2) m2 = BasicModel() m2.cardModels[1].active = True deck.addModel(m2) # convert to basic assert deck.modelUseCount(m1) == 2 assert deck.modelUseCount(m2) == 0 assert deck.cardCount == 4 assert deck.factCount == 2 fmap = { m1.fieldModels[0]: m2.fieldModels[0], m1.fieldModels[1]: None, m1.fieldModels[2]: m2.fieldModels[1] } cmap = {m1.cardModels[0]: m2.cardModels[0], m1.cardModels[1]: None} deck.changeModel([f.id], m2, fmap, cmap) assert deck.modelUseCount(m1) == 1 assert deck.modelUseCount(m2) == 1 assert deck.cardCount == 3 assert deck.factCount == 2 (q, a) = deck.s.first(""" select question, answer from cards where factId = :id""", id=f.id) assert stripHTML(q) == u"e" assert stripHTML(a) == u"r"
def test_modelChange(): deck = DeckStorage.Deck() m = Model(u"Japanese") m1 = m f = FieldModel(u'Expression', True, True) m.addFieldModel(f) m.addFieldModel(FieldModel(u'Meaning', False, False)) f = FieldModel(u'Reading', False, False) m.addFieldModel(f) m.addCardModel(CardModel(u"Recognition", u"%(Expression)s", u"%(Reading)s<br>%(Meaning)s")) m.addCardModel(CardModel(u"Recall", u"%(Meaning)s", u"%(Expression)s<br>%(Reading)s", active=False)) m.tags = u"Japanese" m1.cardModels[1].active = True deck.addModel(m1) f = deck.newFact() f['Expression'] = u'e' f['Meaning'] = u'm' f['Reading'] = u'r' f = deck.addFact(f) f2 = deck.newFact() f2['Expression'] = u'e2' f2['Meaning'] = u'm2' f2['Reading'] = u'r2' deck.addFact(f2) m2 = BasicModel() m2.cardModels[1].active = True deck.addModel(m2) # convert to basic assert deck.modelUseCount(m1) == 2 assert deck.modelUseCount(m2) == 0 assert deck.cardCount == 4 assert deck.factCount == 2 fmap = {m1.fieldModels[0]: m2.fieldModels[0], m1.fieldModels[1]: None, m1.fieldModels[2]: m2.fieldModels[1]} cmap = {m1.cardModels[0]: m2.cardModels[0], m1.cardModels[1]: None} deck.changeModel([f.id], m2, fmap, cmap) deck.reset() assert deck.modelUseCount(m1) == 1 assert deck.modelUseCount(m2) == 1 assert deck.cardCount == 3 assert deck.factCount == 2 (q, a) = deck.s.first(""" select question, answer from cards where factId = :id""", id=f.id) assert stripHTML(q) == u"e" assert stripHTML(a) == u"r"
def alertUser(self): text ='%d new card(s) added. %d card(s) were duplicates' % ( len(self.added), len(self.duplicates)) text += '\n\nNEW CARDS:\n==========\n' for card in self.added: text += stripHTML(card.front) + '\n' text += '\nDUPLICATES:\n===========\n' for card in self.duplicates: text += stripHTML(card.front) + '\n' showText(text)
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 stripHTML(splitFields(flds)[0]) == stripHTML(self.fields[0]): return 2 return False
def _updateListItems(self): cardInfo = self._getCardInfo(self.did) self.cardListWidget.clear() posWidth = len(str(len(cardInfo) + 1)) for i, card in enumerate(cardInfo, start=1): if self.settings['prioEnabled']: text = ' {} \t{}'.format(card['priority'], stripHTML(card['title'])) else: text = '❰ {} ❱\t{}'.format( str(i).zfill(posWidth), stripHTML(card['title'])) item = QListWidgetItem(text) item.setData(Qt.UserRole, card) self.cardListWidget.addItem(item)
def new_name_base(old_base, note): """ Get the base of a new file name Look at the information on the card and use the data to create a base new name. """ def find_field(note, old_base): """ Compare the candidate fields and the notes fields. Look through the two lists, name_source_fields and the note’s items, to find the field we should use. Put in function so we can break out of nested loops. """ for sf in name_source_fields: for name, value in note.items(): if name == sf: value = stripHTML(value) # Check here if we have something left. So we # can keep on looking when we have a candidate # field but it’s empty. if value and not old_base in value: # Avoid the field we got the name from # (i.e. the audio, image field). return name, value # We got here: no match. return None, None # Several tries. First, look through the list. name, value = find_field(note, old_base) if value and not old_base in value: return value # Still here, next try the sort field. name, value = note.items()[mw.col.models.sortIdx(note.model())] value = stripHTML(value) if value and not old_base in value: return value for name, value in note.items(): # Last resort: go through the fields and grab the first # non-empty one, except the one with the file. value = stripHTML(value) if value and not old_base in value: return value # Well, shoot. Looks like the only field with anything interesting # is the one with the file. (Almost reasonable. One-side cards to # just listen to something and decide without further info if you # recoginze that.) raise ValueError(_(u'No data for new name found'))
def doNote(note): changed = 0 try: #This should be the dictionary form of the word srcTxt = stripHTML(note[expField]) #strip any readings srcTxt = re.sub('(\[.*\])', '', srcTxt) #wtfdowedo qjapR, ajapR, engR = find_examples(srcTxt) if len(qjapR) < 1: return False Examples = '<br>'.join(ajapR) Questions = '<br>'.join(qjapR) English = '<br>'.join(engR) if (note['Tatoeba Examples'] != Examples or note['Tatoeba English'] != English or note['Tatoeba Questions'] != Questions): note['Tatoeba Examples'] = Examples note['Tatoeba English'] = English note['Tatoeba Questions'] = Questions return True return False except KeyError: return False
def hanzi_context(txt, extra, context, tag, fullname): ''' For use on a Hanzi field. Return a list of all the other Hanzi synonyms, with the common characters hidden, to allow the user to identify the correct hanzi from a note. ''' other_hanzi = [] for k, v in context.iteritems(): if re.match(r'Hanzi.*', k, flags=re.IGNORECASE) and v != txt: other_hanzi += [k] if len(other_hanzi) < 1: return "" other_hanzi.sort() other_hanzi_values = [] for v in other_hanzi: value = stripHTML(re.sub(r, r'\1', no_sound(context[v]))) if len(value) > 0: other_hanzi_values += [value] if len(other_hanzi_values) < 1: return "" def concat(a, b): return a + " / " + b context_string = reduce(concat, other_hanzi_values) for h in txt: if h >= u'\u4e00' and h <= u'\u9fff': context_string = re.sub(h, " _ ", context_string) context_string = re.sub(" ", " ", context_string) return context_string
def per(st, n): notecfg = getFilter(n) if notecfg is None: return changed = False proper_nouns_known = cfg('Option_ProperNounsAlreadyKnown') morphemizer = getMorphemizerByName(notecfg['Morphemizer']) for f in notecfg['Fields']: ms = getMorphemes(morphemizer, stripHTML(n[f]), n.tags) for m in sorted(ms, key=lambda x: len(x.inflected), reverse=True): # largest subs first locs = allDb().getMatchingLocs(m) mat = max(loc.maturity for loc in locs) if locs else 0 if (proper_nouns_known and m.isProperNoun()) or (mat >= cfg('threshold_known')): continue n[f] = nonSpanSub('(%s)' % m.inflected, '<b>\\1</b>', n[f]) changed = True if changed: n.flush() return st
def TTS_record_old(text, language): text = re.sub("\[sound:.*?\]", "", stripHTML(text.replace("\n", "")).encode('utf-8')) address = TTS_ADDRESS + '?tl=' + language + '&q=' + quote_plus(text) file = util.generateFileName(text, 'g', slanguages[get_language_id(language)][2]) if subprocess.mswindows: subprocess.Popen([ 'mplayer.exe', '-ao', 'win32', '-slave', '-user-agent', "'Mozilla/5.0'", address, '-dumpstream', '-dumpfile', file ], startupinfo=util.si, stdin=PIPE, stdout=PIPE, stderr=STDOUT).wait() if not config.quote_mp3: return file.decode(slanguages[get_language_id(language)][2]) else: subprocess.Popen([ 'mplayer', '-slave', '-user-agent', "'Mozilla/5.0'", address, '-dumpstream', '-dumpfile', file ], stdin=PIPE, stdout=PIPE, stderr=STDOUT).wait() return file.decode('utf-8')
def TTS_read(text, language=TTS_language): text = stripHTML(text.replace("\n", "")).encode("utf-8") text = text.replace(chr(194), " ") # address = TTS_ADDRESS+'?tl='+language+'&q='+ quote_plus(text) address = ( "http://api.ispeech.org/api/rest?apikey=8d1e2e5d3909929860aede288d6b974e&Speed=-3&format=mp3&action=convert&text=" + quote_plus(text) ) if subprocess.mswindows: if speech_engine == "Akapela": param = ["SayStatic.exe", text] else: param = ["1.bat", quote_plus(text)] if subprocessing: subprocess.Popen(param, startupinfo=si, stdin=PIPE, stdout=PIPE, stderr=STDOUT) else: subprocess.Popen(param, startupinfo=si, stdin=PIPE, stdout=PIPE, stderr=STDOUT).communicate() else: if speech_engine == "Akapela": param = ["SayStatic.exe", text] else: param = ["1.bat", quote_plus(text)] if subprocessing: subprocess.Popen(param, stdin=PIPE, stdout=PIPE, stderr=STDOUT) else: subprocess.Popen(param, stdin=PIPE, stdout=PIPE, stderr=STDOUT).communicate()
def formatQA(cid, mid, fact, tags, cm): "Return a dict of {id, question, answer}" d = {'id': cid} fields = {} for (k, v) in fact.items(): fields["text:"+k] = stripHTML(v[1]) if v[1]: fields[k] = '<span class="fm%s">%s</span>' % ( hexifyID(v[0]), v[1]) else: fields[k] = u"" fields['tags'] = tags[0] fields['Tags'] = tags[0] fields['modelTags'] = tags[1] fields['cardModel'] = tags[2] # render q & a ret = [] for (type, format) in (("question", cm.qformat), ("answer", cm.aformat)): try: html = format % fields except (KeyError, TypeError, ValueError): html = _("[invalid question/answer format]") d[type] = runFilter("formatQA", html, type, cid, mid, fact, tags, cm) return d
def hanzi_context(txt, extra, context, tag, fullname): ''' For use on a Hanzi field. Return a list of all the other Hanzi synonyms, with the common characters hidden, to allow the user to identify the correct hanzi from a note. ''' other_hanzi = [] for k, v in context.iteritems(): if re.match(r'Hanzi.*', k, flags=re.IGNORECASE) and v != txt : other_hanzi += [k] if len(other_hanzi)<1: return "" other_hanzi.sort() other_hanzi_values = [] for v in other_hanzi: value = stripHTML(re.sub(r, r'\1', no_sound(context[v]))) if len(value)>0: other_hanzi_values += [value] if len(other_hanzi_values)<1: return "" def concat(a, b): return a + " / " + b context_string = reduce(concat, other_hanzi_values) for h in txt: if h >= u'\u4e00' and h <= u'\u9fff': context_string = re.sub(h, " _ ", context_string) context_string = re.sub(" ", " ", context_string) return context_string
def playOSXsayTTS(text, voice): text = re.sub("\[sound:.*?\]", "", stripHTML(text.replace("\n", "")).encode('utf-8')) subprocess.Popen(['say', '-v', voice, text], stdin=PIPE, stdout=PIPE, stderr=STDOUT).communicate()
def playEkhoTTS(text, language): text = re.sub("\[sound:.*?\]", "", stripHTML(text.replace("\n", "")).encode('utf-8')) subprocess.Popen(['ekho', '-v', language, text], stdin=PIPE, stdout=PIPE, stderr=STDOUT).communicate()
def formatQA(cid, mid, fact, tags, cm, deck, build=False): "Return a dict of {id, question, answer}" d = {'id': cid} fields = {} for (k, v) in fact.items(): fields["text:"+k] = stripHTML(v[1]) if v[1]: fields[k] = '<span class="fm%s">%s</span>' % ( hexifyID(v[0]), v[1]) else: fields[k] = u"" fields['tags'] = tags[0] fields['Tags'] = tags[0] fields['modelTags'] = tags[1] fields['cardModel'] = tags[2] # render q & a ret = [] for (type, format) in (("question", cm.qformat), ("answer", cm.aformat)): # convert old style format = re.sub("%\((.+?)\)s", "{{\\1}}", format) # allow custom rendering functions & info fields = runFilter("prepareFields", fields, cid, mid, fact, tags, cm, deck) html = render(format, fields) d[type] = runFilter("formatQA", html, type, cid, mid, fact, tags, cm, deck, build) return d
def typeAnsAnswerFilter(self, buf): if not self.typeCorrect: return re.sub(self.typeAnsPat, "", buf) # tell webview to call us back with the input content self.web.eval("_getTypedText();") # munge correct value parser = HTMLParser.HTMLParser() cor = stripHTML(self.mw.col.media.strip(self.typeCorrect)) cor = parser.unescape(cor) given = self.typedAnswer # compare with typed answer res = self.correct(cor, given) if cor != given: # Wrap the extra text in an id-ed span. res += u"<span id=rightanswer><br> {0} <br> {1} </span>".format( _(u"Correct answer was:"), cor) # and update the type answer area def repl(match): # can't pass a string in directly, and can't use re.escape as it # escapes too much return """ <span style="font-family: '%s'; font-size: %spx">%s</span>""" % ( self.typeFont, self.typeSize, res) return re.sub(self.typeAnsPat, repl, buf)
def historyRestore(self, mode, sorted_res, model): n = self.currentField field = model['flds'][n]['name'] last_val = {} keys = [] for nid in sorted_res[:100]: oldNote = self.note.col.getNote(nid) if field in oldNote: html = oldNote[field] else: try: html = oldNote.fields[n] except IndexError: pass if html.strip(): text = stripHTML(html) else: text = None if text and text not in last_val: keys.append(text) last_val[text] = html if not last_val: tooltip("No prior entries for this field found.") return False txt = "Set field to:" (text, ret) = myGetField(self.parentWindow, txt, keys, title="Field History") if not ret or not text.strip() or text not in last_val: return False self.note[field] = last_val[text]
def _processHtml(self, mime): html = mime.html() if self.strip: html = stripHTML(html) mime = QMimeData() mime.setHtml(html) return mime
def doNote(note, isBulk=0): try: #Strip out any annoying HTML srcTxt = stripHTML(note[expField]) #get list of results and cancel if none results = getMeaning(srcTxt, isBulk) reading = '' if not results: return False #Insert results if results[1]: reading = "[" + results[1] + "]" if note[definitionField] != results[2] \ or (note[dictionaryForm] != results[0] + reading): note[definitionField] = results[2] note[dictionaryForm] = results[0] + reading #return True for a refresh return True #if no changed were made, return false so no refresh happens return False except KeyError: return False
def _findField(self, token, isNeg): field = value = '' parts = token.split(':', 1); field = parts[0].lower() value = "%" + parts[1].replace("*", "%") + "%" # find models that have that field mods = {} for m in self.deck.models().values(): for f in m.fields: if f['name'].lower() == field: mods[m.id] = (m, f['ord']) if not mods: # nothing has that field self.lims['valid'] = False return # gather fids regex = value.replace("%", ".*") fids = [] for (id,mid,flds) in self.deck.db.execute(""" select id, mid, flds from facts where mid in %s and flds like ? escape '\\'""" % ( ids2str(mods.keys())), "%" if self.full else value): flds = splitFields(flds) ord = mods[mid][1] str = flds[ord] if self.full: str = stripHTML(str) if re.search(regex, str): fids.append(id) extra = "not" if isNeg else "" self.lims['fact'].append("id %s in %s" % (extra, ids2str(fids)))
def typeAnsAnswerFilter(self, buf): # tell webview to call us back with the input content self.web.eval("_getTypedText();") if not self.typeCorrect or not self.typedAnswer: return re.sub(self.typeAnsPat, "", buf) origSize = len(buf) buf = buf.replace("<hr id=answer>", "") hadHR = len(buf) != origSize # munge correct value parser = HTMLParser.HTMLParser() cor = stripHTML(self.mw.col.media.strip(self.typeCorrect)) cor = parser.unescape(cor) given = self.typedAnswer # compare with typed answer res = self.correct(given, cor, showBad=False) # and update the type answer area def repl(match): # can't pass a string in directly, and can't use re.escape as it # escapes too much s = """ <span style="font-family: '%s'; font-size: %spx">%s</span>""" % ( self.typeFont, self.typeSize, res) if hadHR: # a hack to ensure the q/a separator falls before the answer # comparison when user is using {{FrontSide}} s = "<hr id=answer>" + s return s return re.sub(self.typeAnsPat, repl, buf)
def return_data(idx): """ Return a cleaned-up version of the field content. Get the text, remove html, and return the field name, the clean text, and what we got when we tried to split into kanji and kana, when different from the text. """ text = note[field_names[idx]] # This is taken from aqt/browser.py. text = text.replace(u'<br>', u' ') text = text.replace(u'<br />', u' ') if strip_interpunct: text = text.replace(u'・', u'') text = stripHTML(text) text = stripSounds(text) # Reformat so we have exactly one space between words. text = u' '.join(text.split()) if not text and not get_empty: raise ValueError('Source field empty') # We pass the reading/plain on to the update dialog. We don't # look at the texts any more to decide what to do. So don't # set anything to empty here. Rather do the split even if it # is pointless. base = furigana.kanji(text) ruby = furigana.kana(text) return field_names[idx], fname, text, base, ruby, readings
def doNote(note): try: changed = 0 # Strip out any annoying HTML srcTxt = stripHTML(note[expField]) # add splits of 6 kanjis = re.findall(ur'[\u4e00-\u9fbf]', srcTxt) note['Kanji Removed All'] = re.sub(ur'[\u4e00-\u9fbf]', '_', srcTxt) # clear all the kanji fields for i in range(1, 7): if len(kanjis) >= i: if (note['Kanji Removed ' + str(i)] != re.sub(kanjis[i - 1], '_', srcTxt)): note['Kanji Removed ' + str(i)] = re.sub(kanjis[i - 1], '_', srcTxt) changed = 1 else: if (note['Kanji Removed ' + str(i)] != ''): note['Kanji Removed ' + str(i)] = '' changed = 1 except KeyError: raise if changed == 1: return True else: return False
def sel_to_external_editor(note, selected, block=False, lang=None): # if not selected: # tooltip("no text selected.") # return selected = selected.replace(" ", " ") # remove nbsp; if not lang: fldname = gc("file extension from field") if not fldname: tooltip("value missing from config") return for fname, fcont in note.items(): if fname == fldname: lang = stripHTML(fcont) if not lang: for t in note.tags: for k, v in gc("tags to file ending", {}).items(): for l in v: if t == l: lang = k break if lang is None: tooltip("no language set. no relevant field or tag found.") return fn = gc("custom_files_for_langs", {}).get(lang) suf = "Irrelevant" if fn else f".{lang}" try: text = edit_string_externally_and_return_mod(selected, filename=fn, block=block, suffix=suf) except RuntimeError: tooltip('Error when trying to edit externally') return if block: return text
def _findField(self, token, isNeg): field = value = '' parts = token.split(':', 1); field = parts[0].lower() value = "%" + parts[1].replace("*", "%") + "%" # find models that have that field mods = {} for m in self.col.models.all(): for f in m['flds']: if f['name'].lower() == field: mods[str(m['id'])] = (m, f['ord']) if not mods: # nothing has that field self.lims['valid'] = False return # gather nids regex = value.replace("_", ".").replace("%", ".*") nids = [] for (id,mid,flds) in self.col.db.execute(""" select id, mid, flds from notes where mid in %s and flds like ? escape '\\'""" % ( ids2str(mods.keys())), "%" if self.full else value): flds = splitFields(flds) ord = mods[str(mid)][1] strg = flds[ord] if self.full: strg = stripHTML(strg) if re.search(regex, strg): nids.append(id) extra = "not" if isNeg else "" self.lims['preds'].append(""" n.mid in %s and n.id %s in %s""" % ( ids2str(mods.keys()), extra, ids2str(nids)))
def return_data(idx): """ Return a cleaned-up version of the field content. Get the text, remove html, and return the field name, the clean text, and what we got when we tried to split into kanji and kana, when different from the text. """ text = note[field_names[idx]] # This is taken from aqt/browser.py. text = text.replace('<br>', ' ') text = text.replace('<br />', ' ') if strip_interpunct: text = text.replace('・', '') text = stripHTML(text) text = stripSounds(text) # Reformat so we have exactly one space between words. text = ' '.join(text.split()) if not text and not get_empty: raise ValueError('Source field empty') # We pass the reading/plain on to the update dialog. We don't # look at the texts any more to decide what to do. So don't # set anything to empty here. Rather do the split even if it # is pointless. base = kanji_filter(text) ruby = kana_filter(text) return field_names[idx], fname, text, base, ruby, readings
def doNote(note): try: changed = 0 # Strip out any annoying HTML srcTxt = stripHTML(note[expField]) # add splits of 6 kanjis = re.findall(ur"[\u4e00-\u9fbf]", srcTxt) note["Kanji Removed All"] = re.sub(ur"[\u4e00-\u9fbf]", "_", srcTxt) # clear all the kanji fields for i in range(1, 7): if len(kanjis) >= i: if note["Kanji Removed " + str(i)] != re.sub(kanjis[i - 1], "_", srcTxt): note["Kanji Removed " + str(i)] = re.sub(kanjis[i - 1], "_", srcTxt) changed = 1 else: if note["Kanji Removed " + str(i)] != "": note["Kanji Removed " + str(i)] = "" changed = 1 except KeyError: raise if changed == 1: return True else: return False
def playGoogleTTS(text, language): text = re.sub("\[sound:.*?\]", "", stripHTML(text.replace("\n", "")).encode('utf-8')) address = TTS_ADDRESS + '?tl=' + language + '&q=' + quote_plus(text) if subprocess.mswindows: param = [ 'mplayer.exe', '-ao', 'win32', '-slave', '-user-agent', "'Mozilla/5.0'", address ] if config.subprocessing: subprocess.Popen(param, startupinfo=util.si, stdin=PIPE, stdout=PIPE, stderr=STDOUT) else: subprocess.Popen(param, startupinfo=util.si, stdin=PIPE, stdout=PIPE, stderr=STDOUT).communicate() else: param = ['mplayer', '-slave', '-user-agent', "'Mozilla/5.0'", address] if config.subprocessing: subprocess.Popen(param, stdin=PIPE, stdout=PIPE, stderr=STDOUT) else: subprocess.Popen(param, stdin=PIPE, stdout=PIPE, stderr=STDOUT).communicate()
def parse_text(loc_corpus, text): parsed_morphs = getMorphemes(morphemizer, stripHTML(text)) if len(parsed_morphs) == 0: return loc_corpus.add_line_morphs( [m.deinflected() for m in parsed_morphs])
def typeAnsAnswerFilter(self, buf): if not self.typeCorrect: return re.sub(self.typeAnsPat, "", buf) origSize = len(buf) buf = buf.replace("<hr id=answer>", "") hadHR = len(buf) != origSize # munge correct value parser = html.parser.HTMLParser() cor = self.mw.col.media.strip(self.typeCorrect) cor = re.sub("(\n|<br ?/?>|</?div>)+", " ", cor) cor = stripHTML(cor) # ensure we don't chomp multiple whitespace cor = cor.replace(" ", " ") cor = parser.unescape(cor) cor = cor.replace("\xa0", " ") cor = cor.strip() given = self.typedAnswer # compare with typed answer res = self.correct(given, cor, showBad=False) # and update the type answer area def repl(match): # can't pass a string in directly, and can't use re.escape as it # escapes too much s = """ <span style="font-family: '%s'; font-size: %spx">%s</span>""" % ( self.typeFont, self.typeSize, res) if hadHR: # a hack to ensure the q/a separator falls before the answer # comparison when user is using {{FrontSide}} s = "<hr id=answer>" + s return s return re.sub(self.typeAnsPat, repl, buf)
def _get_definition(self, word): # Sanitize string word = stripHTML(word) start_token = '<section class="word__defination--2q7ZH">' end_token = '</section>' context = ssl._create_unverified_context() url = 'https://www.etymonline.com/word/{}'.format(word) try: response = urllib.request.urlopen(url, context=context) except Exception: return 'No etymology entry found.' html = response.read().decode('utf-8') start = html.index(start_token) + len(start_token) end = html.index(end_token) return stripHTML(html[start:end])
def playsapi5TTS(text, voice): text = re.sub("\[sound:.*?\]", "", stripHTML(text.replace("\n", ""))) param = [vbs_launcher, sapi5_path,'-hex', '-voice', util.dumpUnicodeStr(voice), util.dumpUnicodeStr(text)] if config.subprocessing: subprocess.Popen(param, startupinfo=util.si, stdin=PIPE, stdout=PIPE, stderr=STDOUT) else: subprocess.Popen(param, startupinfo=util.si, stdin=PIPE, stdout=PIPE, stderr=STDOUT).communicate()
def free_media_name(base, end): """ Return a useful media name. Return a name that can be used for the media file. That is one that based on the base name and end, but doesn't exist, nor does it clash with another file different only in upper/lower case. """ base = stripHTML(base) # Basically stripping the 'invalidFilenameChars'. (Not tested too much). base = re.sub("[\\/:\*?'\"<>\|]", "", base) if normalize_file_names_on_cards and isMac: base = unicodedata.normalize("NFD", base) mdir = mw.col.media.dir() if not exists_lc(mdir, base + end): return os.path.join(mdir, base + end), base + end for i in range(1, 10000): # Don't be silly. Give up after 9999 tries (by falling out of # this loop). long_name = u"{0}_{1}{2}".format(base, i, end) if not exists_lc(mdir, long_name): # New: return both full path and the file name (with ending). return os.path.join(mdir, long_name), long_name # The only way we can have arrived here is by unsuccessfully # trying the 10000 names. raise ValueError("Could not find free name.")
def doNote(note, expF=None , kanjiDField=None): #If no fields were given then use the defaults if expF==None: global expressionField else: expressionField = expF if kanjiDField==None: global kanjiDstField else: kanjiDstField = kanjiDField #Strip out any annoying HTML srcTxt = stripHTML(note[expField]) changed = 0 #Add the data to the dst field result = joinseparator.join(lookupKanjiInfo(srcTxt, 'keyword')) if result and note[kanjiDstField] != result: changed = 1 note[kanjiDstField] = result if changed == 1: return True else: return False
def free_media_name(base, end): u"""Return a useful media name Return a pair of a file name that can be used for the media file, and the whole file path. The name is based on the base name and end, but doesn’t exist, nor does it clash with another file different only in upper/lower case. If no name can be found, a ValueError is raised. """ base = stripHTML(base) # Strip the ‘invalidFilenameChars’ by hand. base = re.sub(ur'[\\/:\*?\'"<>\|]', '', base) base = unicodedata.normalize('NFC', base) # Looks like the normalization issue has finally been # solved. Always use NFC versions of file names now. mdir = mw.col.media.dir() if not exists_lc(mdir, base + end): return os.path.join(mdir, base + end), base + end for i in range(1, 10000): # Don't be silly. Give up after 9999 tries (by falling out of # this loop). long_name = u'{0}_{1}{2}'.format(base, i, end) if not exists_lc(mdir, long_name): return os.path.join(mdir, long_name), long_name # The only way we can have arrived here is by unsuccessfully # trying the 10000 names. raise ValueError('Could not find free name.')
def doNote(note, isBulk=0): try: #Strip out any annoying HTML srcTxt = stripHTML(note[expField]) #get list of results and cancel if none results = getMeaning(srcTxt, isBulk) reading = '' if not results: return False #Insert results if results[1]: reading = "[" + results[1] + "]" if note[definitionField] != results[2] \ or (note[dictionaryForm] != results[0] + reading) \ or (note[expField] != results[0]): note[expField] = results[0] note[definitionField] = results[2] note[dictionaryForm] = results[0] + reading #return True for a refresh return True #if no changed were made, return false so no refresh happens return False except KeyError: return False
def typeAnsAnswerFilter(self, buf): if not self.typeCorrect: return re.sub(self.typeAnsPat, "", buf) # tell webview to call us back with the input content self.web.eval("_getTypedText();") # munge correct value parser = HTMLParser.HTMLParser() cor = stripHTML(self.mw.col.media.strip(self.typeCorrect)) cor = parser.unescape(cor) given = self.typedAnswer # compare with typed answer res = self.correct(cor, given) if cor != given: # Wrap the extra text in an id-ed span. res += u"<span id=rightanswer><br> {0} <br> {1} </span>".format(_(u"Correct answer was:"), cor) # and update the type answer area def repl(match): # can't pass a string in directly, and can't use re.escape as it # escapes too much return """ <span style="font-family: '%s'; font-size: %spx">%s</span>""" % ( self.typeFont, self.typeSize, res, ) return re.sub(self.typeAnsPat, repl, buf)
def doNote(note): changed = 0 try: #This should be the dictionary form of the word srcTxt = stripHTML(note[expField]) #strip any readings srcTxt = re.sub('(\[.*\])', '', srcTxt) #wtfdowedo qjapR, ajapR, engR = find_examples(srcTxt) if len(qjapR) <1: return False Examples = '<br>'.join(ajapR) Questions = '<br>'.join(qjapR) English = '<br>'.join(engR) if(note['Tatoeba Examples'] != Examples or note['Tatoeba English'] != English or note['Tatoeba Questions'] != Questions): note['Tatoeba Examples'] = Examples note['Tatoeba English'] = English note['Tatoeba Questions'] = Questions return True return False except KeyError: return False
def kana_type_ans_answer_filter(self, buf): # Redo bits of typeQuesAnswerFilter to get the field name typed in # and most of the old typeAnsAnswerFilter. m = re.search(self.typeAnsPat, buf) if not self.typeCorrect: return re.sub(self.typeAnsPat, "", buf) # tell webview to call us back with the input content # Copy-and-pasted. I guess it’s harmless self.web.eval("_getTypedText();") # munge correct value model_name = self.card.model()[u'name'] # Cascade of tests if m: fld = m.group(1) # if not fld.startswith("cq:") and reading_field in fld.lower(): if not fld.startswith("cq:") and reading_field in fld.lower() \ and japanese_model in model_name.lower(): cor = self.mw.col.media.strip(stripHTML(self.typeCorrect)) # The extra kana(...) here is the whole point of this plugin. res = self.correct(kana(cor), self.typedAnswer) return re.sub(self.typeAnsPat, """ <span id=coranskana style="font-family: '%s'; font-size: %spx">%s</span>""" % (self.typeFont, self.typeSize, res), buf) # Still here: we failed one of our tests. So do it the old way. return old_type_ans_answer_filter(self, buf)
def _deHTML(content): # replace HTML-symbols # NOTE: not sure if this is correct lines = content.replace(' ', ' ') lines = re.sub('<br( /)?>|<div>|\r\n|\r', '\n', lines) #return anki.utils.stripHTML(lines).split('\n')[1:] return stripHTML(lines)[1:]
def typeAnsAnswerFilter(self, buf): # tell webview to call us back with the input content self.web.eval("_getTypedText();") if not self.typeCorrect: return re.sub(self.typeAnsPat, "", buf) origSize = len(buf) buf = buf.replace("<hr id=answer>", "") hadHR = len(buf) != origSize # munge correct value parser = HTMLParser.HTMLParser() cor = stripHTML(self.mw.col.media.strip(self.typeCorrect)) # ensure we don't chomp multiple whitespace cor = cor.replace(" ", " ") cor = parser.unescape(cor) cor = cor.replace(u"\xa0", " ") given = self.typedAnswer # compare with typed answer res = self.correct(given, cor, showBad=False) # and update the type answer area def repl(match): # can't pass a string in directly, and can't use re.escape as it # escapes too much s = """ <span style="font-family: '%s'; font-size: %spx">%s</span>""" % ( self.typeFont, self.typeSize, res) if hadHR: # a hack to ensure the q/a separator falls before the answer # comparison when user is using {{FrontSide}} s = "<hr id=answer>" + s return s return re.sub(self.typeAnsPat, repl, buf)
def historyRestore(self, mode, results, model, fld): field = model['flds'][fld]['name'] last_val = {} keys = [] for nid in results[:100]: oldNote = self.note.col.getNote(nid) if field in oldNote: html = oldNote[field] else: try: html = oldNote.fields[fld] except IndexError: pass if html.strip(): text = stripHTML(html) else: text = None if text and text not in last_val: keys.append(text) last_val[text] = html if not last_val: tooltip("No prior entries for this field found.") return False txt = "Set field to:" (text, ret) = myGetField(self.parentWindow, txt, keys, title="Field History") if not ret or not text.strip() or text not in last_val: return False self.note[field] = last_val[text]
def historyRestore(self, mode, sorted_res, model): n = self.currentField field = model['flds'][n]['name'] last_val = {} for nid in sorted_res[:100]: oldNote = self.note.col.getNote(nid) if field in oldNote: html = oldNote[field] else: try: html = oldNote.fields[n] except IndexError: pass if html.strip(): text = stripHTML(html) else: text = None if text and text not in last_val: last_val[text] = html if not last_val: tooltip("No prior entries for this field found.") return False txt = "Set field to:" (text, ret) = myGetField(self.parentWindow, txt, last_val.keys(), title="Field History") if not ret or not text.strip() or text not in last_val: return False self.note[field] = last_val[text]
def __editor_helper(self, saveback): lang = gc("editor field content and save back file extension" ) if saveback else None if saveback: fieldcontent = self.note.fields[thefield] if fieldcontent.count(unique_before) > 1 or fieldcontent.count( unique_after) > 1: tooltip("Aborting. helper strings too often in text field.") set_field_to_text(self, thefield, oldcontent) # restore old content return before, rest = fieldcontent.split(unique_before) selection, after = rest.split(unique_after) stripped = stripHTML(selection) else: stripped = self.web.selectedText() out = sel_to_external_editor(self.note, stripped, block=saveback, lang=lang) if saveback: if out: new = before + adjust_text_to_html(out) + after else: # restore old content, remove temporary insertion new = oldcontent set_field_to_text(self, thefield, new)
def _get_character(card: Card, config: DeckConfig) -> str: """ Extracts the character to write from `card`. Returns: character (str): The character contained in field `config.field` of `card`. Raises: MaobiException: - If there is no field called `deck.field` in `card`. - If the field called `config.field` is empty. - If the field called `config.field` contains more than one character. """ # Check that the character field is really there note = card.note() note_type = note.model()["name"] field_name = config.field if field_name not in note: raise MaobiException( f"There is no field '{field_name}' in note type {note_type}!") # Check that the character is really exactly one character character = note[field_name] character = stripHTML(character) if len(character) == 0: raise MaobiException(f"Field '{field_name}' was empty!") if len(character) > 1: raise MaobiException( f"Expected a single character, but was '{character}'!") return character
def _findField(self, token, isNeg): field = value = '' parts = token.split(':', 1); field = parts[0].lower() value = "%" + parts[1].replace("*", "%") + "%" # find models that have that field mods = {} for m in self.col.models.all(): for f in m['flds']: if f['name'].lower() == field: mods[str(m['id'])] = (m, f['ord']) if not mods: # nothing has that field self.lims['valid'] = False return # gather nids regex = value.replace("%", ".*") nids = [] for (id,mid,flds) in self.col.db.execute(""" select id, mid, flds from notes where mid in %s and flds like ? escape '\\'""" % ( ids2str(mods.keys())), "%" if self.full else value): flds = splitFields(flds) ord = mods[str(mid)][1] strg = flds[ord] if self.full: strg = stripHTML(strg) if re.search(regex, strg): nids.append(id) extra = "not" if isNeg else "" self.lims['preds'].append("n.id %s in %s" % (extra, ids2str(nids)))