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
Exemple #2
0
 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")
Exemple #3
0
    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
Exemple #4
0
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)
Exemple #5
0
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}")
Exemple #6
0
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")
Exemple #7
0
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")
Exemple #8
0
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")
Exemple #9
0
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")
Exemple #10
0
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
Exemple #11
0
 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)
Exemple #12
0
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()
Exemple #13
0
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")
Exemple #14
0
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")
Exemple #15
0
    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)
Exemple #16
0
    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")
Exemple #17
0
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")
Exemple #21
0
def go_study():
    """Start studying cards."""
    mw.col.reset()
    mw.col.startTimebox()
    mw.moveToState("review")
Exemple #22
0
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)
Exemple #23
0
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()
Exemple #24
0
def go_deck_browse():
    """Open the deck browser."""
    mw.moveToState("deckBrowser")
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
Exemple #29
0
def on_open_settings():
    if mw.state != "deckBrowser":
        mw.moveToState("deckBrowser")
    dialog = SettingsMenuDialog(mw)
    dialog.exec_()
Exemple #30
0
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")
Exemple #31
0
    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")
Exemple #33
0
def study():
    mw.moveToState("review")