Rewrite history page
This commit is contained in:
1 parent
f19a6f64c3
commit
532640058e
11 files changed
+201
-230
No files matched your search
@@ -1,97 +1,92 @@
|
||||
use crate::core::srs::WordOpenMode;
|
||||
use crate::data::Id;
|
||||
use crate::data::entities::HistoryItem;
|
||||
use crate::fs::*;
|
||||
use crate::notification::NotificationData::PermissionDenied;
|
||||
use crate::notification::{MaybeError, NotificationData};
|
||||
use chrono::{DateTime, NaiveDate};
|
||||
use rusqlite::Connection;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
use crate::data::entities::HistoryNote;
|
||||
use crate::data::repositories::{DataRepository, DataRepositoryInner};
|
||||
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
||||
use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef};
|
||||
use rusqlite::{Error, Params, Row, ToSql};
|
||||
|
||||
pub fn get_open_count_of_set(
|
||||
id: Id,
|
||||
connection: &Connection,
|
||||
) -> Result<Vec<(NaiveDate, u32)>, NotificationData> {
|
||||
todo!()
|
||||
}
|
||||
pub type HistoryRepository = DataRepository<HistoryNote, HistoryDataLayer>;
|
||||
pub type HistoryRepositoryInner = DataRepositoryInner<HistoryNote, HistoryDataLayer>;
|
||||
pub type HistoryDataLayer = SqliteDbLayer<HistoryNote, HistoryQuerySet>;
|
||||
|
||||
pub fn get_history_of_set(id: Id) -> Result<Vec<HistoryItem>, NotificationData> {
|
||||
let app_dir = history_dir()?;
|
||||
let head = app_dir.clone().join(format!("set_{}_history.csv", id));
|
||||
let mut lines = Vec::new();
|
||||
|
||||
append_lines_if_exists(head, &mut lines)?;
|
||||
lines.reverse();
|
||||
Ok(parse_history_items(lines))
|
||||
}
|
||||
|
||||
fn append_lines_if_exists(path: PathBuf, lines: &mut Vec<String>) -> MaybeError {
|
||||
if path.exists() {
|
||||
let reader = BufReader::new(File::open(path).map_err(|_| PermissionDenied)?);
|
||||
reader
|
||||
.lines()
|
||||
.map_while(Result::ok)
|
||||
.for_each(|i| lines.push(i));
|
||||
#[derive(Clone)]
|
||||
pub struct HistoryQuerySet;
|
||||
impl TableAccessQuerySet<HistoryNote> for HistoryQuerySet {
|
||||
fn load() -> &'static str {
|
||||
"SELECT rowid, set_id, state_id, timestamp, answer, before, after, fsrs FROM history"
|
||||
}
|
||||
|
||||
fn save() -> &'static str {
|
||||
todo!("History is readonly")
|
||||
}
|
||||
|
||||
fn add() -> &'static str {
|
||||
"INSERT INTO history (set_id, state_id, timestamp, answer, before, after, fsrs) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
"history"
|
||||
}
|
||||
|
||||
fn from_sql(row: &Row) -> Result<HistoryNote, Error> {
|
||||
Ok(HistoryNote {
|
||||
id: row.get(0)?,
|
||||
timestamp: row.get(3)?,
|
||||
set_id: row.get(1)?,
|
||||
word_id: row.get(2)?,
|
||||
answer: row.get(4)?,
|
||||
before: row.get(5)?,
|
||||
after: row.get(6)?,
|
||||
fsrs: row.get(7)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn insert_params(entity: &HistoryNote) -> impl Params {
|
||||
(
|
||||
entity.set_id,
|
||||
entity.word_id,
|
||||
entity.timestamp,
|
||||
entity.answer,
|
||||
entity.before,
|
||||
entity.after,
|
||||
entity.fsrs,
|
||||
)
|
||||
}
|
||||
|
||||
fn update_params(entity: &HistoryNote) -> impl Params {
|
||||
(
|
||||
entity.set_id,
|
||||
entity.word_id,
|
||||
entity.timestamp,
|
||||
entity.answer,
|
||||
entity.before,
|
||||
entity.after,
|
||||
entity.fsrs,
|
||||
)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_history_items(strings: Vec<String>) -> Vec<HistoryItem> {
|
||||
let mut items = Vec::with_capacity(strings.len());
|
||||
for string in strings {
|
||||
if let [time, word, mode, before, after] = string.split(';').collect::<Vec<&str>>()[..] {
|
||||
let id = word.parse::<Id>();
|
||||
if id.is_err() {
|
||||
continue;
|
||||
impl FromSql for WordOpenMode {
|
||||
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
if let ValueRef::Integer(s) = value {
|
||||
return match s {
|
||||
1 => Ok(WordOpenMode::Hard),
|
||||
2 => Ok(WordOpenMode::Ok),
|
||||
3 => Ok(WordOpenMode::Easy),
|
||||
_ => Ok(WordOpenMode::None),
|
||||
}
|
||||
let id = id.unwrap_or_default();
|
||||
if !id.is_valid() {
|
||||
continue;
|
||||
}
|
||||
items.push(HistoryItem {
|
||||
timestamp: DateTime::from_timestamp(time.parse().unwrap_or_default(), 0)
|
||||
.unwrap_or_default(),
|
||||
word_id: id,
|
||||
mode: match mode.parse::<u8>().unwrap_or_default() {
|
||||
2 => WordOpenMode::Hard,
|
||||
3 => WordOpenMode::Ok,
|
||||
4 => WordOpenMode::Easy,
|
||||
_ => WordOpenMode::None,
|
||||
},
|
||||
before: before.parse().unwrap_or_default(),
|
||||
after: after.parse().unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
Ok(WordOpenMode::None)
|
||||
}
|
||||
|
||||
items
|
||||
}
|
||||
|
||||
pub fn push_note(set_id: Id, item: HistoryItem) -> MaybeError {
|
||||
let app_dir = history_dir()?;
|
||||
let path = app_dir.clone().join(format!("set_{}_history.csv", set_id));
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(path)
|
||||
.map_err(|_| PermissionDenied)?;
|
||||
let line_str = format!(
|
||||
"{};{};{};{};{}",
|
||||
item.timestamp.timestamp(),
|
||||
item.word_id,
|
||||
match item.mode {
|
||||
WordOpenMode::Easy => 4,
|
||||
WordOpenMode::Ok => 3,
|
||||
WordOpenMode::Hard => 2,
|
||||
WordOpenMode::None => 1,
|
||||
},
|
||||
item.before,
|
||||
item.after
|
||||
);
|
||||
writeln!(&mut file, "{}", line_str).map_err(|_| PermissionDenied)?;
|
||||
Ok(())
|
||||
}
|
||||
impl ToSql for WordOpenMode {
|
||||
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
|
||||
Ok(ToSqlOutput::from(match self {
|
||||
WordOpenMode::Easy => 3,
|
||||
WordOpenMode::Ok => 2,
|
||||
WordOpenMode::Hard => 1,
|
||||
WordOpenMode::None => 0
|
||||
}))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user