mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 23:12:21 -04:00
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.
This commit is contained in:
parent
133fe8518b
commit
76acf04dc0
10 changed files with 93 additions and 16 deletions
|
@ -14,7 +14,8 @@ pub use crate::backend_proto::{
|
|||
DeckConfigInner,
|
||||
};
|
||||
pub use schema11::{DeckConfSchema11, NewCardOrderSchema11};
|
||||
pub const INITIAL_EASE_FACTOR: u16 = 2500;
|
||||
/// Old deck config and cards table store 250% as 2500.
|
||||
pub const INITIAL_EASE_FACTOR_THOUSANDS: u16 = 2500;
|
||||
|
||||
mod schema11;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use super::{DeckConf, DeckConfID, INITIAL_EASE_FACTOR};
|
||||
use super::{DeckConf, DeckConfID, INITIAL_EASE_FACTOR_THOUSANDS};
|
||||
use crate::backend_proto::deck_config_inner::NewCardOrder;
|
||||
use crate::backend_proto::DeckConfigInner;
|
||||
use crate::{serde::default_on_invalid, timestamp::TimestampSecs, types::Usn};
|
||||
|
@ -153,7 +153,7 @@ impl Default for NewConfSchema11 {
|
|||
NewConfSchema11 {
|
||||
bury: false,
|
||||
delays: vec![1.0, 10.0],
|
||||
initial_factor: INITIAL_EASE_FACTOR,
|
||||
initial_factor: INITIAL_EASE_FACTOR_THOUSANDS,
|
||||
ints: NewCardIntervals::default(),
|
||||
order: NewCardOrderSchema11::default(),
|
||||
per_day: 20,
|
||||
|
@ -237,7 +237,7 @@ impl From<DeckConfSchema11> 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<DeckConf> 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,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use crate::{
|
||||
card::{Card, CardQueue, CardType},
|
||||
deckconf::INITIAL_EASE_FACTOR,
|
||||
deckconf::INITIAL_EASE_FACTOR_THOUSANDS,
|
||||
};
|
||||
|
||||
impl Card {
|
||||
|
@ -29,7 +29,7 @@ impl Card {
|
|||
self.interval = 0;
|
||||
self.due = 0;
|
||||
self.original_due = 0;
|
||||
self.ease_factor = INITIAL_EASE_FACTOR;
|
||||
self.ease_factor = INITIAL_EASE_FACTOR_THOUSANDS;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use crate::{
|
||||
card::{Card, CardID, CardQueue, CardType},
|
||||
collection::Collection,
|
||||
deckconf::INITIAL_EASE_FACTOR,
|
||||
deckconf::INITIAL_EASE_FACTOR_THOUSANDS,
|
||||
decks::DeckID,
|
||||
err::Result,
|
||||
notes::NoteID,
|
||||
|
@ -24,7 +24,7 @@ impl Card {
|
|||
if self.ease_factor == 0 {
|
||||
// unlike the old Python code, we leave the ease factor alone
|
||||
// if it's already set
|
||||
self.ease_factor = INITIAL_EASE_FACTOR;
|
||||
self.ease_factor = INITIAL_EASE_FACTOR_THOUSANDS;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use crate::{
|
||||
card::{Card, CardID, CardQueue, CardType},
|
||||
collection::Collection,
|
||||
deckconf::INITIAL_EASE_FACTOR,
|
||||
deckconf::INITIAL_EASE_FACTOR_THOUSANDS,
|
||||
err::Result,
|
||||
};
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
|
@ -19,7 +19,7 @@ impl Card {
|
|||
if self.ease_factor == 0 {
|
||||
// unlike the old Python code, we leave the ease factor alone
|
||||
// if it's already set
|
||||
self.ease_factor = INITIAL_EASE_FACTOR;
|
||||
self.ease_factor = INITIAL_EASE_FACTOR_THOUSANDS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
10
rslib/src/storage/card/fix_low_ease.sql
Normal file
10
rslib/src/storage/card/fix_low_ease.sql
Normal file
|
@ -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
|
||||
)
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
use crate::{
|
||||
card::{Card, CardID, CardQueue, CardType},
|
||||
decks::{Deck, DeckID},
|
||||
deckconf::DeckConfID,
|
||||
decks::{Deck, DeckID, DeckKind},
|
||||
err::Result,
|
||||
notes::NoteID,
|
||||
sched::congrats::CongratsInfo,
|
||||
|
@ -18,6 +19,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<Self, FromSqlError> {
|
||||
if let ValueRef::Integer(i) = value {
|
||||
|
@ -350,6 +353,38 @@ 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)]
|
||||
|
|
|
@ -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<()> {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue