def _linkHandler(self, url: str) -> Any: if ":" in url: (cmd, arg) = url.split(":", 1) else: cmd = url if cmd == "open": self.set_current_deck(DeckId(int(arg))) elif cmd == "opts": self._showOptions(arg) elif cmd == "shared": self._onShared() elif cmd == "import": self.mw.onImport() elif cmd == "create": self._on_create() elif cmd == "drag": source, target = arg.split(",") self._handle_drag_and_drop(DeckId(int(source)), DeckId(int(target or 0))) elif cmd == "collapse": self._collapse(DeckId(int(arg))) elif cmd == "v2upgrade": self._confirm_upgrade() elif cmd == "v2upgradeinfo": openLink("https://faqs.ankiweb.net/the-anki-2.1-scheduler.html") return False
def _showOptions(self, did: str) -> None: m = QMenu(self.mw) a = m.addAction(tr.actions_rename()) qconnect(a.triggered, lambda b, did=did: self._rename(DeckId(int(did)))) a = m.addAction(tr.actions_options()) qconnect(a.triggered, lambda b, did=did: self._options(DeckId(int(did)))) a = m.addAction(tr.actions_export()) qconnect(a.triggered, lambda b, did=did: self._export(DeckId(int(did)))) a = m.addAction(tr.actions_delete()) qconnect(a.triggered, lambda b, did=did: self._delete(DeckId(int(did)))) gui_hooks.deck_browser_will_show_options_menu(m, int(did)) m.exec_(QCursor.pos())
def _handle_drag_drop_decks(self, sources: list[SidebarItem], target: SidebarItem) -> bool: deck_ids = [ DeckId(source.id) for source in sources if source.item_type == SidebarItemType.DECK ] if not deck_ids: return False new_parent = DeckId(target.id) reparent_decks(parent=self.browser, deck_ids=deck_ids, new_parent=new_parent).run_in_background() return True
def _on_rename_with_parents(self, item: SidebarItem) -> None: title = "Anki" if item.item_type is SidebarItemType.TAG: title = tr.actions_rename_tag() elif item.item_type is SidebarItemType.DECK: title = tr.actions_rename_deck() new_name = getOnlyText(tr.actions_new_name(), title=title, default=item.full_name).replace('"', "") if not new_name or new_name == item.full_name: return if item.item_type is SidebarItemType.TAG: def success(out: OpChangesWithCount) -> None: if out.count: tooltip(tr.browsing_notes_updated(count=out.count), parent=self) else: showInfo(tr.browsing_tag_rename_warning_empty(), parent=self) rename_tag( parent=self, current_name=item.full_name, new_name=new_name, ).success(success).run_in_background() elif item.item_type is SidebarItemType.DECK: rename_deck( parent=self, deck_id=DeckId(item.id), new_name=new_name, ).run_in_background()
def __init__( self, mw: AnkiQt, deck_id: DeckId = DeckId(0), search: str | None = None, search_2: str | None = None, ) -> None: """If 'deck_id' is non-zero, load and modify its settings. Otherwise, build a new deck and derive settings from the current deck. If search or search_2 are provided, they will be used as the default search text. """ QDialog.__init__(self, mw) self.mw = mw self.col = self.mw.col self._desired_search_1 = search self._desired_search_2 = search_2 self._initial_dialog_setup() # set on successful query self.deck: FilteredDeckForUpdate QueryOp( parent=self.mw, op=lambda col: col.sched.get_or_create_filtered_deck(deck_id= deck_id), success=self.load_deck_and_show, ).failure(self.on_fetch_error).run_in_background()
def rename_deck(self, item: SidebarItem, new_name: str) -> None: if not new_name: return # update UI immediately, to avoid redraw item.name = new_name full_name = item.name_prefix + new_name deck_id = DeckId(item.id) def after_fetch(deck: Deck) -> None: if full_name == deck.name: return rename_deck( parent=self, deck_id=deck_id, new_name=full_name, ).run_in_background() QueryOp( parent=self.browser, op=lambda col: col.get_deck(deck_id), success=after_fetch, ).run_in_background()
def toggle_expand(node: DeckTreeNode) -> Callable[[bool], None]: return lambda expanded: set_deck_collapsed( parent=self, deck_id=DeckId(node.deck_id), collapsed=not expanded, scope=DeckCollapseScope.BROWSER, ).run_in_background(initiator=self, )
def _checkLeech(self, card: Card, conf: QueueConfig) -> bool: "Leech handler. True if card was a leech." lf = conf["leechFails"] if not lf: return False # if over threshold or every half threshold reps after that if card.lapses >= lf and (card.lapses - lf) % (max(lf // 2, 1)) == 0: # add a leech tag f = card.note() f.add_tag("leech") f.flush() # handle a = conf["leechAction"] if a == LEECH_SUSPEND: # if it has an old due, remove it from cram/relearning if card.odue: card.due = card.odue if card.odid: card.did = card.odid card.odue = 0 card.odid = DeckId(0) card.queue = QUEUE_TYPE_SUSPENDED # notify UI hooks.card_did_leech(card) return True else: return False
def success(out: OpChangesWithId) -> None: deck = self.mw.col.decks.get(DeckId(out.id)) self.name = deck["name"] # make sure we clean up reset hook when manually exiting gui_hooks.state_did_reset.remove(self.onReset) QDialog.accept(self)
def __init__( self, mw: AnkiQt, widget: QWidget, label: bool = True, starting_deck_id: Optional[DeckId] = None, ) -> None: QHBoxLayout.__init__(self) self._widget = widget # type: ignore self.mw = mw self._setup_ui(show_label=label) self._selected_deck_id = DeckId(0) # default to current deck if starting id not provided if starting_deck_id is None: starting_deck_id = DeckId(self.mw.col.get_config("curDeck", default=1) or 1) self.selected_deck_id = starting_deck_id
def __init__(self, col: anki.collection.Collection) -> None: super().__init__(col) self.queueLimit = 50 self.reportLimit = 1000 self.dynReportLimit = 99999 self.reps = 0 self._haveQueues = False self._lrnCutoff = 0 self._active_decks: List[DeckId] = [] self._current_deck_id = DeckId(1)
def __init__( self, mw: AnkiQt, widget: QWidget, label: bool = True, starting_deck_id: DeckId | None = None, on_deck_changed: Callable[[int], None] | None = None, ) -> None: QHBoxLayout.__init__(self) self._widget = widget # type: ignore self.mw = mw self._setup_ui(show_label=label) self._selected_deck_id = DeckId(0) # default to current deck if starting id not provided if starting_deck_id is None: starting_deck_id = DeckId(self.mw.col.get_config("curDeck", default=1) or 1) self.selected_deck_id = starting_deck_id self.on_deck_changed = on_deck_changed gui_hooks.operation_did_execute.append(self.on_operation_did_execute)
def display_options_for_deck(deck: DeckDict) -> None: if not deck["dyn"]: if KeyboardModifiersPressed().shift or aqt.mw.col.schedVer() == 1: deck_legacy = aqt.mw.col.decks.get(DeckId(deck["id"])) aqt.deckconf.DeckConf(aqt.mw, deck_legacy) else: DeckOptionsDialog(aqt.mw, deck) else: aqt.dialogs.open("FilteredDeckConfigDialog", aqt.mw, deck_id=deck["id"])
def create_filtered_deck(search_string) -> int: search_term = FilteredDeckConfig.SearchTerm( search=search_string, limit=100, order=0, # random? ) filtered_deck = get_scheduler().get_or_create_filtered_deck(DeckId(0)) del filtered_deck.config.search_terms[:] filtered_deck.config.search_terms.append(search_term) return get_scheduler().add_or_update_filtered_deck(filtered_deck).id
def rename_deck(self, item: SidebarItem, new_name: str) -> None: if not new_name or new_name == item.name: return # update UI immediately, to avoid redraw item.name = new_name rename_deck( parent=self, deck_id=DeckId(item.id), new_name=item.name_prefix + new_name, ).run_in_background()
def setup_choosers(self) -> None: defaults = self.col.defaults_for_adding( current_review_card=self.mw.reviewer.card) self.notetype_chooser = NotetypeChooser( mw=self.mw, widget=self.form.modelArea, starting_notetype_id=NotetypeId(defaults.notetype_id), on_button_activated=self.show_notetype_selector, on_notetype_changed=self.on_notetype_change, ) self.deck_chooser = aqt.deckchooser.DeckChooser( self.mw, self.form.deckArea, starting_deck_id=DeckId(defaults.deck_id))
def _rescheduleRev(self, card: Card, ease: int) -> None: # type: ignore[override] # update interval card.lastIvl = card.ivl if self._resched(card): self._updateRevIvl(card, ease) # then the rest card.factor = max(1300, card.factor + [-150, 0, 150][ease - 2]) card.due = self.today + card.ivl else: card.due = card.odue if card.odid: card.did = card.odid card.odid = DeckId(0) card.odue = 0
def set_up_test_deck_and_test_model_and_two_notes(): deck_id = create_deck("test_deck") model_id = create_model( model_name="test_model", field_names=["field1", "field2"], card_descriptions=[ CardDescription(name="card_1", front="{{field1}}", back="{{field1}}<br> {{field2}}"), CardDescription(name="card_2", front="{{field2}}", back="{{field2}}<br> {{field1}}") ], ) note_id = add_note( model_name="test_model", deck_name="test_deck", fields={ "field1": "note1 field1", "field2": "note1 field2" }, tags=["tag1"], ) get_decks().set_current(DeckId(deck_id)) card_ids = get_collection().find_cards(query=f"nid:{note_id}") import delay_siblings return Setup( delay_siblings=delay_siblings, model_id=model_id, deck_id=deck_id, note_id=note_id, card1_id=card_ids[0], card2_id=card_ids[1], )
def _rescheduleAsRev(self, card: Card, conf: QueueConfig, early: bool) -> None: lapse = card.type == CARD_TYPE_REV if lapse: if self._resched(card): card.due = max(self.today + 1, card.odue) else: card.due = card.odue card.odue = 0 else: self._rescheduleNew(card, conf, early) card.queue = QUEUE_TYPE_REV card.type = CARD_TYPE_REV # if we were dynamic, graduating means moving back to the old deck resched = self._resched(card) if card.odid: card.did = card.odid card.odue = 0 card.odid = DeckId(0) # if rescheduling is off, it needs to be set back to a new card if not resched and not lapse: card.queue = QUEUE_TYPE_NEW card.type = CARD_TYPE_NEW card.due = self.col.nextID("pos")
def _removeFromFiltered(self, card: Card) -> None: if card.odid: card.did = card.odid card.odue = 0 card.odid = DeckId(0)
def success(out: OpChangesWithId) -> None: deck = self.mw.col.decks.get(DeckId(out.id)) self.name = deck["name"] self.accept_with_callback()
def filtered_deck_created(search_string): deck_id = create_filtered_deck(search_string) yield deck_id get_decks().remove([DeckId(deck_id)])
def _selected_decks(self) -> list[DeckId]: return [ DeckId(item.id) for item in self._selected_items() if item.item_type == SidebarItemType.DECK ]