2020-04-25 11:44:48 +02:00
|
|
|
# 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 re
|
|
|
|
|
|
|
|
import aqt
|
|
|
|
from anki.backend_pb2 import EmptyCardsReport, NoteWithEmptyCards
|
2020-05-20 05:39:21 +02:00
|
|
|
from aqt import gui_hooks
|
2020-04-25 11:44:48 +02:00
|
|
|
from aqt.qt import QDialog, QDialogButtonBox, qconnect
|
2021-01-07 05:24:49 +01:00
|
|
|
from aqt.utils import TR, disable_help_button, restoreGeom, saveGeom, tooltip, tr
|
2020-04-25 11:44:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
def show_empty_cards(mw: aqt.main.AnkiQt) -> None:
|
|
|
|
mw.progress.start()
|
|
|
|
|
|
|
|
def on_done(fut):
|
|
|
|
mw.progress.finish()
|
|
|
|
report: EmptyCardsReport = fut.result()
|
|
|
|
if not report.notes:
|
|
|
|
tooltip(tr(TR.EMPTY_CARDS_NOT_FOUND))
|
|
|
|
return
|
|
|
|
diag = EmptyCardsDialog(mw, report)
|
|
|
|
diag.show()
|
|
|
|
|
2020-05-23 04:58:13 +02:00
|
|
|
mw.taskman.run_in_background(mw.col.backend.get_empty_cards, on_done)
|
2020-04-25 11:44:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
class EmptyCardsDialog(QDialog):
|
|
|
|
silentlyClose = True
|
|
|
|
|
|
|
|
def __init__(self, mw: aqt.main.AnkiQt, report: EmptyCardsReport) -> None:
|
|
|
|
super().__init__(mw)
|
|
|
|
self.mw = mw.weakref()
|
|
|
|
self.report = report
|
|
|
|
self.form = aqt.forms.emptycards.Ui_Dialog()
|
|
|
|
self.form.setupUi(self)
|
|
|
|
restoreGeom(self, "emptycards")
|
|
|
|
self.setWindowTitle(tr(TR.EMPTY_CARDS_WINDOW_TITLE))
|
2021-01-07 05:24:49 +01:00
|
|
|
disable_help_button(self)
|
2020-04-25 11:44:48 +02:00
|
|
|
self.form.keep_notes.setText(tr(TR.EMPTY_CARDS_PRESERVE_NOTES_CHECKBOX))
|
|
|
|
self.form.webview.title = "empty cards"
|
|
|
|
self.form.webview.set_bridge_command(self._on_note_link_clicked, self)
|
|
|
|
|
2020-05-20 05:39:21 +02:00
|
|
|
gui_hooks.empty_cards_will_show(self)
|
|
|
|
|
2020-04-25 11:44:48 +02:00
|
|
|
# make the note ids clickable
|
|
|
|
html = re.sub(
|
|
|
|
r"\[anki:nid:(\d+)\]",
|
|
|
|
"<a href=# onclick=\"pycmd('nid:\\1'); return false\">\\1</a>: ",
|
|
|
|
report.report,
|
|
|
|
)
|
|
|
|
style = "<style>.allempty { color: red; }</style>"
|
|
|
|
self.form.webview.stdHtml(style + html, context=self)
|
|
|
|
|
|
|
|
def on_finished(code):
|
|
|
|
saveGeom(self, "emptycards")
|
|
|
|
|
|
|
|
qconnect(self.finished, on_finished)
|
|
|
|
|
|
|
|
self._delete_button = self.form.buttonBox.addButton(
|
|
|
|
tr(TR.EMPTY_CARDS_DELETE_BUTTON), QDialogButtonBox.ActionRole
|
|
|
|
)
|
|
|
|
self._delete_button.setAutoDefault(False)
|
|
|
|
self._delete_button.clicked.connect(self._on_delete)
|
|
|
|
|
|
|
|
def _on_note_link_clicked(self, link):
|
2021-01-29 18:27:33 +01:00
|
|
|
self.mw.browser_search(link)
|
2020-04-25 11:44:48 +02:00
|
|
|
|
|
|
|
def _on_delete(self):
|
|
|
|
self.mw.progress.start()
|
|
|
|
|
|
|
|
def delete():
|
|
|
|
return self._delete_cards(self.form.keep_notes.isChecked())
|
|
|
|
|
|
|
|
def on_done(fut):
|
|
|
|
self.mw.progress.finish()
|
|
|
|
try:
|
|
|
|
count = fut.result()
|
|
|
|
finally:
|
|
|
|
self.close()
|
|
|
|
tooltip(tr(TR.EMPTY_CARDS_DELETED_COUNT, cards=count))
|
2020-08-09 04:42:58 +02:00
|
|
|
self.mw.reset()
|
2020-04-25 11:44:48 +02:00
|
|
|
|
|
|
|
self.mw.taskman.run_in_background(delete, on_done)
|
|
|
|
|
2020-07-31 01:54:42 +02:00
|
|
|
def _delete_cards(self, keep_notes: bool) -> int:
|
2020-04-25 11:44:48 +02:00
|
|
|
to_delete = []
|
2020-07-31 01:54:42 +02:00
|
|
|
note: NoteWithEmptyCards
|
2020-04-25 11:44:48 +02:00
|
|
|
for note in self.report.notes:
|
|
|
|
if keep_notes and note.will_delete_note:
|
|
|
|
# leave first card
|
|
|
|
to_delete.extend(note.card_ids[1:])
|
|
|
|
else:
|
|
|
|
to_delete.extend(note.card_ids)
|
|
|
|
|
2020-06-04 10:21:04 +02:00
|
|
|
self.mw.col.remove_cards_and_orphaned_notes(to_delete)
|
2020-04-25 11:44:48 +02:00
|
|
|
return len(to_delete)
|