diff --git a/src/info.rs b/src/info.rs index 2e9b1a3..fb27f17 100644 --- a/src/info.rs +++ b/src/info.rs @@ -3,14 +3,17 @@ use regex::{Captures, Regex}; use scraper::Selector; use std::{collections::HashMap, sync::Arc}; -use crate::utils::{get_semester, get_webpage, get_year}; +use crate::utils::{ + get_semester, get_webpage, get_year, + models::{Info, InfoList, InfoType}, +}; pub async fn info( level: i8, semester_opt: Option, year_opt: Option, user_agent: &str, -) -> HashMap, i64)>> { +) -> Info { let semester = get_semester(semester_opt); let year = get_year(year_opt, semester); @@ -45,18 +48,42 @@ pub async fn info( let date_s2_1 = date_s1_2 + Duration::weeks(weeks_s1_2 + 4); // Get first week - add week of 'christmas/new year holidays' let date_s2_2 = date_s2_1 + Duration::weeks(weeks_s2_1 + 2); // Back-to-school week - add week of holidays + // Group courses values and derive it for TD/TP + let cours_s1 = vec![(date_s1_1, weeks_s1_1), (date_s1_2, weeks_s1_2)]; + let cours_s2 = vec![(date_s2_1, weeks_s2_1), (date_s2_2, weeks_s2_2)]; + + let tdtp_s1 = derive_from_cours(&cours_s1); + let tdtp_s2 = derive_from_cours(&cours_s2); + HashMap::from([ ( 1_usize, - vec![(date_s1_1, weeks_s1_1), (date_s1_2, weeks_s1_2)], + InfoType { + course: cours_s1, + td_tp: tdtp_s1, + }, ), ( 2_usize, - vec![(date_s2_1, weeks_s2_1), (date_s2_2, weeks_s2_2)], + InfoType { + course: cours_s2, + td_tp: tdtp_s2, + }, ), ]) } +/// Find TD/TP dates, based on the ones from courses +fn derive_from_cours(courses: &InfoList) -> Vec<(DateTime, i64)> { + // TD/TP start one week after courses + let before_break = courses.first().unwrap(); + let after_break = courses.last().unwrap(); + vec![ + (before_break.0 + Duration::weeks(1), before_break.1 - 1), + (after_break.0, after_break.1 + 1), + ] +} + /// Turn a french date to an english one fn anglophonization(date: &str) -> String { let dico = HashMap::from([ diff --git a/src/timetable.rs b/src/timetable.rs index 45be47a..c98bf52 100644 --- a/src/timetable.rs +++ b/src/timetable.rs @@ -5,10 +5,12 @@ use std::{collections::HashMap, sync::Arc}; use crate::utils::{ self, get_hours, get_semester, get_webpage, get_year, - models::{Position, TabChar}, + models::{Info, InfoList, Position, TabChar}, Capitalize, }; +use self::models::Day; + pub mod models; /// Fetch the timetable for a class @@ -121,16 +123,8 @@ pub async fn timetable( (schedules, (semester as usize, timetable)) } -// Data builded in the info webpage -type D = HashMap< - // Semester - usize, - // List of start and repetition of course weeks - Vec<(chrono::DateTime, i64)>, ->; - /// Build the timetable -pub fn build(timetable: models::Timetable, dates: D) -> Vec { +pub fn build(timetable: models::Timetable, dates: Info) -> Vec { let mut schedules = Vec::new(); // h1 => heure de début | m1 => minute de début // h2 => heure de fin | m2 => minute de fin @@ -162,19 +156,67 @@ pub fn build(timetable: models::Timetable, dates: D) -> Vec { // Start date of the back-to-school week let datetimes = dates.get(&timetable.1 .0).unwrap(); - let before_break = datetimes.first().unwrap(); + add_courses( + &mut semester, + &schedules, + &timetable.1 .1, + &datetimes.course, + Some(vec![models::Category::Cours]), + None, + ); + add_courses( + &mut semester, + &schedules, + &timetable.1 .1, + &datetimes.td_tp, + None, + Some(vec![models::Category::Cours]), + ); + + semester +} + +type Schedule = [((u32, u32), (u32, u32))]; + +/// Add a course to the semester list +fn add_courses( + // Accumulator of courses of semester + semester: &mut Vec, + // Hours used + schedules: &Schedule, + // List of days + days: &Vec, + // Current courses list + info: &InfoList, + // List of category allowed + keep: Option>, + // List of category excluded + exclude: Option>, +) { + let before_break = info.first().unwrap(); let mut date = before_break.0; let mut rep = before_break.1; // For each weeks for _ in 0..2 { for _ in 0..rep { - for day in &timetable.1 .1 { - for mut course in day.courses.clone().into_iter().flatten() { + for day in days { + for mut course in day.courses.iter().flatten().cloned() { // Get the hours let start = schedules.get(course.start).unwrap().0; // -1 because we only add when the size is > 1 let end = schedules.get(course.start + course.size - 1).unwrap().1; + // Check keep and exclude filters + if keep + .to_owned() + .is_some_and(|list| !course.category.iter().any(|item| list.contains(item))) + || exclude.to_owned().is_some_and(|list| { + course.category.iter().all(|item| list.contains(item)) + }) + { + continue; + } + // Add the changed datetimes course.dtstart = Some( Utc.with_ymd_and_hms( @@ -206,12 +248,10 @@ pub fn build(timetable: models::Timetable, dates: D) -> Vec { // From friday to monday date += Duration::days(2); } - let after_break = datetimes.get(1).unwrap(); + let after_break = info.last().unwrap(); date = after_break.0; rep = after_break.1; } - - semester } /// Display the timetable diff --git a/src/utils/models.rs b/src/utils/models.rs index de9502c..977581b 100644 --- a/src/utils/models.rs +++ b/src/utils/models.rs @@ -1,3 +1,7 @@ +use std::collections::HashMap; + +use chrono::Utc; + /// Collection of char for the table pub enum TabChar { /// Vertical bar @@ -49,3 +53,18 @@ pub enum Position { Middle, Bottom, } + +pub type InfoList = Vec<(chrono::DateTime, i64)>; + +pub struct InfoType { + pub course: InfoList, + pub td_tp: InfoList, +} + +// Info who old the start and end of courses +pub type Info = HashMap< + // Semester + usize, + // List of start and repetition of course and TD/TP weeks + InfoType, +>;