add v1 and v2 legacy timing code

This commit is contained in:
Damien Elmes 2020-03-18 11:53:02 +10:00
parent 9f3cc0982d
commit 2beccd377b
4 changed files with 147 additions and 9 deletions

View file

@ -12,7 +12,7 @@ use crate::log::{default_logger, Logger};
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::cutoff::{local_minutes_west_for_stamp, sched_timing_today_v2_new};
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,
@ -348,7 +348,7 @@ impl Backend {
}
fn sched_timing_today(&self, input: pb::SchedTimingTodayIn) -> pb::SchedTimingTodayOut {
let today = sched_timing_today(
let today = sched_timing_today_v2_new(
input.created_secs as i64,
input.created_mins_west,
input.now_secs as i64,

View file

@ -5,7 +5,11 @@ use crate::types::ObjID;
use serde_derive::Deserialize;
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
#[serde(rename = "curDeck")]
pub(crate) current_deck_id: ObjID,
pub(crate) rollover: Option<i8>,
pub(crate) creation_offset: Option<i32>,
pub(crate) local_offset: Option<i32>,
}

View file

@ -1,8 +1,10 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use crate::time::i64_unix_secs;
use chrono::{Date, Duration, FixedOffset, Local, TimeZone};
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct SchedTimingToday {
/// The number of days that have passed since the collection was created.
pub days_elapsed: u32,
@ -17,7 +19,7 @@ pub struct SchedTimingToday {
/// - now_secs is a timestamp of the current time
/// - now_mins_west is the current offset west of UTC
/// - rollover_hour is the hour of the day the rollover happens (eg 4 for 4am)
pub fn sched_timing_today(
pub fn sched_timing_today_v2_new(
created_secs: i64,
created_mins_west: i32,
now_secs: i64,
@ -90,11 +92,86 @@ pub fn local_minutes_west_for_stamp(stamp: i64) -> i32 {
Local.timestamp(stamp, 0).offset().utc_minus_local() / 60
}
// Legacy code
// ----------------------------------
fn sched_timing_today_v1(crt: i64, now: i64) -> SchedTimingToday {
let days_elapsed = (now - crt) / 86_400;
let next_day_at = crt + (days_elapsed + 1) * 86_400;
SchedTimingToday {
days_elapsed: days_elapsed as u32,
next_day_at,
}
}
fn sched_timing_today_v2_legacy(
crt: i64,
rollover: i8,
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)
.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)
.timestamp();
if next_day_at < now {
next_day_at += 86_400;
}
SchedTimingToday {
days_elapsed: days_elapsed as u32,
next_day_at,
}
}
// ----------------------------------
/// Based on provided input, get timing info from the relevant function.
pub(crate) fn sched_timing_today(
created_secs: i64,
created_mins_west: Option<i32>,
now_mins_west: Option<i32>,
rollover_hour: Option<i8>,
) -> SchedTimingToday {
let now = i64_unix_secs();
match (rollover_hour, created_mins_west) {
(None, _) => {
// if rollover unset, v1 scheduler
sched_timing_today_v1(created_secs, now)
}
(Some(roll), None) => {
// if creation offset unset, v2 legacy cutoff using local timezone
let offset = local_minutes_west_for_stamp(now);
sched_timing_today_v2_legacy(created_secs, roll, now, offset)
}
(Some(roll), Some(crt_west)) => {
// new cutoff code, using provided current timezone, falling back on local timezone
let now_west = now_mins_west.unwrap_or_else(|| local_minutes_west_for_stamp(now));
sched_timing_today_v2_new(created_secs, crt_west, now, now_west, roll)
}
}
}
#[cfg(test)]
mod test {
use super::SchedTimingToday;
use crate::sched::cutoff::sched_timing_today_v1;
use crate::sched::cutoff::sched_timing_today_v2_legacy;
use crate::sched::cutoff::{
fixed_offset_from_minutes, local_minutes_west_for_stamp, normalized_rollover_hour,
sched_timing_today,
sched_timing_today_v2_new,
};
use chrono::{FixedOffset, Local, TimeZone, Utc};
@ -117,7 +194,7 @@ mod test {
// helper
fn elap(start: i64, end: i64, start_west: i32, end_west: i32, rollhour: i8) -> u32 {
let today = sched_timing_today(start, start_west, end, end_west, rollhour);
let today = sched_timing_today_v2_new(start, start_west, end, end_west, rollhour);
today.days_elapsed
}
@ -228,7 +305,7 @@ mod test {
// before the rollover, the next day should be later on the same day
let now = Local.ymd(2019, 1, 3).and_hms(2, 0, 0);
let next_day_at = Local.ymd(2019, 1, 3).and_hms(rollhour, 0, 0);
let today = sched_timing_today(
let today = sched_timing_today_v2_new(
crt.timestamp(),
crt.offset().utc_minus_local() / 60,
now.timestamp(),
@ -240,7 +317,7 @@ mod test {
// after the rollover, the next day should be the next day
let now = Local.ymd(2019, 1, 3).and_hms(rollhour, 0, 0);
let next_day_at = Local.ymd(2019, 1, 4).and_hms(rollhour, 0, 0);
let today = sched_timing_today(
let today = sched_timing_today_v2_new(
crt.timestamp(),
crt.offset().utc_minus_local() / 60,
now.timestamp(),
@ -252,7 +329,7 @@ mod test {
// after the rollover, the next day should be the next day
let now = Local.ymd(2019, 1, 3).and_hms(rollhour + 3, 0, 0);
let next_day_at = Local.ymd(2019, 1, 4).and_hms(rollhour, 0, 0);
let today = sched_timing_today(
let today = sched_timing_today_v2_new(
crt.timestamp(),
crt.offset().utc_minus_local() / 60,
now.timestamp(),
@ -261,4 +338,34 @@ mod test {
);
assert_eq!(today.next_day_at, next_day_at.timestamp());
}
#[test]
fn legacy_timing() {
let now = 1584491078;
let mins_west = -600;
assert_eq!(
sched_timing_today_v1(1575226800, now),
SchedTimingToday {
days_elapsed: 107,
next_day_at: 1584558000
}
);
assert_eq!(
sched_timing_today_v2_legacy(1533564000, 0, now, mins_west),
SchedTimingToday {
days_elapsed: 589,
next_day_at: 1584540000
}
);
assert_eq!(
sched_timing_today_v2_legacy(1524038400, 4, now, mins_west),
SchedTimingToday {
days_elapsed: 700,
next_day_at: 1584554400
}
);
}
}

View file

@ -6,7 +6,11 @@ use crate::config::Config;
use crate::err::Result;
use crate::err::{AnkiError, DBErrorKind};
use crate::time::{i64_unix_millis, i64_unix_secs};
use crate::{decks::Deck, types::Usn};
use crate::{
decks::Deck,
sched::cutoff::{sched_timing_today, SchedTimingToday},
types::Usn,
};
use rusqlite::{params, Connection, NO_PARAMS};
use std::path::{Path, PathBuf};
@ -121,6 +125,8 @@ pub(crate) struct StorageContext<'a> {
server: bool,
#[allow(dead_code)]
usn: Option<Usn>,
timing_today: Option<SchedTimingToday>,
}
impl StorageContext<'_> {
@ -129,6 +135,7 @@ impl StorageContext<'_> {
db,
server,
usn: None,
timing_today: None,
}
}
@ -225,4 +232,24 @@ impl StorageContext<'_> {
Ok(serde_json::from_str(row.get_raw(0).as_str()?)?)
})
}
#[allow(dead_code)]
pub(crate) fn timing_today(&mut self) -> Result<SchedTimingToday> {
if self.timing_today.is_none() {
let crt: i64 = self
.db
.prepare_cached("select crt from col")?
.query_row(NO_PARAMS, |row| row.get(0))?;
let conf = self.all_config()?;
let now_offset = if self.server { conf.local_offset } else { None };
self.timing_today = Some(sched_timing_today(
crt,
conf.creation_offset,
now_offset,
conf.rollover,
));
}
Ok(*self.timing_today.as_ref().unwrap())
}
}