konnektoren_core/challenges/
challenge_stats.rs1use 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}