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)]
#[clap(version, about, long_about = None)]
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)]
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")]
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)
#[clap(short, long, value_name = "FILE NAME")]
export: Option<String>,
@ -30,38 +34,31 @@ struct Args {
async fn main() {
let args = Args::parse();
let matches = Regex::new(r"[Ll](?P<year>\d)[-–•·]?(?P<letter>.)?")
.unwrap()
.captures(&args.class)
.unwrap();
let matches =
Regex::new(r"(?i)M(?P<level>[1,2])[-–•·]?(?P<pathway>(LP|IMPAIRS|DATA|GENIAL|MPRI))?")
.unwrap()
.captures(&args.class)
.unwrap();
let year = matches
.name("year")
let level = matches
.name("level")
.unwrap()
.as_str()
.parse::<i8>()
.unwrap();
let letter = matches
.name("letter")
.map(|c| c.as_str().chars().next().expect("Error in letter"));
let pathway = matches.name("pathway").unwrap().as_str();
// Show a separator only if we need one
let seperator = match letter {
Some(_) => "-",
None => "",
};
let user_agent = format!("cal8tor/{}", env!("CARGO_PKG_VERSION"));
let user_agent = format!("cal7tor/{}", env!("CARGO_PKG_VERSION"));
println!(
"Récupération de l'emploi du temps des L{}{}{}...",
year,
seperator,
letter.unwrap_or_default().to_uppercase()
"Récupération de l'emploi du temps des M{}-{}...",
level,
pathway.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;
if args.export.is_some() {
@ -77,5 +74,5 @@ async fn main() {
println!("Affichage...");
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.");
}
} */
}

View file

@ -12,19 +12,24 @@ pub mod models;
/// Fetch the timetable for a class
pub async fn timetable(
year: i8,
level: i8,
semester_opt: Option<i8>,
letter: Option<char>,
year_opt: Option<i32>,
pathway: &str,
user_agent: &str,
) -> (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
.expect("Can't reach timetable website.");
(vec![], (0, vec![]))
// Selectors
let sel_table = Selector::parse("table").unwrap();
/* let sel_table = Selector::parse("table").unwrap();
let sel_tr = Selector::parse("tr").unwrap();
let sel_tbody = Selector::parse("tbody").unwrap();
let sel_th = Selector::parse("th").unwrap();
@ -116,59 +121,17 @@ pub async fn timetable(
panic!("Error when building the timetable.");
}
(schedules, (semester as usize, timetable))
(schedules, (semester as usize, timetable)) */
}
/// Get timetable webpage
async fn get_webpage(
year: i8,
level: i8,
semester: i8,
letter: Option<char>,
year: &str,
user_agent: &str,
) -> Result<Html, Box<dyn std::error::Error>> {
let url = {
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."),
}
};
let url = format!("https://silice.informatique.univ-paris-diderot.fr/ufr/U{}/EDT/visualiserEmploiDuTemps.php?quoi=M{},{}", year, level, semester);
// Use custom User-Agent
let client = reqwest::Client::builder().user_agent(user_agent).build()?;
@ -314,34 +277,37 @@ pub fn build(timetable: T, dates: D) -> Vec<models::Course> {
semester
}
/// Get the current semester depending on the letter or the current date
fn get_semester(semester: Option<i8>, letter: Option<char>) -> i8 {
/// Get the current semester depending on the current date
fn get_semester(semester: Option<i8>) -> i8 {
match semester {
// Force the asked semester
Some(n) => n,
// 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
}
None => {
if Utc::now().month() > 6 {
// From july to december
1
} else {
// from january to june
2
}
// Based on the time (kinda less accurate)
None => {
if Utc::now().month() > 6 {
// From july to december
1
} else {
// from january to june
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
pub fn check_errors(html: &String, loc: &str) {
let no_timetable = "Aucun créneau horaire affecté";
match html {
t if t.contains(&err_code(429)) => panic!(
"URL: {} • HTTP 429: Slow down - Rate limited (too many access attempts detected)",
loc
),
t if t.contains(no_timetable) => panic!("URL: {}{}", loc, no_timetable),
_ => (),
}
}
/// Create String error code
fn err_code(code: i32) -> String {
format!("HTTP Code : {}", code)
}
/// Print a line for the table
pub fn line_table(
cell_length_hours: usize,