Add anki21 format and fix multi import bug

This commit is contained in:
2026-08-15 20:16:48 +03:00
parent e676898135
commit 8f789facd7
11 changed files with 214 additions and 167 deletions
+19 -111
View File
@@ -1,7 +1,6 @@
use crate::data_provider::card_sets::{delete_set, update_card_set};
use crate::data_provider::card_stats::load_stats_of_set;
use crate::history::HistoryState;
use crate::lang::{SetOrderMode, WordData};
use crate::lang::{AppendMode, CardSetSettings, OrderMode};
use crate::navigation::Page::{History, PreviousPage, Repetition, RepetitionSettings};
use crate::navigation::{NavigatedPage, Page};
use crate::repetition::RepetitionState;
@@ -19,7 +18,6 @@ use iced::{Background, Border, Center, Color, Element, Fill, Length, Shadow, Tas
use iced_core::border::Radius;
use iced_core::svg::Handle;
use iced_core::Padding;
use rhai::{Engine, Scope};
use std::sync::{Arc, Mutex};
#[derive(Clone)]
@@ -121,6 +119,10 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
.unwrap()
.open_mode = mode;
}
SetAppendMode(mode) => {
let set = state.card_sets.get_mut(self.selected_set.unwrap()).unwrap();
set.append_mode = mode;
}
}
Task::none()
}
@@ -230,11 +232,12 @@ impl RepetitionsState {
},
radio(
"Обычный режим",
SetOrderMode::Default,
OrderMode::Default,
Some(set.open_mode),
SetOpenMode
),
self.words_words_view(&set),
// self.words_words_view(&set),
self.word_append_panel(&set),
button("История").style(jl_button).on_press(GoToHistory),
]
.spacing(DEFAULT_SPACING)
@@ -342,13 +345,13 @@ impl RepetitionsState {
container(scrollable(self.worst_words_list(set)).height(200)).style(bordered_box),
radio(
"Начать с плохих слов",
SetOrderMode::TrainWorstFirst,
OrderMode::TrainWorstFirst,
Some(set.open_mode),
SetOpenMode
),
radio(
"Полностью случайно",
SetOrderMode::FullRandom,
OrderMode::FullRandom,
Some(set.open_mode),
SetOpenMode
)
@@ -357,6 +360,13 @@ impl RepetitionsState {
.into()
}
fn word_append_panel(&self, set: &CardSetSettings) -> Element<'_, RepetitionsMessage> {
column![
text!("Режим добавления карточек"),
row![radio("Добавлять все доступные", AppendMode::Full, Some(set.append_mode), SetAppendMode), radio("Добавлять вручную", AppendMode::Manual, Some(set.append_mode), SetAppendMode)].spacing(HALF_SPACING),
].spacing(HALF_SPACING).into()
}
fn worst_words_list(&self, set: &CardSetSettings) -> Element<'_, RepetitionsMessage> {
let mut column = Column::new();
@@ -421,111 +431,9 @@ pub enum RepetitionsMessage {
SetBackward(String),
SetFilter(String),
TryFilter,
SetOpenMode(SetOrderMode),
SetOpenMode(OrderMode),
SetAppendMode(AppendMode),
GoToHistory,
GoToSettings,
}
#[derive(Clone)]
pub struct CardSetSettings {
pub id: u32,
pub name: String,
pub forward: String,
pub backward: String,
pub filter: String,
pub count: Option<usize>,
pub worst_words_list: Option<Vec<WordData>>,
pub open_mode: SetOrderMode,
}
impl CardSetSettings {
fn with_name(name: String) -> CardSetSettings {
CardSetSettings {
id: 0,
name,
forward: "".to_string(),
backward: "".to_string(),
filter: "true".to_string(),
count: None,
worst_words_list: None,
open_mode: SetOrderMode::Default,
}
}
fn check_filter(&self) -> bool {
let engine = Engine::new();
let ast = engine.compile(&self.filter);
ast.is_ok()
}
pub fn get_word_list(&self, state: &AppState) -> Vec<WordData> {
let mut list = vec![];
let engine = Engine::new();
let ast = engine.compile(&self.filter);
if ast.is_err() {
return list;
}
let ast = ast.unwrap();
let groups = &state.word_groups;
for word in &state.dictionary {
let mut more = rhai::Map::new();
for iced in &word.additional {
more.insert(iced.0.clone().into(), iced.1.clone().into());
}
let mut scope = Scope::new();
scope
.push_constant("key", word.key.clone())
.push_constant("value", word.value.clone())
.push_constant("tags", word.tags.clone())
.push_constant("more", more)
.push_constant(
"group",
groups
.iter()
.find(|g| g.id == word.group_id)
.cloned()
.unwrap()
.name,
);
let result = engine.eval_ast_with_scope::<bool>(&mut scope, &ast);
if result.is_ok() && result.unwrap() {
list.push(word.clone());
}
}
list
}
pub fn require_speech(&self) -> bool {
self.forward == "speech" || self.backward == "speech"
}
fn update_worst_words(&mut self, state: &AppState) {
if self.worst_words_list.is_some() {
return;
}
let connection = &state.connection;
let mut stats = load_stats_of_set(self, connection);
stats.sort_by_key(|s| s.calculated_score() as i32);
let avg = stats.iter().map(|s| s.calculated_score()).sum::<f32>() / stats.len() as f32;
let avg = avg * 0.7;
let bad: Vec<WordData> = stats
.iter()
.take_while(|word| word.calculated_score() < avg)
.map(|stat| {
state.dictionary[state
.dictionary
.binary_search_by_key(&stat.word_id, |x| x.id)
.unwrap()]
.clone()
})
.collect();
self.worst_words_list = Some(bad.clone());
}
}