find the right timetable for the current semester/year combo

This commit is contained in:
Mylloon 2023-09-18 19:50:30 +02:00
parent ade74b4e4c
commit 16a9bba6dd
Signed by: Anri
GPG key ID: A82D63DFF8D1317F
3 changed files with 64 additions and 108 deletions

View file

@ -9,14 +9,18 @@ mod utils;
#[derive(Parser)] #[derive(Parser)]
#[clap(version, about, long_about = None)] #[clap(version, about, long_about = None)]
struct Args { struct Args {
/// The class you want to get the timetable, i.e.: L2-A /// The class you want to get the timetable, i.e.: M1-LP
#[clap(value_parser)] #[clap(value_parser)]
class: String, class: String,
/// The semester you want (useful only in 3rd year, 1-2 use letter in class) /// The semester you want (1 or 2)
#[clap(short, long, value_parser, value_name = "SEMESTER NUMBER")] #[clap(short, long, value_parser, value_name = "SEMESTER NUMBER")]
semester: Option<i8>, semester: Option<i8>,
/// The year, default to the current year
#[clap(short, long, value_parser, value_name = "YEAR")]
year: Option<i32>,
/// Export to iCalendar format (.ics) /// Export to iCalendar format (.ics)
#[clap(short, long, value_name = "FILE NAME")] #[clap(short, long, value_name = "FILE NAME")]
export: Option<String>, export: Option<String>,
@ -30,38 +34,31 @@ struct Args {
async fn main() { async fn main() {
let args = Args::parse(); let args = Args::parse();
let matches = Regex::new(r"[Ll](?P<year>\d)[-–•·]?(?P<letter>.)?") let matches =
Regex::new(r"(?i)M(?P<level>[1,2])[-–•·]?(?P<pathway>(LP|IMPAIRS|DATA|GENIAL|MPRI))?")
.unwrap() .unwrap()
.captures(&args.class) .captures(&args.class)
.unwrap(); .unwrap();
let year = matches let level = matches
.name("year") .name("level")
.unwrap() .unwrap()
.as_str() .as_str()
.parse::<i8>() .parse::<i8>()
.unwrap(); .unwrap();
let letter = matches let pathway = matches.name("pathway").unwrap().as_str();
.name("letter")
.map(|c| c.as_str().chars().next().expect("Error in letter"));
// Show a separator only if we need one let user_agent = format!("cal7tor/{}", env!("CARGO_PKG_VERSION"));
let seperator = match letter {
Some(_) => "-",
None => "",
};
let user_agent = format!("cal8tor/{}", env!("CARGO_PKG_VERSION"));
println!( println!(
"Récupération de l'emploi du temps des L{}{}{}...", "Récupération de l'emploi du temps des M{}-{}...",
year, level,
seperator, pathway.to_uppercase()
letter.unwrap_or_default().to_uppercase()
); );
let timetable = timetable::timetable(year, args.semester, letter, &user_agent).await; let timetable =
timetable::timetable(level, args.semester, args.year, pathway, &user_agent).await;
println!("Récupération des informations par rapport à l'année..."); /* println!("Récupération des informations par rapport à l'année...");
let info = info::info(&user_agent).await; let info = info::info(&user_agent).await;
if args.export.is_some() { if args.export.is_some() {
@ -77,5 +74,5 @@ async fn main() {
println!("Affichage..."); println!("Affichage...");
timetable::display(timetable, args.cl); timetable::display(timetable, args.cl);
println!("Vous devrez peut-être mettre votre terminal en plein écran si ce n'est pas déjà le cas."); println!("Vous devrez peut-être mettre votre terminal en plein écran si ce n'est pas déjà le cas.");
} } */
} }

View file

@ -12,19 +12,24 @@ pub mod models;
/// Fetch the timetable for a class /// Fetch the timetable for a class
pub async fn timetable( pub async fn timetable(
year: i8, level: i8,
semester_opt: Option<i8>, semester_opt: Option<i8>,
letter: Option<char>, year_opt: Option<i32>,
pathway: &str,
user_agent: &str, user_agent: &str,
) -> (Vec<String>, (usize, Vec<models::Day>)) { ) -> (Vec<String>, (usize, Vec<models::Day>)) {
let semester = get_semester(semester_opt, letter); let semester = get_semester(semester_opt);
let document = get_webpage(year, semester, letter, user_agent) let year = get_year(year_opt, semester);
let document = get_webpage(level, semester, &year, user_agent)
.await .await
.expect("Can't reach timetable website."); .expect("Can't reach timetable website.");
(vec![], (0, vec![]))
// Selectors // Selectors
let sel_table = Selector::parse("table").unwrap(); /* let sel_table = Selector::parse("table").unwrap();
let sel_tr = Selector::parse("tr").unwrap(); let sel_tr = Selector::parse("tr").unwrap();
let sel_tbody = Selector::parse("tbody").unwrap(); let sel_tbody = Selector::parse("tbody").unwrap();
let sel_th = Selector::parse("th").unwrap(); let sel_th = Selector::parse("th").unwrap();
@ -116,59 +121,17 @@ pub async fn timetable(
panic!("Error when building the timetable."); panic!("Error when building the timetable.");
} }
(schedules, (semester as usize, timetable)) (schedules, (semester as usize, timetable)) */
} }
/// Get timetable webpage /// Get timetable webpage
async fn get_webpage( async fn get_webpage(
year: i8, level: i8,
semester: i8, semester: i8,
letter: Option<char>, year: &str,
user_agent: &str, user_agent: &str,
) -> Result<Html, Box<dyn std::error::Error>> { ) -> Result<Html, Box<dyn std::error::Error>> {
let url = { let url = format!("https://silice.informatique.univ-paris-diderot.fr/ufr/U{}/EDT/visualiserEmploiDuTemps.php?quoi=M{},{}", year, level, semester);
let panic_semester_message = "Unknown semester.";
let panic_letter_message = "Unknown letter.";
let base_url = "https://informatique.up8.edu/licence-iv/edt";
let allow_letters_1 = match semester {
1 => ['a', 'b', 'c'],
2 => ['x', 'y', 'z'],
_ => panic!("{}", panic_semester_message),
};
let allow_letters_2_3 = match semester {
1 => ['a', 'b'],
2 => ['x', 'y'],
_ => panic!("{}", panic_semester_message),
};
match year {
1 => {
let c = letter.expect(panic_letter_message).to_ascii_lowercase();
if allow_letters_1.contains(&c) {
format!("{}/l1-{}.html", base_url, c)
} else {
panic!("{}", panic_letter_message)
}
}
2 => {
let c = letter.expect(panic_letter_message).to_ascii_lowercase();
if allow_letters_2_3.contains(&c) {
format!("{}/l2-{}.html", base_url, c)
} else {
panic!("{}", panic_letter_message)
}
}
3 => {
let c = letter.expect(panic_letter_message).to_ascii_lowercase();
if allow_letters_2_3.contains(&c) {
format!("{}/l3-{}.html", base_url, c)
} else {
panic!("{}", panic_letter_message)
}
}
_ => panic!("Unknown year."),
}
};
// Use custom User-Agent // Use custom User-Agent
let client = reqwest::Client::builder().user_agent(user_agent).build()?; let client = reqwest::Client::builder().user_agent(user_agent).build()?;
@ -314,24 +277,12 @@ pub fn build(timetable: T, dates: D) -> Vec<models::Course> {
semester semester
} }
/// Get the current semester depending on the letter or the current date /// Get the current semester depending on the current date
fn get_semester(semester: Option<i8>, letter: Option<char>) -> i8 { fn get_semester(semester: Option<i8>) -> i8 {
match semester { match semester {
// Force the asked semester // Force the asked semester
Some(n) => n, Some(n) => n,
// Find the potential semester // Find the potential semester
None => match letter {
// Based on letter (kinda accurate)
Some(c) => {
if c.to_ascii_uppercase() as i8 > 77 {
// If letter is N or after
2
} else {
// If letter is before N
1
}
}
// Based on the time (kinda less accurate)
None => { None => {
if Utc::now().month() > 6 { if Utc::now().month() > 6 {
// From july to december // From july to december
@ -341,7 +292,22 @@ fn get_semester(semester: Option<i8>, letter: Option<char>) -> i8 {
2 2
} }
} }
}, }
}
/// Get the current year depending on the current date
fn get_year(year: Option<i32>, semester: i8) -> String {
let wanted_year = match year {
// Force the asked semester
Some(n) => n,
// Find the potential semester
None => Utc::now().year(),
};
if semester == 1 {
format!("{}-{}", wanted_year, wanted_year + 1)
} else {
format!("{}-{}", wanted_year - 1, wanted_year)
} }
} }

View file

@ -2,20 +2,13 @@ pub mod models;
/// Panic if an error happened /// Panic if an error happened
pub fn check_errors(html: &String, loc: &str) { pub fn check_errors(html: &String, loc: &str) {
let no_timetable = "Aucun créneau horaire affecté";
match html { match html {
t if t.contains(&err_code(429)) => panic!( t if t.contains(no_timetable) => panic!("URL: {}{}", loc, no_timetable),
"URL: {} • HTTP 429: Slow down - Rate limited (too many access attempts detected)",
loc
),
_ => (), _ => (),
} }
} }
/// Create String error code
fn err_code(code: i32) -> String {
format!("HTTP Code : {}", code)
}
/// Print a line for the table /// Print a line for the table
pub fn line_table( pub fn line_table(
cell_length_hours: usize, cell_length_hours: usize,