use std::collections::HashMap;
use time::{Duration, OffsetDateTime};
use crate::{noun_attempt::NounAttemptSaved, nouns::SavedNoun};
pub struct History {
pub nouns: Vec<SavedNoun>,
pub attempts: Vec<NounAttemptSaved>,
}
impl History {
#[must_use]
pub fn attempt_per_noun(&self) -> Vec<(SavedNoun, Vec<NounAttemptSaved>)> {
let n: Vec<_> = self
.nouns
.iter()
.map(|n| {
let attempts: Vec<_> = self
.attempts
.iter()
.filter_map(|s| {
if s.noun_attempt.for_word == n.id {
Some(s.clone())
} else {
None
}
})
.collect();
(n.to_owned(), attempts)
})
.collect();
n
}
#[must_use]
pub fn confidence_map(&self) -> Vec<(SavedNoun, u8)> {
self.attempt_per_noun()
.iter()
.map(|s| {
let confidence =
s.1.iter()
.fold(0u8, |confidence, a| match a.noun_attempt.what_happened {
crate::noun_attempt::Conclusion::Success => match confidence {
u8::MAX => confidence,
_ => confidence + 1,
},
crate::noun_attempt::Conclusion::WrongArticle(_) => match confidence {
u8::MIN => confidence,
_ => confidence - 1,
},
});
(s.0.clone(), confidence)
})
.collect()
}
#[must_use]
pub fn grouped_confidence_map(&self) -> HashMap<String, Vec<(SavedNoun, u8)>> {
self.confidence_map().into_iter().fold(
HashMap::new(),
|mut acc: HashMap<String, Vec<(SavedNoun, u8)>>, (n, c)| {
match acc.get_mut(&n.noun.group) {
Some(cu) => cu.push((n, c)),
None => _ = acc.insert(n.noun.group.clone(), vec![(n, c)]),
};
acc
},
)
}
#[must_use]
pub fn next(&self) -> Option<(SavedNoun, u8)> {
let mut sets = self.confidence_map();
sets.sort_unstable_by_key(|s| s.1);
sets.first().cloned()
}
#[must_use]
pub fn next_group(&self, threshold: u8) -> Option<Vec<(SavedNoun, u8)>> {
let sets = self.grouped_confidence_map();
if sets.is_empty() {
return None;
}
let under_threshold: HashMap<_, _> = sets
.into_iter()
.filter(|(_, nouns)| nouns.iter().any(|(_, c)| c <= &threshold))
.collect();
if under_threshold.is_empty() {
return self.next_group(u8::MAX);
}
let mut ks: Vec<_> = under_threshold.keys().collect();
ks.sort_unstable();
let mut left = under_threshold.get(ks.first()?.to_owned())?.to_owned();
left.sort_unstable_by_key(|s| s.1);
Some(left)
}
#[must_use]
pub fn next_with_group(&self, threshold: u8) -> Option<(SavedNoun, u8)> {
let sets = self.grouped_confidence_map();
if sets.is_empty() {
return None;
}
let under_threshold: HashMap<_, _> = sets
.into_iter()
.filter(|(_, nouns)| nouns.iter().any(|(_, c)| c <= &threshold))
.collect();
if under_threshold.is_empty() {
return self.next_with_group(u8::MAX);
}
let mut ks: Vec<_> = under_threshold.keys().collect();
ks.sort_unstable();
let mut left = under_threshold.get(ks.first()?.to_owned())?.to_owned();
left.sort_unstable_by_key(|s| s.1);
left.first().cloned()
}
pub fn number_of_words_at(
&self,
from_time: OffsetDateTime,
duration: Duration,
) -> miette::Result<usize> {
let to_time = from_time.checked_add(duration).ok_or_else(|| {
miette::miette!("duration could not be added to the time for some reason!")
})?;
let found_attempts = self
.attempts
.iter()
.filter(|a| a.at.ge(&from_time) && a.at.lt(&to_time))
.fold(vec![], |mut accu, att| {
if !accu.contains(&att.noun_attempt.for_word) {
accu.push(att.noun_attempt.for_word.clone());
}
accu
});
Ok(found_attempts.len())
}
}