From 87c6686930999d01a43bcaea563d8a625ceb53cc Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Tue, 22 Sep 2020 08:52:48 +1000 Subject: [PATCH] update schema to fix default initial ease in deck configs Closes #766 - changes the on-disk representation from % to a multiplier, eg 250 -> 2.5, as this is consistent with the other options - resets deck configs at or below 1.3 to 2.5 - for any cards that were using a reset deck config, reset their current factor if it's at or below 2.0x. The cutoff is arbitrary, and just intended to make sure we catch cards the user has rated Easy on multiple times. The existing due dates are left alone. --- rslib/src/deckconf/schema11.rs | 4 +-- rslib/src/sched/learning.rs | 0 rslib/src/sched/new.rs | 0 rslib/src/sched/reviews.rs | 0 rslib/src/storage/card/fix_low_ease.sql | 10 +++++++ rslib/src/storage/card/mod.rs | 39 ++++++++++++++++++++++++- rslib/src/storage/deckconf/mod.rs | 24 ++++++++++++++- rslib/src/storage/sqlite.rs | 4 +-- rslib/src/storage/upgrades/mod.rs | 11 +++++++ 9 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 rslib/src/sched/learning.rs create mode 100644 rslib/src/sched/new.rs create mode 100644 rslib/src/sched/reviews.rs create mode 100644 rslib/src/storage/card/fix_low_ease.sql diff --git a/rslib/src/deckconf/schema11.rs b/rslib/src/deckconf/schema11.rs index 585fbfe55..d799d562d 100644 --- a/rslib/src/deckconf/schema11.rs +++ b/rslib/src/deckconf/schema11.rs @@ -237,7 +237,7 @@ impl From for DeckConf { reviews_per_day: c.rev.per_day, bury_new: c.new.bury, bury_reviews: c.rev.bury, - initial_ease: (c.new.initial_factor as f32) / 10.0, + initial_ease: (c.new.initial_factor as f32) / 1000.0, easy_multiplier: c.rev.ease4, hard_multiplier: c.rev.hard_factor, lapse_multiplier: c.lapse.mult, @@ -298,7 +298,7 @@ impl From for DeckConfSchema11 { new: NewConfSchema11 { bury: i.bury_new, delays: i.learn_steps, - initial_factor: (i.initial_ease * 10.0) as u16, + initial_factor: (i.initial_ease * 1000.0) as u16, ints: NewCardIntervals { good: i.graduating_interval_good as u16, easy: i.graduating_interval_easy as u16, diff --git a/rslib/src/sched/learning.rs b/rslib/src/sched/learning.rs new file mode 100644 index 000000000..e69de29bb diff --git a/rslib/src/sched/new.rs b/rslib/src/sched/new.rs new file mode 100644 index 000000000..e69de29bb diff --git a/rslib/src/sched/reviews.rs b/rslib/src/sched/reviews.rs new file mode 100644 index 000000000..e69de29bb diff --git a/rslib/src/storage/card/fix_low_ease.sql b/rslib/src/storage/card/fix_low_ease.sql new file mode 100644 index 000000000..d905613d0 --- /dev/null +++ b/rslib/src/storage/card/fix_low_ease.sql @@ -0,0 +1,10 @@ +update cards +set factor = 2500, + usn = ?, + mod = ? +where factor != 0 + and factor <= 2000 + and ( + did in DECK_IDS + or odid in DECK_IDS + ) \ No newline at end of file diff --git a/rslib/src/storage/card/mod.rs b/rslib/src/storage/card/mod.rs index db3981bf9..9e7d70be6 100644 --- a/rslib/src/storage/card/mod.rs +++ b/rslib/src/storage/card/mod.rs @@ -3,7 +3,8 @@ use crate::{ card::{Card, CardID, CardQueue, CardType}, - decks::DeckID, + deckconf::DeckConfID, + decks::{Deck, DeckID, DeckKind}, err::Result, notes::NoteID, timestamp::{TimestampMillis, TimestampSecs}, @@ -16,6 +17,8 @@ use rusqlite::{ }; use std::{collections::HashSet, convert::TryFrom, result}; +use super::ids_to_string; + impl FromSql for CardType { fn column_result(value: ValueRef<'_>) -> std::result::Result { if let ValueRef::Integer(i) = value { @@ -277,6 +280,40 @@ impl super::SqliteStorage { Ok(()) } + + + /// Fix cards with low eases due to schema 15 bug. + /// Deck configs were defaulting to 2.5% ease, which was capped to + /// 130% when the deck options were edited for the first time. + pub(crate) fn fix_low_card_eases_for_configs( + &self, + configs: &[DeckConfID], + server: bool, + ) -> Result<()> { + let mut affected_decks = vec![]; + for conf in configs { + for (deck_id, _name) in self.get_all_deck_names()? { + if let Some(deck) = self.get_deck(deck_id)? { + if let DeckKind::Normal(normal) = &deck.kind { + if normal.config_id == conf.0 { + affected_decks.push(deck.id); + } + } + } + } + } + + let mut ids = String::new(); + ids_to_string(&mut ids, &affected_decks); + let sql = include_str!("fix_low_ease.sql").replace("DECK_IDS", &ids); + + self.db + .prepare(&sql)? + .execute(params![self.usn(server)?, TimestampSecs::now()])?; + + Ok(()) + } + } #[cfg(test)] diff --git a/rslib/src/storage/deckconf/mod.rs b/rslib/src/storage/deckconf/mod.rs index 11cde03ec..9c9e150f3 100644 --- a/rslib/src/storage/deckconf/mod.rs +++ b/rslib/src/storage/deckconf/mod.rs @@ -159,13 +159,35 @@ impl SqliteStorage { pub(super) fn upgrade_deck_conf_to_schema15(&self) -> Result<()> { for conf in self.all_deck_config_schema14()? { - let conf: DeckConf = conf.into(); + let mut conf: DeckConf = conf.into(); + // schema 15 stored starting ease of 2.5 as 250 + conf.inner.initial_ease *= 100.0; self.update_deck_conf(&conf)?; } Ok(()) } + // schema 15->16 + + pub(super) fn upgrade_deck_conf_to_schema16(&self, server: bool) -> Result<()> { + let mut invalid_configs = vec![]; + for mut conf in self.all_deck_config()? { + // schema 16 changed starting ease of 250 to 2.5 + conf.inner.initial_ease /= 100.0; + // new deck configs created with schema 15 had the wrong + // ease set - reset any deck configs at the minimum ease + // to the default 250% + if conf.inner.initial_ease <= 1.3 { + conf.inner.initial_ease = 2.5; + invalid_configs.push(conf.id); + } + self.update_deck_conf(&conf)?; + } + + self.fix_low_card_eases_for_configs(&invalid_configs, server) + } + // schema 15->11 pub(super) fn downgrade_deck_conf_from_schema15(&self) -> Result<()> { diff --git a/rslib/src/storage/sqlite.rs b/rslib/src/storage/sqlite.rs index 34d385b13..36dc0c05b 100644 --- a/rslib/src/storage/sqlite.rs +++ b/rslib/src/storage/sqlite.rs @@ -12,9 +12,7 @@ use std::cmp::Ordering; use std::{borrow::Cow, path::Path, sync::Arc}; use unicase::UniCase; -const SCHEMA_MIN_VERSION: u8 = 11; -const SCHEMA_STARTING_VERSION: u8 = 11; -const SCHEMA_MAX_VERSION: u8 = 15; +use super::upgrades::{SCHEMA_MAX_VERSION, SCHEMA_MIN_VERSION, SCHEMA_STARTING_VERSION}; fn unicase_compare(s1: &str, s2: &str) -> Ordering { UniCase::new(s1).cmp(&UniCase::new(s2)) diff --git a/rslib/src/storage/upgrades/mod.rs b/rslib/src/storage/upgrades/mod.rs index e84f2c570..ae53cb40e 100644 --- a/rslib/src/storage/upgrades/mod.rs +++ b/rslib/src/storage/upgrades/mod.rs @@ -1,6 +1,13 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +/// The minimum schema version we can open. +pub(super) const SCHEMA_MIN_VERSION: u8 = 11; +/// The version new files are initially created with. +pub(super) const SCHEMA_STARTING_VERSION: u8 = 11; +/// The maximum schema version we can open. +pub(super) const SCHEMA_MAX_VERSION: u8 = 16; + use super::SqliteStorage; use crate::err::Result; @@ -20,6 +27,10 @@ impl SqliteStorage { self.upgrade_decks_to_schema15(server)?; self.upgrade_deck_conf_to_schema15()?; } + if ver < 16 { + self.upgrade_deck_conf_to_schema16(server)?; + self.db.execute_batch("update col set ver = 16")?; + } Ok(()) }