49a1580566
Avoids duplicate work, and is a step towards allowing the next states to be modified by third-party code. Also: - fixed incorrect underlined count, due to reviews being labeled as learning cards - fixed reviewer not refreshing when undoing a test review, by splitting up backend queue rebuilding from frontend reviewer refresh - moved answering into a CollectionOp
225 lines
5.8 KiB
Python
225 lines
5.8 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
|
|
|
|
from typing import Optional, Sequence
|
|
|
|
import aqt
|
|
from anki.cards import CardId
|
|
from anki.collection import (
|
|
CARD_TYPE_NEW,
|
|
Collection,
|
|
Config,
|
|
OpChanges,
|
|
OpChangesWithCount,
|
|
OpChangesWithId,
|
|
)
|
|
from anki.decks import DeckId
|
|
from anki.notes import NoteId
|
|
from anki.scheduler import FilteredDeckForUpdate, UnburyDeck
|
|
from anki.scheduler.v3 import CardAnswer
|
|
from anki.scheduler.v3 import Scheduler as V3Scheduler
|
|
from aqt.operations import CollectionOp
|
|
from aqt.qt import *
|
|
from aqt.utils import disable_help_button, getText, tooltip, tr
|
|
|
|
|
|
def set_due_date_dialog(
|
|
*,
|
|
parent: QWidget,
|
|
card_ids: Sequence[CardId],
|
|
config_key: Optional[Config.String.Key.V],
|
|
) -> Optional[CollectionOp[OpChanges]]:
|
|
assert aqt.mw
|
|
if not card_ids:
|
|
return None
|
|
|
|
default_text = (
|
|
aqt.mw.col.get_config_string(config_key) if config_key is not None else ""
|
|
)
|
|
prompt = "\n".join(
|
|
[
|
|
tr.scheduling_set_due_date_prompt(cards=len(card_ids)),
|
|
tr.scheduling_set_due_date_prompt_hint(),
|
|
]
|
|
)
|
|
(days, success) = getText(
|
|
prompt=prompt,
|
|
parent=parent,
|
|
default=default_text,
|
|
title=tr.actions_set_due_date(),
|
|
)
|
|
if not success or not days.strip():
|
|
return None
|
|
else:
|
|
return CollectionOp(
|
|
parent, lambda col: col.sched.set_due_date(card_ids, days, config_key)
|
|
).success(
|
|
lambda _: tooltip(
|
|
tr.scheduling_set_due_date_done(cards=len(card_ids)),
|
|
parent=parent,
|
|
)
|
|
)
|
|
|
|
|
|
def forget_cards(
|
|
*, parent: QWidget, card_ids: Sequence[CardId]
|
|
) -> CollectionOp[OpChanges]:
|
|
return CollectionOp(
|
|
parent, lambda col: col.sched.schedule_cards_as_new(card_ids)
|
|
).success(
|
|
lambda _: tooltip(
|
|
tr.scheduling_forgot_cards(cards=len(card_ids)), parent=parent
|
|
)
|
|
)
|
|
|
|
|
|
def reposition_new_cards_dialog(
|
|
*, parent: QWidget, card_ids: Sequence[CardId]
|
|
) -> Optional[CollectionOp[OpChangesWithCount]]:
|
|
from aqt import mw
|
|
|
|
assert mw
|
|
assert mw.col.db
|
|
|
|
row = mw.col.db.first(
|
|
f"select min(due), max(due) from cards where type={CARD_TYPE_NEW} and odid=0"
|
|
)
|
|
assert row
|
|
(min_position, max_position) = row
|
|
min_position = max(min_position or 0, 0)
|
|
max_position = max_position or 0
|
|
|
|
d = QDialog(parent)
|
|
disable_help_button(d)
|
|
d.setWindowModality(Qt.WindowModal)
|
|
frm = aqt.forms.reposition.Ui_Dialog()
|
|
frm.setupUi(d)
|
|
|
|
txt = tr.browsing_queue_top(val=min_position)
|
|
txt += "\n" + tr.browsing_queue_bottom(val=max_position)
|
|
frm.label.setText(txt)
|
|
|
|
frm.start.selectAll()
|
|
if not d.exec_():
|
|
return None
|
|
|
|
start = frm.start.value()
|
|
step = frm.step.value()
|
|
randomize = frm.randomize.isChecked()
|
|
shift = frm.shift.isChecked()
|
|
|
|
return reposition_new_cards(
|
|
parent=parent,
|
|
card_ids=card_ids,
|
|
starting_from=start,
|
|
step_size=step,
|
|
randomize=randomize,
|
|
shift_existing=shift,
|
|
)
|
|
|
|
|
|
def reposition_new_cards(
|
|
*,
|
|
parent: QWidget,
|
|
card_ids: Sequence[CardId],
|
|
starting_from: int,
|
|
step_size: int,
|
|
randomize: bool,
|
|
shift_existing: bool,
|
|
) -> CollectionOp[OpChangesWithCount]:
|
|
return CollectionOp(
|
|
parent,
|
|
lambda col: col.sched.reposition_new_cards(
|
|
card_ids=card_ids,
|
|
starting_from=starting_from,
|
|
step_size=step_size,
|
|
randomize=randomize,
|
|
shift_existing=shift_existing,
|
|
),
|
|
).success(
|
|
lambda out: tooltip(
|
|
tr.browsing_changed_new_position(count=out.count), parent=parent
|
|
)
|
|
)
|
|
|
|
|
|
def suspend_cards(
|
|
*,
|
|
parent: QWidget,
|
|
card_ids: Sequence[CardId],
|
|
) -> CollectionOp[OpChangesWithCount]:
|
|
return CollectionOp(parent, lambda col: col.sched.suspend_cards(card_ids))
|
|
|
|
|
|
def suspend_note(
|
|
*,
|
|
parent: QWidget,
|
|
note_ids: Sequence[NoteId],
|
|
) -> CollectionOp[OpChangesWithCount]:
|
|
return CollectionOp(parent, lambda col: col.sched.suspend_notes(note_ids))
|
|
|
|
|
|
def unsuspend_cards(
|
|
*, parent: QWidget, card_ids: Sequence[CardId]
|
|
) -> CollectionOp[OpChanges]:
|
|
return CollectionOp(parent, lambda col: col.sched.unsuspend_cards(card_ids))
|
|
|
|
|
|
def bury_cards(
|
|
*,
|
|
parent: QWidget,
|
|
card_ids: Sequence[CardId],
|
|
) -> CollectionOp[OpChangesWithCount]:
|
|
return CollectionOp(parent, lambda col: col.sched.bury_cards(card_ids))
|
|
|
|
|
|
def bury_notes(
|
|
*,
|
|
parent: QWidget,
|
|
note_ids: Sequence[NoteId],
|
|
) -> CollectionOp[OpChangesWithCount]:
|
|
return CollectionOp(parent, lambda col: col.sched.bury_notes(note_ids))
|
|
|
|
|
|
def rebuild_filtered_deck(
|
|
*, parent: QWidget, deck_id: DeckId
|
|
) -> CollectionOp[OpChangesWithCount]:
|
|
return CollectionOp(parent, lambda col: col.sched.rebuild_filtered_deck(deck_id))
|
|
|
|
|
|
def empty_filtered_deck(*, parent: QWidget, deck_id: DeckId) -> CollectionOp[OpChanges]:
|
|
return CollectionOp(parent, lambda col: col.sched.empty_filtered_deck(deck_id))
|
|
|
|
|
|
def add_or_update_filtered_deck(
|
|
*,
|
|
parent: QWidget,
|
|
deck: FilteredDeckForUpdate,
|
|
) -> CollectionOp[OpChangesWithId]:
|
|
return CollectionOp(parent, lambda col: col.sched.add_or_update_filtered_deck(deck))
|
|
|
|
|
|
def unbury_deck(
|
|
*,
|
|
parent: QWidget,
|
|
deck_id: DeckId,
|
|
mode: UnburyDeck.Mode.V = UnburyDeck.ALL,
|
|
) -> CollectionOp[OpChanges]:
|
|
return CollectionOp(
|
|
parent, lambda col: col.sched.unbury_deck(deck_id=deck_id, mode=mode)
|
|
)
|
|
|
|
|
|
def answer_card(
|
|
*,
|
|
parent: QWidget,
|
|
answer: CardAnswer,
|
|
) -> CollectionOp[OpChanges]:
|
|
def answer_v3(col: Collection) -> OpChanges:
|
|
assert isinstance(col.sched, V3Scheduler)
|
|
return col.sched.answer_card(answer)
|
|
|
|
return CollectionOp(parent, answer_v3)
|