Data provider refactor #11
No files matched your search
+10
-16
@@ -58,30 +58,24 @@ impl DeckData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(&mut self) -> (WordData, CardStatistics) {
|
pub fn next(&mut self) -> (WordData, CardStatistics) {
|
||||||
let index = match self.order_module.clone() {
|
let index = match self.order_module {
|
||||||
OrderModule::SemiRandomSRS(mut module) => {
|
OrderModule::SemiRandomSRS(ref mut module) => {
|
||||||
if !module.initialized {
|
if !module.initialized {
|
||||||
module.init(self)
|
module.init(self.set.as_slice())
|
||||||
}
|
}
|
||||||
let index = module.next(self);
|
module.next(self.set.as_slice())
|
||||||
self.order_module = OrderModule::SemiRandomSRS(module);
|
|
||||||
index
|
|
||||||
}
|
}
|
||||||
OrderModule::RandomSRS(mut module) => {
|
OrderModule::RandomSRS(ref mut module) => {
|
||||||
if !module.initialized {
|
if !module.initialized {
|
||||||
module.init(self)
|
module.init(self.set.as_slice())
|
||||||
}
|
}
|
||||||
let index = module.next(self);
|
module.next(self.set.as_slice())
|
||||||
self.order_module = OrderModule::RandomSRS(module);
|
|
||||||
index
|
|
||||||
}
|
}
|
||||||
OrderModule::WorstWordsSRS(mut module) => {
|
OrderModule::WorstWordsSRS(ref mut module) => {
|
||||||
if !module.initialized {
|
if !module.initialized {
|
||||||
module.init(self)
|
module.init(self.set.as_slice())
|
||||||
}
|
}
|
||||||
let index = module.next(self);
|
module.next(self.set.as_slice())
|
||||||
self.order_module = OrderModule::WorstWordsSRS(module);
|
|
||||||
index
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+16
-22
@@ -1,9 +1,8 @@
|
|||||||
use crate::core::deck::DeckData;
|
use crate::data::entities::CardStatistics;
|
||||||
use rand::distr::weighted::WeightedIndex;
|
use rand::distr::weighted::WeightedIndex;
|
||||||
use rand::prelude::ThreadRng;
|
use rand::prelude::ThreadRng;
|
||||||
use rand::prelude::{Distribution, SliceRandom};
|
use rand::prelude::{Distribution, SliceRandom};
|
||||||
use rand::rng;
|
use rand::rng;
|
||||||
use crate::data::entities::CardStatistics;
|
|
||||||
|
|
||||||
const MAX_HISTORY_LEN: usize = 20;
|
const MAX_HISTORY_LEN: usize = 20;
|
||||||
const MAX_HISTORY_LEN_PART: f32 = 0.33;
|
const MAX_HISTORY_LEN_PART: f32 = 0.33;
|
||||||
@@ -29,9 +28,9 @@ pub enum OrderModule {
|
|||||||
WorstWordsSRS(WorstWordsSRSModule),
|
WorstWordsSRS(WorstWordsSRSModule),
|
||||||
}
|
}
|
||||||
pub trait SRSModule {
|
pub trait SRSModule {
|
||||||
fn next(&mut self, set: &mut DeckData) -> usize;
|
fn next(&mut self, set: &[CardStatistics]) -> usize;
|
||||||
fn open(&mut self, status: WordOpenMode, index: usize, updated_word: &CardStatistics);
|
fn open(&mut self, status: WordOpenMode, index: usize, updated_word: &CardStatistics);
|
||||||
fn init(&mut self, set: &mut DeckData);
|
fn init(&mut self, set: &[CardStatistics]);
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RandomSRSModule {
|
pub struct RandomSRSModule {
|
||||||
@@ -47,11 +46,11 @@ impl RandomSRSModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SRSModule for RandomSRSModule {
|
impl SRSModule for RandomSRSModule {
|
||||||
fn next(&mut self, set: &mut DeckData) -> usize {
|
fn next(&mut self, set: &[CardStatistics]) -> usize {
|
||||||
match self.basket.pop() {
|
match self.basket.pop() {
|
||||||
Some(index) => index,
|
Some(index) => index,
|
||||||
None => {
|
None => {
|
||||||
self.basket = (0..set.words.len()).collect::<Vec<usize>>();
|
self.basket = (0..set.len()).collect::<Vec<usize>>();
|
||||||
self.basket.shuffle(&mut rand::rng());
|
self.basket.shuffle(&mut rand::rng());
|
||||||
self.basket.pop().expect("empty basket")
|
self.basket.pop().expect("empty basket")
|
||||||
}
|
}
|
||||||
@@ -60,9 +59,9 @@ impl SRSModule for RandomSRSModule {
|
|||||||
|
|
||||||
fn open(&mut self, _: WordOpenMode, _: usize, _: &CardStatistics) {}
|
fn open(&mut self, _: WordOpenMode, _: usize, _: &CardStatistics) {}
|
||||||
|
|
||||||
fn init(&mut self, set: &mut DeckData) {
|
fn init(&mut self, set: &[CardStatistics]) {
|
||||||
self.initialized = true;
|
self.initialized = true;
|
||||||
self.basket = (0..set.set.len()).collect::<Vec<usize>>();
|
self.basket = (0..set.len()).collect::<Vec<usize>>();
|
||||||
self.basket.shuffle(&mut rand::rng())
|
self.basket.shuffle(&mut rand::rng())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,7 +83,7 @@ impl SemiRandomSRSModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SRSModule for SemiRandomSRSModule {
|
impl SRSModule for SemiRandomSRSModule {
|
||||||
fn next(&mut self, set: &mut DeckData) -> usize {
|
fn next(&mut self, set: &[CardStatistics]) -> usize {
|
||||||
let index = self.last_weights.sample(&mut self.generator);
|
let index = self.last_weights.sample(&mut self.generator);
|
||||||
|
|
||||||
if self.history.contains(&index) && self.history.len() != 1 {
|
if self.history.contains(&index) && self.history.len() != 1 {
|
||||||
@@ -104,10 +103,9 @@ impl SRSModule for SemiRandomSRSModule {
|
|||||||
.update_weights(&[(index, &new_weight)])
|
.update_weights(&[(index, &new_weight)])
|
||||||
.expect("update weights error");
|
.expect("update weights error");
|
||||||
}
|
}
|
||||||
fn init(&mut self, set: &mut DeckData) {
|
fn init(&mut self, set: &[CardStatistics]) {
|
||||||
self.initialized = true;
|
self.initialized = true;
|
||||||
let weights = set
|
let weights = set
|
||||||
.set
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| (100.0 / s.calculated_score()).powf(2.0) * 2.0)
|
.map(|s| (100.0 / s.calculated_score()).powf(2.0) * 2.0)
|
||||||
.collect::<Vec<f32>>();
|
.collect::<Vec<f32>>();
|
||||||
@@ -115,7 +113,7 @@ impl SRSModule for SemiRandomSRSModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SemiRandomSRSModule {
|
impl SemiRandomSRSModule {
|
||||||
fn history_len(&self, set: &DeckData) -> usize {
|
fn history_len(&self, set: &[CardStatistics]) -> usize {
|
||||||
((set.len() as f32 * MAX_HISTORY_LEN_PART) as usize).clamp(1, MAX_HISTORY_LEN)
|
((set.len() as f32 * MAX_HISTORY_LEN_PART) as usize).clamp(1, MAX_HISTORY_LEN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +127,7 @@ pub struct WorstWordsSRSModule {
|
|||||||
rounds_count: u8,
|
rounds_count: u8,
|
||||||
}
|
}
|
||||||
impl SRSModule for WorstWordsSRSModule {
|
impl SRSModule for WorstWordsSRSModule {
|
||||||
fn next(&mut self, set: &mut DeckData) -> usize {
|
fn next(&mut self, set: &[CardStatistics]) -> usize {
|
||||||
if self.rounds_remaining == 0 {
|
if self.rounds_remaining == 0 {
|
||||||
self.fill_pool(set);
|
self.fill_pool(set);
|
||||||
self.rounds_remaining = self.rounds_count;
|
self.rounds_remaining = self.rounds_count;
|
||||||
@@ -147,7 +145,7 @@ impl SRSModule for WorstWordsSRSModule {
|
|||||||
|
|
||||||
fn open(&mut self, _: WordOpenMode, _: usize, _: &CardStatistics) {}
|
fn open(&mut self, _: WordOpenMode, _: usize, _: &CardStatistics) {}
|
||||||
|
|
||||||
fn init(&mut self, _: &mut DeckData) {
|
fn init(&mut self, _: &[CardStatistics]) {
|
||||||
self.initialized = true;
|
self.initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,17 +160,13 @@ impl WorstWordsSRSModule {
|
|||||||
queue: vec![],
|
queue: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn fill_pool(&mut self, set: &DeckData) {
|
fn fill_pool(&mut self, set: &[CardStatistics]) {
|
||||||
let mut sorted = set
|
let mut sorted = set.iter().enumerate()
|
||||||
.set
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.zip(0..set.set.len())
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
sorted.sort_by_key(|c| c.0.score);
|
sorted.sort_by_key(|c| c.1.score);
|
||||||
let mut worst = sorted[0..self.pool_size]
|
let mut worst = sorted[0..self.pool_size]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, index)| *index)
|
.map(|(index, _)| *index)
|
||||||
.collect::<Vec<usize>>();
|
.collect::<Vec<usize>>();
|
||||||
worst.shuffle(&mut rand::rng());
|
worst.shuffle(&mut rand::rng());
|
||||||
self.pool = worst;
|
self.pool = worst;
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ fn compress(data: Vec<u8>) -> Result<Vec<u8>, NotificationData> {
|
|||||||
let mut encoder =
|
let mut encoder =
|
||||||
Encoder::new(Vec::new(), DEFAULT_COMPRESSION_LEVEL).map_err(|_| CompressError)?;
|
Encoder::new(Vec::new(), DEFAULT_COMPRESSION_LEVEL).map_err(|_| CompressError)?;
|
||||||
io::copy(&mut &data[..], &mut encoder).map_err(|_| PermissionDenied)?;
|
io::copy(&mut &data[..], &mut encoder).map_err(|_| PermissionDenied)?;
|
||||||
encoder.finish().map_err(|_| CompressError)?;
|
encoder.finish().map_err(|_| CompressError)
|
||||||
Ok(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn load_data(id: String, temp: bool) -> MaybeError {
|
pub async fn load_data(id: String, temp: bool) -> MaybeError {
|
||||||
|
|||||||
+34
-36
@@ -6,11 +6,11 @@ use crate::data_provider::web_api::{
|
|||||||
get_local_version, get_web_version, load_data, set_local_version,
|
get_local_version, get_web_version, load_data, set_local_version,
|
||||||
};
|
};
|
||||||
use crate::fs::*;
|
use crate::fs::*;
|
||||||
use crate::{message_navigation, push_error, push_error_task};
|
|
||||||
use crate::navigation::Page::*;
|
use crate::navigation::Page::*;
|
||||||
use crate::navigation::RootMessage::{
|
use crate::navigation::RootMessage::{
|
||||||
DataLoaded, Keyboard, Notification, PushNotification, UpdateData,
|
DataLoaded, Keyboard, Notification, PushNotification, UpdateData,
|
||||||
};
|
};
|
||||||
|
use crate::notification::NotificationData::PermissionDenied;
|
||||||
use crate::notification::{
|
use crate::notification::{
|
||||||
NotificationData, NotificationManager, NotificationMessage, NotificationRequest,
|
NotificationData, NotificationManager, NotificationMessage, NotificationRequest,
|
||||||
};
|
};
|
||||||
@@ -30,6 +30,7 @@ use crate::pages::writing::{WritingMessage, WritingState};
|
|||||||
use crate::state_navigate_handle;
|
use crate::state_navigate_handle;
|
||||||
use crate::state_update;
|
use crate::state_update;
|
||||||
use crate::view_navigation;
|
use crate::view_navigation;
|
||||||
|
use crate::{message_navigation, push_error, push_error_task};
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use iced::keyboard::Event;
|
use iced::keyboard::Event;
|
||||||
@@ -41,7 +42,6 @@ use parking_lot::Mutex;
|
|||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use crate::notification::NotificationData::PermissionDenied;
|
|
||||||
|
|
||||||
impl Default for ScreenState {
|
impl Default for ScreenState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@@ -109,7 +109,8 @@ impl ScreenState {
|
|||||||
create_db();
|
create_db();
|
||||||
info!("Db created");
|
info!("Db created");
|
||||||
let state = ScreenState::default();
|
let state = ScreenState::default();
|
||||||
let reading_additional_task = Task::perform(Self::load_additional_data(), |data| data);
|
let reading_additional_task =
|
||||||
|
Task::perform(Self::load_additional_data(), |data| data.unwrap_or_else(|err| push_error!(err)));
|
||||||
|
|
||||||
let mut final_task = reading_additional_task;
|
let mut final_task = reading_additional_task;
|
||||||
let state_guard = state.app_state.lock();
|
let state_guard = state.app_state.lock();
|
||||||
@@ -126,7 +127,7 @@ impl ScreenState {
|
|||||||
RootMessage::None
|
RootMessage::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => PushNotification(NotificationRequest::Warning(err)),
|
Err(err) => push_error!(err),
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -136,39 +137,36 @@ impl ScreenState {
|
|||||||
(state, final_task)
|
(state, final_task)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_additional_data() -> RootMessage {
|
async fn load_additional_data() -> Result<RootMessage, NotificationData> {
|
||||||
let directory = history_dir();
|
let directory = history_dir()?;
|
||||||
|
|
||||||
if let Ok(directory) = directory {
|
let mut map = HashMap::new();
|
||||||
let mut map = HashMap::new();
|
for file in directory
|
||||||
for file in directory
|
.read_dir()
|
||||||
.read_dir()
|
.map_err(|_| PermissionDenied)?
|
||||||
.expect("Cant read history directory")
|
.filter_map(Result::ok)
|
||||||
.flatten()
|
{
|
||||||
|
let mut vec = vec![];
|
||||||
|
let history_file_name = match file.file_name().into_string() {
|
||||||
|
Err(_) => continue,
|
||||||
|
Ok(v) => v,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(id) = history_file_name.split('_').collect::<Vec<_>>()[1].parse::<Id>()
|
||||||
|
&& let Ok(history) = get_history_of_set(id)
|
||||||
{
|
{
|
||||||
let mut vec = vec![];
|
let by_date = history.chunk_by(|x, x1| {
|
||||||
let history_file_name = match file.file_name().into_string() {
|
x.timestamp.naive_local().date() == x1.timestamp.naive_local().date()
|
||||||
Err(_) => continue,
|
});
|
||||||
Ok(v) => v,
|
for group in by_date {
|
||||||
};
|
vec.push((group[0].timestamp.naive_local().date(), group.len() as u32));
|
||||||
|
|
||||||
if let Ok(id) = history_file_name[4..history_file_name.len() - 12].parse::<Id>()
|
|
||||||
&& let Ok(history) = get_history_of_set(id)
|
|
||||||
{
|
|
||||||
let by_date = history.chunk_by(|x, x1| {
|
|
||||||
x.timestamp.naive_local().date() == x1.timestamp.naive_local().date()
|
|
||||||
});
|
|
||||||
for group in by_date {
|
|
||||||
vec.push((group[0].timestamp.naive_local().date(), group.len() as u32));
|
|
||||||
}
|
|
||||||
map.insert(id, vec);
|
|
||||||
} else {
|
|
||||||
return PushNotification(NotificationRequest::Warning(PermissionDenied));
|
|
||||||
}
|
}
|
||||||
|
map.insert(id, vec);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
return DataLoaded(map);
|
|
||||||
}
|
}
|
||||||
PushNotification(NotificationRequest::Warning(directory.err().unwrap()))
|
Ok(DataLoaded(map))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_web_backup(string: String) -> Result<bool, NotificationData> {
|
async fn load_web_backup(string: String) -> Result<bool, NotificationData> {
|
||||||
@@ -205,8 +203,8 @@ impl ScreenState {
|
|||||||
}
|
}
|
||||||
let db_file = db_path();
|
let db_file = db_path();
|
||||||
let temp_db_file = temp_db_path();
|
let temp_db_file = temp_db_path();
|
||||||
if std::fs::rename(temp_db_file, &db_file).is_err(){
|
if std::fs::rename(temp_db_file, &db_file).is_err() {
|
||||||
return push_error_task!(PermissionDenied)
|
return push_error_task!(PermissionDenied);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.connection = default_connection();
|
state.connection = default_connection();
|
||||||
@@ -294,11 +292,11 @@ impl ScreenState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_keyboard(&mut self, message: Event) -> Task<RootMessage> {
|
fn handle_keyboard(&mut self, message: Event) -> Task<RootMessage> {
|
||||||
if let Some(page) = self.stack.last_mut(){
|
if let Some(page) = self.stack.last_mut() {
|
||||||
return match page {
|
return match page {
|
||||||
Repetition(page) => page.press(&message),
|
Repetition(page) => page.press(&message),
|
||||||
_ => Task::none(),
|
_ => Task::none(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user