'set due date' now undoable

This commit is contained in:
Damien Elmes 2021-03-12 14:50:31 +10:00
parent ec8adf7371
commit c1316bb65f
11 changed files with 53 additions and 19 deletions

View File

@ -21,7 +21,7 @@ import anki.template
from anki import hooks
from anki._backend import RustBackend
from anki.cards import Card
from anki.config import ConfigManager
from anki.config import Config, ConfigManager
from anki.consts import *
from anki.dbproxy import DBProxy
from anki.decks import DeckManager
@ -49,7 +49,6 @@ from anki.utils import (
SearchNode = _pb.SearchNode
SearchJoiner = Literal["AND", "OR"]
Progress = _pb.Progress
Config = _pb.Config
EmptyCardsReport = _pb.EmptyCardsReport
GraphPreferences = _pb.GraphPreferences
BuiltinSort = _pb.SortOrder.Builtin

View File

@ -24,9 +24,12 @@ from typing import Any
from weakref import ref
import anki
from anki._backend import backend_pb2 as _pb
from anki.errors import NotFoundError
from anki.utils import from_json_bytes, to_json_bytes
Config = _pb.Config
class ConfigManager:
def __init__(self, col: anki.collection.Collection):

View File

@ -5,6 +5,7 @@ from __future__ import annotations
import anki
import anki._backend.backend_pb2 as _pb
from anki.config import Config
SchedTimingToday = _pb.SchedTimingTodayOut
@ -129,10 +130,20 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
"Put cards at the end of the new queue."
self.col._backend.schedule_cards_as_new(card_ids=card_ids, log=True)
def set_due_date(self, card_ids: List[int], days: str) -> None:
def set_due_date(
self,
card_ids: List[int],
days: str,
config_key: Optional[Config.String.Key.V] = None,
) -> None:
"""Set cards to be due in `days`, turning them into review cards if necessary.
`days` can be of the form '5' or '5..7'"""
self.col._backend.set_due_date(card_ids=card_ids, days=days)
`days` can be of the form '5' or '5..7'
If `config_key` is provided, provided days will be remembered in config."""
if config_key:
key = Config.String(key=config_key)
else:
key = None
self.col._backend.set_due_date(card_ids=card_ids, days=days, config_key=key)
def resetCards(self, ids: List[int]) -> None:
"Completely reset cards for export."

View File

@ -1349,6 +1349,7 @@ where id in %s"""
def _after_schedule(self) -> None:
self.model.reset()
# updates undo status
self.mw.requireReset(reason=ResetReason.BrowserReschedule, context=self)
@save_editor
@ -1357,7 +1358,7 @@ where id in %s"""
mw=self.mw,
parent=self,
card_ids=self.selectedCards(),
default_key=Config.String.SET_DUE_BROWSER,
config_key=Config.String.SET_DUE_BROWSER,
on_done=self._after_schedule,
)

View File

@ -809,7 +809,7 @@ time = %(time)d;
mw=self.mw,
parent=self.mw,
card_ids=[self.card.id],
default_key=Config.String.SET_DUE_REVIEWER,
config_key=Config.String.SET_DUE_REVIEWER,
on_done=self.mw.reset,
)

View File

@ -4,7 +4,7 @@
from __future__ import annotations
from concurrent.futures import Future
from typing import List
from typing import List, Optional
import aqt
from anki.collection import Config
@ -18,20 +18,19 @@ def set_due_date_dialog(
mw: aqt.AnkiQt,
parent: QDialog,
card_ids: List[int],
default_key: Config.String.Key.V,
config_key: Optional[Config.String.Key.V],
on_done: Callable[[], None],
) -> None:
if not card_ids:
return
default = mw.col.get_config_string(default_key)
default = mw.col.get_config_string(config_key) if config_key else ""
prompt = "\n".join(
[
tr(TR.SCHEDULING_SET_DUE_DATE_PROMPT, cards=len(card_ids)),
tr(TR.SCHEDULING_SET_DUE_DATE_PROMPT_HINT),
]
)
(days, success) = getText(
prompt=prompt,
parent=parent,
@ -42,9 +41,7 @@ def set_due_date_dialog(
return
def set_due() -> None:
mw.col.sched.set_due_date(card_ids, days)
if days != default:
mw.col.set_config_string(default_key, days)
mw.col.sched.set_due_date(card_ids, days, config_key)
def after_set(fut: Future) -> None:
try:

View File

@ -1273,6 +1273,7 @@ message ScheduleCardsAsNewIn {
message SetDueDateIn {
repeated int64 card_ids = 1;
string days = 2;
Config.String config_key = 3;
}
message SortCardsIn {

View File

@ -43,6 +43,12 @@ impl From<StringKeyProto> for StringKey {
}
}
impl From<pb::config::String> for StringKey {
fn from(key: pb::config::String) -> Self {
key.key().into()
}
}
impl ConfigService for Backend {
fn get_config_json(&self, input: pb::String) -> Result<pb::Json> {
self.with_col(|col| {

View File

@ -10,7 +10,6 @@ use crate::{
prelude::*,
scheduler::{
new::NewCardSortOrder,
parse_due_date_str,
states::{CardState, NextCardStates},
},
stats::studied_today,
@ -113,9 +112,10 @@ impl SchedulingService for Backend {
}
fn set_due_date(&self, input: pb::SetDueDateIn) -> Result<pb::Empty> {
let config = input.config_key.map(Into::into);
let days = input.days;
let cids: Vec<_> = input.card_ids.into_iter().map(CardID).collect();
let spec = parse_due_date_str(&input.days)?;
self.with_col(|col| col.set_due_date(&cids, spec).map(Into::into))
self.with_col(|col| col.set_due_date(&cids, &days, config).map(Into::into))
}
fn sort_cards(&self, input: pb::SortCardsIn) -> Result<pb::Empty> {

View File

@ -4,9 +4,11 @@
use crate::{
card::{Card, CardID, CardQueue, CardType},
collection::Collection,
config::StringKey,
deckconf::INITIAL_EASE_FACTOR_THOUSANDS,
err::Result,
prelude::AnkiError,
undo::UndoableOpKind,
};
use lazy_static::lazy_static;
use rand::distributions::{Distribution, Uniform};
@ -84,12 +86,21 @@ pub fn parse_due_date_str(s: &str) -> Result<DueDateSpecifier> {
}
impl Collection {
pub fn set_due_date(&mut self, cids: &[CardID], spec: DueDateSpecifier) -> Result<()> {
/// `days` should be in a format parseable by `parse_due_date_str`.
/// If `context` is provided, provided key will be updated with the new
/// value of `days`.
pub fn set_due_date(
&mut self,
cids: &[CardID],
days: &str,
context: Option<StringKey>,
) -> Result<()> {
let spec = parse_due_date_str(days)?;
let usn = self.usn()?;
let today = self.timing_today()?.days_elapsed;
let mut rng = rand::thread_rng();
let distribution = Uniform::from(spec.min..=spec.max);
self.transact(None, |col| {
self.transact(Some(UndoableOpKind::SetDueDate), |col| {
col.storage.set_search_table_to_card_ids(cids, false)?;
for mut card in col.storage.all_searched_cards()? {
let original = card.clone();
@ -99,6 +110,9 @@ impl Collection {
col.update_card_inner(&mut card, &original, usn)?;
}
col.storage.clear_searched_cards_table()?;
if let Some(key) = context {
col.set_string(key, days)?;
}
Ok(())
})
}

View File

@ -12,6 +12,7 @@ pub enum UndoableOpKind {
RemoveDeck,
RemoveNote,
RenameDeck,
SetDueDate,
Suspend,
UnburyUnsuspend,
UpdateCard,
@ -37,6 +38,7 @@ impl Collection {
UndoableOpKind::RemoveDeck => TR::DecksDeleteDeck,
UndoableOpKind::RemoveNote => TR::StudyingDeleteNote,
UndoableOpKind::RenameDeck => TR::ActionsRenameDeck,
UndoableOpKind::SetDueDate => TR::ActionsSetDueDate,
UndoableOpKind::Suspend => TR::StudyingSuspend,
UndoableOpKind::UnburyUnsuspend => TR::UndoUnburyUnsuspend,
UndoableOpKind::UpdateCard => TR::UndoUpdateCard,