1pub mod files {
6 use crate::grouper;
7 use crate::models::{Student, StudentBuilder};
8 use csv::StringRecord;
9 use serde::Deserialize;
10 use std::io::Read;
11 use std::net::TcpStream;
12 use std::path::Path;
13 use std::{
14 env,
15 fs::{create_dir, read_dir, remove_file, File},
16 io::Write,
17 };
18
19 #[derive(Deserialize, Clone)]
20 pub struct FileHandler {
21 pub temp_path: String,
22 }
23
24 impl Default for FileHandler {
25 fn default() -> Self {
26 Self::new()
27 }
28 }
29
30 impl FileHandler {
31 pub fn new() -> FileHandler {
32 FileHandler {
33 temp_path: format!(
34 "{}grouper-data",
35 env::temp_dir().to_string_lossy().into_owned()
36 ),
37 }
38 }
39
40 pub fn init_base_dir(&self) -> std::io::Result<()> {
43 let path = self.get_temp_path();
44 create_dir(path)?;
45 Ok(())
46 }
47
48 pub fn read_and_return_json(
51 &self,
52 filename: &str,
53 ) -> Result<String, Box<dyn std::error::Error>> {
54 let full_path = self.get_full_path(filename);
55
56 let file = File::open(full_path).expect("Failed to read json file");
57
58 let file_contents = Self::file_to_string(file);
59 Ok(file_contents)
60 }
61
62 pub fn read_and_return_students(&self, filename: &str) -> Result<Vec<Student>, ()> {
65 let full_path = self.get_full_path(filename);
66 let file = File::open(full_path).expect("Failed to read json file");
67
68 let file_contents = Self::file_to_string(file);
69
70 if let Ok(result) = grouper::Utils::students_from_json(&file_contents) {
71 Ok(result)
72 } else {
73 Err(())
74 }
75 }
76
77 fn file_to_string(mut file: File) -> String {
80 let mut file_contents = String::new();
81
82 file.read_to_string(&mut file_contents)
83 .expect("Failed to read json file.");
84 file_contents
85 }
86
87 fn get_full_path(&self, filename: &str) -> String {
90 let temp_path = self.get_temp_path();
91 format!("{}\\{}", &temp_path, &filename)
92 }
93
94 pub fn write_json(
97 &self,
98 data: Vec<Student>,
99 filename: &str,
100 ) -> Result<String, Box<dyn std::error::Error>> {
101 if let false = self.check_for_dir() {
102 println!("Creating base directory for Grouper Desktop user.");
103 self.init_base_dir().unwrap();
104 } else {
105 println!("Base directory found. Proceeding to write file.");
106 }
107 let write_path = format!("{}\\{}.json", self.get_temp_path(), filename);
108 let mut file = File::create(&write_path)?;
109
110 let json = serde_json::to_string(&data)?;
111 file.write_all(json.as_bytes())?;
112
113 Ok(format!(
114 "SUCCESS writing JSON to temp directory ::: @ ::: {}",
115 write_path
116 ))
117 }
118
119 pub fn delete_file(&self, filename: &str) -> Result<String, Box<dyn std::error::Error>> {
122 let path = self.get_temp_path();
123 let full_path = format!("{}\\{}", &path, &filename);
124 match remove_file(full_path) {
125 Ok(_) => Ok("File deleted successfully".to_string()),
126 Err(e) => Err(format!("Error deleting file: {}", e).into()),
127 }
128 }
129
130 fn get_temp_path(&self) -> String {
133 self.temp_path.clone()
134 }
135
136 fn check_for_dir(&self) -> bool {
139 let temp = &self.get_temp_path();
140 let path = Path::new(temp);
141 path.is_dir()
142 }
143
144 pub fn read_directory(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
147 let path = self.get_temp_path();
148 let dir = read_dir(path).unwrap();
149
150 let mut file_list: Vec<String> = Vec::new();
151
152 for file in dir {
153 let file_name = file.unwrap().file_name();
154 println!("{:?}", file_name);
155 file_list.push(file_name.into_string().expect("Failed to parse file name."));
156 }
157
158 Ok(file_list)
159 }
160 pub fn student_from_record(idx: usize, row: Result<StringRecord, csv::Error>) -> Student {
162 let r = row.expect("Unable to parse string record");
163
164 fn parse_avg(r: StringRecord) -> f32 {
165 r.get(42).unwrap().parse::<f32>().unwrap_or(0.0_f32)
166 }
167
168 StudentBuilder::new()
169 .id(idx as u32)
170 .name(r.get(0).unwrap().to_string())
171 .email(r.get(2).unwrap().to_string())
172 .avg(parse_avg(r))
173 .group(0)
174 .build()
175 }
176 pub fn network_available() -> bool {
179 TcpStream::connect("8.8.8.8:53").is_ok()
180 }
181
182 pub fn temp_data_available(&self) -> bool {
185 let path = format!("{}{}", self.get_temp_path(), "\\grouper-students.json");
186 let file_path = Path::new(&path);
187 println!("{}", &path);
188 if let true = file_path.exists() {
189 println!("path found");
190 return true;
191 }
192 false
193 }
194 }
195}
196
197pub mod grouper {
201
202 use crate::err_handle::{self};
203 use crate::models::Student;
204 use rand::Rng;
205 use std::collections::{BTreeMap, HashMap};
206
207 type Students = Vec<Student>;
210
211 #[derive(Debug, Clone)]
215 pub struct GroupsMap(BTreeMap<u16, Vec<Student>>);
216
217 impl GroupsMap {
218 pub fn new(num_students: u16, group_size: u16) -> Self {
219 let tree_map = BTreeMap::new();
220 let mut new_map = GroupsMap(tree_map);
221 new_map.populate(num_students, group_size);
222 new_map
223 }
224
225 fn populate(&mut self, num_students: u16, group_size: u16) -> &mut Self {
226 let num_groups: u16 = (num_students as f32 / group_size as f32).floor() as u16;
227
228 for num in 1..=num_groups {
229 self.0.insert(num, vec![]);
230 }
231 self
232 }
233 }
234
235 #[derive(Debug, Clone, Copy)]
239 pub struct Utils;
240
241 impl Utils {
242 fn rand_idx(vec_length: &usize) -> usize {
245 let mut rng = rand::thread_rng();
246 rng.gen_range(0..*vec_length)
247 }
248
249 pub fn num_groups(num_students: u16, group_size: u16) -> u16 {
252 let res: f32 = num_students as f32 / group_size as f32;
253 res.floor() as u16
254 }
255
256 fn mean(floats: &[f32]) -> f32 {
259 floats.iter().fold(0 as f32, |acc, n| acc + n) / floats.len() as f32
260 }
261
262 fn diffs(floats: &[f32], mean: &f32) -> Vec<f32> {
265 floats.iter().fold(vec![], |mut acc: Vec<f32>, &val| {
266 acc.push((val - mean).abs());
267 acc
268 })
269 }
270
271 fn square_all(floats: &[f32]) -> Vec<f32> {
274 floats.iter().map(|float| float.powi(2)).collect()
275 }
276
277 fn round_to_dec_count(value: f32, dec_count: i32) -> f32 {
280 let multi = 10.0_f32.powi(dec_count);
281 (value * multi).round() / multi
282 }
283
284 pub fn std_dev(floats: Vec<f32>) -> f32 {
287 let mean = Self::mean(&floats);
289 let differences = Self::diffs(&floats, &mean);
291 let all_squared: Vec<f32> = Self::square_all(&differences);
293 let mean_of_squared: f32 = Self::mean(&all_squared);
295 let sd: f32 = mean_of_squared.sqrt();
297
298 sd
299 }
300
301 pub fn sort_students(vec_of_students: &Students) -> Students {
304 let mut students = vec_of_students.clone();
305 students.sort_by(|a, b| a.avg.partial_cmp(&b.avg).unwrap());
306 students
307 }
308
309 pub fn group_avgs_map(groups: &GroupsMap) -> HashMap<u16, f32> {
312 let mut map = HashMap::new();
313
314 for (k, v) in groups.0.clone().into_iter() {
315 let group_avg = v.iter().fold(0 as f32, |mut acc, val| {
316 acc += val.avg;
317 acc
318 }) / v.len() as f32;
319 map.entry(k).or_insert(group_avg);
320 }
321
322 map
323 }
324
325 pub fn group_avgs_vec(map: HashMap<u16, f32>) -> Vec<f32> {
328 let mut avgs = vec![];
329
330 for (_, v) in map.into_iter() {
331 avgs.push(Self::round_to_dec_count(v, 2));
332 }
333
334 avgs
335 }
336
337 pub fn send_group_avgs(groups_json: String) -> Result<String, err_handle::Error> {
340 let data: Students =
341 serde_json::from_str(&groups_json).expect("Failed to parse vector from json ...");
342 println!("{:?}", data);
343
344 Ok("".into())
345 }
346
347 pub fn students_from_json(json_str: &str) -> Result<Vec<Student>, err_handle::Error> {
350 let people: Vec<Student> = serde_json::from_str(json_str)
351 .expect("Failed to parse students from json string ... ");
352 Ok(people)
353 }
354
355 pub fn treemap_to_json(
358 groups: BTreeMap<u16, Vec<Student>>,
359 ) -> Result<String, Box<dyn std::error::Error>> {
360 let json = serde_json::to_string(&groups)?;
361 Ok(json)
362 }
363
364 pub fn groups_from_json(
367 json_str: &str,
368 ) -> Result<BTreeMap<u16, Vec<Student>>, err_handle::Error> {
369 let groups: BTreeMap<u16, Vec<Student>> = serde_json::from_str(json_str)
370 .expect("Failed to parse groups from json string ... ");
371 Ok(groups)
372 }
373
374 fn get_random_student(students: &mut Students) -> (&mut Student, usize) {
377 let rand_idx = Self::rand_idx(&students.len());
378 let rand_student = &mut students[rand_idx];
379 (rand_student, rand_idx)
380 }
381
382 pub fn random_assignment(
383 current: u16,
384 mut students: Students,
385 mut groups_map: GroupsMap,
386 num_groups: u16,
387 ) -> GroupsMap {
388 if students.len() == 0 {
389 return groups_map;
390 };
391
392 let (random_student, rand_idx) = Self::get_random_student(&mut students);
393
394 let mut current_group = current;
395 random_student.set_group(current_group);
396
397 let mut new_vec = groups_map.0.get(¤t_group).unwrap().clone();
398 new_vec.push(random_student.to_owned());
399
400 groups_map.0.insert(current_group, new_vec);
401
402 if current_group == num_groups {
403 current_group = 1;
404 } else {
405 current_group += 1;
406 }
407
408 students.remove(rand_idx);
409
410 Self::random_assignment(current_group, students, groups_map, num_groups)
411
412 }
414
415 fn balance(
418 students: Vec<Student>,
419 group_size: u16,
420 _target_sd: u8,
421 ) -> BTreeMap<u16, Vec<Student>> {
422 let groups_map = GroupsMap::new(students.len() as u16, group_size);
424 let sorted = Self::sort_students(&students);
425 let num_groups = Self::num_groups(sorted.len() as u16, group_size);
426 Self::random_assignment(1, sorted, groups_map, num_groups).0
429
430 }
432
433 pub fn multi_balance(
436 _num_workers: u8,
437 students: Vec<Student>,
438 group_size: u16,
439 target_sd: u8,
440 ) -> BTreeMap<u16, Vec<Student>> {
441 Self::balance(students, group_size, target_sd)
449
450 }
452
453 }
455
456 #[cfg(test)]
457 mod tests {
458 use super::Utils;
459
460 #[test]
461 fn test_standard_deviation() {
462 let test_vector = vec![
463 79.08, 83.15, 96.23, 85.11, 90.73, 77.79, 80.34,
465 ];
466 let mean = Utils::mean(&test_vector);
468 let differences = Utils::diffs(&test_vector, &mean);
470 let all_squared: Vec<f32> = Utils::square_all(&differences);
472 let mean_of_squared: f32 = Utils::mean(&all_squared);
474 let sd: f32 = mean_of_squared.sqrt();
476
477 assert_eq!(sd, 6.2126956 as f32);
478 }
479 }
480}
481
482pub mod models {
486 use serde::{Deserialize, Serialize};
487
488 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
489 pub struct Student {
490 id: u32,
491 name: String,
492 pub avg: f32,
493 group: u16,
494 email: String,
495 }
496 impl Student {
497 pub fn set_group(&mut self, g: u16) {
498 self.group = g;
499 }
500 }
501
502 pub struct StudentBuilder {
503 id: Option<u32>,
504 name: Option<String>,
505 avg: Option<f32>,
506 group: Option<u16>,
507 email: Option<String>,
508 }
509
510 impl Default for StudentBuilder {
511 fn default() -> Self {
512 Self::new()
513 }
514 }
515
516 impl StudentBuilder {
517 pub fn new() -> Self {
518 StudentBuilder {
519 id: None,
520 name: None,
521 avg: None,
522 group: None,
523 email: None,
524 }
525 }
526
527 pub fn id(mut self, id: u32) -> Self {
528 self.id = Some(id);
529 self
530 }
531
532 pub fn name(mut self, name: String) -> Self {
533 self.name = Some(name);
534 self
535 }
536
537 pub fn avg(mut self, avg: f32) -> Self {
538 self.avg = Some(avg);
539 self
540 }
541
542 pub fn group(mut self, group: u16) -> Self {
543 self.group = Some(group);
544 self
545 }
546
547 pub fn email(mut self, email: String) -> Self {
548 self.email = Some(email);
549 self
550 }
551
552 pub fn build(self) -> Student {
553 Student {
554 id: self.id.unwrap(),
555 name: self.name.unwrap(),
556 avg: self.avg.unwrap(),
557 group: self.group.unwrap(),
558 email: self.email.unwrap(),
559 }
560 }
561 }
562}
563
564pub mod mutant {}
568
569pub mod err_handle {
573 use std::fmt;
574
575 #[derive(Debug, thiserror::Error)]
576
577 pub enum Error {
578 #[error(transparent)]
579 Io(#[from] std::io::Error),
580 Std(#[from] Box<dyn std::error::Error>),
581 }
582
583 impl serde::Serialize for Error {
584 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
585 where
586 S: serde::ser::Serializer,
587 {
588 serializer.serialize_str(self.to_string().as_ref())
589 }
590 }
591
592 impl fmt::Display for Error {
593 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
594 write!(f, "Error reading file ... ")
595 }
596 }
597}
598
599pub use err_handle::Error;
600pub use files::FileHandler;
601pub use grouper::{GroupsMap, Utils};
602pub use models::{Student, StudentBuilder};