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

View file

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

View file

@ -5,6 +5,7 @@ from __future__ import annotations
import anki import anki
import anki._backend.backend_pb2 as _pb import anki._backend.backend_pb2 as _pb
from anki.config import Config
SchedTimingToday = _pb.SchedTimingTodayOut 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." "Put cards at the end of the new queue."
self.col._backend.schedule_cards_as_new(card_ids=card_ids, log=True) 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. """Set cards to be due in `days`, turning them into review cards if necessary.
`days` can be of the form '5' or '5..7'""" `days` can be of the form '5' or '5..7'
self.col._backend.set_due_date(card_ids=card_ids, days=days) 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: def resetCards(self, ids: List[int]) -> None:
"Completely reset cards for export." "Completely reset cards for export."

View file

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

View file

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

View file

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

View file

@ -1273,6 +1273,7 @@ message ScheduleCardsAsNewIn {
message SetDueDateIn { message SetDueDateIn {
repeated int64 card_ids = 1; repeated int64 card_ids = 1;
string days = 2; string days = 2;
Config.String config_key = 3;
} }
message SortCardsIn { 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 { impl ConfigService for Backend {
fn get_config_json(&self, input: pb::String) -> Result<pb::Json> { fn get_config_json(&self, input: pb::String) -> Result<pb::Json> {
self.with_col(|col| { self.with_col(|col| {

View file

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

View file

@ -4,9 +4,11 @@
use crate::{ use crate::{
card::{Card, CardID, CardQueue, CardType}, card::{Card, CardID, CardQueue, CardType},
collection::Collection, collection::Collection,
config::StringKey,
deckconf::INITIAL_EASE_FACTOR_THOUSANDS, deckconf::INITIAL_EASE_FACTOR_THOUSANDS,
err::Result, err::Result,
prelude::AnkiError, prelude::AnkiError,
undo::UndoableOpKind,
}; };
use lazy_static::lazy_static; use lazy_static::lazy_static;
use rand::distributions::{Distribution, Uniform}; use rand::distributions::{Distribution, Uniform};
@ -84,12 +86,21 @@ pub fn parse_due_date_str(s: &str) -> Result<DueDateSpecifier> {
} }
impl Collection { 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 usn = self.usn()?;
let today = self.timing_today()?.days_elapsed; let today = self.timing_today()?.days_elapsed;
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let distribution = Uniform::from(spec.min..=spec.max); 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)?; col.storage.set_search_table_to_card_ids(cids, false)?;
for mut card in col.storage.all_searched_cards()? { for mut card in col.storage.all_searched_cards()? {
let original = card.clone(); let original = card.clone();
@ -99,6 +110,9 @@ impl Collection {
col.update_card_inner(&mut card, &original, usn)?; col.update_card_inner(&mut card, &original, usn)?;
} }
col.storage.clear_searched_cards_table()?; col.storage.clear_searched_cards_table()?;
if let Some(key) = context {
col.set_string(key, days)?;
}
Ok(()) Ok(())
}) })
} }

View file

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