forked from Anri/cal8tor
find the right timetable for the current semester/year combo
This commit is contained in:
parent
ade74b4e4c
commit
16a9bba6dd
3 changed files with 64 additions and 108 deletions
47
src/main.rs
47
src/main.rs
|
@ -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.");
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
|
114
src/timetable.rs
114
src/timetable.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
src/utils.rs
11
src/utils.rs
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue