# 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 html from collections.abc import Callable from dataclasses import dataclass from typing import Any import aqt import aqt.operations from anki.collection import OpChanges from anki.scheduler import UnburyDeck from aqt import gui_hooks from aqt.deckdescription import DeckDescriptionDialog from aqt.deckoptions import display_options_for_deck from aqt.operations import QueryOp from aqt.operations.scheduling import ( empty_filtered_deck, rebuild_filtered_deck, unbury_deck, ) from aqt.sound import av_player from aqt.toolbar import BottomBar from aqt.utils import askUserDialog, openLink, shortcut, tooltip, tr class OverviewBottomBar: def __init__(self, overview: Overview) -> None: self.overview = overview @dataclass class OverviewContent: """Stores sections of HTML content that the overview will be populated with. Attributes: deck {str} -- Plain text deck name shareLink {str} -- HTML of the share link section desc {str} -- HTML of the deck description section table {str} -- HTML of the deck stats table section """ deck: str shareLink: str desc: str table: str class Overview: "Deck overview." def __init__(self, mw: aqt.AnkiQt) -> None: self.mw = mw self.web = mw.web self.bottom = BottomBar(mw, mw.bottomWeb) self._refresh_needed = False def show(self) -> None: av_player.stop_and_clear_queue() self.web.set_bridge_command(self._linkHandler, self) self.mw.setStateShortcuts(self._shortcutKeys()) self.refresh() def refresh(self) -> None: def success(_counts: tuple) -> None: self._refresh_needed = False self._renderPage() self._renderBottom() self.mw.web.setFocus() gui_hooks.overview_did_refresh(self) QueryOp( parent=self.mw, op=lambda col: col.sched.counts(), success=success ).run_in_background() def refresh_if_needed(self) -> None: if self._refresh_needed: self.refresh() def op_executed( self, changes: OpChanges, handler: object | None, focused: bool ) -> bool: if changes.study_queues: self._refresh_needed = True if focused: self.refresh_if_needed() return self._refresh_needed # Handlers ############################################################ def _linkHandler(self, url: str) -> bool: if url == "study": self.mw.col.startTimebox() self.mw.moveToState("review") if self.mw.state == "overview": tooltip(tr.studying_no_cards_are_due_yet()) elif url == "anki": print("anki menu") elif url == "opts": display_options_for_deck(self.mw.col.decks.current()) elif url == "cram": aqt.dialogs.open("FilteredDeckConfigDialog", self.mw) elif url == "refresh": self.rebuild_current_filtered_deck() elif url == "empty": self.empty_current_filtered_deck() elif url == "decks": self.mw.moveToState("deckBrowser") elif url == "review": openLink(f"{aqt.appShared}info/{self.sid}?v={self.sidVer}") elif url in {"studymore", "customStudy"}: self.onStudyMore() elif url == "unbury": self.on_unbury() elif url == "description": self.edit_description() elif url.lower().startswith("http"): openLink(url) return False def _shortcutKeys(self) -> list[tuple[str, Callable]]: return [ ("o", lambda: display_options_for_deck(self.mw.col.decks.current())), ("r", self.rebuild_current_filtered_deck), ("e", self.empty_current_filtered_deck), ("c", self.onCustomStudyKey), ("u", self.on_unbury), ] def _current_deck_is_filtered(self) -> int: return self.mw.col.decks.current()["dyn"] def rebuild_current_filtered_deck(self) -> None: rebuild_filtered_deck( parent=self.mw, deck_id=self.mw.col.decks.selected() ).run_in_background() def empty_current_filtered_deck(self) -> None: empty_filtered_deck( parent=self.mw, deck_id=self.mw.col.decks.selected() ).run_in_background() def onCustomStudyKey(self) -> None: if not self._current_deck_is_filtered(): self.onStudyMore() def on_unbury(self) -> None: mode = UnburyDeck.Mode.ALL info = self.mw.col.sched.congratulations_info() if info.have_sched_buried and info.have_user_buried: opts = [ tr.studying_manually_buried_cards(), tr.studying_buried_siblings(), tr.studying_all_buried_cards(), tr.actions_cancel(), ] diag = askUserDialog(tr.studying_what_would_you_like_to_unbury(), opts) diag.setDefault(0) ret = diag.run() if ret == opts[0]: mode = UnburyDeck.Mode.USER_ONLY elif ret == opts[1]: mode = UnburyDeck.Mode.SCHED_ONLY elif ret == opts[3]: return unbury_deck( parent=self.mw, deck_id=self.mw.col.decks.get_current_id(), mode=mode ).run_in_background() onUnbury = on_unbury # HTML ############################################################ def _renderPage(self) -> None: deck = self.mw.col.decks.current() self.sid = deck.get("sharedFrom") if self.sid: self.sidVer = deck.get("ver", None) shareLink = 'Reviews and Updates' else: shareLink = "" if self.mw.col.sched._is_finished(): self._show_finished_screen() return content = OverviewContent( deck=deck["name"], shareLink=shareLink, desc=self._desc(deck), table=self._table(), ) gui_hooks.overview_will_render_content(self, content) content.deck = html.escape(content.deck) self.web.stdHtml( self._body % content.__dict__, css=["css/overview.css"], js=["js/vendor/jquery.min.js"], context=self, ) def _show_finished_screen(self) -> None: self.web.load_sveltekit_page("congrats") def _desc(self, deck: dict[str, Any]) -> str: if deck["dyn"]: desc = tr.studying_this_is_a_special_deck_for() desc += f" {tr.studying_cards_will_be_automatically_returned_to()}" desc += f" {tr.studying_deleting_this_deck_from_the_deck()}" else: desc = deck.get("desc", "") if deck.get("md", False): desc = self.mw.col.render_markdown(desc) if not desc: return "
" if deck["dyn"]: dyn = "dyn" else: dyn = "" return f'
| {but("study", tr.studying_study_now(), id="study", extra=" autofocus")} |