# 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 from concurrent.futures import Future from typing import Any import aqt import aqt.forms import aqt.main from anki.cards import CardId from anki.collection import EmptyCardsReport from aqt import gui_hooks from aqt.qt import QDialog, QDialogButtonBox, qconnect from aqt.utils import disable_help_button, restoreGeom, saveGeom, tooltip, tr from aqt.webview import AnkiWebViewKind def show_empty_cards(mw: aqt.main.AnkiQt) -> None: mw.progress.start() def on_done(fut: Future) -> None: mw.progress.finish() report: EmptyCardsReport = fut.result() if not report.notes: tooltip(tr.empty_cards_not_found()) return diag = EmptyCardsDialog(mw, report) diag.show() mw.taskman.run_in_background(mw.col.get_empty_cards, on_done) class EmptyCardsDialog(QDialog): silentlyClose = True def __init__(self, mw: aqt.main.AnkiQt, report: EmptyCardsReport) -> None: super().__init__(mw) self.mw = mw.weakref() self.mw.garbage_collect_on_dialog_finish(self) self.report = report self.form = aqt.forms.emptycards.Ui_Dialog() self.form.setupUi(self) restoreGeom(self, "emptycards") self.setWindowTitle(tr.empty_cards_window_title()) disable_help_button(self) self.form.keep_notes.setText(tr.empty_cards_preserve_notes_checkbox()) self.form.webview.set_kind(AnkiWebViewKind.EMPTY_CARDS) self.form.webview.set_bridge_command(self._on_note_link_clicked, self) gui_hooks.empty_cards_will_show(self) # make the note ids clickable html = re.sub( r"\[anki:nid:(\d+)\]", "\\1: ", report.report, ) style = "" self.form.webview.stdHtml(style + html, context=self) def on_finished(code: Any) -> None: self.form.webview.cleanup() self.form.webview = None saveGeom(self, "emptycards") qconnect(self.finished, on_finished) self._delete_button = self.form.buttonBox.addButton( tr.empty_cards_delete_button(), QDialogButtonBox.ButtonRole.ActionRole ) self._delete_button.setAutoDefault(False) qconnect(self._delete_button.clicked, self._on_delete) def _on_note_link_clicked(self, link: str) -> None: aqt.dialogs.open("Browser", self.mw, search=(link,)) def _on_delete(self) -> None: self.mw.progress.start() def delete() -> int: return self._delete_cards(self.form.keep_notes.isChecked()) def on_done(fut: Future) -> None: self.mw.progress.finish() try: count = fut.result() finally: self.close() tooltip(tr.empty_cards_deleted_count(cards=count)) self.mw.reset() self.mw.taskman.run_in_background(delete, on_done) def _delete_cards(self, keep_notes: bool) -> int: to_delete: list[CardId] = [] note: EmptyCardsReport.NoteWithEmptyCards for note in self.report.notes: if keep_notes and note.will_delete_note: # leave first card to_delete.extend([CardId(id) for id in note.card_ids[1:]]) else: to_delete.extend([CardId(id) for id in note.card_ids]) self.mw.col.remove_cards_and_orphaned_notes(to_delete) return len(to_delete)