add next learning due time + remaining count to congrats screen

https://anki.tenderapp.com/discussions/ankidesktop/38000-v2-scheduler-learning-cards
This commit is contained in:
Damien Elmes 2020-02-21 19:26:45 +10:00
parent 49fe080636
commit 972aee5f7a
7 changed files with 97 additions and 4 deletions

View file

@ -46,6 +46,7 @@ message BackendInput {
TranslateStringIn translate_string = 30;
FormatTimeSpanIn format_time_span = 31;
StudiedTodayIn studied_today = 32;
CongratsLearnMsgIn congrats_learn_msg = 33;
}
}
@ -68,6 +69,7 @@ message BackendOutput {
string translate_string = 30;
string format_time_span = 31;
string studied_today = 32;
string congrats_learn_msg = 33;
BackendError error = 2047;
}
@ -322,3 +324,8 @@ message StudiedTodayIn {
uint32 cards = 1;
double seconds = 2;
}
message CongratsLearnMsgIn {
float next_due = 1;
uint32 remaining = 2;
}

View file

@ -362,3 +362,12 @@ class RustBackend:
studied_today=pb.StudiedTodayIn(cards=cards, seconds=seconds)
)
).studied_today
def learning_congrats_msg(self, next_due: float, remaining: int) -> str:
return self._run_command(
pb.BackendInput(
congrats_learn_msg=pb.CongratsLearnMsgIn(
next_due=next_due, remaining=remaining
)
)
).congrats_learn_msg

View file

@ -1458,8 +1458,29 @@ where id = ?
+ self._nextDueMsg()
)
def next_learn_msg(self) -> str:
dids = self._deckLimit()
(next, remaining) = self.col.db.first(
f"""
select min(due), count(*)
from cards where did in {dids} and queue = {QUEUE_TYPE_LRN}
"""
)
next = next or 0
remaining = remaining or 0
if next and next < self.dayCutoff:
next -= intTime() - self.col.conf["collapseTime"]
return self.col.backend.learning_congrats_msg(abs(next), remaining)
else:
return ""
def _nextDueMsg(self) -> str:
line = []
learn_msg = self.next_learn_msg()
if learn_msg:
line.append(learn_msg)
# the new line replacements are so we don't break translations
# in a point release
if self.revDue():

View file

@ -11,7 +11,7 @@ use crate::media::check::MediaChecker;
use crate::media::sync::MediaSyncProgress;
use crate::media::MediaManager;
use crate::sched::cutoff::{local_minutes_west_for_stamp, sched_timing_today};
use crate::sched::timespan::{answer_button_time, studied_today, time_span};
use crate::sched::timespan::{answer_button_time, learning_congrats, studied_today, time_span};
use crate::template::{
render_card, without_legacy_template_directives, FieldMap, FieldRequirements, ParsedTemplate,
RenderedNode,
@ -207,6 +207,11 @@ impl Backend {
input.seconds as f32,
&self.i18n,
)),
Value::CongratsLearnMsg(input) => OValue::CongratsLearnMsg(learning_congrats(
input.remaining as usize,
input.next_due,
&self.i18n,
)),
})
}

View file

@ -44,3 +44,27 @@ time-span-years = { $amount ->
[one] {$amount} year
*[other] {$amount} years
}
## Shown in the "Congratulations!" message after study finishes.
# eg "The next learning card will be ready in 5 minutes."
next-learn-due =
The next learning card will be ready in { $unit ->
[seconds] { $amount ->
[one] {$amount} second
*[other] {$amount} seconds
}
[minutes] { $amount ->
[one] {$amount} minute
*[other] {$amount} minutes
}
*[hours] { $amount ->
[one] {$amount} hour
*[other] {$amount} hours
}
}.
learn-remaining = { $remaining ->
[one] There is one remaining learning card due later today.
*[other] There are {$remaining} learning cards due later today.
}

View file

@ -12,7 +12,7 @@ cards-per-min = {$cards-per-minute} cards/minute
average-answer-time = {$average-seconds}s ({cards-per-min})
## A span of time studying took place in, for example
## "(studied 30 cards) in 3 minutes".
## "(studied 30 cards) in 3 minutes"
in-time-span-seconds = { $amount ->
[one] in {$amount} second

View file

@ -44,6 +44,27 @@ pub fn studied_today(cards: usize, secs: f32, i18n: &I18n) -> String {
.trn("studied-today", args)
}
// fixme: this doesn't belong here
pub fn learning_congrats(remaining: usize, next_due: f32, i18n: &I18n) -> String {
// next learning card not due (/ until tomorrow)?
if next_due == 0.0 || next_due >= 86_400.0 {
return "".to_string();
}
let span = Timespan::from_secs(next_due).natural_span();
let amount = span.as_unit().round();
let unit = span.unit().as_str();
let next_args = tr_args!["amount" => amount, "unit" => unit];
let remaining_args = tr_args!["remaining" => remaining];
format!(
"{} {}",
i18n.get(StringsGroup::Scheduling)
.trn("next-learn-due", next_args),
i18n.get(StringsGroup::Scheduling)
.trn("learn-remaining", remaining_args)
)
}
const SECOND: f32 = 1.0;
const MINUTE: f32 = 60.0 * SECOND;
const HOUR: f32 = 60.0 * MINUTE;
@ -134,7 +155,9 @@ impl Timespan {
#[cfg(test)]
mod test {
use crate::i18n::I18n;
use crate::sched::timespan::{answer_button_time, studied_today, time_span, MONTH};
use crate::sched::timespan::{
answer_button_time, learning_congrats, studied_today, time_span, MONTH,
};
#[test]
fn answer_buttons() {
@ -158,7 +181,11 @@ mod test {
let i18n = I18n::new(&["zz"], "");
assert_eq!(
&studied_today(3, 13.0, &i18n).replace("\n", " "),
"Studied 3 cards in 13 seconds today (4.33s/card)."
"Studied 3 cards in 13 seconds today (4.33s/card)"
);
assert_eq!(
&learning_congrats(3, 3700.0, &i18n).replace("\n", " "),
"The next learning card will be ready in 1 hour. There are 3 learning cards due later today."
);
}
}