Use memory state to calculate relative overdueness

This commit is contained in:
Damien Elmes 2023-09-24 13:07:11 +10:00
parent 907a73eb88
commit 69c4efea8a
2 changed files with 63 additions and 1 deletions

View File

@ -707,6 +707,9 @@ enum ReviewOrderSubclause {
RelativeOverdueness {
today: u32,
},
RelativeOverduenessFsrs {
today: u32,
},
}
impl fmt::Display for ReviewOrderSubclause {
@ -726,6 +729,10 @@ impl fmt::Display for ReviewOrderSubclause {
temp_string = format!("ivl / cast({today}-due+0.001 as real)", today = today);
&temp_string
}
ReviewOrderSubclause::RelativeOverduenessFsrs { today } => {
temp_string = format!("extract_fsrs_relative_overdueness(data, due, {today}) desc");
&temp_string
}
};
write!(f, "{}", clause)
}
@ -751,7 +758,11 @@ fn review_order_sql(order: ReviewCardOrder, today: u32, fsrs: bool) -> String {
ReviewOrderSubclause::EaseDescending
}],
ReviewCardOrder::RelativeOverdueness => {
vec![ReviewOrderSubclause::RelativeOverdueness { today }]
vec![if fsrs {
ReviewOrderSubclause::RelativeOverduenessFsrs { today }
} else {
ReviewOrderSubclause::RelativeOverdueness { today }
}]
}
ReviewCardOrder::Random => vec![],
};

View File

@ -73,6 +73,7 @@ fn open_or_create_collection_db(path: &Path) -> Result<Connection> {
add_extract_custom_data_function(&db)?;
add_extract_fsrs_variable(&db)?;
add_extract_fsrs_retrievability(&db)?;
add_extract_fsrs_relative_overdueness(&db)?;
db.create_collation("unicase", unicase_compare)?;
@ -306,6 +307,56 @@ fn add_extract_fsrs_retrievability(db: &Connection) -> rusqlite::Result<()> {
)
}
/// eg. extract_fsrs_retrievability(card.data, card.due, timing.days_elapsed) ->
/// float | null. The higher the number, the more overdue.
fn add_extract_fsrs_relative_overdueness(db: &Connection) -> rusqlite::Result<()> {
db.create_scalar_function(
"extract_fsrs_relative_overdueness",
3,
FunctionFlags::SQLITE_DETERMINISTIC,
move |ctx| {
assert_eq!(ctx.len(), 3, "called with unexpected number of arguments");
let Ok(card_data) = ctx.get_raw(0).as_str() else {
return Ok(None);
};
if card_data.is_empty() {
return Ok(None);
}
let card_data = &CardData::from_str(card_data);
let Ok(due) = ctx.get_raw(1).as_i64() else {
return Ok(None);
};
if due > 365_000 {
// learning card
return Ok(None);
}
let Ok(days_elapsed) = ctx.get_raw(2).as_i64() else {
return Ok(None);
};
let Some(state) = card_data.memory_state() else {
return Ok(None);
};
let Some(mut desired_retrievability) = card_data.fsrs_desired_retention else {
return Ok(None);
};
// avoid div by zero
desired_retrievability = desired_retrievability.max(0.0001);
let review_day = due.saturating_sub(state.stability as i64);
let days_elapsed = days_elapsed.saturating_sub(review_day) as u32;
let current_retrievability = FSRS::new(None)
.unwrap()
.current_retrievability(state.into(), days_elapsed)
.max(0.0001);
Ok(Some(
(1. / current_retrievability - 1.) / (1. / desired_retrievability - 1.),
))
},
)
}
/// Fetch schema version from database.
/// Return (must_create, version)
fn schema_version(db: &Connection) -> Result<(bool, u8)> {