def _style(self): from anki import version style = '* {font-family: sans-serif; }' if version.startswith('2.0.'): return style style += 'td { font-size: 80%; }' return style
def _style(self): from anki import version anki_version = int(version.replace('.', '')) if anki_version > 2119: from aqt.theme import theme_manager config = mw.addonManager.getConfig(__name__) sidebar_theme = config['Card Info sidebar_ theme'] sidebar_font = config['Card Info sidebar_ Font'] from . import styles dark_styles = styles.dark light_styles = styles.light if anki_version > 2119: if sidebar_theme == 2: mystyle = dark_styles elif sidebar_theme == 1: mystyle = light_styles else: if theme_manager.night_mode: mystyle = dark_styles else: mystyle = light_styles else: if sidebar_theme == 2: mystyle = dark_styles else: mystyle = light_styles from anki import version if version.startswith("2.0."): return "" return mystyle + "td { font-size: 75%; font-family:" + "{}".format( sidebar_font) + ";}"
def todayStats_old(self): """We need to overwrite the entire method to change the mature ivl""" b = self._title(_("Today")) # studied today lim = self._revlogLimit() if lim: lim = " and " + lim cards, thetime, failed, lrn, rev, relrn, filt = self.col.db.first( """ select count(), sum(time)/1000, sum(case when ease = 1 then 1 else 0 end), /* failed */ sum(case when type = 0 then 1 else 0 end), /* learning */ sum(case when type = 1 then 1 else 0 end), /* review */ sum(case when type = 2 then 1 else 0 end), /* relearn */ sum(case when type = 3 then 1 else 0 end) /* filter */ from revlog where id > ? """ + lim, (self.col.sched.dayCutoff - 86400) * 1000) cards = cards or 0 thetime = thetime or 0 failed = failed or 0 lrn = lrn or 0 rev = rev or 0 relrn = relrn or 0 filt = filt or 0 # studied if anki_version.startswith("2.0."): def bold(s): return "<b>" + unicode(s) + "</b>" else: def bold(s): return "<b>" + str(s) + "</b>" msgp1 = ngettext("<!--studied-->%d card", "<!--studied-->%d cards", cards) % cards b += _("Studied %(a)s in %(b)s today.") % dict( a=bold(msgp1), b=bold(fmtTimeSpan(thetime, unit=1))) # again/pass count b += "<br>" + _("Again count: %s") % bold(failed) if cards: b += " " + _("(%s correct)") % bold("%0.1f%%" % ( (1 - failed / float(cards)) * 100)) # type breakdown b += "<br>" b += (_("Learn: %(a)s, Review: %(b)s, Relearn: %(c)s, Filtered: %(d)s") % dict(a=bold(lrn), b=bold(rev), c=bold(relrn), d=bold(filt))) # mature today mcnt, msum = self.col.db.first( """ select count(), sum(case when ease = 1 then 0 else 1 end) from revlog where lastIvl >= %d and id > ?""" % MATURE_IVL + lim, (self.col.sched.dayCutoff - 86400) * 1000) b += "<br>" if mcnt: b += _("Correct answers on mature cards: %(a)d/%(b)d (%(c).1f%%)" ) % dict(a=msum, b=mcnt, c=(msum / float(mcnt) * 100)) else: b += _("No mature cards were studied today.") return b
def showDebugInfo(): from aqt import mw from anki import version as anki_version from aqt.utils import showInfo, showText txt = """ <h2>Bug Status: Affected</h2> <p>It seems like your system is affected by a module import bug.</p> <p>To report your findings, please copy the debug info below and post it <a href="https://anki.tenderapp.com/discussions/add-ons/35343">here</a>. (no account needed). Feel free to uninstall "Import Bug Test" once you're done</p> <p>Thanks so much for your help in tracking this issue down!</p> <p><b>Debug info</b>:</p> """ txt += "<div style='white-space: pre-wrap'>" if not anki_version.startswith("2.0"): from aqt.utils import supportText txt += "\n" + supportText() + "\n" addmgr = mw.addonManager txt += "Add-ons:\n\n" + "\n".join( addmgr.annotatedName(d) for d in addmgr.allAddons()) + "\n\n" else: from aqt import appVersion from aqt.qt import QT_VERSION_STR, PYQT_VERSION_STR txt += '<p>' + "Version %s" % appVersion + '\n' txt += ("Qt %s PyQt %s\n\n") % (QT_VERSION_STR, PYQT_VERSION_STR) txt += "Add-ons:\n\n" + repr(mw.addonManager.files()) + "\n\n" txt += "Import Errors:\n\n" txt += "\n\n".join(error[0] + ":\n" + error[1] for error in errors) txt += "</div>" kwargs = dict(title="Import Bug Test", type="html") if not anki_version.startswith("2.0"): kwargs["copyBtn"] = True showText(txt, **kwargs)
def todayStats_old(self): """We need to overwrite the entire method to change the mature ivl""" b = self._title(_("Today")) # studied today lim = self._revlogLimit() if lim: lim = " and " + lim cards, thetime, failed, lrn, rev, relrn, filt = self.col.db.first(""" select count(), sum(time)/1000, sum(case when ease = 1 then 1 else 0 end), /* failed */ sum(case when type = 0 then 1 else 0 end), /* learning */ sum(case when type = 1 then 1 else 0 end), /* review */ sum(case when type = 2 then 1 else 0 end), /* relearn */ sum(case when type = 3 then 1 else 0 end) /* filter */ from revlog where id > ? """+lim, (self.col.sched.dayCutoff-86400)*1000) cards = cards or 0 thetime = thetime or 0 failed = failed or 0 lrn = lrn or 0 rev = rev or 0 relrn = relrn or 0 filt = filt or 0 # studied if anki_version.startswith("2.0."): def bold(s): return "<b>"+unicode(s)+"</b>" else: def bold(s): return "<b>"+str(s)+"</b>" msgp1 = ngettext("<!--studied-->%d card", "<!--studied-->%d cards", cards) % cards b += _("Studied %(a)s in %(b)s today.") % dict( a=bold(msgp1), b=bold(fmtTimeSpan(thetime, unit=1))) # again/pass count b += "<br>" + _("Again count: %s") % bold(failed) if cards: b += " " + _("(%s correct)") % bold( "%0.1f%%" %((1-failed/float(cards))*100)) # type breakdown b += "<br>" b += (_("Learn: %(a)s, Review: %(b)s, Relearn: %(c)s, Filtered: %(d)s") % dict(a=bold(lrn), b=bold(rev), c=bold(relrn), d=bold(filt))) # mature today mcnt, msum = self.col.db.first(""" select count(), sum(case when ease = 1 then 0 else 1 end) from revlog where lastIvl >= %d and id > ?""" % MATURE_IVL +lim, (self.col.sched.dayCutoff-86400)*1000) b += "<br>" if mcnt: b += _("Correct answers on mature cards: %(a)d/%(b)d (%(c).1f%%)") % dict( a=msum, b=mcnt, c=(msum / float(mcnt) * 100)) else: b += _("No mature cards were studied today.") return b
############## USER CONFIGURATION START ############## # These settings only apply to Anki 2.0. For Anki 2.1 please use # Anki's built-in add-on configuration menu HOTKEY_TOGGLE_LIST = "Alt+Shift+L" ############## USER CONFIGURATION END ############## from aqt import editor, mw from anki.hooks import wrap, addHook from anki import version ANKI21 = version.startswith("2.1") if ANKI21: config = mw.addonManager.getConfig(__name__) HOTKEY_TOGGLE_LIST = config["hotkeyToggleList"] editor_style = """ <style> ul.shuffle{ background-color: yellow; } </style> """ def toggleRandUl(self):
# -*- coding: utf-8 -*- # Copyright: (C) 2018-2020 Lovac42 # Support: https://github.com/lovac42/ReMemorize # License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html from anki import version CCBC = version.endswith("ccbc") ANKI21 = version.startswith("2.1.") and not CCBC BROWSER_TAG = "_reschedule" if ANKI21 else "reschedule"
from __future__ import (absolute_import, division, print_function, unicode_literals) ############## USER CONFIGURATION START ############## STRIP_TAGS = ['b', 'i', 'u'] # list of html tags to remove ############## USER CONFIGURATION END ############## from aqt.qt import * from aqt import mw from aqt.utils import tooltip from anki.hooks import addHook from anki import version as anki_version if anki_version.startswith("2.1"): from bs4 import BeautifulSoup ANKI21 = True else: from BeautifulSoup import BeautifulSoup ANKI21 = False def stripFormatting(fields): """ Uses BeautifulSoup to remove STRIP_TAGS from each string in supplied list. Parameters ---------- fields : list of strings
from anki import version assert version.startswith("2.1.") minor_ver = int(version.split(".")[-1]) COMPAT = { # backend.find_and_replace "find_replace": minor_ver >= 27, # media.write_data() "write_data": minor_ver >= 22 } from . import editor
"Default": 2, # show 1 new card every 2 days in "Default" deck u"Very Slöw": 7 # deck names with unicode need to be prepended with u } option_limits = { "hard": 5, "Chapter": 2, } #In ANKI21, you can edit the options in the configuration manager, #instead of here. Thus, the configurations will be kept when the #add-on is updated. ############## USER CONFIGURATION END ############## from anki import version as anki_version import aqt anki21 = anki_version.startswith("2.1.") if anki21: userOption = aqt.mw.addonManager.getConfig(__name__) deck_limits.update(userOption["deck limits"]) option_limits.update(userOption["option limits"]) from anki.sched import Scheduler from anki.schedv2 import Scheduler as SchedulerV2 def debug(t): print(t) pass def myDeckNewLimitSingle(self, g):
# Unix (Linux/macOS) external_handler_unix = r"notify-send" ############## USER CONFIGURATION END ############## import subprocess import re from aqt.utils import tooltip from aqt.reviewer import Reviewer from anki.hooks import wrap, addHook from anki.utils import isWin from anki import version as anki_version anki21 = anki_version.startswith("2.1.") pycmd = "pycmd" if anki21 else "py.link" regex_link = r"(qv.+?\..+?\b(#\b.+?\b)?)" replacement = r"""<a href='' class="flink" onclick='{}("open:\1");return false;'>\1</a>""".format(pycmd) def openFileHandler(file): try: if isWin: external_handler = external_handler_win else: external_handler = external_handler_unix subprocess.Popen([external_handler, file]) except OSError:
# Remove that annoying separator strip if we have Night Mode, avoiding conflicts with this add-on. import Night_Mode Night_Mode.nm_css_menu \ += Night_Mode.nm_css_menu \ + ''' QMainWindow::separator { width: 0px; height: 0px; } ''' except ImportError: nmUnavailable = 1 useOldAnkiAPI = anki_version.startswith("2.0.") or ( anki_version.startswith("2.1.") and int(anki_version.split(".")[-1]) < 28) def initPB() -> None: """Initialize and set parameters for progress bar, adding it to the dock.""" global progressBar progressBar = QProgressBar() progressBar.setTextVisible(showPercent or showNumber) progressBar.setInvertedAppearance(invertTF) progressBar.setOrientation(orientationHV) if pbdStyle is None: progressBar.setStyleSheet(''' QProgressBar { text-align:center;
// Cloze Overlapper support if (typeof olToggle === "function") { olToggle(); } // Image Occlusion Enhanced support var ioBtn = document.getElementById("io-revl-btn"); if (!(typeof ioBtn === 'undefined' || !ioBtn)) { ioBtn.click(); } for (var i=0; i<arr.length; i++) { var l=arr[i]; if (l.style.display === 'none') { continue; } if (l.href.charAt(l.href.length-1) === '#') { l.dispatchEvent(customEvent); if ('%s' === 'True') { break; } } } """ % incremental) # Hooks and Patches if ankiversion.startswith("2.0"): # 2.0.x Reviewer._keyHandler = wrap(Reviewer._keyHandler, _newKeyHandler, "around") else: # 2.1.x addHook("reviewStateShortcuts", _addShortcuts)
for check_ivl in range(min_ivl, max_ivl + 1): num_cards = self.col.db.scalar( """select count() from cards where due = ? and queue = 2""", self.today + check_ivl) if num_cards < lowest_num_cards_in_range: acceptable_ivls = [check_ivl] lowest_num_cards_in_range = num_cards elif num_cards == lowest_num_cards_in_range: acceptable_ivls.append(check_ivl) min_num_cards = num_cards else: log_debug(" ") log_debug( "check_ivl {0:<4} num_cards {1:<4} acceptable_ivls {2}\n".format( check_ivl, num_cards, acceptable_ivls)) best_ivl = choice(acceptable_ivls) log_info( "{0:<28} orig_ivl {1:<4} min_ivl {2:<4} max_ivl {3:<4} acceptable_ivls {4}\n best_ivl {5:<4}" .format(str(datetime.datetime.now()), orig_ivl, min_ivl, max_ivl, acceptable_ivls, best_ivl)) return best_ivl # Patch Anki 2.0 and Anki 2.1 default scheduler anki.sched.Scheduler._fuzzedIvl = load_balanced_ivl # Patch Anki 2.1 experimental v2 scheduler if version.startswith("2.1"): from anki.schedv2 import Scheduler anki.schedv2.Scheduler._fuzzedIvl = load_balanced_ivl
# -*- coding: utf-8 -*- """ This file is part of the Image Occlusion Enhanced add-on for Anki. Global variables Copyright: (c) 2018 Glutanimate <https://glutanimate.com/> License: GNU AGPLv3 <https://www.gnu.org/licenses/agpl.html> """ import sys import os from anki import version ANKI21 = version.startswith("2.1.") SYS_ENCODING = sys.getfilesystemencoding() if ANKI21: ADDON_PATH = os.path.dirname(__file__) else: ADDON_PATH = os.path.dirname(__file__).decode(SYS_ENCODING) ICONS_PATH = os.path.join(ADDON_PATH, "icons")
# -*- coding: utf-8 -*- # Copyright: (C) 2019-2020 Lovac42 # Support: https://github.com/lovac42/Hashworth # License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html from aqt import mw from anki import version CCBC = version.endswith("ccbc") ANKI21 = not CCBC and version.startswith("2.1.") ANKI20 = version.startswith("2.0.") ADDONNAME = "Hashworth" TITLE = "Hashworth: Field Duplicate Finder" DEFAULT_HOTKEY = "Ctrl+Shift+H"
Note: For debugging, use either sys.stderr.write() or aqt.utils.showInfo(). """ import sys import os import aqt from anki import version from anki.hooks import addHook, wrap from aqt import editor, browser, reviewer, utils, mw from .symbol_manager import SymbolManager from .browser_replacer import BrowserReplacer from .symbol_window import SymbolWindow ANKI_VER_21 = version.startswith("2.1.") SYS_ENCODING = sys.getfilesystemencoding() if ANKI_VER_21: ADDON_PATH = os.path.dirname(__file__) else: ADDON_PATH = os.path.dirname(__file__).decode(SYS_ENCODING) """ Keeps track of all WebViews with symbol replacement Javascript. """ ins_sym_webviews = { "editors": [], "reviewer": None } def _update_JS(webview):
def _style(self): from anki import version if version.startswith("2.0."): return "" return "td { font-size: 80%; }"
if getMX() >= 1: if mx > getMX(): _updatePB() else: mx = getMX() progressBar.setRange(0, mx) progressBar.reset() else: progressBar.setValue(mx) addHook("afterStateChange", _renderBar) addHook("showQuestion", _updatePB) if anki_version.startswith("2.0.x"): """Workaround for QSS issue in EditCurrent, only necessary on Anki 2.0.x""" from aqt.editcurrent import EditCurrent def changeStylesheet(*args): mw.setStyleSheet(''' QMainWindow::separator { width: 0px; height: 0px; } ''') def restoreStylesheet(*args):
def _style(self): from anki import version if version.startswith("2.0."): return "" return "td { font-size: 80%; }"
lapsed = '_ansfeed_lapsed.png' # image for passed cards passed = '_ansfeed_passed.png' ############## USER CONFIGURATION END ############## __version__ = "1.1.0" from aqt.reviewer import Reviewer from anki.hooks import addHook, wrap from aqt import mw from aqt.qt import * import shutil, os from anki import version ANKI20 = version.startswith("2.0") folder = 'images' def imgLoad(): lp = [lapsed, passed] for p in lp: pth = os.path.join(mw.col.media.dir(), p) if not os.path.exists(pth): shutil.copy( os.path.join(mw.pm.addonFolder(), "visual_feedback", folder, p), p) addHook("profileLoaded", imgLoad)
from __future__ import (absolute_import, division, print_function, unicode_literals) ############## USER CONFIGURATION START ############## STRIP_TAGS = ['b', 'i', 'u'] # list of html tags to remove ############## USER CONFIGURATION END ############## from aqt.qt import * from aqt import mw from aqt.utils import tooltip from anki.hooks import addHook from anki import version as anki_version if anki_version.startswith("2.1"): from bs4 import BeautifulSoup ANKI21 = True else: from BeautifulSoup import BeautifulSoup ANKI21 = False def stripFormatting(fields): """ Uses BeautifulSoup to remove STRIP_TAGS from each string in supplied list. Parameters ---------- fields : list of strings
from __future__ import (absolute_import, division, print_function, unicode_literals) import os import tempfile from aqt.qt import * from aqt.utils import tooltip, askUser, getFile from anki.hooks import addHook from anki.lang import _ from anki import version as anki_version from .gui import initializeQtResources ANKI20 = anki_version.startswith("2.0") unicode = str if not ANKI20 else unicode initializeQtResources() class BatchEditDialog(QDialog): """Browser batch editing dialog""" def __init__(self, browser, nids): QDialog.__init__(self, parent=browser) self.browser = browser self.nids = nids self._setupUi() def _setupUi(self): tlabel = QLabel("Content to add to or replace with:")
from pprint import pprint as pp from anki import version from anki.hooks import addHook from anki.lang import getLang from anki.sched import Scheduler as schedv1 from anki.utils import ids2str, intTime from aqt import mw from aqt.utils import showInfo, tooltip from aqt.qt import * anki20 = version.startswith("2.0.") if not anki20: from anki.schedv2 import Scheduler as schedv2 German = getLang() == "de" def my_burySiblings(self, card): toBury = [] nconf = self._newConf(card) buryNew = nconf.get("bury", True) rconf = self._revConf(card) buryRev = rconf.get("bury", True) # loop through and remove from queues for cid, queue in self.col.db.execute(""" select id, queue from cards where nid=? and id!=? and (queue=0 or (queue=2 and due<=?))""",
#Based on | xquercus code, in add-on "Load Balanced Scheduler" #License | GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html #Source in | https://github.com/cjdduarte/Free_Weekend_Load_Balancer # LOG_LEVEL = 0 Disables logging. # LOG_LEVEL = 1 Logs a one line summary each time a card is load balanced. # LOG_LEVEL = 2 Logs additional detailed information about each step of the load balancing process. LOG_LEVEL = 0 import sys import anki import datetime from aqt import mw from anki import version ANKI21 = version.startswith("2.1.") from anki.sched import Scheduler from aqt.utils import tooltip import aqt import aqt.deckconf from anki.hooks import wrap if ANKI21: from PyQt5 import QtCore, QtGui, QtWidgets else: from PyQt4 import QtCore, QtGui as QtWidgets #from random import * #seed()
import aqt from aqt.qt import * from aqt import mw from aqt.overview import Overview from aqt.deckbrowser import DeckBrowser from aqt.stats import DeckStats from aqt.webview import AnkiWebView from aqt.utils import restoreGeom, maybeHideClose, addCloseShortcut from anki.stats import CollectionStats from anki.find import Finder from anki.hooks import wrap, addHook from anki import version as anki_version isAnki20 = anki_version.startswith("2.0.") from .config import * from .html import (heatmap_boilerplate, streak_css, streak_div, heatmap_css, heatmap_div, heatmap_script, ov_body) ### Stats and Heatmap generation ### def report_activity(self, limhist, limfcst, smode=False): """Calculate stats and generate report""" #self is anki.stats.CollectionStats config = mw.col.conf["heatmap"] limhist = None if limhist == 0 else limhist limfcst = None if limfcst == 0 else limfcst # get data