Changes
- We are now searching backtoschool day in the same page that the timetable - It should work now
This commit is contained in:
parent
c671587b7a
commit
be636f552a
3 changed files with 72 additions and 120 deletions
|
@ -58,7 +58,7 @@ async fn main() {
|
||||||
let timetable = timetable::timetable(level, args.semester, args.year, &user_agent).await;
|
let timetable = timetable::timetable(level, args.semester, args.year, &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(level, args.semester, args.year, &user_agent).await;
|
||||||
|
|
||||||
if args.export.is_some() {
|
if args.export.is_some() {
|
||||||
// Export the calendar
|
// Export the calendar
|
||||||
|
|
128
src/timetable.rs
128
src/timetable.rs
|
@ -1,10 +1,10 @@
|
||||||
use chrono::{Datelike, Duration, NaiveTime, TimeZone, Utc};
|
use chrono::{Datelike, Duration, TimeZone, Utc};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use scraper::{Html, Selector};
|
use scraper::Selector;
|
||||||
use std::{collections::HashMap, str::FromStr};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
self,
|
self, get_semester, get_webpage, get_year,
|
||||||
models::{Position, TabChar},
|
models::{Position, TabChar},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,16 +35,21 @@ pub async fn timetable(
|
||||||
// Find the timetable
|
// Find the timetable
|
||||||
let raw_timetable = document.select(&sel_table).next().unwrap();
|
let raw_timetable = document.select(&sel_table).next().unwrap();
|
||||||
|
|
||||||
let mut hours = Vec::new();
|
let mut schedules = Vec::new();
|
||||||
for hour in 8..=20 {
|
for hour in 8..=20 {
|
||||||
for minute in &[0, 15, 30, 45] {
|
for minute in &[0, 15, 30, 45] {
|
||||||
let hour_str = format!("{}h{:02}", hour, minute);
|
let hour_str = format!("{}h{:02}", hour, minute);
|
||||||
hours.push(hour_str);
|
if let Some(last_hour) = schedules.pop() {
|
||||||
|
schedules.push(format!("{}-{}", last_hour, hour_str));
|
||||||
}
|
}
|
||||||
|
schedules.push(hour_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _ in 0..4 {
|
||||||
|
schedules.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut timetable: Vec<models::Day> = Vec::new();
|
let mut timetable: Vec<models::Day> = Vec::new();
|
||||||
let mut schedules = Vec::new();
|
|
||||||
|
|
||||||
raw_timetable
|
raw_timetable
|
||||||
.select(&sel_tbody)
|
.select(&sel_tbody)
|
||||||
|
@ -94,8 +99,8 @@ pub async fn timetable(
|
||||||
.unwrap().name("location")
|
.unwrap().name("location")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str().to_owned(),
|
.as_str().to_owned(),
|
||||||
start: hours.iter().position(|r| r == startime).unwrap(),
|
start: schedules.iter().position(|r| r.starts_with(startime)).unwrap(),
|
||||||
size: i.value().attr("rowspan").unwrap().parse::<usize >().unwrap(),
|
size: i.value().attr("rowspan").unwrap().parse::<usize>().unwrap(),
|
||||||
dtstart: None,
|
dtstart: None,
|
||||||
dtend: None,
|
dtend: None,
|
||||||
};
|
};
|
||||||
|
@ -110,78 +115,11 @@ pub async fn timetable(
|
||||||
courses: vec![Some(course)],
|
courses: vec![Some(course)],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let duration = Regex::new(r"(?P<h>\d{1,2})h(?P<m>\d{1,2})?")
|
|
||||||
.unwrap()
|
|
||||||
.captures(matches
|
|
||||||
.name("duration")
|
|
||||||
.unwrap()
|
|
||||||
.as_str()).unwrap();
|
|
||||||
schedules.push(format!("{}-{}", startime, NaiveTime::from_str(&startime.replace('h', ":")).unwrap().overflowing_add_signed(Duration::minutes(duration.name("h").unwrap().as_str().parse::<i64>().unwrap() * 60 + match duration.name("m") {
|
|
||||||
Some(x) => x.as_str().parse::<i64>().unwrap(),
|
|
||||||
None => 0
|
|
||||||
})).0.format("%Hh%M")));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if !check_consistency(&schedules, &timetable) {
|
|
||||||
panic!("Error when building the timetable.");
|
|
||||||
}
|
|
||||||
|
|
||||||
(schedules, (semester as usize, timetable))
|
(schedules, (semester as usize, timetable))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get timetable webpage
|
|
||||||
async fn get_webpage(
|
|
||||||
level: i8,
|
|
||||||
semester: i8,
|
|
||||||
year: &str,
|
|
||||||
user_agent: &str,
|
|
||||||
) -> Result<Html, Box<dyn std::error::Error>> {
|
|
||||||
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()?;
|
|
||||||
let html = client.get(&url).send().await?.text().await?;
|
|
||||||
|
|
||||||
// Panic on error
|
|
||||||
crate::utils::check_errors(&html, &url);
|
|
||||||
|
|
||||||
// Parse document
|
|
||||||
let document = Html::parse_document(&html);
|
|
||||||
|
|
||||||
Ok(document)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if the timetable is well built
|
|
||||||
fn check_consistency(schedules: &[String], timetable: &Vec<models::Day>) -> bool {
|
|
||||||
let mut checker = true;
|
|
||||||
for day in timetable {
|
|
||||||
let mut i = 0;
|
|
||||||
for course in &day.courses {
|
|
||||||
match course {
|
|
||||||
Some(course_it) => {
|
|
||||||
// Checks the consistency of course start times
|
|
||||||
if i != course_it.start {
|
|
||||||
checker = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Keep the track of how many courses are in the day
|
|
||||||
i += course_it.size
|
|
||||||
}
|
|
||||||
None => i += 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The counter should be the same as the amount of possible hours of the day
|
|
||||||
if i != schedules.len() {
|
|
||||||
checker = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checker
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data builded in the timetable webpage
|
// Data builded in the timetable webpage
|
||||||
type T = (
|
type T = (
|
||||||
// Schedules
|
// Schedules
|
||||||
|
@ -202,9 +140,7 @@ pub fn build(timetable: T, dates: D) -> Vec<models::Course> {
|
||||||
let mut schedules = Vec::new();
|
let mut schedules = Vec::new();
|
||||||
// h1 => heure de début | m1 => minute de début
|
// h1 => heure de début | m1 => minute de début
|
||||||
// h2 => heure de fin | m2 => minute de fin
|
// h2 => heure de fin | m2 => minute de fin
|
||||||
let re =
|
let re = Regex::new(r"(?P<h1>\d{1,2})h(?P<m1>\d{2})-(?P<h2>\d{1,2})h(?P<m2>\d{2})").unwrap();
|
||||||
Regex::new(r"(?P<h1>\d{1,2})(h|:)(?P<m1>\d{1,2})?.(?P<h2>\d{1,2})(h|:)(?P<m2>\d{1,2})?")
|
|
||||||
.unwrap();
|
|
||||||
for hour in timetable.0 {
|
for hour in timetable.0 {
|
||||||
let captures = re.captures(&hour).unwrap();
|
let captures = re.captures(&hour).unwrap();
|
||||||
|
|
||||||
|
@ -284,40 +220,6 @@ pub fn build(timetable: T, dates: D) -> Vec<models::Course> {
|
||||||
semester
|
semester
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 => {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Display the timetable
|
/// Display the timetable
|
||||||
pub fn display(timetable: (Vec<String>, (usize, Vec<models::Day>)), cell_length: usize) {
|
pub fn display(timetable: (Vec<String>, (usize, Vec<models::Day>)), cell_length: usize) {
|
||||||
// Cell length for hours
|
// Cell length for hours
|
||||||
|
|
62
src/utils.rs
62
src/utils.rs
|
@ -1,3 +1,6 @@
|
||||||
|
use chrono::{Datelike, Utc};
|
||||||
|
use scraper::Html;
|
||||||
|
|
||||||
pub mod models;
|
pub mod models;
|
||||||
|
|
||||||
/// Panic if an error happened
|
/// Panic if an error happened
|
||||||
|
@ -105,11 +108,58 @@ pub fn etc_str(text: &str) -> String {
|
||||||
format!("{}...", split_half(text).0.trim())
|
format!("{}...", split_half(text).0.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capitalize string
|
/// Get timetable webpage
|
||||||
pub fn capitalize(text: &mut str) -> String {
|
pub async fn get_webpage(
|
||||||
if let Some(r) = text.get_mut(0..1) {
|
level: i8,
|
||||||
r.make_ascii_uppercase();
|
semester: i8,
|
||||||
}
|
year: &str,
|
||||||
|
user_agent: &str,
|
||||||
|
) -> Result<Html, Box<dyn std::error::Error>> {
|
||||||
|
let url = format!("https://silice.informatique.univ-paris-diderot.fr/ufr/U{}/EDT/visualiserEmploiDuTemps.php?quoi=M{},{}", year, level, semester);
|
||||||
|
|
||||||
text.to_string()
|
// Use custom User-Agent
|
||||||
|
let client = reqwest::Client::builder().user_agent(user_agent).build()?;
|
||||||
|
let html = client.get(&url).send().await?.text().await?;
|
||||||
|
|
||||||
|
// Panic on error
|
||||||
|
crate::utils::check_errors(&html, &url);
|
||||||
|
|
||||||
|
// Parse document
|
||||||
|
let document = Html::parse_document(&html);
|
||||||
|
|
||||||
|
Ok(document)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current semester depending on the current date
|
||||||
|
pub fn get_semester(semester: Option<i8>) -> i8 {
|
||||||
|
match semester {
|
||||||
|
// Force the asked semester
|
||||||
|
Some(n) => n,
|
||||||
|
// Find the potential semester
|
||||||
|
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
|
||||||
|
pub 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue