konnektoren_core/challenges/
challenge_stats.rs

1use crate::challenges::{ChallengeConfig, ChallengeHistory, Performance};
2
3pub trait ChallengeStats {
4    fn challenges(&self) -> usize;
5
6    fn completed_challenges(&self) -> usize;
7
8    fn stars(&self) -> u32;
9
10    fn performance(&self) -> u32;
11
12    fn solved(&self) -> bool;
13}
14
15impl ChallengeStats for ChallengeHistory {
16    fn challenges(&self) -> usize {
17        self.len()
18    }
19
20    fn completed_challenges(&self) -> usize {
21        self.challenges.iter().filter(|c| c.solved()).count()
22    }
23
24    fn stars(&self) -> u32 {
25        let performance = self.performance();
26        if performance >= 80 {
27            3
28        } else if performance >= 60 {
29            2
30        } else if performance >= 40 {
31            1
32        } else {
33            0
34        }
35    }
36
37    fn performance(&self) -> u32 {
38        let challenges = self.challenges();
39        if challenges == 0 {
40            return 0;
41        }
42        let completed_challenges = self.completed_challenges();
43        (completed_challenges as f32 / challenges as f32 * 100.0) as u32
44    }
45
46    fn solved(&self) -> bool {
47        self.challenges.iter().all(|c| c.solved())
48    }
49}
50
51impl ChallengeStats for (&ChallengeConfig, &ChallengeHistory) {
52    fn challenges(&self) -> usize {
53        self.1
54            .challenges
55            .iter()
56            .filter(|c| c.challenge_config.id == self.0.id)
57            .count()
58    }
59
60    fn completed_challenges(&self) -> usize {
61        self.1
62            .challenges
63            .iter()
64            .filter(|c| {
65                c.challenge_config.id == self.0.id
66                    && c.challenge_result.len() == c.challenge_config.tasks.len()
67            })
68            .count()
69    }
70
71    fn stars(&self) -> u32 {
72        let performance = self.performance();
73        if performance >= 80 {
74            3
75        } else if performance >= 60 {
76            2
77        } else if performance >= 40 {
78            1
79        } else {
80            0
81        }
82    }
83
84    fn performance(&self) -> u32 {
85        let challenges = self.challenges();
86        if challenges == 0 {
87            return 0;
88        }
89        let total_performance: u32 = self
90            .1
91            .challenges
92            .iter()
93            .map(|c| c.performance(&c.challenge_result))
94            .sum();
95        total_performance / challenges as u32
96    }
97
98    fn solved(&self) -> bool {
99        let challenge_id = self.0.id.clone();
100        self.1
101            .challenges
102            .iter()
103            .filter(|c| c.challenge_config.id == challenge_id)
104            .count()
105            > 0
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use crate::challenges::challenge::Challenge;
113    use crate::challenges::challenge_config::ChallengeConfig;
114    use crate::challenges::challenge_type::ChallengeType;
115    use crate::challenges::{ChallengeResult, MultipleChoiceOption};
116
117    #[test]
118    fn test_challenges() {
119        let mut challenge_history = ChallengeHistory::new();
120        assert_eq!(challenge_history.challenges(), 0);
121
122        let challenge = Challenge::new(&ChallengeType::default(), &ChallengeConfig::default());
123        challenge_history.add_challenge(challenge);
124        assert_eq!(challenge_history.challenges(), 1);
125    }
126
127    #[test]
128    fn test_completed_challenges() {
129        let mut challenge_history = ChallengeHistory::new();
130        assert_eq!(challenge_history.completed_challenges(), 0);
131
132        let mut config = ChallengeConfig::default();
133        config.tasks = 3.into();
134        let challenge = Challenge::new(&ChallengeType::default(), &config);
135        challenge_history.add_challenge(challenge);
136        assert_eq!(challenge_history.completed_challenges(), 0);
137
138        let mut challenge = Challenge::new(&ChallengeType::default(), &config);
139        challenge.challenge_result = ChallengeResult::MultipleChoice(vec![
140            MultipleChoiceOption {
141                id: 1,
142                name: "Option 1".to_string(),
143            },
144            MultipleChoiceOption {
145                id: 2,
146                name: "Option 2".to_string(),
147            },
148            MultipleChoiceOption {
149                id: 3,
150                name: "Option 3".to_string(),
151            },
152        ]);
153        challenge_history.add_challenge(challenge);
154        assert_eq!(challenge_history.completed_challenges(), 1);
155    }
156
157    #[test]
158    fn test_stars() {
159        let mut challenge_history = ChallengeHistory::new();
160        assert_eq!(challenge_history.stars(), 0);
161
162        let mut config = ChallengeConfig::default();
163        config.tasks = 3.into();
164
165        let mut challenge = Challenge::new(&ChallengeType::default(), &config);
166        challenge.challenge_result = ChallengeResult::MultipleChoice(vec![
167            MultipleChoiceOption {
168                id: 1,
169                name: "Option 1".to_string(),
170            },
171            MultipleChoiceOption {
172                id: 2,
173                name: "Option 2".to_string(),
174            },
175            MultipleChoiceOption {
176                id: 3,
177                name: "Option 3".to_string(),
178            },
179        ]);
180        challenge_history.add_challenge(challenge);
181
182        assert!(challenge_history.stars() > 0);
183    }
184
185    #[test]
186    fn test_tuple_challenge_stats() {
187        let config = ChallengeConfig::default();
188        let mut history = ChallengeHistory::new();
189        let challenge = Challenge::new(&ChallengeType::default(), &config);
190        history.add_challenge(challenge);
191        let tuple_stats = (&config, &history);
192        assert_eq!(tuple_stats.challenges(), 1);
193        assert!(tuple_stats.solved());
194    }
195}