From 95853bb603efacc9890f2b86bd28b0377e448ee5 Mon Sep 17 00:00:00 2001 From: ThanosApollo Date: Thu, 1 Sep 2022 07:46:56 +0300 Subject: Re-add anki addons --- .../Anki2/addons21/advanced_review/Card_Info.py | 508 +++++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 .local/share/Anki2/addons21/advanced_review/Card_Info.py (limited to '.local/share/Anki2/addons21/advanced_review/Card_Info.py') diff --git a/.local/share/Anki2/addons21/advanced_review/Card_Info.py b/.local/share/Anki2/addons21/advanced_review/Card_Info.py new file mode 100644 index 0000000..3d5031d --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/Card_Info.py @@ -0,0 +1,508 @@ +#// auth_ Mohamad Janati +#// Copyright (c) 2019-2021 Mohamad Janati (freaking stupid, right? :|) + +import os +import io + +from anki.hooks import addHook +from aqt.qt import * +from aqt.webview import AnkiWebView +import aqt.stats +import time +import datetime +from anki.lang import _ +from anki.utils import fmtTimeSpan +from anki.stats import CardStats +from aqt import * +from anki.utils import htmlToTextLine +from anki.collection import _Collection +from aqt.reviewer import Reviewer + + +#// sidebar functions +class StatsSidebar(object): + def __init__(self, mw): + config = mw.addonManager.getConfig(__name__) + sidebar_autoOpen = config['Card Info sidebar_ Auto Open'] + self.mw = mw + self.shown = False + addHook("showQuestion", self._update) + addHook("reviewCleanup", self._update) + if sidebar_autoOpen: + addHook("showQuestion", self.show) + + def _addDockable(self, title, w): + class DockableWithClose(QDockWidget): + closed = pyqtSignal() + def closeEvent(self, evt): + self.closed.emit() + QDockWidget.closeEvent(self, evt) + dock = DockableWithClose(title, mw) + dock.setObjectName(title) + dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) + dock.setFeatures(QDockWidget.AllDockWidgetFeatures) + dock.setWidget(w) + if mw.width() < 600: + mw.resize(QSize(600, mw.height())) + config = mw.addonManager.getConfig(__name__) + sidebar_defaultPosition = config['Card Info sidebar_ Default Position'] + if sidebar_defaultPosition == 1: + mw.addDockWidget(Qt.LeftDockWidgetArea, dock) + else: + mw.addDockWidget(Qt.RightDockWidgetArea, dock) + return dock + + def _remDockable(self, dock): + mw.removeDockWidget(dock) + + def show(self): + if not self.shown: + class ThinAnkiWebView(AnkiWebView): + def sizeHint(self): + return QSize(200, 100) + self.web = ThinAnkiWebView() + self.shown = self._addDockable("Card Info", self.web) + self.shown.closed.connect(self._onClosed) + self._update() + + def hide(self): + if self.shown: + self._remDockable(self.shown) + self.shown = None + + def toggle(self): + if self.shown: + self.hide() + else: + self.show() + + def _onClosed(self): + # schedule removal for after evt has finished + self.mw.progress.timer(100, self.hide, False) + + # modified _revlogData function + def _revlogData_mod(self, card, cs): + config = mw.addonManager.getConfig(__name__) + sidebar_font = config['Card Info sidebar_ Font'] + reviewsToShow = config['Card Info sidebar_ number of reviews to show for a card'] + limited_review_warning_note = config['Card Info sidebar_ warning note'] + custom_colors = config[' Review_ Custom Colors'] + again_color = config['Color_ Again'] + hard_color = config['Color_ Hard'] + good_color = config['Color_ Good'] + easy_color = config['Color_ Easy'] + entries = self.mw.col.db.all("select id/1000.0, ease, ivl, factor, time/1000.0, type from revlog where cid = ?", card.id) + if not entries: + return "" + s = "
Reviews
" + s += ("") % (sidebar_font, "Date") + s += ("" * 5) % ("Type", "Button", "Interval", "Ease", "Time") + cnt = 0 + for (date, ease, ivl, factor, taken, type) in reversed(entries): + cnt += 1 + s += "" % time.strftime("%y/%m/%d
%H:%M", time.localtime(date)) + tstr = ["Learn", "Review", "Relearn", "Filtered", "Resched"][type] + import anki.stats as st + + fmt = "%s" + if type == 0: + tstr = fmt % (st.colLearn, tstr) + elif type == 1: + tstr = fmt % (st.colMature, tstr) + elif type == 2: + tstr = fmt % (st.colRelearn, tstr) + elif type == 3: + tstr = fmt % (st.colCram, tstr) + else: + tstr = fmt % ("#000", tstr) + if ease == 1: + tstr = fmt % (st.colRelearn, tstr) + #################### + int_due = "%s" % time.strftime("%y/%m/%d", time.localtime(date)) + if ivl > 0: + int_due_date = time.localtime(date + (ivl * 24 * 60 * 60)) + int_due = time.strftime("%y/%m/%d", int_due_date) + #################### + if ivl == 0: + ivl = "0d" + elif ivl > 0: + ivl = fmtTimeSpan(ivl * 86400, short=True) + else: + ivl = cs.time(-ivl) + + if not custom_colors: + again_color = "#FF1111" + hard_color = "#FF9814" + good_color = "#33FF2D" + easy_color = "#21C0FF" + if self.mw.col.sched_ver() == 1 and type == 3: + if ease == 1: + button = "
Again
".format(again_color) + elif ease == 2: + button = "
Good
".format(good_color) + elif ease == 3: + button = "
Good
".format(good_color) + elif ease == 4: + button = "
Easy
".format(easy_color) + else: + button = "ease: {}".format(ease) + elif self.mw.col.sched_ver() == 1 and (type == 0 or type == 2): + if ease == 1: + button = "
Again
".format(again_color) + elif ease == 2: + button = "
Good
".format(good_color) + elif ease == 3: + button = "
Easy
".format(easy_color) + elif ease == 4: + button = "
Easy
".format(easy_color) + else: + button = "ease: {}".format(ease) + else: + if ease == 1: + button = "
Again
".format(again_color) + elif ease == 2: + button = "
Hard
".format(hard_color) + elif ease == 3: + button = "
Good
".format(good_color) + elif ease == 4: + button = "
Easy
".format(easy_color) + else: + button = "ease: {}".format(ease) + s += ("" * 5) % (tstr, button, "%s
(%s)" %(ivl, int_due), "%d%%" % (factor / 10) if factor else "", cs.time(taken)) + "" + if reviewsToShow != 0: + if cnt > int(reviewsToShow) - 1: + break + else: + continue + s += "
%s%s
%s%s
" + warning = "" + if limited_review_warning_note: + if cnt < card.reps: + try: + a = int(reviewsToShow) + warning = """

You have limited previous review information number to "{}" reviews.
""".format(reviewsToShow) + except ValueError: + warning = """

Some of the history is missing. For more information, please see the browser documentation.
""" + return s + warning + + + # adds the modified _revlogData function to Reviewer class in aqt.browser + Reviewer._revlogData_mod = _revlogData_mod + + + # modified report function + def report_mod(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__) + + infobar_created = config['Card Info sidebar_ Created'] + infobar_edited = config['Card Info sidebar_ Edited'] + infobar_firstReview = config['Card Info sidebar_ First Review'] + infobar_latestReview = config['Card Info sidebar_ Latest Review'] + infobar_due = config['Card Info sidebar_ Due'] + infobar_interval = config['Card Info sidebar_ Interval'] + infobar_ease = config['Card Info sidebar_ Ease'] + infobar_reviews = config['Card Info sidebar_ Reviews'] + infobar_lapses = config['Card Info sidebar_ Lapses'] + infobar_correctPercent = config['Card Info Sidebar_ Correct Percent'] + infobar_fastestReview = config['Card Info Sidebar_ Fastest Review'] + infobar_slowestReview = config['Card Info Sidebar_ Slowest Review'] + infobar_avgTime = config['Card Info sidebar_ Average Time'] + infobar_totalTime = config['Card Info sidebar_ Total Time'] + infobar_cardType = config['Card Info sidebar_ Card Type'] + infobar_noteType = config['Card Info sidebar_ Note Type'] + infobar_deck = config['Card Info sidebar_ Deck'] + infobar_tags = config['Card Info sidebar_ Tags'] + infobar_noteID = config['Card Info Sidebar_ Note ID'] + infobar_cardID = config['Card Info Sidebar_ Card ID'] + infobar_sortField = config['Card Info sidebar_ Sort Field'] + + c = self.card + fmt = lambda x, **kwargs: fmtTimeSpan(x, short=True, **kwargs) + self.txt = "" + if infobar_created: + self.addLine("Created", time.strftime("%Y-%m-%d | %H:%M", time.localtime(c.id/1000))) + if infobar_edited: + if c.note().mod != False and time.localtime(c.id/1000) != time.localtime(c.note().mod): + self.addLine("Edited", time.strftime("%Y-%m-%d | %H:%M", time.localtime(c.note().mod))) + first = self.col.db.scalar("select min(id) from revlog where cid = ?", c.id) + last = self.col.db.scalar("select max(id) from revlog where cid = ?", c.id) + if first: + if infobar_firstReview: + self.addLine("First Review", time.strftime("%Y-%m-%d | %H:%M", time.localtime(first/1000))) + if infobar_latestReview: + self.addLine("Latest Review", time.strftime("%Y-%m-%d | %H:%M", time.localtime(last/1000))) + if c.type != 0: + if c.odid or c.queue < 0: + next = None + else: + if c.queue in (2,3): + next = time.time()+((c.due - self.col.sched.today)*86400) + else: + next = c.due + next = self.date(next) + if next: + if infobar_due: + self.addLine("Due", next) + if c.queue == 2: + if infobar_interval: + self.addLine("Interval", fmt(c.ivl * 86400)) + if infobar_ease: + self.addLine("Ease", "%d%%" % (c.factor/10.0)) + if infobar_lapses: + self.addLine("Lapses", "%d" % c.lapses) + if self.col.sched_ver() == 1: + pressed_again = mw.col.db.scalar("select sum(case when ease = 1 then 1 else 0 end) from revlog where cid = ?", c.id) + pressed_good = mw.col.db.scalar("select sum(case when ease = 2 then 1 else 0 end) from revlog where cid = ?", c.id) + pressed_easy = mw.col.db.scalar("select sum(case when ease = 3 then 1 else 0 end) from revlog where cid = ?", c.id) + pressed_all = pressed_again + pressed_good + pressed_easy + self.addLine("Again", "{} | {:.0f}%".format(str(pressed_again).rjust(4), float(pressed_again/pressed_all)*100)) + self.addLine("Good", "{} | {:.0f}%".format(str(pressed_good).rjust(4), float(pressed_good/pressed_all)*100)) + self.addLine("Easy", "{} | {:.0f}%".format(str(pressed_easy).rjust(4), float(pressed_easy/pressed_all)*100)) + elif self.col.sched_ver() == 2: + pressed_again = mw.col.db.scalar("select sum(case when ease = 1 then 1 else 0 end) from revlog where cid = ?", c.id) + pressed_hard = mw.col.db.scalar("select sum(case when ease = 2 then 1 else 0 end) from revlog where cid = ?", c.id) + pressed_good = mw.col.db.scalar("select sum(case when ease = 3 then 1 else 0 end) from revlog where cid = ?", c.id) + pressed_easy = mw.col.db.scalar("select sum(case when ease = 4 then 1 else 0 end) from revlog where cid = ?", c.id) + pressed_all = pressed_again + pressed_hard + pressed_good + pressed_easy + if pressed_all < 1: + pressed_all = 1 + self.addLine("Again", "{} | {:.0f}%".format(str(pressed_again).rjust(4), float(pressed_again/pressed_all)*100)) + self.addLine("Hard", "{} | {:.0f}%".format(str(pressed_hard).rjust(4), float(pressed_hard/pressed_all)*100)) + self.addLine("Good", "{} | {:.0f}%".format(str(pressed_good).rjust(4), float(pressed_good/pressed_all)*100)) + self.addLine("Easy", "{} | {:.0f}%".format(str(pressed_easy).rjust(4), float(pressed_easy/pressed_all)*100)) + if infobar_reviews: + self.addLine("Reviews", "%d" % c.reps) + (cnt, total) = self.col.db.first("select count(), sum(time)/1000 from revlog where cid = ?", c.id) + if infobar_correctPercent and c.reps > 0: + self.addLine("Correct Percentage", "{:.0f}%".format(float((c.reps-c.lapses)/c.reps)*100)) + if infobar_fastestReview: + fastes_rev = mw.col.db.scalar("select time/1000.0 from revlog where cid = ? order by time asc limit 1", c.id) + self.addLine("Fastest Review", self.time(fastes_rev)) + if infobar_slowestReview: + slowest_rev = mw.col.db.scalar("select time/1000.0 from revlog where cid = ? order by time desc limit 1", c.id) + self.addLine("Slowest Review", self.time(slowest_rev)) + if cnt: + if infobar_avgTime: + self.addLine("Average Time", self.time(total / float(cnt))) + if infobar_totalTime: + self.addLine("Total Time", self.time(total)) + elif c.queue == 0: + if infobar_due: + self.addLine("Position", c.due) + if infobar_cardType: + self.addLine("Card Type", c.template()['name']) + if infobar_noteType: + self.addLine("Note Type", c.model()['name']) + if infobar_noteID: + self.addLine("Note ID", c.nid) + if infobar_cardID: + self.addLine("Card ID", c.id) + if infobar_deck: + self.addLine("Deck", self.col.decks.name(c.did)) + if c.note().tags: + if infobar_tags: + self.addLine("Tags", " | ".join(c.note().tags)) + f = c.note() + sort_field = htmlToTextLine(f.fields[self.col.models.sortIdx(f.model())]) + if infobar_sortField: + if len(sort_field) > 40: + self.addLine("Sort Field", "[{}
{}
{}...]".format(sort_field[:20], sort_field[20:41], sort_field[41:58])) + else: + self.addLine("Sort Field", htmlToTextLine(f.fields[self.col.models.sortIdx(f.model())])) + self.txt += "
" + return self.txt + + + # adds the modified report functions to CardStats class in anki.stats + CardStats.report_mod = report_mod + + + # modified cardStats function + def cardStats_mod(self, card): + from anki.stats import CardStats + return CardStats(self, card).report_mod() + + + # adds a modified cardStats function to _Collection class in anki.collection + _Collection.cardStats_mod = cardStats_mod + + + # functions to get more previous cards to add them to sidebard + def lastCard2(self): + if self._answeredIds: + if len(self._answeredIds) > 1: + try: + return self.mw.col.getCard(self._answeredIds[-2]) + except TypeError: + return + def lastCard3(self): + if self._answeredIds: + if len(self._answeredIds) > 2: + try: + return self.mw.col.getCard(self._answeredIds[-3]) + except TypeError: + return + def lastCard4(self): + if self._answeredIds: + if len(self._answeredIds) > 3: + try: + return self.mw.col.getCard(self._answeredIds[-4]) + except TypeError: + return + + # adds functions above to Reviewer class in aqt.reviewer + Reviewer.lastCard2 = lastCard2 + Reviewer.lastCard3 = lastCard3 + Reviewer.lastCard4 = lastCard4 + + + def _update(self): + config = mw.addonManager.getConfig(__name__) + infobar_currentReviewCount = config['Card Info sidebar_ Current Review Count'] + try: + sidebar_PreviousCards = int(config['Card Info sidebar_ Number of previous cards to show']) + except ValueError: + sidebar_PreviousCards = 2 + if not self.shown: + return + txt = "" + r = self.mw.reviewer + d = self.mw.col + cs = CardStats(d, r.card) + current_card = r.card + review_count = len(self.mw.reviewer._answeredIds) + styles = """""" + currentReviewCount = "
Current Card
Current Review Count: {}
".format(review_count) + if current_card: + txt += styles + if infobar_currentReviewCount: + txt += currentReviewCount + else: + txt += "
Current Card
" + txt += d.cardStats_mod(current_card) + txt += "

" + txt += r._revlogData_mod(current_card, cs) + card2 = r.lastCard() + if card2 and sidebar_PreviousCards > 1: + if sidebar_PreviousCards == 2: + txt += "


Last Card
" + else: + txt += "
Card 2
" + txt += d.cardStats_mod(card2) + txt += "

" + txt += r._revlogData_mod(card2, cs) + if sidebar_PreviousCards < 3: + if infobar_currentReviewCount: + txt += currentReviewCount + card3 = r.lastCard2() + if card3 and sidebar_PreviousCards > 2: + txt += "


Card 3
" + txt += d.cardStats_mod(card3) + txt += "

" + txt += r._revlogData_mod(card3, cs) + if sidebar_PreviousCards < 4: + if infobar_currentReviewCount: + txt += currentReviewCount + card4 = r.lastCard3() + if card4 and sidebar_PreviousCards > 3: + txt += "


Card 4
" + txt += d.cardStats_mod(card4) + txt += "

" + txt += r._revlogData_mod(card4, cs) + if infobar_currentReviewCount: + txt += currentReviewCount + if not txt: + styles = """""" + txt = styles + card2 = r.lastCard() + if card2 and sidebar_PreviousCards > 1: + txt += "

Last Card
" + txt += d.cardStats_mod(card2) + txt += "

" + txt += r._revlogData_mod(card2, cs) + if sidebar_PreviousCards < 3: + if infobar_currentReviewCount: + txt += currentReviewCount + card3 = r.lastCard2() + if card3 and sidebar_PreviousCards > 2: + txt += "


Card 2
" + txt += d.cardStats_mod(card3) + txt += "

" + txt += r._revlogData_mod(card3, cs) + if sidebar_PreviousCards < 4: + if infobar_currentReviewCount: + txt += currentReviewCount + card4 = r.lastCard3() + if card4 and sidebar_PreviousCards > 3: + txt += "


Card 3
" + txt += d.cardStats_mod(card4) + txt += "

" + txt += r._revlogData_mod(card4, cs) + if infobar_currentReviewCount: + txt += currentReviewCount + style = self._style() + self.web.setHtml(""" + + + + + +

%s
+ + +"""% (style, txt)) + + + 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) + ";}" + + +_cs = StatsSidebar(mw) + + +def cardStats(on): + _cs.toggle() -- cgit v1.2.3