forked from Anri/cal8tor
WIP: fix: correct display of the timetable #13
4 changed files with 32 additions and 250 deletions
|
@ -27,10 +27,6 @@ struct Args {
|
|||
#[clap(short, long, value_name = "FILE NAME")]
|
||||
export: Option<String>,
|
||||
|
||||
/// Size of cell of the timetable (irrelevant when exporting)
|
||||
#[clap(short, long, value_name = "CELL LENGTH", default_value_t = 35)]
|
||||
cl: usize,
|
||||
|
||||
/// Doesn't distinguish TD from TP
|
||||
#[clap(short, long)]
|
||||
td_are_tp: bool,
|
||||
|
@ -88,10 +84,8 @@ async fn main() {
|
|||
|
||||
println!("Fichier .ICS construit et exporté => {filename}");
|
||||
} else {
|
||||
println!("\x1b[93mNOTICE: IT WON'T WORK!!!\x1b[0m");
|
||||
// Show the calendar
|
||||
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.");
|
||||
timetable::display(&timetable);
|
||||
}
|
||||
}
|
||||
|
|
117
src/timetable.rs
117
src/timetable.rs
|
@ -6,8 +6,8 @@ use scraper::Selector;
|
|||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use crate::utils::{
|
||||
self, get_hours, get_semester, get_webpage, get_year,
|
||||
models::{Info, InfoList, Position, TabChar},
|
||||
format_time_slot, get_hours, get_semester, get_webpage, get_year,
|
||||
models::{Info, InfoList},
|
||||
Capitalize,
|
||||
};
|
||||
|
||||
|
@ -270,101 +270,28 @@ fn add_courses(
|
|||
}
|
||||
|
||||
/// Display the timetable
|
||||
pub fn display(timetable: &(Arc<[String]>, (usize, Vec<models::Day>)), cell_length: usize) {
|
||||
// Cell length for hours
|
||||
let clh = 11;
|
||||
// Cell number
|
||||
let cn = 6;
|
||||
// 3/4 of cell length
|
||||
let quarter = (3 * cell_length) / 4;
|
||||
pub fn display(timetable: &(Arc<[String]>, (usize, Vec<Day>))) {
|
||||
for day in &timetable.1 .1 {
|
||||
for (index, course_option) in day.courses.iter().enumerate() {
|
||||
if let Some(course) = course_option {
|
||||
if index == 0 {
|
||||
println!("\n{}:", day.name);
|
||||
}
|
||||
|
||||
let sep = TabChar::Bv.val();
|
||||
|
||||
// Top of the tab
|
||||
utils::line_table(clh, cell_length, cn, &Position::Top, &HashMap::new());
|
||||
|
||||
// First empty case
|
||||
print!("{}{:^clh$}{}", sep, "", sep);
|
||||
|
||||
// Print day's of the week
|
||||
let mut days = HashMap::new();
|
||||
for (i, data) in timetable.1 .1.iter().enumerate() {
|
||||
days.insert(i, &data.name);
|
||||
print!("{:^cell_length$}{}", &data.name, sep);
|
||||
}
|
||||
|
||||
// Store the data of the course for utils::line_table
|
||||
let mut next_skip = HashMap::new();
|
||||
// For each hours -- i the hour's number
|
||||
for (i, hour) in timetable.0.iter().enumerate() {
|
||||
// Draw separator line
|
||||
utils::line_table(clh, cell_length, cn, &Position::Middle, &next_skip);
|
||||
|
||||
// Reset
|
||||
next_skip = HashMap::new();
|
||||
|
||||
// Print hour
|
||||
print!("{sep}{hour:^clh$}");
|
||||
|
||||
// For all the days - `j` the day's number
|
||||
for (j, day) in timetable.1 .1.iter().enumerate() {
|
||||
// True if we found something about the slot we are looking for
|
||||
let mut info_slot = false;
|
||||
|
||||
// For all the courses of each days - `k` the possible course.start
|
||||
for (k, course_opt) in day.courses.iter().enumerate() {
|
||||
match course_opt {
|
||||
// If there is a course
|
||||
Some(course) => {
|
||||
// Check the course's hour
|
||||
if i == course.start {
|
||||
// If the course uses more than one time slot
|
||||
if course.size > 1 {
|
||||
// If the data is too long
|
||||
if course.name.len() > quarter {
|
||||
let data = utils::split_half(&course.name);
|
||||
next_skip.insert(j, data.1.trim());
|
||||
print!("{}{:^cell_length$}", sep, data.0.trim());
|
||||
} else {
|
||||
next_skip.insert(j, &course.name);
|
||||
print!("{}{:^cell_length$}", sep, "");
|
||||
}
|
||||
info_slot = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Else simply print the course
|
||||
// If the data is too long
|
||||
if course.name.len() > quarter {
|
||||
print!("{}{:^cell_length$}", sep, utils::etc_str(&course.name));
|
||||
} else {
|
||||
print!("{}{:^cell_length$}", sep, &course.name);
|
||||
}
|
||||
info_slot = true;
|
||||
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 {
|
||||
// If yes print empty row because there is no course
|
||||
print!("{}{:^cell_length$}", sep, "");
|
||||
info_slot = true;
|
||||
break;
|
||||
}
|
||||
// Else it was a course of another day/time
|
||||
}
|
||||
};
|
||||
}
|
||||
if !info_slot {
|
||||
// We found nothing about the slot because the precedent course
|
||||
// takes more place than one slot
|
||||
print!("{}{:^cell_length$}", sep, "");
|
||||
println!(
|
||||
" {} - {} : {} ({}) // {}",
|
||||
format_time_slot(course.start, course.size),
|
||||
course
|
||||
.category
|
||||
.iter()
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
course.name,
|
||||
course.room,
|
||||
course.professor.as_deref().unwrap_or("N/A"),
|
||||
);
|
||||
}
|
||||
}
|
||||
print!("{sep}");
|
||||
}
|
||||
// Bottom of the table
|
||||
utils::line_table(clh, cell_length, cn, &Position::Bottom, &HashMap::new());
|
||||
}
|
||||
|
|
105
src/utils.rs
105
src/utils.rs
|
@ -16,102 +16,6 @@ pub fn check_errors(html: &String, loc: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Print a line for the table
|
||||
pub fn line_table(
|
||||
cell_length_hours: usize,
|
||||
cell_length: usize,
|
||||
number_cell: usize,
|
||||
pos: &models::Position,
|
||||
skip_with: &std::collections::HashMap<usize, &str>,
|
||||
) {
|
||||
// Left side
|
||||
let ls = match pos {
|
||||
models::Position::Top => models::TabChar::Jtl.val(),
|
||||
models::Position::Middle => models::TabChar::Jl.val(),
|
||||
models::Position::Bottom => models::TabChar::Jbl.val(),
|
||||
};
|
||||
|
||||
// Middle
|
||||
let ms = match pos {
|
||||
models::Position::Top => models::TabChar::Jtb.val(),
|
||||
models::Position::Middle => models::TabChar::Jm.val(),
|
||||
models::Position::Bottom => models::TabChar::Jtt.val(),
|
||||
};
|
||||
|
||||
// Right side
|
||||
let rs = match pos {
|
||||
models::Position::Top => models::TabChar::Jtr.val(),
|
||||
models::Position::Middle => models::TabChar::Jr.val(),
|
||||
models::Position::Bottom => models::TabChar::Jbr.val(),
|
||||
};
|
||||
|
||||
// Right side before big cell
|
||||
let rs_bbc = models::TabChar::Jr.val();
|
||||
// Right side big cell before big cell
|
||||
let rsbc_bbc = models::TabChar::Bv.val();
|
||||
// Right side big cell
|
||||
let rsbc = models::TabChar::Jl.val();
|
||||
|
||||
let line = models::TabChar::Bh.val().to_string().repeat(cell_length);
|
||||
let line_h = models::TabChar::Bh
|
||||
.val()
|
||||
.to_string()
|
||||
.repeat(cell_length_hours);
|
||||
|
||||
// Hours column
|
||||
match skip_with.get(&0) {
|
||||
Some(_) => print!("\n{ls}{line_h}{rs_bbc}"),
|
||||
None => print!("\n{ls}{line_h}{ms}"),
|
||||
};
|
||||
|
||||
// Courses columns
|
||||
let range = number_cell - 1;
|
||||
let mut last_day = false;
|
||||
for i in 0..range {
|
||||
// Check if it's a big cell
|
||||
if i == range - 1 {
|
||||
// Friday only
|
||||
if let Some(text) = skip_with.get(&i) {
|
||||
println!("{text:^cell_length$}{rsbc_bbc}");
|
||||
last_day = true;
|
||||
}
|
||||
} else {
|
||||
match skip_with.get(&i) {
|
||||
Some(text) => match skip_with.get(&(i + 1)) {
|
||||
// Match check if the next cell will be big
|
||||
Some(_) => print!("{text:^cell_length$}{rsbc_bbc}"),
|
||||
None => print!("{text:^cell_length$}{rsbc}"),
|
||||
},
|
||||
None => match skip_with.get(&(i + 1)) {
|
||||
// Match check if the next cell will be big
|
||||
Some(_) => print!("{line}{rs_bbc}"),
|
||||
None => print!("{line}{ms}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
if !last_day {
|
||||
println!("{line}{rs}");
|
||||
}
|
||||
}
|
||||
|
||||
// Split a string in half with respect of words
|
||||
pub fn split_half(text: &str) -> (&str, &str) {
|
||||
let mid = text.len() / 2;
|
||||
for (i, j) in (mid..text.len()).enumerate() {
|
||||
if text.as_bytes()[j] == b' ' {
|
||||
return text.split_at(mid + i);
|
||||
}
|
||||
}
|
||||
|
||||
text.split_at(mid)
|
||||
}
|
||||
|
||||
// Reduce size of string by adding etc. to it, and cutting some info
|
||||
pub fn etc_str(text: &str) -> String {
|
||||
format!("{}...", split_half(text).0.trim())
|
||||
}
|
||||
|
||||
/// Get timetable webpage
|
||||
pub async fn get_webpage(
|
||||
level: i8,
|
||||
|
@ -257,3 +161,12 @@ pub fn get_count<'a>(
|
|||
|
||||
(courses, counts)
|
||||
}
|
||||
|
||||
pub fn format_time_slot(start: usize, size: usize) -> String {
|
||||
let start_hour = 8 + (start * 15) / 60;
|
||||
let start_minute = (start * 15) % 60;
|
||||
let end_hour = start_hour + (size * 15) / 60;
|
||||
let end_minute = (start_minute + (size * 15)) % 60;
|
||||
|
||||
format!("{start_hour:02}h{start_minute:02}-{end_hour:02}h{end_minute:02}")
|
||||
}
|
||||
|
|
|
@ -2,58 +2,6 @@ use std::collections::HashMap;
|
|||
|
||||
use chrono::Utc;
|
||||
|
||||
/// Collection of char for the table
|
||||
pub enum TabChar {
|
||||
/// Vertical bar
|
||||
Bv,
|
||||
/// Horizontal bar
|
||||
Bh,
|
||||
/// Joint left
|
||||
Jl,
|
||||
/// Joint right
|
||||
Jr,
|
||||
/// Joint bottom left
|
||||
Jbl,
|
||||
/// Joint bottom right
|
||||
Jbr,
|
||||
/// Joint top left
|
||||
Jtl,
|
||||
/// Joint top right
|
||||
Jtr,
|
||||
/// Joint to top
|
||||
Jtt,
|
||||
/// Joint to bottom
|
||||
Jtb,
|
||||
/// Joint of the middle
|
||||
Jm,
|
||||
}
|
||||
|
||||
impl TabChar {
|
||||
/// Value of the element
|
||||
pub fn val(&self) -> char {
|
||||
match *self {
|
||||
Self::Bv => '│',
|
||||
Self::Bh => '─',
|
||||
Self::Jl => '├',
|
||||
Self::Jr => '┤',
|
||||
Self::Jbl => '└',
|
||||
Self::Jbr => '┘',
|
||||
Self::Jtl => '┌',
|
||||
Self::Jtr => '┐',
|
||||
Self::Jtt => '┴',
|
||||
Self::Jtb => '┬',
|
||||
Self::Jm => '┼',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Position for lines inside the table
|
||||
pub enum Position {
|
||||
Top,
|
||||
Middle,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
pub type InfoList = Vec<(chrono::DateTime<Utc>, i64)>;
|
||||
|
||||
pub struct InfoType {
|
||||
|
|
Loading…
Reference in a new issue