From 0ff90b39deb2ff120c8d43af8651567d2d7a81a4 Mon Sep 17 00:00:00 2001 From: albugowy15 Date: Thu, 1 Feb 2024 21:19:44 +0700 Subject: [PATCH] feat: LecturerSubjectSessionMap struct --- Cargo.lock | 53 +++++++--------------- Cargo.toml | 8 +++- src/commands/compare.rs | 11 +---- src/commands/mod.rs | 40 ++++++---------- src/commands/update.rs | 5 +- src/db/repository/mod.rs | 29 ++++++++++++ src/main.rs | 2 +- src/utils/env.rs | 1 - src/utils/excel/mod.rs | 15 ++---- src/utils/excel/schedule_parser.rs | 22 ++++++--- src/utils/excel/schedule_parser_with_id.rs | 21 ++++++--- 11 files changed, 106 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a474b68..bddad64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,9 +47,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -486,17 +486,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_logger" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8" dependencies = [ + "anstream", + "anstyle", + "env_filter", "humantime", - "is-terminal", "log", - "regex", - "termcolor", ] [[package]] @@ -836,17 +846,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", -] - [[package]] name = "itertools" version = "0.12.0" @@ -1856,15 +1855,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "termcolor" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "1.0.51" @@ -2085,15 +2075,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 103b388..4f39418 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,11 @@ tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros"] } cuid = "1.3.2" clap = { version = "4.4.11", features = ["derive"] } anyhow = "1.0.76" -sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-native-tls","mysql" ] } +sqlx = { version = "0.7", features = [ + "runtime-tokio", + "tls-native-tls", + "mysql", +] } indicatif = "0.17.7" log = "0.4.20" -env_logger = "0.10.1" +env_logger = "0.11.1" diff --git a/src/commands/compare.rs b/src/commands/compare.rs index f22a28e..5720866 100644 --- a/src/commands/compare.rs +++ b/src/commands/compare.rs @@ -1,11 +1,10 @@ use std::path::PathBuf; use crate::{ - commands::prepare_data, db::{ repository::{ class::{ClassFromSchedule, ClassRepository}, - Repository, + prepare_data, Repository, }, Connection, }, @@ -35,13 +34,7 @@ pub async fn compare_handler(file: &PathBuf, sheet: &str, outdir: &PathBuf) { }; log::info!("Get latest schedule from Excel"); - let excel = match Excel::new( - file, - sheet, - repo_data_res.0, - repo_data_res.1, - repo_data_res.2, - ) { + let excel = match Excel::new(file, sheet, repo_data_res) { Ok(excel) => excel, Err(e) => { log::error!("Error opening excel file: {}", e); diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 8dabdf2..af7f2c6 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,13 +1,6 @@ -use std::{collections::HashMap, path::PathBuf}; +use std::path::PathBuf; -use anyhow::Result; use clap::Subcommand; -use sqlx::{MySql, Pool}; - -use crate::db::repository::{ - lecturer::LecturerRepository, session::SessionRepository, subject::SubjectRepository, - Repository, -}; pub mod clean; pub mod compare; @@ -16,6 +9,9 @@ pub mod update; #[derive(Subcommand)] pub enum Commands { + #[command( + long_about = "Compares the class schedule stored in the database with the latest data from an Excel file." + )] Compare { #[arg(short, long, value_name = "Required for latest schedule excel file")] file: PathBuf, @@ -26,6 +22,9 @@ pub enum Commands { #[arg(short, long, value_name = "Required for output path")] outdir: PathBuf, }, + #[command( + long_about = "Parses all class data from an Excel file and subsequently updates the MySQL database. Alternatively, it provides an option to save the parsed data to an SQL file." + )] Update { #[arg( short, @@ -47,25 +46,12 @@ pub enum Commands { )] outdir: Option, }, + #[command( + long_about = "Removes any invalid foreign keys present in the _ClassToPlan and _ClassToLecturer tables." + )] Clean, + #[command( + long_about = "Synchronizes the taken field in the Class table and the totalSks field in the Plan table to reflect their current values." + )] Sync, } - -pub async fn prepare_data( - pool: &Pool, -) -> Result<( - HashMap, - HashMap, - HashMap, -)> { - log::info!("Get all subjects from DB"); - let lecturer_repo = LecturerRepository::new(pool); - let subject_repo = SubjectRepository::new(pool); - let session_repo = SessionRepository::new(pool); - let (subjects, lecturers, sessions) = tokio::try_join!( - subject_repo.get_all_subjects(), - lecturer_repo.get_all_lecturers(), - session_repo.get_all_sessions() - )?; - Ok((subjects, lecturers, sessions)) -} diff --git a/src/commands/update.rs b/src/commands/update.rs index c24c822..c5d45c9 100644 --- a/src/commands/update.rs +++ b/src/commands/update.rs @@ -1,9 +1,8 @@ use std::{path::PathBuf, sync::Arc}; use crate::{ - commands::prepare_data, db::{ - repository::{class::ClassRepository, Repository}, + repository::{class::ClassRepository, prepare_data, Repository}, Connection, }, utils::{ @@ -28,7 +27,7 @@ pub async fn update_handler(push: &bool, file: &PathBuf, sheet: &String, outdir: } }; log::info!("Parse class schedule from Excel"); - let excel = match Excel::new(file, sheet, repo_data.0, repo_data.1, repo_data.2) { + let excel = match Excel::new(file, sheet, repo_data) { Ok(excel) => excel, Err(e) => { log::error!( diff --git a/src/db/repository/mod.rs b/src/db/repository/mod.rs index 15fda5d..3d335cd 100644 --- a/src/db/repository/mod.rs +++ b/src/db/repository/mod.rs @@ -1,4 +1,10 @@ +use anyhow::Result; use sqlx::{MySql, Pool}; +use std::collections::HashMap; + +use crate::db::repository::{ + lecturer::LecturerRepository, session::SessionRepository, subject::SubjectRepository, +}; pub mod class; pub mod lecturer; @@ -10,3 +16,26 @@ pub mod subject; pub trait Repository<'a> { fn new(db_pool: &'a Pool) -> Self; } + +pub struct LecturerSubjectSessionMap { + pub subjects: HashMap, + pub lecturers: HashMap, + pub sessions: HashMap, +} + +pub async fn prepare_data(pool: &Pool) -> Result { + log::info!("Get all subjects from DB"); + let lecturer_repo = LecturerRepository::new(pool); + let subject_repo = SubjectRepository::new(pool); + let session_repo = SessionRepository::new(pool); + let (subjects, lecturers, sessions) = tokio::try_join!( + subject_repo.get_all_subjects(), + lecturer_repo.get_all_lecturers(), + session_repo.get_all_sessions() + )?; + Ok(LecturerSubjectSessionMap { + subjects, + lecturers, + sessions, + }) +} diff --git a/src/main.rs b/src/main.rs index d896415..03ed9d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use auto_frs_schedule::{ use clap::Parser; #[derive(Parser)] -#[command(version, about)] +#[command(version, author, about)] struct Cli { #[command(subcommand)] command: Commands, diff --git a/src/utils/env.rs b/src/utils/env.rs index d40be8b..cc0c3d2 100644 --- a/src/utils/env.rs +++ b/src/utils/env.rs @@ -10,7 +10,6 @@ pub fn setup_env() { env::set_var("AUTO_FRS_SCHEDULE_LOG_LEVEL", "INFO"); env::set_var("AUTO_FRS_SCHEDULE_LOG_STYLE", "AUTO"); Builder::from_env(env) - .format_timestamp(None) .format_module_path(false) .format_target(false) .init(); diff --git a/src/utils/excel/mod.rs b/src/utils/excel/mod.rs index 698c302..da9ed46 100644 --- a/src/utils/excel/mod.rs +++ b/src/utils/excel/mod.rs @@ -3,37 +3,32 @@ pub mod retrieve; pub mod schedule_parser; pub mod schedule_parser_with_id; -use std::collections::HashMap; use std::path::PathBuf; use anyhow::{Context, Result}; use calamine::{open_workbook, DataType, Range, Reader, Xlsx}; +use crate::db::repository::LecturerSubjectSessionMap; + pub const DAYS: [&str; 5] = ["Senin", "Selasa", "Rabu", "Kamis", "Jum'at"]; pub struct Excel { range: Range, - subject_to_id: HashMap, - lecturer_to_id: HashMap, - session_to_id: HashMap, + lecturer_subjects_session_map: LecturerSubjectSessionMap, } impl Excel { pub fn new( file_path: &PathBuf, sheet_name: &str, - subject_to_id: HashMap, - lecturer_to_id: HashMap, - session_to_id: HashMap, + lecturer_subjects_session_map: LecturerSubjectSessionMap, ) -> Result { let mut excel: Xlsx<_> = open_workbook(file_path).with_context(|| "Cannot open excel file")?; let range = excel.worksheet_range(sheet_name)?; Ok(Self { range, - subject_to_id, - lecturer_to_id, - session_to_id, + lecturer_subjects_session_map, }) } } diff --git a/src/utils/excel/schedule_parser.rs b/src/utils/excel/schedule_parser.rs index 3eccc89..4ed8e56 100644 --- a/src/utils/excel/schedule_parser.rs +++ b/src/utils/excel/schedule_parser.rs @@ -5,7 +5,8 @@ use super::{AsStringParser, Excel, Parser, Retrieve, ScheduleParser, SessionPars impl AsStringParser for Excel { fn get_subject_with_code(&self, val: &str) -> Option<(String, String)> { let (subject_name, code) = Self::parse_subject_with_code_2(val)?; - self.subject_to_id + self.lecturer_subjects_session_map + .subjects .get(&subject_name.to_lowercase()) .map(|_| (subject_name, code)) } @@ -16,7 +17,11 @@ impl AsStringParser for Excel { let lecturers_code: Vec = lecturers .into_iter() .flat_map(|lecture_code| { - let code = match self.lecturer_to_id.contains_key(lecture_code.trim()) { + let code = match self + .lecturer_subjects_session_map + .lecturers + .contains_key(lecture_code.trim()) + { true => lecture_code.trim().to_string(), false => "UNK".to_string(), }; @@ -35,7 +40,8 @@ impl SessionParser for Excel { fn get_session(&self, row_idx: u32) -> Option { let session_str = self.retrieve_session(row_idx)?; let session_name = Excel::parse_session(&session_str)?; - self.session_to_id + self.lecturer_subjects_session_map + .sessions .contains_key(&session_name) .then_some(session_name) } @@ -82,6 +88,8 @@ mod tests { use calamine::Range; + use crate::db::repository::LecturerSubjectSessionMap; + use super::*; #[test] @@ -94,9 +102,11 @@ mod tests { ); let excel = Excel { - subject_to_id, - lecturer_to_id: HashMap::new(), - session_to_id: HashMap::new(), + lecturer_subjects_session_map: LecturerSubjectSessionMap { + subjects: subject_to_id, + lecturers: HashMap::new(), + sessions: HashMap::new(), + }, range: Range::new((0, 0), (100, 100)), }; diff --git a/src/utils/excel/schedule_parser_with_id.rs b/src/utils/excel/schedule_parser_with_id.rs index 2fedb08..9b1aa5d 100644 --- a/src/utils/excel/schedule_parser_with_id.rs +++ b/src/utils/excel/schedule_parser_with_id.rs @@ -5,7 +5,8 @@ use super::{AsIdParser, Excel, Parser, Retrieve, ScheduleParser, SessionParser, impl AsIdParser for Excel { fn get_subject_id_with_code(&self, val: &str) -> Option<(String, String)> { let (subject_name, code) = Self::parse_subject_with_code_2(val)?; - self.subject_to_id + self.lecturer_subjects_session_map + .subjects .get(&subject_name.to_lowercase()) .map(|val| (val.to_string(), code)) } @@ -16,7 +17,8 @@ impl AsIdParser for Excel { let lecturers_id: Vec = lecturers .into_iter() .map(|lecture_code| { - self.lecturer_to_id + self.lecturer_subjects_session_map + .lecturers .get(lecture_code.trim()) .unwrap_or(&"UNK".to_string()) .to_string() @@ -31,7 +33,10 @@ impl SessionParser for Excel { fn get_session(&self, row_idx: u32) -> Option { let session_str = self.retrieve_session(row_idx)?; let session_name = Excel::parse_session(&session_str)?; - self.session_to_id.get(&session_name).cloned() + self.lecturer_subjects_session_map + .sessions + .get(&session_name) + .cloned() } } @@ -77,6 +82,8 @@ mod tests { use calamine::Range; + use crate::db::repository::LecturerSubjectSessionMap; + use super::*; #[test] @@ -89,9 +96,11 @@ mod tests { ); let excel = Excel { - subject_to_id, - lecturer_to_id: HashMap::new(), - session_to_id: HashMap::new(), + lecturer_subjects_session_map: LecturerSubjectSessionMap { + subjects: subject_to_id, + lecturers: HashMap::new(), + sessions: HashMap::new(), + }, range: Range::new((0, 0), (100, 100)), };