fetch timing_today() params in backend

This commit is contained in:
Damien Elmes 2020-05-11 13:58:40 +10:00
parent eee0d7e92f
commit ea4f150455
9 changed files with 81 additions and 149 deletions

View file

@ -29,7 +29,7 @@ message I18nBackendInit {
message BackendInput {
oneof value {
SchedTimingTodayIn sched_timing_today = 17;
Empty sched_timing_today = 17;
DeckTreeIn deck_tree = 18;
SearchCardsIn search_cards = 19;
SearchNotesIn search_notes = 20;
@ -101,7 +101,6 @@ message BackendInput {
message BackendOutput {
oneof value {
// infallible commands
SchedTimingTodayOut sched_timing_today = 17;
sint32 local_minutes_west = 22;
string strip_av_tags = 23;
ExtractAVTagsOut extract_av_tags = 24;
@ -114,6 +113,7 @@ message BackendOutput {
AllStockNotetypesOut all_stock_notetypes = 60;
// fallible commands
SchedTimingTodayOut sched_timing_today = 17;
DeckTreeNode deck_tree = 18;
SearchCardsOut search_cards = 19;
SearchNotesOut search_notes = 20;
@ -237,14 +237,6 @@ message MediaSyncUploadProgress {
uint32 deletions = 2;
}
message SchedTimingTodayIn {
int64 created_secs = 1;
int64 now_secs = 2;
OptionalInt32 created_mins_west = 3;
OptionalInt32 now_mins_west = 4;
OptionalInt32 rollover_hour = 5;
}
message SchedTimingTodayOut {
uint32 days_elapsed = 1;
int64 next_day_at = 2;

View file

@ -287,38 +287,9 @@ class RustBackend:
release_gil=True,
)
def sched_timing_today(
self,
created_secs: int,
created_mins_west: Optional[int],
now_mins_west: Optional[int],
rollover: Optional[int],
) -> SchedTimingToday:
if created_mins_west is not None:
crt_west = pb.OptionalInt32(val=created_mins_west)
else:
crt_west = None
if now_mins_west is not None:
now_west = pb.OptionalInt32(val=now_mins_west)
else:
now_west = None
if rollover is not None:
roll = pb.OptionalInt32(val=rollover)
else:
roll = None
def sched_timing_today(self,) -> SchedTimingToday:
return self._run_command(
pb.BackendInput(
sched_timing_today=pb.SchedTimingTodayIn(
created_secs=created_secs,
now_secs=intTime(),
created_mins_west=crt_west,
now_mins_west=now_west,
rollover_hour=roll,
)
)
pb.BackendInput(sched_timing_today=pb.Empty())
).sched_timing_today
def render_card(

View file

@ -1374,28 +1374,8 @@ where id = ?
if time.time() > self.dayCutoff:
self.reset()
def _rolloverHour(self) -> int:
return self.col.conf.get("rollover", 4)
def _timing_today(self) -> SchedTimingToday:
roll: Optional[int] = None
if self.col.schedVer() > 1:
roll = self._rolloverHour()
return self.col.backend.sched_timing_today(
self.col.crt,
self._creation_timezone_offset(),
self._current_timezone_offset(),
roll,
)
def _current_timezone_offset(self) -> Optional[int]:
if self.col.server:
return self.col.conf.get("localOffset", 0)
else:
return None
def _creation_timezone_offset(self) -> Optional[int]:
return self.col.conf.get("creationOffset", None)
return self.col.backend.sched_timing_today()
# New timezone handling - GUI helpers
##########################################################################

View file

@ -106,6 +106,7 @@ def test_export_anki_due():
f["Front"] = "foo"
deck.addNote(f)
deck.crt -= 86400 * 10
deck.flush()
deck.sched.reset()
c = deck.sched.getCard()
deck.sched.answerCard(c, 3)

View file

@ -22,7 +22,7 @@ use crate::{
media::MediaManager,
notes::{Note, NoteID},
notetype::{all_stock_notetypes, NoteType, NoteTypeID, NoteTypeSchema11},
sched::cutoff::{local_minutes_west_for_stamp, sched_timing_today},
sched::cutoff::local_minutes_west_for_stamp,
sched::timespan::{answer_button_time, learning_congrats, studied_today, time_span},
search::SortMode,
template::{render_card, RenderedNode},
@ -214,9 +214,7 @@ impl Backend {
) -> Result<pb::backend_output::Value> {
use pb::backend_output::Value as OValue;
Ok(match ival {
Value::SchedTimingToday(input) => {
OValue::SchedTimingToday(self.sched_timing_today(input))
}
Value::SchedTimingToday(_) => OValue::SchedTimingToday(self.sched_timing_today()?),
Value::DeckTree(input) => OValue::DeckTree(self.deck_tree(input)?),
Value::RenderCard(input) => OValue::RenderCard(self.render_template(input)?),
Value::LocalMinutesWest(stamp) => {
@ -436,18 +434,8 @@ impl Backend {
self.progress_callback = progress_cb;
}
fn sched_timing_today(&self, input: pb::SchedTimingTodayIn) -> pb::SchedTimingTodayOut {
let today = sched_timing_today(
TimestampSecs(input.created_secs),
TimestampSecs(input.now_secs),
input.created_mins_west.map(|v| v.val),
input.now_mins_west.map(|v| v.val),
input.rollover_hour.map(|v| v.val as i8),
);
pb::SchedTimingTodayOut {
days_elapsed: today.days_elapsed,
next_day_at: today.next_day_at,
}
fn sched_timing_today(&self) -> Result<pb::SchedTimingTodayOut> {
self.with_col(|col| col.timing_today().map(Into::into))
}
fn deck_tree(&self, input: pb::DeckTreeIn) -> Result<pb::DeckTreeNode> {
@ -1269,3 +1257,12 @@ fn pbcard_to_native(c: pb::Card) -> Result<Card> {
data: c.data,
})
}
impl From<crate::sched::cutoff::SchedTimingToday> for pb::SchedTimingTodayOut {
fn from(t: crate::sched::cutoff::SchedTimingToday) -> pb::SchedTimingTodayOut {
pb::SchedTimingTodayOut {
days_elapsed: t.days_elapsed,
next_day_at: t.next_day_at,
}
}
}

View file

@ -4,12 +4,10 @@
use crate::err::{AnkiError, Result};
use crate::i18n::I18n;
use crate::log::Logger;
use crate::timestamp::TimestampSecs;
use crate::types::Usn;
use crate::{
decks::{Deck, DeckID},
notetype::{NoteType, NoteTypeID},
sched::cutoff::{sched_timing_today, SchedTimingToday},
storage::SqliteStorage,
undo::UndoManager,
};
@ -51,7 +49,6 @@ pub fn open_test_collection() -> Collection {
pub struct CollectionState {
task_state: CollectionTaskState,
pub(crate) undo: UndoManager,
timing_today: Option<SchedTimingToday>,
pub(crate) notetype_cache: HashMap<NoteTypeID, Arc<NoteType>>,
pub(crate) deck_cache: HashMap<DeckID, Arc<Deck>>,
}
@ -142,37 +139,6 @@ impl Collection {
self.storage.close(downgrade)
}
// fixme: invalidate when config changes
pub fn timing_today(&mut self) -> Result<SchedTimingToday> {
if let Some(timing) = &self.state.timing_today {
if timing.next_day_at > TimestampSecs::now().0 {
return Ok(*timing);
}
}
let local_offset = if self.server {
self.get_local_mins_west()
} else {
None
};
let timing = sched_timing_today(
self.storage.creation_stamp()?,
TimestampSecs::now(),
self.get_creation_mins_west(),
local_offset,
self.get_rollover(),
);
self.state.timing_today = Some(timing);
Ok(timing)
}
pub(crate) fn learn_cutoff(&self) -> u32 {
TimestampSecs::now().0 as u32 + self.learn_ahead_secs()
}
pub(crate) fn usn(&self) -> Result<Usn> {
// if we cache this in the future, must make sure to invalidate cache when usn bumped in sync.finish()
self.storage.usn(self.server)

View file

@ -138,8 +138,9 @@ impl Collection {
self.set_config(ConfigKey::LocalOffset, &mins)
}
pub(crate) fn get_rollover(&self) -> Option<i8> {
self.get_config_optional(ConfigKey::Rollover)
pub(crate) fn get_rollover(&self) -> Option<u8> {
self.get_config_optional::<u8, _>(ConfigKey::Rollover)
.map(|r| r.min(23))
}
#[allow(dead_code)]

View file

@ -2,7 +2,7 @@
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use crate::timestamp::TimestampSecs;
use chrono::{Date, Duration, FixedOffset, Local, TimeZone};
use chrono::{Date, Duration, FixedOffset, Local, TimeZone, Timelike};
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct SchedTimingToday {
@ -24,7 +24,7 @@ pub fn sched_timing_today_v2_new(
created_mins_west: i32,
now_secs: i64,
now_mins_west: i32,
rollover_hour: i8,
rollover_hour: u8,
) -> SchedTimingToday {
// get date(times) based on timezone offsets
let created_date = fixed_offset_from_minutes(created_mins_west)
@ -34,7 +34,6 @@ pub fn sched_timing_today_v2_new(
let today = now_datetime.date();
// rollover
let rollover_hour = normalized_rollover_hour(rollover_hour);
let rollover_today_datetime = today.and_hms(rollover_hour as u32, 0, 0);
let rollover_passed = rollover_today_datetime <= now_datetime;
let next_day_at = if rollover_passed {
@ -67,17 +66,6 @@ fn days_elapsed(
days.max(0) as u32
}
/// Negative rollover hours are relative to the next day, eg -1 = 23.
/// Cap hour to 23.
fn normalized_rollover_hour(hour: i8) -> u8 {
let capped_hour = hour.max(-23).min(23);
if capped_hour < 0 {
(24 + capped_hour) as u8
} else {
capped_hour as u8
}
}
/// Build a FixedOffset struct, capping minutes_west if out of bounds.
fn fixed_offset_from_minutes(minutes_west: i32) -> FixedOffset {
let bounded_minutes = minutes_west.max(-23 * 60).min(23 * 60);
@ -95,6 +83,10 @@ pub fn local_minutes_west_for_stamp(stamp: i64) -> i32 {
// Legacy code
// ----------------------------------
pub(crate) fn v1_rollover_from_creation_stamp(crt: i64) -> u8 {
Local.timestamp(crt, 0).hour() as u8
}
pub(crate) fn v1_creation_date() -> i64 {
let now = TimestampSecs::now();
v1_creation_date_inner(now, local_minutes_west_for_stamp(now.0))
@ -124,24 +116,23 @@ fn sched_timing_today_v1(crt: i64, now: i64) -> SchedTimingToday {
fn sched_timing_today_v2_legacy(
crt: i64,
rollover: i8,
rollover: u8,
now: i64,
mins_west: i32,
) -> SchedTimingToday {
let normalized_rollover = normalized_rollover_hour(rollover);
let offset = fixed_offset_from_minutes(mins_west);
let crt_at_rollover = offset
.timestamp(crt, 0)
.date()
.and_hms(normalized_rollover as u32, 0, 0)
.and_hms(rollover as u32, 0, 0)
.timestamp();
let days_elapsed = (now - crt_at_rollover) / 86_400;
let mut next_day_at = offset
.timestamp(now, 0)
.date()
.and_hms(normalized_rollover as u32, 0, 0)
.and_hms(rollover as u32, 0, 0)
.timestamp();
if next_day_at < now {
next_day_at += 86_400;
@ -161,7 +152,7 @@ pub(crate) fn sched_timing_today(
now_secs: TimestampSecs,
created_mins_west: Option<i32>,
now_mins_west: Option<i32>,
rollover_hour: Option<i8>,
rollover_hour: Option<u8>,
) -> SchedTimingToday {
let now_west = now_mins_west.unwrap_or_else(|| local_minutes_west_for_stamp(now_secs.0));
match (rollover_hour, created_mins_west) {
@ -188,17 +179,6 @@ mod test {
// static timezone for tests
const AEST_MINS_WEST: i32 = -600;
#[test]
fn rollover() {
assert_eq!(normalized_rollover_hour(4), 4);
assert_eq!(normalized_rollover_hour(23), 23);
assert_eq!(normalized_rollover_hour(24), 23);
assert_eq!(normalized_rollover_hour(-1), 23);
assert_eq!(normalized_rollover_hour(-2), 22);
assert_eq!(normalized_rollover_hour(-23), 1);
assert_eq!(normalized_rollover_hour(-24), 1);
}
#[test]
fn fixed_offset() {
let offset = fixed_offset_from_minutes(AEST_MINS_WEST);
@ -206,7 +186,7 @@ mod test {
}
// helper
fn elap(start: i64, end: i64, start_west: i32, end_west: i32, rollhour: i8) -> u32 {
fn elap(start: i64, end: i64, start_west: i32, end_west: i32, rollhour: u8) -> u32 {
let today = sched_timing_today_v2_new(start, start_west, end, end_west, rollhour);
today.days_elapsed
}
@ -300,7 +280,7 @@ mod test {
end_stamp,
crt_offset,
end_offset,
*rollover_hour as i8
*rollover_hour as u8
),
elap_day
);
@ -323,7 +303,7 @@ mod test {
crt.offset().utc_minus_local() / 60,
now.timestamp(),
now.offset().utc_minus_local() / 60,
rollhour as i8,
rollhour as u8,
);
assert_eq!(today.next_day_at, next_day_at.timestamp());
@ -335,7 +315,7 @@ mod test {
crt.offset().utc_minus_local() / 60,
now.timestamp(),
now.offset().utc_minus_local() / 60,
rollhour as i8,
rollhour as u8,
);
assert_eq!(today.next_day_at, next_day_at.timestamp());
@ -347,7 +327,7 @@ mod test {
crt.offset().utc_minus_local() / 60,
now.timestamp(),
now.offset().utc_minus_local() / 60,
rollhour as i8,
rollhour as u8,
);
assert_eq!(today.next_day_at, next_day_at.timestamp());
}

View file

@ -1,2 +1,46 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use crate::{
collection::Collection, config::SchedulerVersion, err::Result, timestamp::TimestampSecs,
};
pub mod cutoff;
pub mod timespan;
use cutoff::{sched_timing_today, v1_rollover_from_creation_stamp, SchedTimingToday};
impl Collection {
pub fn timing_today(&mut self) -> Result<SchedTimingToday> {
self.timing_for_timestamp(TimestampSecs::now())
}
pub(crate) fn timing_for_timestamp(&mut self, now: TimestampSecs) -> Result<SchedTimingToday> {
let local_offset = if self.server {
self.get_local_mins_west()
} else {
None
};
Ok(sched_timing_today(
self.storage.creation_stamp()?,
now,
self.get_creation_mins_west(),
local_offset,
self.get_rollover(),
))
}
pub fn rollover_for_current_scheduler(&self) -> Result<u8> {
match self.sched_ver() {
SchedulerVersion::V1 => Ok(v1_rollover_from_creation_stamp(
self.storage.creation_stamp()?.0,
)),
SchedulerVersion::V2 => Ok(self.get_rollover().unwrap_or(4)),
}
}
pub(crate) fn learn_cutoff(&self) -> u32 {
TimestampSecs::now().0 as u32 + self.learn_ahead_secs()
}
}