cal7tor/src/info.rs

145 lines
4.4 KiB
Rust
Raw Normal View History

2023-09-28 00:00:46 +02:00
use chrono::{DateTime, Duration, Utc};
2022-08-15 17:25:36 +02:00
use regex::{Captures, Regex};
2023-09-28 00:00:46 +02:00
use scraper::Selector;
2024-01-01 14:14:13 +01:00
use std::{collections::HashMap, sync::Arc};
2022-08-15 12:20:16 +02:00
use crate::utils::{
get_semester, get_webpage, get_year,
models::{Info, InfoList, InfoType},
};
2024-09-13 19:00:18 +02:00
pub async fn get_start_date(
2023-09-28 00:00:46 +02:00
level: i8,
semester_opt: Option<i8>,
year_opt: Option<i32>,
user_agent: &str,
2024-09-13 19:00:18 +02:00
) -> String {
2023-09-28 00:00:46 +02:00
let semester = get_semester(semester_opt);
let year = get_year(year_opt, semester);
2022-08-15 20:12:22 +02:00
2023-09-28 00:23:51 +02:00
// Fetch the timetable of the FIRST semester
let document = get_webpage(level, 1, &year, user_agent)
2023-09-28 00:00:46 +02:00
.await
.expect("Can't reach info website.");
2022-08-15 12:20:16 +02:00
2023-09-28 00:00:46 +02:00
// Selectors
let sel_b = Selector::parse("b").unwrap();
let sel_font = Selector::parse("font").unwrap();
// Find when is the back-to-school date
let raw_data = document
.select(&sel_b)
.find(|element| element.select(&sel_font).next().is_some())
.unwrap()
.inner_html();
let re = Regex::new(r"\d{1,2} (septembre|octobre)").unwrap();
2024-09-13 19:00:18 +02:00
re.captures(&raw_data)
.and_then(|caps| caps.get(0))
.map_or("1 septembre".to_owned(), |m| m.as_str().to_owned())
}
2024-09-13 19:07:18 +02:00
pub fn info(semester_opt: Option<i8>, year_opt: Option<i32>, date: &str, skip_week: bool) -> Info {
2024-09-13 19:00:18 +02:00
let semester = get_semester(semester_opt);
let year = get_year(year_opt, semester);
2023-09-28 00:00:46 +02:00
// 1st semester
let weeks_s1_1 = 6; // Weeks before break
let weeks_s1_2 = 7; // Weeks after break
let date_s1_1 = get_date(&format!("{} {}", date, year.split_once('-').unwrap().0)); // Get first week of school
let date_s1_2 = date_s1_1 + Duration::weeks(weeks_s1_1 + 1); // Back-to-school week - add week of holidays
2023-09-28 00:00:46 +02:00
// 2nd semester
let weeks_s2_1 = 11; // Weeks before break
let weeks_s2_2 = 1; // Weeks after break
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
2023-09-28 00:00:46 +02:00
// 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)];
2024-09-13 19:07:18 +02:00
let delta = i64::from(skip_week);
let tdtp_s1 = derive_from_cours(&cours_s1, delta);
let tdtp_s2 = derive_from_cours(&cours_s2, delta);
2023-09-28 00:00:46 +02:00
HashMap::from([
(
1_usize,
InfoType {
course: cours_s1,
td_tp: tdtp_s1,
},
2023-09-28 00:00:46 +02:00
),
(
2_usize,
InfoType {
course: cours_s2,
td_tp: tdtp_s2,
},
2023-09-28 00:00:46 +02:00
),
])
2022-08-15 12:20:16 +02:00
}
2022-08-15 17:25:36 +02:00
/// Find TD/TP dates, based on the ones from courses
2024-09-13 19:07:18 +02:00
fn derive_from_cours(courses: &InfoList, delta: i64) -> Vec<(DateTime<Utc>, i64)> {
// TD/TP start one week after courses
let before_break = courses.first().unwrap();
let after_break = courses.last().unwrap();
vec![
2024-09-13 19:07:18 +02:00
(
before_break.0 + Duration::weeks(delta),
before_break.1 - delta,
),
(after_break.0, after_break.1 + delta),
]
}
2022-08-15 17:25:36 +02:00
/// Turn a french date to an english one
fn anglophonization(date: &str) -> String {
let dico = HashMap::from([
("janvier", "january"),
2023-09-07 18:20:51 +02:00
("février", "february"),
2022-08-15 17:25:36 +02:00
("mars", "march"),
2023-09-07 18:20:51 +02:00
("avril", "april"),
("mai", "may"),
("juin", "june"),
("juillet", "july"),
("août", "august"),
2022-08-15 17:25:36 +02:00
("septembre", "september"),
2023-09-07 18:20:51 +02:00
("octobre", "october"),
2022-08-15 17:25:36 +02:00
("novembre", "november"),
2023-09-07 18:20:51 +02:00
("décembre", "december"),
2022-08-15 17:25:36 +02:00
]);
// New regex of all the french month
let re = Regex::new(&format!(
"({})",
dico.keys().copied().collect::<Arc<[_]>>().join("|")
2022-08-15 17:25:36 +02:00
))
.unwrap();
format!(
2023-09-07 18:29:09 +02:00
// Use 12:00 and UTC TZ for chrono parser
"{} 12:00 +0000",
2022-08-15 17:25:36 +02:00
// Replace french by english month
2024-09-13 19:00:18 +02:00
re.replace_all(&date.to_lowercase(), |cap: &Captures| match &cap[0] {
2023-09-07 18:19:29 +02:00
month if dico.contains_key(month) => dico.get(month).unwrap(),
month => {
panic!("Unknown month: {month}")
2022-08-15 17:25:36 +02:00
}
})
)
}
/// Turn a string to a `DateTime`
2022-08-15 19:06:36 +02:00
fn get_date(date: &str) -> DateTime<Utc> {
// Use and keep UTC time, we have the hour set to 12h and
2023-09-18 18:46:50 +02:00
// Paris 7 is in France so there is no problems
2023-09-07 18:29:09 +02:00
DateTime::parse_from_str(&anglophonization(date), "%e %B %Y %H:%M %z")
.unwrap()
2023-09-07 18:29:09 +02:00
.into()
}