mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 14:32:22 -04:00
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:
parent
fd835d9b64
commit
fbbbbd6a7d
6 changed files with 39 additions and 20 deletions
|
@ -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;
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Reference in a new issue