Exam base
This commit is contained in:
1 parent
6628d79307
commit
4b9e257cdf
12 files changed
+183
-17
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
create table exams(
|
||||||
|
id integer primary key,
|
||||||
|
timestamp integer,
|
||||||
|
deck_id integer not null references decks on delete cascade
|
||||||
|
);
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
create table exam_entry
|
||||||
|
(
|
||||||
|
id integer primary key,
|
||||||
|
exam_id integer not null references exam on delete cascade,
|
||||||
|
word_id integer not null references words on delete cascade,
|
||||||
|
status integer default null
|
||||||
|
);
|
||||||
+7
-1
@@ -18,6 +18,7 @@ use log::info;
|
|||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use crate::data_provider::exam::{ExamEntryRepository, ExamRepository};
|
||||||
|
|
||||||
pub const USER_FONT: Font = Font::with_name("Noto Sans JP");
|
pub const USER_FONT: Font = Font::with_name("Noto Sans JP");
|
||||||
|
|
||||||
@@ -70,6 +71,8 @@ pub struct AppState {
|
|||||||
pub connection: Arc<Mutex<SqliteConnection>>,
|
pub connection: Arc<Mutex<SqliteConnection>>,
|
||||||
pub sync_data: Arc<RwLock<AppSettings>>,
|
pub sync_data: Arc<RwLock<AppSettings>>,
|
||||||
pub history: HistoryRepository,
|
pub history: HistoryRepository,
|
||||||
|
pub exam: ExamRepository,
|
||||||
|
pub exam_entries: ExamEntryRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppState {
|
impl Default for AppState {
|
||||||
@@ -87,7 +90,9 @@ impl AppState {
|
|||||||
word_groups: WordGroupRepository::new(connection.clone()),
|
word_groups: WordGroupRepository::new(connection.clone()),
|
||||||
card_states: CardStateRepository::new(connection.clone()),
|
card_states: CardStateRepository::new(connection.clone()),
|
||||||
history: HistoryRepository::new(connection.clone()),
|
history: HistoryRepository::new(connection.clone()),
|
||||||
|
exam: ExamRepository::new(connection.clone()),
|
||||||
custom_fields: CustomFieldRepository::new(connection.clone()),
|
custom_fields: CustomFieldRepository::new(connection.clone()),
|
||||||
|
exam_entries: ExamEntryRepository::new(connection.clone()),
|
||||||
sync_data: Arc::new(RwLock::new(AppSettings {
|
sync_data: Arc::new(RwLock::new(AppSettings {
|
||||||
key: None,
|
key: None,
|
||||||
auto_web_fetch: false,
|
auto_web_fetch: false,
|
||||||
@@ -108,7 +113,8 @@ pub fn fill_state(state: &mut AppState) {
|
|||||||
state.history.write().start().unwrap();
|
state.history.write().start().unwrap();
|
||||||
state.card_states.write().start().unwrap();
|
state.card_states.write().start().unwrap();
|
||||||
state.custom_fields.write().start().unwrap();
|
state.custom_fields.write().start().unwrap();
|
||||||
|
state.exam.write().start().unwrap();
|
||||||
|
state.exam_entries.write().start().unwrap();
|
||||||
state.sync_data = Arc::new(RwLock::new(setting))
|
state.sync_data = Arc::new(RwLock::new(setting))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -216,6 +216,40 @@ impl EntityData for CustomField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Exam {
|
||||||
|
pub id: Id,
|
||||||
|
pub timestamp: DateTime<Utc>,
|
||||||
|
pub deck_id: Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntityData for Exam {
|
||||||
|
fn id(&self) -> Id {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
fn set_id(&mut self, id: Id) {
|
||||||
|
self.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ExamEntry{
|
||||||
|
pub id: Id,
|
||||||
|
pub exam_id: Id,
|
||||||
|
pub word_id: Id,
|
||||||
|
pub status: Option<bool>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntityData for ExamEntry {
|
||||||
|
fn id(&self) -> Id {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_id(&mut self, id: Id) {
|
||||||
|
self.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct EntityHandle<E> {
|
pub struct EntityHandle<E> {
|
||||||
pub(crate) id: Id,
|
pub(crate) id: Id,
|
||||||
index: u32,
|
index: u32,
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
use crate::data::Id;
|
|
||||||
use crate::data::entities::CardState;
|
use crate::data::entities::CardState;
|
||||||
use crate::data::repositories::{DataRepository, DataRepositoryInner, Query};
|
use crate::data::repositories::{DataRepository, DataRepositoryInner, Query};
|
||||||
|
use crate::data::Id;
|
||||||
|
use crate::data_access_methods;
|
||||||
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use fsrs::MemoryState;
|
use fsrs::MemoryState;
|
||||||
use rusqlite::{Error, Params, Row};
|
use rusqlite::{Error, Params, Row};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub type CardStateRepository = DataRepository<CardState, CardStateDbLayer>;
|
data_access_methods!(
|
||||||
pub type CardStateRepositoryInner = DataRepositoryInner<CardState, CardStateDbLayer>;
|
CardStateDbLayer,
|
||||||
pub type CardStateDbLayer = SqliteDbLayer<CardState, CardStatesQuerySet>;
|
CardStateRepository,
|
||||||
|
CardStateRepositoryInner,
|
||||||
|
CardState,
|
||||||
|
CardStatesQuerySet,
|
||||||
|
CardStateQuery
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CardStatesQuerySet;
|
pub struct CardStatesQuerySet;
|
||||||
impl TableAccessQuerySet<CardState> for CardStatesQuerySet {
|
impl TableAccessQuerySet<CardState> for CardStatesQuerySet {
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ use crate::data::entities::CustomField;
|
|||||||
use crate::data::repositories::{DataRepository, DataRepositoryInner};
|
use crate::data::repositories::{DataRepository, DataRepositoryInner};
|
||||||
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
||||||
use rusqlite::{Error, Params, Row};
|
use rusqlite::{Error, Params, Row};
|
||||||
|
use crate::data_access_methods;
|
||||||
|
|
||||||
pub type CustomFieldRepository = DataRepository<CustomField, CustomFieldDbLayer>;
|
data_access_methods!(CustomFieldDbLayer, CustomFieldRepository, CustomFieldRepositoryInner, CustomField, CustomFieldsQuerySet, CustomFieldQuery);
|
||||||
pub type CustomFieldRepositoryInner = DataRepositoryInner<CustomField, CustomFieldDbLayer>;
|
|
||||||
pub type CustomFieldDbLayer = SqliteDbLayer<CustomField, CustomFieldsQuerySet>;
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CustomFieldsQuerySet;
|
pub struct CustomFieldsQuerySet;
|
||||||
impl TableAccessQuerySet<CustomField> for CustomFieldsQuerySet {
|
impl TableAccessQuerySet<CustomField> for CustomFieldsQuerySet {
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
use crate::data::entities::{Exam, ExamEntry};
|
||||||
|
use crate::data::repositories::*;
|
||||||
|
use crate::data_access_methods;
|
||||||
|
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
||||||
|
use rusqlite::{Error, Params, Row};
|
||||||
|
|
||||||
|
data_access_methods!(
|
||||||
|
ExamDataLayer,
|
||||||
|
ExamRepository,
|
||||||
|
ExamRepositoryInner,
|
||||||
|
Exam,
|
||||||
|
ExamQuerySet,
|
||||||
|
ExamQuery
|
||||||
|
);
|
||||||
|
|
||||||
|
data_access_methods!(
|
||||||
|
ExamEntryLayer,
|
||||||
|
ExamEntryRepository,
|
||||||
|
ExamEntryRepositoryInner,
|
||||||
|
ExamEntry,
|
||||||
|
ExamEntryQuerySet,
|
||||||
|
ExamEntryQuery
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ExamQuerySet;
|
||||||
|
|
||||||
|
impl TableAccessQuerySet<Exam> for ExamQuerySet {
|
||||||
|
fn load() -> &'static str {
|
||||||
|
"SELECT id, timestamp, deck_id FROM exams"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save() -> &'static str {
|
||||||
|
"UPDATE exams set deck_id = $1, timestamp = $2 WHERE id = $3"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add() -> &'static str {
|
||||||
|
"INSERT INTO exams (timestamp, deck_id) values ($1, $2)"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name() -> &'static str {
|
||||||
|
"exams"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_sql(row: &Row) -> Result<Exam, Error> {
|
||||||
|
Ok(Exam {
|
||||||
|
id: row.get(0)?,
|
||||||
|
timestamp: row.get(1)?,
|
||||||
|
deck_id: row.get(2)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_params(entity: &Exam) -> impl Params {
|
||||||
|
(entity.timestamp.timestamp(), entity.deck_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_params(entity: &Exam) -> impl Params {
|
||||||
|
(entity.timestamp.timestamp(), entity.deck_id, entity.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ExamEntryQuerySet;
|
||||||
|
|
||||||
|
impl TableAccessQuerySet<ExamEntry> for ExamEntryQuerySet {
|
||||||
|
fn load() -> &'static str {
|
||||||
|
"SELECT id, exam_id, word_id, status FROM exam_entry"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save() -> &'static str {
|
||||||
|
"UPDATE exam_entry set status = $1 WHERE id = $2"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add() -> &'static str {
|
||||||
|
"INSERT into exam_entry (exam_id, word_id, status) VALUES ($1, $2, $3)"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name() -> &'static str {
|
||||||
|
"exam_entry"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_sql(row: &Row) -> Result<ExamEntry, Error> {
|
||||||
|
Ok(ExamEntry {
|
||||||
|
id: row.get(0)?,
|
||||||
|
exam_id: row.get(1)?,
|
||||||
|
word_id: row.get(2)?,
|
||||||
|
status: row.get(3)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_params(entity: &ExamEntry) -> impl Params {
|
||||||
|
(entity.exam_id, entity.word_id, entity.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_params(entity: &ExamEntry) -> impl Params {
|
||||||
|
(entity.status, entity.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,9 @@ use crate::data::repositories::{DataRepository, DataRepositoryInner};
|
|||||||
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
||||||
use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef};
|
use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef};
|
||||||
use rusqlite::{Error, Params, Row, ToSql};
|
use rusqlite::{Error, Params, Row, ToSql};
|
||||||
|
use crate::data_access_methods;
|
||||||
|
|
||||||
pub type HistoryRepository = DataRepository<HistoryNote, HistoryDataLayer>;
|
data_access_methods!(HistoryDataLayer, HistoryRepository, HistoryRepositoryInner, HistoryNote, HistoryQuerySet, HistoryQuery);
|
||||||
pub type HistoryRepositoryInner = DataRepositoryInner<HistoryNote, HistoryDataLayer>;
|
|
||||||
pub type HistoryDataLayer = SqliteDbLayer<HistoryNote, HistoryQuerySet>;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HistoryQuerySet;
|
pub struct HistoryQuerySet;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub(crate) mod voice;
|
|||||||
pub(crate) mod web_api;
|
pub(crate) mod web_api;
|
||||||
pub mod word_groups;
|
pub mod word_groups;
|
||||||
pub(crate) mod words;
|
pub(crate) mod words;
|
||||||
|
pub(crate) mod exam;
|
||||||
|
|
||||||
pub trait TableAccessQuerySet<T> {
|
pub trait TableAccessQuerySet<T> {
|
||||||
fn load() -> &'static str;
|
fn load() -> &'static str;
|
||||||
@@ -190,3 +191,12 @@ impl<T: EntityData, Q: TableAccessQuerySet<T>> BatchedDbLayer<T> for SqliteDbLay
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! data_access_methods {
|
||||||
|
($layer:ident, $repo:ident, $repoinner:ident, $data:ty, $queryset:ty, $query:ident) => {
|
||||||
|
pub type $repo = DataRepository<$data, $layer>;
|
||||||
|
pub type $repoinner = DataRepositoryInner<$data, $layer>;
|
||||||
|
pub type $layer = SqliteDbLayer<$data, $queryset>;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,10 +2,9 @@ use crate::data::entities::WordGroup;
|
|||||||
use crate::data::repositories::{DataRepository, DataRepositoryInner};
|
use crate::data::repositories::{DataRepository, DataRepositoryInner};
|
||||||
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
||||||
use rusqlite::{Error, Params, Row};
|
use rusqlite::{Error, Params, Row};
|
||||||
|
use crate::data_access_methods;
|
||||||
|
|
||||||
pub type WordGroupRepository = DataRepository<WordGroup, WordGroupDataLayer>;
|
data_access_methods!(WordGroupDataLayer, WordGroupRepository, WordGroupRepositoryInner, WordGroup, WordGroupQuerySet, WordGroupQuery);
|
||||||
pub type WordGroupRepositoryInner = DataRepositoryInner<WordGroup, WordGroupDataLayer>;
|
|
||||||
pub type WordGroupDataLayer = SqliteDbLayer<WordGroup, WordGroupQuerySet>;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WordGroupQuerySet;
|
pub struct WordGroupQuerySet;
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ use crate::data::repositories::{DataRepository, DataRepositoryInner};
|
|||||||
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
||||||
use litemap::LiteMap;
|
use litemap::LiteMap;
|
||||||
use rusqlite::{Error, Params, Row};
|
use rusqlite::{Error, Params, Row};
|
||||||
|
use crate::data_access_methods;
|
||||||
|
|
||||||
|
data_access_methods!(WordDataLayer, WordRepository, WordRepositoryInner, WordData, WordQuerySet, WordQuery);
|
||||||
|
|
||||||
pub type WordRepository = DataRepository<WordData, WordDataLayer>;
|
|
||||||
pub type WordRepositoryInner = DataRepositoryInner<WordData, WordDataLayer>;
|
|
||||||
pub type WordDataLayer = SqliteDbLayer<WordData, WordQuerySet>;
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WordQuerySet;
|
pub struct WordQuerySet;
|
||||||
impl TableAccessQuerySet<WordData> for WordQuerySet {
|
impl TableAccessQuerySet<WordData> for WordQuerySet {
|
||||||
|
|||||||
@@ -218,6 +218,8 @@ impl SyncState {
|
|||||||
self.state.decks.write().reload()?;
|
self.state.decks.write().reload()?;
|
||||||
self.state.dictionary.write().reload()?;
|
self.state.dictionary.write().reload()?;
|
||||||
self.state.history.write().reload()?;
|
self.state.history.write().reload()?;
|
||||||
|
self.state.exam.write().reload()?;
|
||||||
|
self.state.exam_entries.write().reload()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user