1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use level::{Level, Quality};
use serde::{Deserialize, Serialize};
use ssr_core::task::{level::TaskLevel, Feedback, InterationItem, Task, UserInteraction};
use std::{collections::HashSet, time::SystemTime};

mod level;

#[derive(Serialize, Deserialize)]
pub struct WriteAnswer {
    level: Level,
    description: String,
    correct_answers: HashSet<String>,
    explanation: Option<String>,
}

impl WriteAnswer {
    pub fn new(
        description: String,
        correct_answers: impl IntoIterator<Item = String>,
        explanation: Option<String>,
    ) -> Self {
        Self {
            level: Default::default(),
            description,
            correct_answers: correct_answers.into_iter().collect(),
            explanation,
        }
    }
}

impl<'a> Task<'a> for WriteAnswer {
    type SharedState = ();
    fn get_desctiption(&self) -> &str {
        &self.description
    }

    fn next_repetition(&self, retrievability_goal: f64) -> SystemTime {
        self.level.next_repetition(retrievability_goal)
    }

    fn complete(mut self, _: &mut (), interaction: &mut impl UserInteraction) -> (Self, Feedback) {
        let user_answer = {
            let mut user_answer = interaction.interact(vec![
                InterationItem::Text(self.description.clone()),
                InterationItem::BlankField,
            ]);
            assert!(user_answer.len() == 1);
            user_answer.swap_remove(0)
        };
        match self.correct_answers.contains(&user_answer) {
            false => {
                let items = [
                    Quality::CompleteBlackout,
                    Quality::IncorrectResponseButCorrectRemembered,
                    Quality::IncorrectResponseAndSeemedEasyToRecall,
                ];
                let quality = items[interaction.interact(vec![
                    InterationItem::Text("choose difficulty".to_string()),
                    InterationItem::OneOf(vec![
                        (0, "complete blackout".to_string()),
                        (1, "incorrect response, but correct remembered".to_string()),
                        (
                            2,
                            "incorrect response, but seemed easy to recall".to_string(),
                        ),
                    ]),
                ])[0]
                    .parse::<usize>()
                    .unwrap()];
                self.level.update(&mut (), (SystemTime::now(), quality));
                let correct_answers = self.correct_answers.clone().into_iter().collect();
                let explanation = self.explanation.clone();
                (
                    self,
                    Feedback::WrongAnswer {
                        correct_answers,
                        explanation,
                    },
                )
            }
            true => {
                let items = [
                    Quality::CorrectResponseRecalledWithSeriousDifficulty,
                    Quality::CorrectResponseAfterHesitation,
                    Quality::PerfectResponse,
                ];
                let quality = items[interaction.interact(vec![
                    InterationItem::Text("choose difficulty".to_string()),
                    InterationItem::OneOf(vec![
                        (0, "recalled with serious difficulty".to_string()),
                        (1, "correct, but after hesitation".to_string()),
                        (2, "perfect response".to_string()),
                    ]),
                ])[0]
                    .parse::<usize>()
                    .unwrap()];
                self.level.update(&mut (), (SystemTime::now(), quality));
                (self, Feedback::CorrectAnswer)
            }
        }
    }
}