2023-09-26 23:55:54 +02:00
|
|
|
use chrono::{Datelike, Duration, NaiveTime, TimeZone, Utc};
|
2022-08-16 10:41:42 +02:00
|
|
|
use regex::Regex;
|
2022-08-16 11:57:36 +02:00
|
|
|
use scraper::{Html, Selector};
|
2023-09-26 23:55:54 +02:00
|
|
|
use std::{collections::HashMap, str::FromStr};
|
2022-08-23 16:51:02 +02:00
|
|
|
|
|
|
|
use crate::utils::{
|
2023-09-26 23:08:41 +02:00
|
|
|
self,
|
2022-08-23 16:51:02 +02:00
|
|
|
models::{Position, TabChar},
|
|
|
|
};
|
|
|
|
|
2022-08-15 19:20:04 +02:00
|
|
|
pub mod models;
|
2022-08-15 12:18:08 +02:00
|
|
|
|
2022-08-15 17:25:14 +02:00
|
|
|
/// Fetch the timetable for a class
|
2022-08-15 20:09:41 +02:00
|
|
|
pub async fn timetable(
|
2023-09-18 19:50:30 +02:00
|
|
|
level: i8,
|
2022-08-17 14:09:08 +02:00
|
|
|
semester_opt: Option<i8>,
|
2023-09-18 19:50:30 +02:00
|
|
|
year_opt: Option<i32>,
|
2022-08-29 11:44:35 +02:00
|
|
|
user_agent: &str,
|
2022-08-16 09:33:35 +02:00
|
|
|
) -> (Vec<String>, (usize, Vec<models::Day>)) {
|
2023-09-18 19:50:30 +02:00
|
|
|
let semester = get_semester(semester_opt);
|
2022-08-17 14:09:08 +02:00
|
|
|
|
2023-09-18 19:50:30 +02:00
|
|
|
let year = get_year(year_opt, semester);
|
|
|
|
|
|
|
|
let document = get_webpage(level, semester, &year, user_agent)
|
2022-08-15 14:52:57 +02:00
|
|
|
.await
|
|
|
|
.expect("Can't reach timetable website.");
|
2022-08-15 12:18:08 +02:00
|
|
|
|
|
|
|
// Selectors
|
2023-09-18 20:52:18 +02:00
|
|
|
let sel_table = Selector::parse("table").unwrap();
|
2023-09-26 19:16:15 +02:00
|
|
|
let sel_tbody = Selector::parse("tbody").unwrap();
|
|
|
|
let sel_td = Selector::parse("td").unwrap();
|
2023-09-26 23:08:41 +02:00
|
|
|
let sel_small = Selector::parse("small").unwrap();
|
|
|
|
let sel_b = Selector::parse("b").unwrap();
|
2022-08-15 12:18:08 +02:00
|
|
|
|
|
|
|
// Find the timetable
|
2022-08-15 12:23:01 +02:00
|
|
|
let raw_timetable = document.select(&sel_table).next().unwrap();
|
2022-08-15 12:18:08 +02:00
|
|
|
|
2023-09-26 23:55:54 +02:00
|
|
|
let mut hours = Vec::new();
|
2023-09-26 23:08:41 +02:00
|
|
|
for hour in 8..=20 {
|
|
|
|
for minute in &[0, 15, 30, 45] {
|
|
|
|
let hour_str = format!("{}h{:02}", hour, minute);
|
2023-09-26 23:55:54 +02:00
|
|
|
hours.push(hour_str);
|
2023-09-26 23:08:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut timetable: Vec<models::Day> = Vec::new();
|
2023-09-26 23:55:54 +02:00
|
|
|
let mut schedules = Vec::new();
|
2023-09-26 23:08:41 +02:00
|
|
|
|
|
|
|
raw_timetable
|
2023-09-26 19:16:15 +02:00
|
|
|
.select(&sel_tbody)
|
2023-09-18 20:52:18 +02:00
|
|
|
.next()
|
|
|
|
.unwrap()
|
2023-09-26 19:16:15 +02:00
|
|
|
.select(&sel_td)
|
2023-09-26 23:08:41 +02:00
|
|
|
.filter(|element| element.value().attr("title").is_some())
|
|
|
|
.for_each(|i| {
|
|
|
|
let matches =
|
|
|
|
Regex::new(r"(?P<type>COURS|TD|TP) (?P<name>.*) : (?P<day>(lundi|mardi|mercredi|jeudi|vendredi)) (?P<startime>.*) \(durée : (?P<duration>.*)\)")
|
|
|
|
.unwrap()
|
|
|
|
.captures(i.value().attr("title").unwrap())
|
|
|
|
.unwrap();
|
|
|
|
|
2023-09-26 23:55:54 +02:00
|
|
|
let day = matches
|
2023-09-26 23:08:41 +02:00
|
|
|
.name("day")
|
|
|
|
.unwrap()
|
|
|
|
.as_str();
|
|
|
|
|
2023-09-26 23:55:54 +02:00
|
|
|
let startime = matches
|
|
|
|
.name("startime")
|
|
|
|
.unwrap()
|
|
|
|
.as_str();
|
2023-09-26 23:08:41 +02:00
|
|
|
|
|
|
|
let binding = i.select(&sel_b).last().unwrap().inner_html();
|
|
|
|
let course = models::Course{
|
|
|
|
typee: match matches
|
|
|
|
.name("type")
|
|
|
|
.unwrap()
|
|
|
|
.as_str() {
|
|
|
|
"COURS" => models::Type::Cours,
|
|
|
|
"TP" => models::Type::TP,
|
|
|
|
"TD" => models::Type::TD,
|
|
|
|
_ => panic!("Unknown type of course")
|
|
|
|
},
|
|
|
|
name: matches
|
|
|
|
.name("name")
|
|
|
|
.unwrap()
|
|
|
|
.as_str().to_owned(),
|
|
|
|
professor: match i.select(&sel_small).last().unwrap().inner_html() {
|
|
|
|
i if i.starts_with("<span") => None,
|
|
|
|
i => Some(i),
|
|
|
|
},
|
|
|
|
room: Regex::new(r"(<table.*<\/table>|<br>.*?<br>.*?)<br>(?P<location>.*?)<br>")
|
|
|
|
.unwrap()
|
|
|
|
.captures(&binding)
|
|
|
|
.unwrap().name("location")
|
|
|
|
.unwrap()
|
|
|
|
.as_str().to_owned(),
|
2023-09-26 23:55:54 +02:00
|
|
|
start: hours.iter().position(|r| r == startime).unwrap(),
|
2023-09-26 23:08:41 +02:00
|
|
|
size: i.value().attr("rowspan").unwrap().parse::<usize >().unwrap(),
|
|
|
|
dtstart: None,
|
|
|
|
dtend: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Search for the day in the timetable
|
|
|
|
if let Some(existing_day) = timetable.iter_mut().find(|x| x.name == day) {
|
|
|
|
existing_day.courses.push(Some(course));
|
|
|
|
} else {
|
|
|
|
// Day with the name doesn't exist, create a new Day
|
|
|
|
timetable.push(models::Day {
|
|
|
|
name: day.to_owned(),
|
|
|
|
courses: vec![Some(course)],
|
|
|
|
});
|
|
|
|
}
|
2023-09-26 23:55:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
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")));
|
2023-09-26 23:08:41 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
if !check_consistency(&schedules, &timetable) {
|
|
|
|
panic!("Error when building the timetable.");
|
2023-09-26 19:16:15 +02:00
|
|
|
}
|
2022-08-15 12:18:08 +02:00
|
|
|
|
2023-09-26 23:08:41 +02:00
|
|
|
(schedules, (semester as usize, timetable))
|
2022-08-15 12:18:08 +02:00
|
|
|
}
|
|
|
|
|
2022-08-15 17:25:14 +02:00
|
|
|
/// Get timetable webpage
|
2022-08-15 12:18:08 +02:00
|
|
|
async fn get_webpage(
|
2023-09-18 19:50:30 +02:00
|
|
|
level: i8,
|
2022-08-15 12:18:08 +02:00
|
|
|
semester: i8,
|
2023-09-18 19:50:30 +02:00
|
|
|
year: &str,
|
2022-08-29 11:44:35 +02:00
|
|
|
user_agent: &str,
|
2022-08-15 12:18:08 +02:00
|
|
|
) -> Result<Html, Box<dyn std::error::Error>> {
|
2023-09-18 19:50:30 +02:00
|
|
|
let url = format!("https://silice.informatique.univ-paris-diderot.fr/ufr/U{}/EDT/visualiserEmploiDuTemps.php?quoi=M{},{}", year, level, semester);
|
2022-08-15 12:18:08 +02:00
|
|
|
|
2022-08-29 11:44:35 +02:00
|
|
|
// Use custom User-Agent
|
|
|
|
let client = reqwest::Client::builder().user_agent(user_agent).build()?;
|
|
|
|
let html = client.get(&url).send().await?.text().await?;
|
2022-08-15 12:18:08 +02:00
|
|
|
|
2022-08-16 15:48:13 +02:00
|
|
|
// Panic on error
|
2022-08-23 17:52:45 +02:00
|
|
|
crate::utils::check_errors(&html, &url);
|
2022-08-15 12:18:08 +02:00
|
|
|
|
2022-08-16 15:48:13 +02:00
|
|
|
// Parse document
|
|
|
|
let document = Html::parse_document(&html);
|
2022-08-15 12:18:08 +02:00
|
|
|
|
|
|
|
Ok(document)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if the timetable is well built
|
2022-08-23 17:53:41 +02:00
|
|
|
fn check_consistency(schedules: &[String], timetable: &Vec<models::Day>) -> bool {
|
2022-08-15 12:18:08 +02:00
|
|
|
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
|
|
|
|
}
|
2022-08-16 10:41:42 +02:00
|
|
|
|
2022-08-16 11:57:36 +02:00
|
|
|
// Data builded in the timetable webpage
|
2022-08-16 10:41:42 +02:00
|
|
|
type T = (
|
|
|
|
// Schedules
|
|
|
|
Vec<String>,
|
|
|
|
// Timetable per days with the semester as the key
|
2022-08-16 11:57:36 +02:00
|
|
|
(usize, Vec<models::Day>),
|
2022-08-16 10:41:42 +02:00
|
|
|
);
|
2022-08-16 11:57:36 +02:00
|
|
|
// Data builded in the info webpage
|
2022-08-23 16:51:02 +02:00
|
|
|
type D = HashMap<
|
2022-08-16 11:57:36 +02:00
|
|
|
// Semester
|
2022-08-16 10:41:42 +02:00
|
|
|
usize,
|
2022-08-16 14:41:51 +02:00
|
|
|
// List of start and repetition of course weeks
|
|
|
|
Vec<(chrono::DateTime<Utc>, i64)>,
|
2022-08-16 10:41:42 +02:00
|
|
|
>;
|
|
|
|
|
|
|
|
/// Build the timetable
|
2022-08-16 11:57:36 +02:00
|
|
|
pub fn build(timetable: T, dates: D) -> Vec<models::Course> {
|
2022-08-16 10:41:42 +02:00
|
|
|
let mut schedules = Vec::new();
|
|
|
|
// h1 => heure de début | m1 => minute de début
|
|
|
|
// h2 => heure de fin | m2 => minute de fin
|
|
|
|
let re =
|
2022-09-19 23:13:07 +02:00
|
|
|
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();
|
2022-08-16 10:41:42 +02:00
|
|
|
for hour in timetable.0 {
|
|
|
|
let captures = re.captures(&hour).unwrap();
|
|
|
|
|
|
|
|
let h1 = match captures.name("h1") {
|
|
|
|
Some(h) => h.as_str().parse().unwrap(),
|
|
|
|
None => 0,
|
|
|
|
};
|
|
|
|
let m1 = match captures.name("m1") {
|
|
|
|
Some(h) => h.as_str().parse().unwrap(),
|
|
|
|
None => 0,
|
|
|
|
};
|
|
|
|
let h2 = match captures.name("h2") {
|
|
|
|
Some(h) => h.as_str().parse().unwrap(),
|
|
|
|
None => 0,
|
|
|
|
};
|
|
|
|
let m2 = match captures.name("m2") {
|
|
|
|
Some(h) => h.as_str().parse().unwrap(),
|
|
|
|
None => 0,
|
|
|
|
};
|
|
|
|
schedules.push(((h1, m1), (h2, m2)));
|
|
|
|
}
|
|
|
|
|
2022-08-16 14:05:25 +02:00
|
|
|
// Store all the courses for the semester
|
|
|
|
let mut semester = Vec::new();
|
2022-08-16 10:41:42 +02:00
|
|
|
|
2022-08-16 14:05:25 +02:00
|
|
|
// Start date of the back-to-school week
|
2022-08-16 15:48:13 +02:00
|
|
|
let datetimes = dates.get(&timetable.1 .0).unwrap();
|
|
|
|
let before_break = datetimes.get(0).unwrap();
|
2022-08-16 14:50:22 +02:00
|
|
|
let mut date = before_break.0;
|
|
|
|
let mut rep = before_break.1;
|
2022-08-16 14:41:51 +02:00
|
|
|
// For each weeks
|
2022-08-16 14:50:22 +02:00
|
|
|
for _ in 0..2 {
|
|
|
|
for _ in 0..rep {
|
|
|
|
for day in &timetable.1 .1 {
|
|
|
|
for mut course in day.courses.clone().into_iter().flatten() {
|
|
|
|
// Get the hours
|
|
|
|
let start = schedules.get(course.start).unwrap().0;
|
|
|
|
// -1 because we only add when the size is > 1
|
|
|
|
let end = schedules.get(course.start + course.size - 1).unwrap().1;
|
2022-08-16 10:41:42 +02:00
|
|
|
|
2022-08-16 14:50:22 +02:00
|
|
|
// Add the changed datetimes
|
|
|
|
course.dtstart = Some(
|
2023-01-10 12:01:45 +01:00
|
|
|
Utc.with_ymd_and_hms(
|
|
|
|
date.year(),
|
|
|
|
date.month(),
|
|
|
|
date.day(),
|
|
|
|
start.0,
|
|
|
|
start.1,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2022-08-16 14:50:22 +02:00
|
|
|
);
|
|
|
|
course.dtend = Some(
|
2023-01-10 12:01:45 +01:00
|
|
|
Utc.with_ymd_and_hms(
|
|
|
|
date.year(),
|
|
|
|
date.month(),
|
|
|
|
date.day(),
|
|
|
|
end.0,
|
|
|
|
end.1,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2022-08-16 14:50:22 +02:00
|
|
|
);
|
2022-08-16 10:41:42 +02:00
|
|
|
|
2022-08-16 14:50:22 +02:00
|
|
|
semester.push(course);
|
|
|
|
}
|
|
|
|
date += Duration::days(1);
|
2022-08-16 14:41:51 +02:00
|
|
|
}
|
2022-08-16 14:50:22 +02:00
|
|
|
// From friday to monday
|
|
|
|
date += Duration::days(2);
|
2022-08-16 14:05:25 +02:00
|
|
|
}
|
2022-08-16 15:48:13 +02:00
|
|
|
let after_break = datetimes.get(1).unwrap();
|
2022-08-16 14:50:22 +02:00
|
|
|
date = after_break.0;
|
|
|
|
rep = after_break.1;
|
2022-08-16 14:05:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
semester
|
2022-08-16 10:41:42 +02:00
|
|
|
}
|
2022-08-17 14:09:08 +02:00
|
|
|
|
2023-09-18 19:50:30 +02:00
|
|
|
/// Get the current semester depending on the current date
|
|
|
|
fn get_semester(semester: Option<i8>) -> i8 {
|
2022-08-17 16:51:09 +02:00
|
|
|
match semester {
|
|
|
|
// Force the asked semester
|
|
|
|
Some(n) => n,
|
|
|
|
// Find the potential semester
|
2023-09-18 19:50:30 +02:00
|
|
|
None => {
|
|
|
|
if Utc::now().month() > 6 {
|
|
|
|
// From july to december
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
// from january to june
|
|
|
|
2
|
2022-08-17 16:51:09 +02:00
|
|
|
}
|
2023-09-18 19:50:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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)
|
2022-08-17 16:51:09 +02:00
|
|
|
}
|
2022-08-17 14:09:08 +02:00
|
|
|
}
|
2022-08-17 17:08:30 +02:00
|
|
|
|
|
|
|
/// Display the timetable
|
2022-08-23 18:38:47 +02:00
|
|
|
pub fn display(timetable: (Vec<String>, (usize, Vec<models::Day>)), cell_length: usize) {
|
2022-08-23 16:51:02 +02:00
|
|
|
// Cell length for hours
|
|
|
|
let clh = 11;
|
|
|
|
// Cell number
|
|
|
|
let cn = 6;
|
2022-08-23 18:30:01 +02:00
|
|
|
// 3/4 of cell length
|
2022-08-23 18:38:47 +02:00
|
|
|
let quarter = (3 * cell_length) / 4;
|
2022-08-23 16:51:02 +02:00
|
|
|
|
|
|
|
let sep = TabChar::Bv.val();
|
|
|
|
|
|
|
|
// Top of the tab
|
2022-08-23 18:38:47 +02:00
|
|
|
utils::line_table(clh, cell_length, cn, Position::Top, HashMap::new());
|
2022-08-23 16:51:02 +02:00
|
|
|
|
|
|
|
// First empty case
|
|
|
|
print!("{}{:^clh$}{}", sep, "", sep);
|
|
|
|
|
|
|
|
// Print day's of the week
|
|
|
|
let mut days = HashMap::new();
|
2023-01-10 11:53:19 +01:00
|
|
|
for (i, data) in timetable.1 .1.iter().enumerate() {
|
2022-08-23 16:51:02 +02:00
|
|
|
days.insert(i, &data.name);
|
2022-08-23 18:38:47 +02:00
|
|
|
print!("{:^cell_length$}{}", &data.name, sep);
|
2022-08-23 16:51:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Store the data of the course for utils::line_table
|
|
|
|
let mut next_skip = HashMap::new();
|
2022-08-23 17:23:29 +02:00
|
|
|
// For each hours -- i the hour's number
|
2022-08-23 16:51:02 +02:00
|
|
|
for (i, hour) in timetable.0.into_iter().enumerate() {
|
|
|
|
// Draw separator line
|
2022-08-23 18:38:47 +02:00
|
|
|
utils::line_table(clh, cell_length, cn, Position::Middle, next_skip);
|
2022-08-23 16:51:02 +02:00
|
|
|
|
|
|
|
// Reset
|
|
|
|
next_skip = HashMap::new();
|
|
|
|
|
|
|
|
// Print hour
|
|
|
|
print!("{}{:^clh$}", sep, hour);
|
|
|
|
|
2022-08-23 17:23:29 +02:00
|
|
|
// For all the days - `j` the day's number
|
2023-01-10 11:53:19 +01:00
|
|
|
for (j, day) in timetable.1 .1.iter().enumerate() {
|
2022-08-23 16:58:22 +02:00
|
|
|
// True if we found something about the slot we are looking for
|
|
|
|
let mut info_slot = false;
|
2022-08-23 17:23:29 +02:00
|
|
|
|
|
|
|
// For all the courses of each days - `k` the possible course.start
|
2023-01-10 11:53:19 +01:00
|
|
|
for (k, course_opt) in day.courses.iter().enumerate() {
|
2022-08-23 16:51:02 +02:00
|
|
|
match course_opt {
|
|
|
|
// If there is a course
|
|
|
|
Some(course) => {
|
2022-08-23 16:58:22 +02:00
|
|
|
// Check the course's hour
|
2022-08-23 17:23:29 +02:00
|
|
|
if i == course.start {
|
2022-08-23 18:30:01 +02:00
|
|
|
// If the course uses more than one time slot
|
2022-08-23 16:58:22 +02:00
|
|
|
if course.size > 1 {
|
2022-08-23 18:30:01 +02:00
|
|
|
// If the data is too long
|
|
|
|
if course.name.len() > quarter {
|
|
|
|
let data = utils::split_half(&course.name);
|
2022-08-23 18:32:06 +02:00
|
|
|
next_skip.insert(j, data.1.trim());
|
2022-08-23 18:38:47 +02:00
|
|
|
print!("{}{:^cell_length$}", sep, data.0.trim());
|
2022-08-23 18:30:01 +02:00
|
|
|
} else {
|
|
|
|
next_skip.insert(j, &course.name);
|
2022-08-23 18:38:47 +02:00
|
|
|
print!("{}{:^cell_length$}", sep, "");
|
2022-08-23 18:30:01 +02:00
|
|
|
}
|
2022-08-23 16:58:22 +02:00
|
|
|
info_slot = true;
|
2022-08-23 16:51:02 +02:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// Else simply print the course
|
2022-08-23 18:30:01 +02:00
|
|
|
// If the data is too long
|
|
|
|
if course.name.len() > quarter {
|
2022-08-23 18:38:47 +02:00
|
|
|
print!("{}{:^cell_length$}", sep, utils::etc_str(&course.name));
|
2022-08-23 18:30:01 +02:00
|
|
|
} else {
|
2022-08-23 18:38:47 +02:00
|
|
|
print!("{}{:^cell_length$}", sep, &course.name);
|
2022-08-23 18:30:01 +02:00
|
|
|
}
|
2022-08-23 16:58:22 +02:00
|
|
|
info_slot = true;
|
2022-08-23 16:51:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If no course was found
|
|
|
|
None => {
|
|
|
|
// Verify the "no course" is in the correct day and hour
|
|
|
|
if *days.get(&j).unwrap() == &day.name.to_string() && k == i {
|
2022-08-23 16:58:22 +02:00
|
|
|
// If yes print empty row because there is no course
|
2022-08-23 18:38:47 +02:00
|
|
|
print!("{}{:^cell_length$}", sep, "");
|
2022-08-23 16:58:22 +02:00
|
|
|
info_slot = true;
|
2022-08-23 16:51:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Else it was a course of another day/time
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2022-08-23 16:58:22 +02:00
|
|
|
if !info_slot {
|
|
|
|
// We found nothing about the slot because the precedent course
|
|
|
|
// takes more place than one slot
|
2022-08-23 18:38:47 +02:00
|
|
|
print!("{}{:^cell_length$}", sep, "");
|
2022-08-23 16:58:22 +02:00
|
|
|
}
|
2022-08-23 16:51:02 +02:00
|
|
|
}
|
|
|
|
print!("{}", sep);
|
|
|
|
}
|
|
|
|
// Bottom of the table
|
2022-08-23 18:38:47 +02:00
|
|
|
utils::line_table(clh, cell_length, cn, Position::Bottom, HashMap::new());
|
2022-08-23 16:51:02 +02:00
|
|
|
}
|