tweak rounding

- avoid rounding minutes
- round the seconds taken in the card info screen
- provide different precise and imprecise modes, since we need
to display seconds to multiple decimals in some areas
This commit is contained in:
Damien Elmes 2020-02-25 17:29:06 +10:00
parent fd835d9b64
commit fbbbbd6a7d
6 changed files with 39 additions and 20 deletions

View file

@ -307,8 +307,9 @@ message TranslateArgValue {
message FormatTimeSpanIn { message FormatTimeSpanIn {
enum Context { enum Context {
NORMAL = 0; PRECISE = 0;
ANSWER_BUTTONS = 1; ANSWER_BUTTONS = 1;
INTERVALS = 2;
} }
float seconds = 1; float seconds = 1;

View file

@ -342,7 +342,7 @@ class RustBackend:
def format_time_span( def format_time_span(
self, self,
seconds: float, seconds: float,
context: FormatTimeSpanContext = FormatTimeSpanContext.NORMAL, context: FormatTimeSpanContext = FormatTimeSpanContext.INTERVALS,
) -> str: ) -> str:
return self._run_command( return self._run_command(
pb.BackendInput( pb.BackendInput(

View file

@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple
import anki import anki
from anki.consts import * from anki.consts import *
from anki.lang import _, ngettext from anki.lang import _, ngettext
from anki.rsbackend import FString from anki.rsbackend import FormatTimeSpanContext, FString
from anki.utils import ids2str from anki.utils import ids2str
# Card stats # Card stats
@ -85,7 +85,9 @@ class CardStats:
return time.strftime("%Y-%m-%d", time.localtime(tm)) return time.strftime("%Y-%m-%d", time.localtime(tm))
def time(self, tm: float) -> str: def time(self, tm: float) -> str:
return self.col.backend.format_time_span(tm) return self.col.backend.format_time_span(
tm, context=FormatTimeSpanContext.PRECISE
)
# Collection stats # Collection stats

View file

@ -1503,7 +1503,7 @@ border: 1px solid #000; padding: 3px; '>%s</div>"""
s += ("<td align=right>%s</td>" * 2) % ( s += ("<td align=right>%s</td>" * 2) % (
"%d%%" % (factor / 10) if factor else "", "%d%%" % (factor / 10) if factor else "",
cs.time(taken), self.col.backend.format_time_span(taken)
) + "</tr>" ) + "</tr>"
s += "</table>" s += "</table>"
if cnt < self.card.reps: if cnt < self.card.reps:

View file

@ -422,7 +422,10 @@ impl Backend {
None => return "".to_string(), None => return "".to_string(),
}; };
match context { match context {
pb::format_time_span_in::Context::Normal => time_span(input.seconds, &self.i18n), pb::format_time_span_in::Context::Precise => time_span(input.seconds, &self.i18n, true),
pb::format_time_span_in::Context::Intervals => {
time_span(input.seconds, &self.i18n, false)
}
pb::format_time_span_in::Context::AnswerButtons => { pb::format_time_span_in::Context::AnswerButtons => {
answer_button_time(input.seconds, &self.i18n) answer_button_time(input.seconds, &self.i18n)
} }

View file

@ -6,13 +6,7 @@ use crate::i18n::{tr_args, FString, I18n};
/// Short string like '4d' to place above answer buttons. /// Short string like '4d' to place above answer buttons.
pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String { pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String {
let span = Timespan::from_secs(seconds).natural_span(); let span = Timespan::from_secs(seconds).natural_span();
let amount = match span.unit() { let args = tr_args!["amount" => span.as_rounded_unit()];
// months/years shown with 1 decimal place
TimespanUnit::Months | TimespanUnit::Years => (span.as_unit() * 10.0).round() / 10.0,
// other values shown without decimals
_ => span.as_unit().round(),
};
let args = tr_args!["amount" => amount];
let key = match span.unit() { let key = match span.unit() {
TimespanUnit::Seconds => FString::SchedulingAnswerButtonTimeSeconds, TimespanUnit::Seconds => FString::SchedulingAnswerButtonTimeSeconds,
TimespanUnit::Minutes => FString::SchedulingAnswerButtonTimeMinutes, TimespanUnit::Minutes => FString::SchedulingAnswerButtonTimeMinutes,
@ -24,11 +18,17 @@ pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String {
i18n.trn(key, args) i18n.trn(key, args)
} }
/// Describe the given seconds using the largest appropriate unit /// Describe the given seconds using the largest appropriate unit.
/// If precise is true, show to two decimal places, eg
/// eg 70 seconds -> "1.17 minutes" /// eg 70 seconds -> "1.17 minutes"
pub fn time_span(seconds: f32, i18n: &I18n) -> String { /// If false, seconds and days are shown without decimals.
pub fn time_span(seconds: f32, i18n: &I18n, precise: bool) -> String {
let span = Timespan::from_secs(seconds).natural_span(); let span = Timespan::from_secs(seconds).natural_span();
let amount = span.as_unit(); let amount = if precise {
span.as_unit()
} else {
span.as_rounded_unit()
};
let args = tr_args!["amount" => amount]; let args = tr_args!["amount" => amount];
let key = match span.unit() { let key = match span.unit() {
TimespanUnit::Seconds => FString::SchedulingTimeSpanSeconds, TimespanUnit::Seconds => FString::SchedulingTimeSpanSeconds,
@ -133,6 +133,17 @@ impl Timespan {
} }
} }
/// Round seconds and days to integers, otherwise
/// truncates to one decimal place.
fn as_rounded_unit(self) -> f32 {
match self.unit {
// seconds/days as integer
TimespanUnit::Seconds | TimespanUnit::Days => self.as_unit().round(),
// other values shown to 1 decimal place
_ => (self.as_unit() * 10.0).round() / 10.0,
}
}
fn unit(self) -> TimespanUnit { fn unit(self) -> TimespanUnit {
self.unit self.unit
} }
@ -173,16 +184,18 @@ mod test {
fn answer_buttons() { fn answer_buttons() {
let i18n = I18n::new(&["zz"], ""); let i18n = I18n::new(&["zz"], "");
assert_eq!(answer_button_time(30.0, &i18n), "30s"); assert_eq!(answer_button_time(30.0, &i18n), "30s");
assert_eq!(answer_button_time(70.0, &i18n), "1m"); assert_eq!(answer_button_time(70.0, &i18n), "1.2m");
assert_eq!(answer_button_time(1.1 * MONTH, &i18n), "1.1mo"); assert_eq!(answer_button_time(1.1 * MONTH, &i18n), "1.1mo");
} }
#[test] #[test]
fn time_spans() { fn time_spans() {
let i18n = I18n::new(&["zz"], ""); let i18n = I18n::new(&["zz"], "");
assert_eq!(time_span(1.0, &i18n), "1 second"); assert_eq!(time_span(1.0, &i18n, false), "1 second");
assert_eq!(time_span(30.0, &i18n), "30 seconds"); assert_eq!(time_span(30.3, &i18n, false), "30 seconds");
assert_eq!(time_span(90.0, &i18n), "1.5 minutes"); assert_eq!(time_span(30.3, &i18n, true), "30.3 seconds");
assert_eq!(time_span(90.0, &i18n, false), "1.5 minutes");
assert_eq!(time_span(45.0 * 86_400.0, &i18n, false), "1.5 months");
} }
#[test] #[test]