anki/qt/aqt/stats.py
Damien Elmes 57d7e3e2ab commit immediately when there's no active checkpoint
Reviews and operations on the backend that support undoing can now be
committed immediately, so they will not be lost in the event of a crash.

This required tweaks to a few places:

- don't set collection mtime on save() unless changes were made in
Python, as otherwise we end up accidentally clearing the backend undo
queue
- autosave() is now run on every reset()
- garbage collection now runs in a timer, instead of relying on
autosave() to be run periodically
2021-03-10 11:47:53 +10:00

197 lines
6.1 KiB
Python

# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from __future__ import annotations
import time
from typing import Any
import aqt
from aqt import gui_hooks
from aqt.qt import *
from aqt.theme import theme_manager
from aqt.utils import (
TR,
addCloseShortcut,
disable_help_button,
getSaveFile,
maybeHideClose,
restoreGeom,
saveGeom,
tooltip,
tr,
)
class NewDeckStats(QDialog):
"""New deck stats."""
def __init__(self, mw: aqt.main.AnkiQt) -> None:
QDialog.__init__(self, mw, Qt.Window)
mw.garbage_collect_on_dialog_finish(self)
self.mw = mw
self.name = "deckStats"
self.period = 0
self.form = aqt.forms.stats.Ui_Dialog()
self.oldPos = None
self.wholeCollection = False
self.setMinimumWidth(700)
disable_help_button(self)
f = self.form
f.setupUi(self)
f.groupBox.setVisible(False)
f.groupBox_2.setVisible(False)
restoreGeom(self, self.name)
b = f.buttonBox.addButton(
tr(TR.STATISTICS_SAVE_PDF), QDialogButtonBox.ActionRole
)
qconnect(b.clicked, self.saveImage)
b.setAutoDefault(False)
maybeHideClose(self.form.buttonBox)
addCloseShortcut(self)
gui_hooks.stats_dialog_will_show(self)
self.show()
self.refresh()
self.form.web.set_bridge_command(self._on_bridge_cmd, self)
self.activateWindow()
def reject(self) -> None:
self.form.web = None
saveGeom(self, self.name)
aqt.dialogs.markClosed("NewDeckStats")
QDialog.reject(self)
def closeWithCallback(self, callback: Callable[[], None]) -> None:
self.reject()
callback()
def _imagePath(self) -> str:
name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time()))
name = f"anki-{tr(TR.STATISTICS_STATS)}{name}"
file = getSaveFile(
self,
title=tr(TR.STATISTICS_SAVE_PDF),
dir_description="stats",
key="stats",
ext=".pdf",
fname=name,
)
return file
def saveImage(self) -> None:
path = self._imagePath()
if not path:
return
self.form.web.page().printToPdf(path)
tooltip(tr(TR.STATISTICS_SAVED))
# legacy add-ons
def changePeriod(self, n: Any) -> None:
pass
def changeScope(self, type: Any) -> None:
pass
def _on_bridge_cmd(self, cmd: str) -> bool:
if cmd.startswith("browserSearch"):
_, query = cmd.split(":", 1)
browser = aqt.dialogs.open("Browser", self.mw)
browser.search_for(query)
return False
def refresh(self) -> None:
self.form.web.load_ts_page("graphs")
class DeckStats(QDialog):
"""Legacy deck stats, used by some add-ons."""
def __init__(self, mw: aqt.main.AnkiQt) -> None:
QDialog.__init__(self, mw, Qt.Window)
mw.garbage_collect_on_dialog_finish(self)
self.mw = mw
self.name = "deckStats"
self.period = 0
self.form = aqt.forms.stats.Ui_Dialog()
self.oldPos = None
self.wholeCollection = False
self.setMinimumWidth(700)
disable_help_button(self)
f = self.form
if theme_manager.night_mode and not theme_manager.macos_dark_mode():
# the grouping box renders incorrectly in the fusion theme. 5.9+
# 5.13 behave differently to 5.14, but it looks bad in either case,
# and adjusting the top margin makes the 'save PDF' button show in
# the wrong place, so for now we just disable the border instead
self.setStyleSheet("QGroupBox { border: 0; }")
f.setupUi(self)
restoreGeom(self, self.name)
b = f.buttonBox.addButton(
tr(TR.STATISTICS_SAVE_PDF), QDialogButtonBox.ActionRole
)
qconnect(b.clicked, self.saveImage)
b.setAutoDefault(False)
qconnect(f.groups.clicked, lambda: self.changeScope("deck"))
f.groups.setShortcut("g")
qconnect(f.all.clicked, lambda: self.changeScope("collection"))
qconnect(f.month.clicked, lambda: self.changePeriod(0))
qconnect(f.year.clicked, lambda: self.changePeriod(1))
qconnect(f.life.clicked, lambda: self.changePeriod(2))
maybeHideClose(self.form.buttonBox)
addCloseShortcut(self)
gui_hooks.stats_dialog_old_will_show(self)
self.show()
self.refresh()
self.activateWindow()
def reject(self) -> None:
self.form.web = None
saveGeom(self, self.name)
aqt.dialogs.markClosed("DeckStats")
QDialog.reject(self)
def closeWithCallback(self, callback: Callable[[], None]) -> None:
self.reject()
callback()
def _imagePath(self) -> str:
name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time()))
name = f"anki-{tr(TR.STATISTICS_STATS)}{name}"
file = getSaveFile(
self,
title=tr(TR.STATISTICS_SAVE_PDF),
dir_description="stats",
key="stats",
ext=".pdf",
fname=name,
)
return file
def saveImage(self) -> None:
path = self._imagePath()
if not path:
return
self.form.web.page().printToPdf(path)
tooltip(tr(TR.STATISTICS_SAVED))
def changePeriod(self, n: int) -> None:
self.period = n
self.refresh()
def changeScope(self, type: str) -> None:
self.wholeCollection = type == "collection"
self.refresh()
def refresh(self) -> None:
self.mw.progress.start(parent=self)
stats = self.mw.col.stats()
stats.wholeCollection = self.wholeCollection
self.report = stats.report(type=self.period)
self.form.web.title = "deck stats"
self.form.web.stdHtml(
f"<html><body>{self.report}</body></html>",
js=["js/vendor/jquery.min.js", "js/vendor/plot.js"],
context=self,
)
self.mw.progress.finish()