def refresh(self, reload=False): """ Refresh display by re-enabling night or normal mode, regenerate customizable css strings. """ state = self.config.state_on.value if not self.profile_loaded: alert(ERROR_NO_PROFILE) return try: if state: if reload: self.off() self.on() else: self.off() except Exception: alert(ERROR_SWITCH % traceback.format_exc()) return # Reload current screen. if mw.state == 'review': mw.moveToState('overview') mw.moveToState('review') if mw.state == 'deckBrowser': mw.deckBrowser.refresh() if mw.state == 'overview': mw.overview.refresh() # Redraw toolbar (should be always visible). mw.toolbar.draw() self.update_menu() return True
def _changeDecks(self, item): up = mw.col.conf.get('Blitzkrieg.updateOV', False) if up and item.type in ('deck','dyn','pinDeck','pinDyn') \ and mw.state == 'overview': d = mw.col.decks.byName(item.fullname) mw.col.decks.select(d["id"]) mw.moveToState("overview")
def refresh(self, reload=False): """ Refresh display by re-enabling redesign or normal mode, regenerate customizable css strings. """ state = self.config.state_on.value if not self.profile_loaded: alert(ERROR_NO_PROFILE) return try: if state: if reload: self.off() self.on() else: self.off() except Exception: alert(ERROR_SWITCH % traceback.format_exc()) return # Reload current screen. if mw.state == 'review': mw.moveToState('overview') mw.moveToState('review') if mw.state == 'deckBrowser': mw.deckBrowser.refresh() if mw.state == 'overview': mw.overview.refresh() # Redraw toolbar (should be always visible). mw.toolbar.draw() self.update_menu() return True
def onCopyDeckClicked(): deck = mw.col.decks.current() models = noteTypes(deck) # models to consider copying if not models: showInfo('Data models not found. Aborted. Try selecting a deck.') return dupMids = [unicode(m['id']) for m in models] # assume "Clone all" modelNames = [m['name'] for m in models] q = '''%s %s The current deck (%s) uses these Note Types: %s Do you want the copied deck to share these settings, or do you want to clone the settings? (Either way, media files will be shared, not cloned.) ''' % (ADDON_NAME, VERSION, deck['name'], ', '.join(modelNames)) buttons = ['Clone all', 'Share all', 'Ask', 'Cancel'] dlg = askUserDialog(q, buttons) dlg.setIcon(4) a = dlg.run() if a == 'Cancel': return elif a == 'Share all': dupMids = [] elif a == 'Ask': q = 'Clone model %s?' buttons = ['Clone', 'Share', 'Cancel'] for i in range(len(dupMids)-1, -1, -1): m = dupMids[i] dlg = askUserDialog(q % modelNames[i], buttons) dlg.setIcon(4) a = dlg.run() if a == 'Cancel': return elif a == 'Share': dupMids.pop(i) # remove # TODO: Also ask whether to copy the media files. This is much trickier as it would have to modify the data to match. dd = DeckDuper(deck) _tmp = dd.dupDeck(dupMids) # refresh the screen so the new deck is visible mw.moveToState("deckBrowser") msg = 'Successfully copied %s notes, and generated %s cards (with fresh scheduling info). \n' % (dd.notesCopied, dd.cardsCopied) if dd.errorCount: s = list(dd.errors) summary = ' '.join(s) msg += 'Encountered %s errors. \n' % dd.errorCount msg += 'Summary: %s \n' % summary showInfo(msg)
def toggle_nm_from_main(): theme_manager.night_mode ^= True if mw.state == "review": reload_reviewer() elif mw.state == "deckBrowser": mw.moveToState("deckBrowser") elif mw.state == "overview": mw.moveToState("deckBrowser") else: tooltip(f"state is: {mw.state}")
def redraw_card(): """Refresh the card in case {{Tags}} is in the template.""" # TODO It would be better to do this without resetting the state. r = mw.reviewer try: r.card.load() except: # card was removed by clayout pass else: mw.reviewer.cardQueue.append(mw.reviewer.card) mw.moveToState("review")
def monsterAnswerCard(card, ease): if ease == 1: return displayMonsters(True) mw.monstersDone = mw.monstersDone + 1 mw.monstersToDo = mw.monstersToDo - 1 displayMonsters(False) if mw.monstersToDo <= 0: mw.moveToState("deckBrowser")
def reset_and_redraw(same_card_shown=False): """Rebuild the scheduler and redraw the card.""" in_answer_state = (mw.reviewer.state == "answer") if same_card_shown: mw.reviewer.card.load() mw.reviewer.cardQueue.append(mw.reviewer.card) mw.moveToState("review") if in_answer_state and same_card_shown: try: mw.reviewer._showAnswer() except: pass
def on_tomato(self, from_timer=True): self.db.end_session() if self.tm: self.tm.stop() self.pb_w.hide() self.pb.reset() if from_timer: mw.moveToState("overview") if not self.dlg_rest: self.dlg_rest = RestDialog(mw) self._set_style_sheet(self.dlg_rest) self.dlg_rest.accepted.connect(self.on_dlg_rest_accepted) self.dlg_rest.rejected.connect(self.on_dlg_rest_rejected) if UserConfig.PLAY_SOUNDS["break"]: play(BREAK) self.dlg_rest.exec_(self.dlg.min)
def ts_switch(): """ Switch TouchScreen. """ if ts_state_on: ts_off() else: ts_on() # Reload current screen. if mw.state == "review": mw.moveToState('overview') mw.moveToState('review') if mw.state == "deckBrowser": mw.deckBrowser.refresh() if mw.state == "overview": mw.overview.refresh()
def monster(): mw.col.sched.answerCard = wrap(mw.col.sched.answerCard, monsterAnswerCard) newCount, lrnCount, revCount = mw.col.sched.counts() totalCount = (newCount + lrnCount + revCount) howManyToDo = totalCount / 2 if howManyToDo < N: howManyToDo = N if totalCount < N: howManyToDo = totalCount mw.monstersToDo = howManyToDo mw.monstersDone = 0 # XXX If howManyToDo is > than the number of pngs, this is bad mw.monsters = [loadMonster(png) for png in os.listdir(MonstersPath)] shuffle(mw.monsters) if mw.monstersToDo > 0: displayMonsters(False) mw.moveToState("review")
def monster(): mw.col.sched.answerCard = wrap( mw.col.sched.answerCard, monsterAnswerCard ) newCount, lrnCount, revCount = mw.col.sched.counts() totalCount = (newCount + lrnCount + revCount) howManyToDo = totalCount / 2 if howManyToDo < N: howManyToDo = N if totalCount < N: howManyToDo = totalCount mw.monstersToDo = howManyToDo mw.monstersDone = 0 # XXX If howManyToDo is > than the number of pngs, this is bad mw.monsters = [ loadMonster(png) for png in os.listdir(MonstersPath)] shuffle(mw.monsters) if mw.monstersToDo > 0: displayMonsters(False) mw.moveToState("review")
def _refresh(self, ms=100): if self.timer: self.timer.stop() anki_version_tuple = tuple(int(i) for i in anki_version.split(".")) if anki_version_tuple < (2, 1, 27): self.timer = mw.progress.timer(ms, lambda: mw.reset(True), False) elif anki_version_tuple < (2, 1, 45): self.timer = mw.progress.timer(ms, self._resetMainWindow, False) else: self.timer = mw.progress.timer( ms, lambda: mw.moveToState("deckBrowser"), False)
def _timedItemClick(self, item, fromTimer=False): item.onClick() try: type = item.type except AttributeError: return if type == 'tag': showConf = self.getConf('Blitzkrieg.showAllTags', True) if (showConf and fromTimer) or \ (not showConf and not fromTimer): #show all subtags option el = self.browser.form.searchEdit.lineEdit() el.setText(el.text() + "*") elif type == 'deck': #Auto update overview summary deck up = self.getConf('Blitzkrieg.updateOV', False) if up and item.type in ('deck','dyn','pinDeck','pinDyn') \ and mw.state == 'overview': d = mw.col.decks.byName(item.fullname) mw.col.decks.select(d["id"]) mw.moveToState("overview")
def updateCode(in_previewer=False): # Update Notetype code with code from src html file fname = file_name(in_previewer) if not os.path.isfile(fname): h = open(fname, 'w+') h.close() # Current path => /usr/python/bin with open(fname, 'r', encoding='utf-8') as htmlFile: source_code = htmlFile.read() _model = model(in_previewer) for i in _model['tmpls']: _state = state(in_previewer) if _state == 'question': i['qfmt'] = source_code elif _state == 'answer': i['afmt'] = source_code mw.col.models.save(_model) mw.col.reset() if in_previewer: browser._previewer._last_state = None browser._previewer.render_card() else: _state = state(in_previewer) if _state == 'question': mw.moveToState('overview') mw.moveToState('review') elif _state == 'answer': mw.col.reset() mw.moveToState('overview') mw.moveToState('review') mw.reviewer._showAnswer()
from PyQt4.QtCore import SIGNAL from PyQt4.QtGui import QKeySequence, QShortcut from anki.hooks import wrap from aqt import mw from aqt.reviewer import Reviewer __version__ = "1.2.0" mw.other_deck = QShortcut(QKeySequence("Ctrl+w"), mw) mw.other_browse = QShortcut(QKeySequence("Ctrl+f"), mw) def replay_6(self, evt): """ Use "6" to replay audio. Use the 6 key to replay audio. Useful for reviewing with the right hand on the numeric key pad. """ key = unicode(evt.text()) if key == "6" or key == 'i': self.replayAudio() Reviewer._keyHandler = wrap(Reviewer._keyHandler, replay_6) mw.connect(mw.other_deck, SIGNAL("activated()"), lambda: mw.moveToState("deckBrowser")) mw.connect(mw.other_browse, SIGNAL("activated()"), lambda: mw.onBrowse())
def go_deck_browse(): """Open the deck browser.""" mw.moveToState("deckBrowser")
def go_study(): """Start studying cards.""" mw.col.reset() mw.col.startTimebox() mw.moveToState("review")
def handleMessage(msg): global caffeinated, caffprocess, cafftimer print('Recd: ' + msg) if msg == "connected" or msg == "disconnected": mw.toolbar.draw() elif msg == 'Hi': removeQRandBlur() mw.toolbar.draw() else: if 'darwin' in sys.platform: if not caffeinated: caffprocess = subprocess.Popen("caffeinate -d", shell=True) caffeinated = True cafftimer = threading.Timer(120, killCaffProcess) cafftimer.start() else: cafftimer.cancel() cafftimer = threading.Timer(120, killCaffProcess) cafftimer.start() elif 'win32' in sys.platform: ctypes.windll.kernel32.SetThreadExecutionState(0x00000002) if msg.startswith('setprefs'): split = msg.split('-') filepath = USERFILES + '/' + split[1] with open(filepath, 'w') as writer: writer.write(split[2]) elif msg.startswith('setdeck'): split = msg.split('~#$#~') mw.col.decks.select(mw.col.decks.id(split[1])) mw.moveToState("review") elif msg.startswith('hook'): for i in range(1, 4): if msg.endswith(str(i)): hookname = 'Ankimote.hook' + str(i) print('runHook(' + hookname + ')') runHook(hookname) elif msg.startswith('cmd'): config = mw.addonManager.getConfig(__name__) for i in range(1, 6): if msg.endswith(str(i)): cmdval = config[('cmd' + str(i))] print('running CMD' + str(i) + ': ' + cmdval) exec(cmdval) elif mw.state == 'review' and msg != 'none': if msg == 'good' and mw.reviewer.state == 'question': mw.reviewer._showAnswer() elif msg == 'undo': mw.onUndo() elif msg == 'scrollup': mw.deckBrowser.web.evalWithCallback('window.pageYOffset', scrollUp) elif msg == 'scrolldown': mw.deckBrowser.web.evalWithCallback('window.pageYOffset', scrollDown) elif msg == 'pageup': mw.deckBrowser.web.evalWithCallback('window.pageYOffset', pageUp) elif msg == 'pagedown': mw.deckBrowser.web.evalWithCallback('window.pageYOffset', pageDown) elif msg == 'ambossnext': mw.web.eval("ambossTooltips.rotateTooltips();") elif msg == 'ambossprev': mw.web.eval("ambossTooltips.rotateTooltips(true);") elif msg == 'ambossclose': mw.web.eval("ambossTooltips.hideAll();") elif msg == 'showhints': showHintsJS = ''' var x=document.getElementsByClassName('hint'); for(i=0;i<x.length;i++) { if(x[i].tagName=='A') { x[i].onclick(); } }; if(!(document.getElementById("io-revl-btn")==null)) { document.getElementById("io-revl-btn").onclick() }; ''' mw.web.eval(showHintsJS) elif mw.reviewer.card and mw.reviewer.state == 'answer': for ease, label in mw.reviewer._answerButtonList(): if msg == label.lower(): mw.reviewer._answerCard(ease) colordict = { "again": "#ec1d24", "hard": "#ecb51d", "good": "#5BAE7E", "easy": "#4BA2CB" } feedbackJSp1 = """ if(document.getElementById('snackbar')) { var snackbar = document.getElementById('snackbar'); snackbar.parentNode.removeChild(snackbar) } var sheet = window.document.styleSheets[0]; sheet.insertRule('#snackbar { visibility: hidden; min-width: 250px; margin-left: -125px; background-color: """ feedbackJSp2 = """; color: #fff; text-align: center; border-radius: 48px; padding: 8px; position: fixed; z-index: 1; left: 50%; bottom: 30px; font-size: 24px; font-weight: bold; }', sheet.cssRules.length); sheet.insertRule('#snackbar.show { visibility: visible; -webkit-animation: fadein 0.05s, fadeout 0.25s 0.75s; animation: fadein 0.05s, fadeout 0.25s 0.75s;}', sheet.cssRules.length); sheet.insertRule('@-webkit-keyframes fadein { from {bottom: 0; opacity: 0;} to {bottom: 30px; opacity: 1;}}', sheet.cssRules.length); sheet.insertRule('@keyframes fadein { from {bottom: 0; opacity: 0;} to {bottom: 30px; opacity: 1;}}', sheet.cssRules.length); sheet.insertRule('@-webkit-keyframes fadeout { from {bottom: 30px; opacity: 1;} to {bottom: 0; opacity: 0;}}', sheet.cssRules.length); sheet.insertRule('@keyframes fadeout { from {bottom: 30px; opacity: 1;} to {bottom: 0; opacity: 0;}}', sheet.cssRules.length); var mytoast = document.createElement("div"); mytoast.innerHTML = '""" feedbackJSp3 = """' mytoast.id="snackbar" document.body.appendChild(mytoast) var toastelement = document.getElementById("snackbar"); toastelement.className = "show"; setTimeout(function(){ toastelement.className = toastelement.className.replace("show", ""); for(i = 0;i<6;i++) { sheet.deleteRule(sheet.cssRules.length-1) } toastelement.parentNode.removeChild(toastelement); }, 1000); """ feedbackJS = feedbackJSp1 + colordict[ msg] + feedbackJSp2 + label + feedbackJSp3 config = mw.addonManager.getConfig(__name__) if config['feedback'] == True: mw.web.eval(feedbackJS)
def sync_duolingo(): model = get_duolingo_model(mw) if not model: showWarning("Could not find or create Duolingo Sync note type.") return note_ids = mw.col.findNotes("tag:duolingo_sync") notes = mw.col.db.list("select flds from notes where id in {}".format( ids2str(note_ids))) gids_to_notes = {splitFields(note)[0]: note for note in notes} try: username, password = duolingo_dialog(mw) except TypeError: return if username and password: try: mw.progress.start(immediate=True, label="Logging in...") login_thread = DuolingoThread(target=Duolingo, args=(username, password)) login_thread.start() while login_thread.is_alive(): time.sleep(0.02) mw.progress.update() lingo = login_thread.join() vocabulary_thread = DuolingoThread(target=lingo.get_vocabulary) vocabulary_thread.start() mw.progress.update(label="Retrieving vocabulary...") while vocabulary_thread.is_alive(): time.sleep(0.02) mw.progress.update() vocabulary_response = vocabulary_thread.join() except LoginFailedException: showWarning(""" <p>Logging in to Duolingo failed. Please check your Duolingo credentials.</p> <p>Having trouble logging in? You must use your <i>Duolingo</i> username and password. You <i>can't</i> use your Google or Facebook credentials, even if that's what you use to sign in to Duolingo.</p> <p>You can find your Duolingo username at <a href="https://www.duolingo.com/settings">https://www.duolingo.com/settings</a> and you can create or set your Duolingo password at <a href="https://www.duolingo.com/settings/password">https://www.duolingo.com/settings/password</a>.</p> """) return except requests.exceptions.ConnectionError: showWarning( "Could not connect to Duolingo. Please check your internet connection." ) return finally: mw.progress.finish() language_string = vocabulary_response["language_string"] vocabs = vocabulary_response["vocab_overview"] did = mw.col.decks.id("Default") mw.col.decks.select(did) deck = mw.col.decks.get(did) deck["mid"] = model["id"] mw.col.decks.save(deck) words_to_add = [ vocab for vocab in vocabs if vocab["id"] not in gids_to_notes ] if not words_to_add: showInfo( "Successfully logged in to Duolingo, but no new words found in {} language." .format(language_string)) elif askUser("Add {} notes from {} language?".format( len(words_to_add), language_string)): word_chunks = [ words_to_add[x:x + 50] for x in range(0, len(words_to_add), 50) ] mw.progress.start( immediate=True, label="Importing from Duolingo...", max=len(words_to_add), ) notes_added = 0 for word_chunk in word_chunks: translations = lingo.get_translations( [vocab["word_string"] for vocab in word_chunk]) for vocab in word_chunk: n = mw.col.newNote() # Update the underlying dictionary to accept more arguments for more customisable cards n._fmap = defaultdict(str, n._fmap) n["Gid"] = vocab["id"] n["Gender"] = vocab["gender"] if vocab["gender"] else "" n["Source"] = "; ".join(translations[vocab["word_string"]]) n["Target"] = vocab["word_string"] n["Pronunciation"] = vocab["normalized_string"].strip() n["Target Language"] = language_string n.addTag(language_string) n.addTag("duolingo_sync") if vocab["pos"]: n.addTag(vocab["pos"]) if vocab["skill"]: n.addTag(vocab["skill"].replace(" ", "-")) mw.col.addNote(n) notes_added += 1 mw.progress.update(value=notes_added) showInfo("{} notes added".format(notes_added)) mw.moveToState("deckBrowser") mw.progress.finish()
def batch_create_filtered_decks(): mirrorlist = gc("mirror all decks as filtered") if not (mirrorlist and isinstance(mirrorlist, list)): tooltip( 'Invalid value for "mirror all decks as filtered". Aborting ...') regDeckIds = [d["id"] for d in mw.col.decks.all() if not d["dyn"]] mw.progress.start() for idx, sl in enumerate(mirrorlist): ## verify if not isinstance(sl, dict): tooltip( 'Invalid entry no {} in "mirror all decks as filtered"'.format( idx)) mw.progress.finish() return additional = sl.get("additional search term", None) if not (additional and isinstance(additional, str)): tooltip( 'Invalid entry no {} in "mirror all decks as filtered"-"additional search term"' .format(idx)) mw.progress.finish() return order = sl.get("selected by/order", False) if not (order and isinstance(order, int)): tooltip( 'Invalid entry no {} in "mirror all decks as filtered"-"selected by/order"' .format(idx)) mw.progress.finish() return parent = sl.get("parent deck name", False) if not (parent and isinstance(parent, str)): tooltip( 'Invalid entry no {} in "mirror all decks as filtered"-"parent deck name"' .format(idx)) mw.progress.finish() return resched = sl.get("reschedule based on answers") if not isinstance(resched, bool): tooltip( 'Invalid entry no {} in "mirror all decks as filtered"-"reschedule based on answers"' .format(idx)) mw.progress.finish() return maxcards = sl.get("max cards", 9999) if not (maxcards and isinstance(maxcards, int)): maxcards = 9999 steps = False # gc("steps") previewDelay = 0 ## abort if parent exists if mw.col.decks.byName(parent): showInfo( "The add-on doesn't work in the parent deck already exists. Aborting ..." ) mw.progress.finish() return ## make sure to build most nested decks first nestedlevels = {} for did in regDeckIds: deck = mw.col.decks.get(did) n = deck['name'].count("::") nestedlevels.setdefault(n, []).append(str(did)) ## actually build them for level in sorted(nestedlevels.keys(), reverse=True): for did in nestedlevels[level]: odeck = mw.col.decks.get(did) # check for subdecks: hassubdecks = False for d in mw.col.decks.all(): if d["name"].startswith(odeck['name'] + "::"): hassubdecks = True continue # check if it has cards that are not in subdecks: cids = mw.col.findCards( f'''deck:"{odeck['name']}" -deck:"{odeck['name']}::*"''') if hassubdecks and cids: # I need to create a special subdeck for cards that are only in the parent # since a filterd deck can't have other filtered decks filtered_name = parent.rstrip("::") + "::" + odeck[ 'name'] + "::" + "!!cards that are only in parent" else: filtered_name = parent.rstrip("::") + "::" + odeck['name'] searchtext = 'deck:"{}"'.format( odeck['name']) + " " + additional CreateFilteredDeckWithoutGUI(filtered_name, searchtext, maxcards, order, resched, steps, previewDelay) mw.reset() mw.moveToState("deckBrowser") mw.progress.finish()
from PyQt5.QtCore import SIGNAL from PyQt5.QtGui import QKeySequence, QShortcut from anki.hooks import wrap from aqt import mw from aqt.reviewer import Reviewer __version__ = "1.2.1" mw.other_deck = QShortcut(QKeySequence("Ctrl+w"), mw) mw.other_browse = QShortcut(QKeySequence("Ctrl+f"), mw) def replay_6(self, evt): """ Use “6” and “i” to replay audio. Use the “6” key to replay audio, useful for reviewing with the right hand on the numeric key pad, and the “i” key, useful when reviewing with the left hand on a Dvorak keyboard. """ key = unicode(evt.text()) if key == "6" or key == 'i': self.replayAudio() Reviewer._keyHandler = wrap(Reviewer._keyHandler, replay_6) mw.connect(mw.other_deck, SIGNAL("activated()"), lambda: mw.moveToState("deckBrowser")) mw.connect(mw.other_browse, SIGNAL("activated()"), lambda: mw.onBrowse())
# -*- coding: utf-8 -*- # Copyright: Damien Elmes <*****@*****.**> # License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html # # Emulate some Anki 1.2 shortcuts. from aqt import mw from aqt.qt import * mw.otherDeck = QShortcut(QKeySequence("Ctrl+w"), mw) mw.otherAdd = QShortcut(QKeySequence("Ctrl+d"), mw) mw.otherBrowse = QShortcut(QKeySequence("Ctrl+f"), mw) mw.connect( mw.otherDeck, SIGNAL("activated()"), lambda: mw.moveToState("deckBrowser")) mw.connect( mw.otherAdd, SIGNAL("activated()"), lambda: mw.onAddCard()) mw.connect( mw.otherBrowse, SIGNAL("activated()"), lambda: mw.onBrowse())
def myTimeboxReached(self, _old): ret = _old(self) if ret: # showInfo(("%(cards)d cards studied in %(mins)s minutes.") % dict(cards=ret[1], mins=ret[0]/60)) mw.moveToState("overview") return ret
def on_open_settings(): if mw.state != "deckBrowser": mw.moveToState("deckBrowser") dialog = SettingsMenuDialog(mw) dialog.exec_()
def sync_duolingo(): model = get_duolingo_model(mw) if not model: showWarning("Could not find or create Duolingo Sync note type.") return note_ids = mw.col.findNotes('tag:duolingo_sync') notes = mw.col.db.list("select flds from notes where id in {}".format( ids2str(note_ids))) gids_to_notes = {splitFields(note)[0]: note for note in notes} try: username, password = duolingo_dialog(mw) except TypeError: return if username and password: try: mw.progress.start(immediate=True, label="Logging in...") login_thread = DuolingoThread(target=Duolingo, args=(username, password)) login_thread.start() while login_thread.is_alive(): time.sleep(.02) mw.progress.update() lingo = login_thread.join() vocabulary_thread = DuolingoThread(target=lingo.get_vocabulary) vocabulary_thread.start() mw.progress.update(label="Retrieving vocabulary...") while vocabulary_thread.is_alive(): time.sleep(.02) mw.progress.update() vocabulary_response = vocabulary_thread.join() except LoginFailedException: showWarning(""" <p>Logging in to Duolingo failed. Please check your Duolingo credentials.</p> <p>Having trouble logging in? You must use your <i>Duolingo</i> username and password. You <i>can't</i> use your Google or Facebook credentials, even if that's what you use to sign in to Duolingo.</p> <p>You can find your Duolingo username at <a href="https://www.duolingo.com/settings">https://www.duolingo.com/settings</a> and you can create or set your Duolingo password at <a href="https://www.duolingo.com/settings/password">https://www.duolingo.com/settings/password</a>.</p> """) return except requests.exceptions.ConnectionError: showWarning( "Could not connect to Duolingo. Please check your internet connection." ) return finally: mw.progress.finish() language_string = vocabulary_response['language_string'] vocabs = vocabulary_response['vocab_overview'] did = mw.col.decks.id("Default") mw.col.decks.select(did) deck = mw.col.decks.get(did) deck['mid'] = model['id'] mw.col.decks.save(deck) words_to_add = [ vocab for vocab in vocabs if vocab['id'] not in gids_to_notes ] if not words_to_add: showInfo( "Successfully logged in to Duolingo, but no new words found in {} language." .format(language_string)) elif askUser("Add {} notes from {} language?".format( len(words_to_add), language_string)): word_chunks = [ words_to_add[x:x + 50] for x in range(0, len(words_to_add), 50) ] mw.progress.start(immediate=True, label="Importing from Duolingo...", max=len(words_to_add)) notes_added = 0 problem_vocabs = [] for word_chunk in word_chunks: lexeme_ids = { vocab['word_string']: vocab['id'] for vocab in word_chunk } translations = lingo.get_translations( [vocab['word_string'] for vocab in word_chunk]) # The `get_translations` endpoint might not always return a translation. In this case, try # a couple of fallback methods for word_string, translation in translations.items(): if not translation: fallback_translation = "Translation not found for '{}'. Edit this card to add it.".format( word_string) try: new_translation = lingo.get_word_definition_by_id( lexeme_ids[word_string])['translations'] except Exception: new_translation = fallback_translation translations[word_string] = [ new_translation if new_translation else fallback_translation ] for vocab in word_chunk: n = mw.col.newNote() # Update the underlying dictionary to accept more arguments for more customisable cards n._fmap = defaultdict(str, n._fmap) n['Gid'] = vocab['id'] n['Gender'] = vocab['gender'] if vocab['gender'] else '' n['Source'] = '; '.join(translations[vocab['word_string']]) n['Target'] = vocab['word_string'] n['Pronunciation'] = vocab['normalized_string'].strip() n['Target Language'] = language_string n.addTag(language_string) n.addTag('duolingo_sync') if vocab['pos']: n.addTag(vocab['pos']) if vocab['skill']: n.addTag(vocab['skill'].replace(" ", "-")) num_cards = mw.col.addNote(n) if num_cards: notes_added += 1 else: problem_vocabs.append(vocab['word_string']) mw.progress.update(value=notes_added) message = "{} notes added.".format(notes_added) if problem_vocabs: message += " Failed to add: " + ", ".join(problem_vocabs) mw.progress.finish() showInfo(message) mw.moveToState("deckBrowser")
def on_import(self): from . import _try_ext_module total_new = 0 total_dup = 0 for i, _ in enumerate(self.yield_one_word()): (id, word, stem, lang, added_tm, usage, title, authors, category) = _ # region save new cards try: note = notes.Note(mw.col, mw.col.models.models[str(self.model['id'])]) except KeyError: continue note.model()['did'] = self.deck['id'] qry_word = stem if stem else word if word else '' if _try_ext_module(): mdx_files = self.MDXFiles else: mdx_files = [self.mdx, ] mdx_files = [m for m in mdx_files if os.path.isfile(m)] if not any(mdx_files): ret = askUser( _trans("ALERT FOR MISSING MDX"), self, defaultno=False, title=_trans("ANKINDLE") ) if not ret: break dict_nm = '' dict_data = '' for mdx_file in mdx_files: self.builder = mdict_query.IndexBuilder(mdx_file) self.builder.get_header() self.builder.check_build() try: mdx_dict = readmdict.MDX(mdx_file, only_header=True) self.builder._encoding = mdx_dict._encoding except MemoryError: showInfo(_trans("MDX MEMORY ERROR"), self, type="warning", title=_trans("ANKINDLE")) continue except TypeError: showInfo(_trans("MDX TYPE ERROR"), self, type="warning", title=_trans("ANKINDLE")) continue dict_nm = os.path.splitext(os.path.basename(mdx_dict._fname))[0] self.missed_css = set() dict_data = self.get_html(qry_word, self.builder) # copy css files if dict_data: mdx_dict_dir = os.path.split(mdx_file)[0] include_mdx_extras = ['.CSS', '.JS'] for root, dirs, files in os.walk(mdx_dict_dir): for _mfile in [css for css in files if os.path.splitext(css) [1].strip().upper() in include_mdx_extras]: _nfile = _mfile if _mfile in self.missed_css: _nfile = "_" + _mfile shutil.copy( os.path.join(root, _mfile), _nfile ) break _usage = self.adapt_to_anki(usage.replace(word, u"<b>%s</b>" % word)) if usage else '' try: _id_in_field = re.sub("[^0-9a-zA-Z]", "", qry_word + usage).strip().upper() except TypeError: return False def update_note(_note): _note.fields[_note._fieldOrd('id')] = _id_in_field if _id_in_field else '' _note.fields[_note._fieldOrd('word')] = word if word else '' _note.fields[_note._fieldOrd('stem')] = stem if stem else '' _note.fields[_note._fieldOrd('lang')] = lang if lang else '' _note.fields[_note._fieldOrd('creation_tm')] = added_tm if added_tm else '' _note.fields[_note._fieldOrd('usage')] = _usage if _usage else '' _note.fields[_note._fieldOrd('title')] = title if title else '' _note.fields[_note._fieldOrd('authors')] = authors if authors else '' _note.fields[_note._fieldOrd('mdx_dict')] = dict_data try: _note.fields[_note._fieldOrd('mdx_name')] = dict_nm except KeyError: pass return True if update_note(note): if note.dupeOrEmpty() != 2: mw.col.addNote(note) total_new += 1 else: total_dup += 1 mw.col.autosave() # endregion mw.moveToState("deckBrowser") showText(_trans("CREATED AND DUPLICATES") % (total_new, total_dup), self)
def apply_config_changes(config): update_css() mw.moveToState("deckBrowser")
def study(): mw.moveToState("review")