Create fsrs upadte method
This commit is contained in:
1 parent
663fa377b1
commit
e7019903b0
5 files changed
+73
-10
No files matched your search
+3
-4
@@ -10,8 +10,7 @@ create table card_stats
|
|||||||
on delete cascade,
|
on delete cascade,
|
||||||
score INTEGER default 1 not null,
|
score INTEGER default 1 not null,
|
||||||
last_opened INTEGER not null,
|
last_opened INTEGER not null,
|
||||||
added INTEGER not null,
|
fsrs_opened INTEGER not null,
|
||||||
fsrs_opened INTEGER,
|
fsrs_memory INTEGER not null,
|
||||||
fsrs_memory INTEGER,
|
fsrs_open_day INTEGER not null
|
||||||
fsrs_open_time INTEGER
|
|
||||||
);
|
);
|
||||||
+9
-1
@@ -7,7 +7,7 @@ use crate::data::entities::*;
|
|||||||
use crate::data_provider::card_stats::{load_stats_of_deck, update_stat_score};
|
use crate::data_provider::card_stats::{load_stats_of_deck, update_stat_score};
|
||||||
use crate::data_provider::history::push_note;
|
use crate::data_provider::history::push_note;
|
||||||
use crate::notification::{MaybeError, NotificationData};
|
use crate::notification::{MaybeError, NotificationData};
|
||||||
use chrono::Utc;
|
use chrono::{Local, Utc};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ pub struct DeckData {
|
|||||||
pub(crate) words: Vec<WordData>,
|
pub(crate) words: Vec<WordData>,
|
||||||
pub(crate) set: Vec<CardStatistics>,
|
pub(crate) set: Vec<CardStatistics>,
|
||||||
current_word_index: Option<usize>,
|
current_word_index: Option<usize>,
|
||||||
|
fsrs_indices: Vec<usize>,
|
||||||
state: Arc<Mutex<AppState>>,
|
state: Arc<Mutex<AppState>>,
|
||||||
order_module: OrderModule,
|
order_module: OrderModule,
|
||||||
settings: DeckSettings,
|
settings: DeckSettings,
|
||||||
@@ -41,7 +42,14 @@ impl DeckData {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let today = Local::now().to_utc().date_naive();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
fsrs_indices: stats
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, c)| c.fsrs_open_day == today)
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
.collect(),
|
||||||
set: stats,
|
set: stats,
|
||||||
words,
|
words,
|
||||||
current_word_index: None,
|
current_word_index: None,
|
||||||
|
|||||||
+29
-1
@@ -1,8 +1,10 @@
|
|||||||
use crate::app::AppState;
|
use crate::app::AppState;
|
||||||
use crate::core::srs::{FADE_PER_DAY, MAX_SCORE, OrderMode, WordOpenMode};
|
use crate::core::srs::{FADE_PER_DAY, MAX_SCORE, OrderMode, WordOpenMode};
|
||||||
use crate::data::{INVALID_ID, Id};
|
use crate::data::{INVALID_ID, Id};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Local, NaiveDate, Utc};
|
||||||
|
use fsrs::{FSRS, MemoryState};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use rand::{RngExt, random, rng};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use rhai::{Engine, Scope};
|
use rhai::{Engine, Scope};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -137,6 +139,9 @@ pub struct CardStatistics {
|
|||||||
pub set_id: Id,
|
pub set_id: Id,
|
||||||
pub last_open: DateTime<Utc>,
|
pub last_open: DateTime<Utc>,
|
||||||
pub score: u8,
|
pub score: u8,
|
||||||
|
pub fsrs_opened: NaiveDate,
|
||||||
|
pub fsrs_memory: Option<MemoryState>,
|
||||||
|
pub fsrs_open_day: NaiveDate,
|
||||||
}
|
}
|
||||||
impl CardStatistics {
|
impl CardStatistics {
|
||||||
pub fn update(&mut self, status: WordOpenMode) {
|
pub fn update(&mut self, status: WordOpenMode) {
|
||||||
@@ -157,6 +162,29 @@ impl CardStatistics {
|
|||||||
self.last_open = Utc::now();
|
self.last_open = Utc::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_fsrs(&mut self, status: WordOpenMode, by_fsrs: bool, fsrs: FSRS)
|
||||||
|
{
|
||||||
|
let today = Local::now().to_utc().date_naive().to_epoch_days();
|
||||||
|
let written = self.fsrs_opened.to_epoch_days();
|
||||||
|
let memory = fsrs
|
||||||
|
.next_states(self.fsrs_memory, 0.9, (today - written) as u32)
|
||||||
|
.expect("Wrong next stage calculation");
|
||||||
|
let next = match status {
|
||||||
|
WordOpenMode::Easy => memory.easy,
|
||||||
|
WordOpenMode::Ok => memory.good,
|
||||||
|
WordOpenMode::Hard => memory.hard,
|
||||||
|
WordOpenMode::None => memory.again,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut next_day = today + next.interval.round() as i32;
|
||||||
|
if next_day - today >= 5 {
|
||||||
|
let offset = ((rng().random::<f32>() - 0.5) * 3.0).round() as i32;
|
||||||
|
next_day += offset;
|
||||||
|
}
|
||||||
|
self.fsrs_memory = Some(next.memory);
|
||||||
|
self.fsrs_open_day = NaiveDate::from_epoch_days(next_day).unwrap();
|
||||||
|
self.fsrs_opened = self.last_open.date_naive();
|
||||||
|
}
|
||||||
pub fn calculated_score(&self) -> f32 {
|
pub fn calculated_score(&self) -> f32 {
|
||||||
let time = Utc::now() - self.last_open;
|
let time = Utc::now() - self.last_open;
|
||||||
let days = time.num_days();
|
let days = time.num_days();
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use crate::core::srs::OrderMode;
|
use crate::core::srs::OrderMode;
|
||||||
|
use crate::data::Id;
|
||||||
use crate::data::entities::DeckSettings;
|
use crate::data::entities::DeckSettings;
|
||||||
use crate::notification::NotificationData::{DbLoadError, DbSaveError};
|
use crate::notification::NotificationData::{DbLoadError, DbSaveError};
|
||||||
use crate::notification::{MaybeError, NotificationData};
|
use crate::notification::{MaybeError, NotificationData};
|
||||||
|
use chrono::{Datelike, Local, NaiveDate};
|
||||||
|
use fsrs::Card;
|
||||||
|
use iced::futures::StreamExt;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
|
|
||||||
pub fn load_sets(connection: &Connection) -> Result<Vec<DeckSettings>, NotificationData> {
|
pub fn load_sets(connection: &Connection) -> Result<Vec<DeckSettings>, NotificationData> {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use crate::data::entities::{CardStatistics, DeckSettings};
|
use crate::data::entities::{CardStatistics, DeckSettings};
|
||||||
use crate::notification::NotificationData::{DbLoadError, DbSaveError};
|
use crate::notification::NotificationData::{DbLoadError, DbSaveError};
|
||||||
use crate::notification::{MaybeError, NotificationData};
|
use crate::notification::{MaybeError, NotificationData};
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
use fsrs::MemoryState;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use reqwest::get;
|
||||||
use rusqlite::{Connection, params};
|
use rusqlite::{Connection, params};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
@@ -10,7 +13,7 @@ pub fn load_stats_of_deck(
|
|||||||
connection: &Connection,
|
connection: &Connection,
|
||||||
) -> Result<Vec<CardStatistics>, NotificationData> {
|
) -> Result<Vec<CardStatistics>, NotificationData> {
|
||||||
let mut stmt = connection
|
let mut stmt = connection
|
||||||
.prepare("SELECT id, word_id, score, last_opened FROM card_stats WHERE set_id = ?1")
|
.prepare("SELECT id, word_id, score, last_opened, fsrs_opened, fsrs_memory, fsrs_open_day FROM card_stats WHERE set_id = ?1")
|
||||||
.expect("Failed to prepare state query");
|
.expect("Failed to prepare state query");
|
||||||
let iter = stmt
|
let iter = stmt
|
||||||
.query_map((set.id,), |row| {
|
.query_map((set.id,), |row| {
|
||||||
@@ -19,7 +22,12 @@ pub fn load_stats_of_deck(
|
|||||||
word_id: row.get(1)?,
|
word_id: row.get(1)?,
|
||||||
set_id: set.id,
|
set_id: set.id,
|
||||||
score: row.get(2)?,
|
score: row.get(2)?,
|
||||||
|
fsrs_opened: NaiveDate::from_epoch_days(row.get::<usize, i32>(4)?)
|
||||||
|
.expect("Incorrect date encoding"),
|
||||||
|
fsrs_memory: Some(get_memory(row.get(5)?)),
|
||||||
last_open: row.get(3)?,
|
last_open: row.get(3)?,
|
||||||
|
fsrs_open_day: NaiveDate::from_epoch_days(row.get::<usize, i32>(6)?)
|
||||||
|
.expect("Incorrect date encoding"),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| DbLoadError)?;
|
.map_err(|_| DbLoadError)?;
|
||||||
@@ -30,6 +38,17 @@ pub fn load_stats_of_deck(
|
|||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_memory(value: i64) -> MemoryState {
|
||||||
|
MemoryState {
|
||||||
|
stability: f32::from_bits(value as u32),
|
||||||
|
difficulty: f32::from_bits((value >> 32) as u32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memory_to_i64(value: &MemoryState) -> i64 {
|
||||||
|
value.stability.to_bits() as i64 | ((value.difficulty.to_bits() as i64) << 32)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_stat_list(stats: &mut [CardStatistics], connection: &mut Connection) -> MaybeError {
|
pub fn add_stat_list(stats: &mut [CardStatistics], connection: &mut Connection) -> MaybeError {
|
||||||
let time = Instant::now();
|
let time = Instant::now();
|
||||||
let tx = connection
|
let tx = connection
|
||||||
@@ -40,7 +59,7 @@ pub fn add_stat_list(stats: &mut [CardStatistics], connection: &mut Connection)
|
|||||||
{
|
{
|
||||||
let mut stmt = tx
|
let mut stmt = tx
|
||||||
.prepare(
|
.prepare(
|
||||||
"INSERT INTO card_stats (word_id, set_id, score, last_opened) VALUES (?1, ?2, ?3, ?4)",
|
"INSERT INTO card_stats (word_id, set_id, score, last_opened, fsrs_opened, fsrs_memory, fsrs_open_day) VALUES (?1, ?2, ?3, ?4)",
|
||||||
)
|
)
|
||||||
.expect("Failed to prepare state query");
|
.expect("Failed to prepare state query");
|
||||||
|
|
||||||
@@ -49,7 +68,13 @@ pub fn add_stat_list(stats: &mut [CardStatistics], connection: &mut Connection)
|
|||||||
stat.word_id,
|
stat.word_id,
|
||||||
stat.set_id,
|
stat.set_id,
|
||||||
stat.score,
|
stat.score,
|
||||||
stat.last_open.timestamp()
|
stat.last_open.timestamp(),
|
||||||
|
stat.fsrs_opened.to_epoch_days(),
|
||||||
|
memory_to_i64(&stat.fsrs_memory.unwrap_or(MemoryState {
|
||||||
|
difficulty: 0.0,
|
||||||
|
stability: 0.0
|
||||||
|
})),
|
||||||
|
stat.fsrs_open_day.to_epoch_days()
|
||||||
])
|
])
|
||||||
.map_err(|_| DbSaveError)?;
|
.map_err(|_| DbSaveError)?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user