fuckass shit
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -371,3 +371,8 @@ compile_commands.json
|
|||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
_deps
|
_deps
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
|||||||
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "ctable"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dotenv = "0.15"
|
||||||
|
chrono = "0.4"
|
||||||
|
tui = "0.19"
|
||||||
|
crossterm = "0.26"
|
||||||
|
anyhow = "1.0"
|
||||||
|
untis = "0.3.0"
|
||||||
42
src/get/get.rs
Normal file
42
src/get/get.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/// Code straight copied from the docs.
|
||||||
|
/// Should work.
|
||||||
|
|
||||||
|
|
||||||
|
use dotenvy::dotenv;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn main() -> Result<(), untis::Error> {
|
||||||
|
// Get the school by its id.
|
||||||
|
let school = untis::schools::get_by_id(&42)?;
|
||||||
|
|
||||||
|
// Log in with your credentials. The school's details are filled in automatically.
|
||||||
|
let result = school.client_login(
|
||||||
|
&env::var("UNTIS_USERNAME")?,
|
||||||
|
&env::var("UNTIS_PASSWORD")? // avoid password in plaintext cause its fucking stupid as fuck. use env instead cuase securrrrre.
|
||||||
|
);
|
||||||
|
let mut client: untis::Client;
|
||||||
|
|
||||||
|
// Match the result to handle specific error cases.
|
||||||
|
match result {
|
||||||
|
Ok(v) => client = v,
|
||||||
|
Err(untis::Error::Rpc(err)) => {
|
||||||
|
if err.code == untis::jsonrpc::ErrorCode::InvalidCredentials.as_isize() {
|
||||||
|
println!("Invalid credentials");
|
||||||
|
}
|
||||||
|
return Err(untis::Error::Rpc(err));
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let date = chrono::Local::now().date_naive() + chrono::Duration::weeks(2);
|
||||||
|
|
||||||
|
// Get the client's own timetable until 2 weeks from now.
|
||||||
|
let timetable = client.own_timetable_until(&date.into())?;
|
||||||
|
|
||||||
|
for lesson in timetable {
|
||||||
|
println!("{:?}", lesson);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} // fn main
|
||||||
|
|
||||||
261
src/main.rs
Normal file
261
src/main.rs
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
use anyhow::{Context, Result};
|
||||||
|
use chrono::{Datelike, Duration, Local, NaiveDate};
|
||||||
|
use crossterm::{
|
||||||
|
event::{self, Event, KeyCode},
|
||||||
|
execute,
|
||||||
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
|
};
|
||||||
|
use dotenv::dotenv;
|
||||||
|
use std::{env, io};
|
||||||
|
use tui::{
|
||||||
|
backend::CrosstermBackend,
|
||||||
|
layout::{Constraint, Direction, Layout},
|
||||||
|
style::{Color, Modifier, Style},
|
||||||
|
text::{Span, Spans},
|
||||||
|
widgets::{Block, Borders, Paragraph},
|
||||||
|
Terminal,
|
||||||
|
};
|
||||||
|
use untis_rs::Client;
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
current_date: NaiveDate,
|
||||||
|
timetable: Vec<untis_rs::models::Period>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
fn new() -> Self {
|
||||||
|
let current_date = Local::now().date_naive();
|
||||||
|
App {
|
||||||
|
current_date,
|
||||||
|
timetable: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch_timetable(&mut self) -> Result<()> {
|
||||||
|
dotenv().ok();
|
||||||
|
|
||||||
|
let username = env::var("UNTIS_USERNAME").context("UNTIS_USERNAME not set")?;
|
||||||
|
let password = env::var("UNTIS_PASSWORD").context("UNTIS_PASSWORD not set")?;
|
||||||
|
let school = env::var("UNTIS_SCHOOL").context("UNTIS_SCHOOL not set")?;
|
||||||
|
let server = env::var("UNTIS_SERVER").context("UNTIS_SERVER not set")?;
|
||||||
|
|
||||||
|
let mut client = Client::new(&username, &password, &school, &server);
|
||||||
|
client.login()?;
|
||||||
|
|
||||||
|
// Get start and end of the current week (Monday to Friday)
|
||||||
|
let start = self
|
||||||
|
.current_date
|
||||||
|
.pred_opt()
|
||||||
|
.unwrap()
|
||||||
|
.week(chrono::Weekday::Mon)
|
||||||
|
.first_day();
|
||||||
|
let end = start + Duration::days(4);
|
||||||
|
|
||||||
|
self.timetable = client.get_timetable(start, end)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_week(&mut self) {
|
||||||
|
self.current_date += Duration::weeks(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prev_week(&mut self) {
|
||||||
|
self.current_date -= Duration::weeks(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_week_range(&self) -> (NaiveDate, NaiveDate) {
|
||||||
|
let start = self
|
||||||
|
.current_date
|
||||||
|
.pred_opt()
|
||||||
|
.unwrap()
|
||||||
|
.week(chrono::Weekday::Mon)
|
||||||
|
.first_day();
|
||||||
|
let end = start + Duration::days(4);
|
||||||
|
(start, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
// Setup terminal
|
||||||
|
enable_raw_mode()?;
|
||||||
|
let mut stdout = io::stdout();
|
||||||
|
execute!(stdout, EnterAlternateScreen)?;
|
||||||
|
let backend = CrosstermBackend::new(stdout);
|
||||||
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
|
// Create app
|
||||||
|
let mut app = App::new();
|
||||||
|
if let Err(e) = app.fetch_timetable() {
|
||||||
|
println!("Failed to fetch timetable: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
let res = run_app(&mut terminal, &mut app);
|
||||||
|
|
||||||
|
// Cleanup terminal
|
||||||
|
disable_raw_mode()?;
|
||||||
|
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
||||||
|
terminal.show_cursor()?;
|
||||||
|
|
||||||
|
if let Err(err) = res {
|
||||||
|
println!("Error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_app<B: tui::backend::Backend>(
|
||||||
|
terminal: &mut Terminal<B>,
|
||||||
|
app: &mut App,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
loop {
|
||||||
|
terminal.draw(|f| ui(f, app))?;
|
||||||
|
|
||||||
|
if let Event::Key(key) = event::read()? {
|
||||||
|
match key.code {
|
||||||
|
KeyCode::Char('q') => return Ok(()),
|
||||||
|
KeyCode::Char('n') => {
|
||||||
|
app.next_week();
|
||||||
|
if let Err(e) = app.fetch_timetable() {
|
||||||
|
println!("Failed to fetch timetable: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Char('p') => {
|
||||||
|
app.prev_week();
|
||||||
|
if let Err(e) = app.fetch_timetable() {
|
||||||
|
println!("Failed to fetch timetable: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ui<B: tui::backend::Backend>(f: &mut tui::Frame<B>, app: &App) {
|
||||||
|
let (week_start, week_end) = app.get_week_range();
|
||||||
|
let title = format!(
|
||||||
|
"Timetable - Week {} ({} - {})",
|
||||||
|
week_start.iso_week().week(),
|
||||||
|
week_start.format("%d.%m.%Y"),
|
||||||
|
week_end.format("%d.%m.%Y")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a layout
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.margin(1)
|
||||||
|
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
|
||||||
|
.split(f.size());
|
||||||
|
|
||||||
|
// Title block
|
||||||
|
let title_block = Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.style(Style::default().fg(Color::White));
|
||||||
|
let title_paragraph = Paragraph::new(title)
|
||||||
|
.block(title_block)
|
||||||
|
.style(Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD));
|
||||||
|
f.render_widget(title_paragraph, chunks[0]);
|
||||||
|
|
||||||
|
// Timetable content
|
||||||
|
let mut periods_by_day: Vec<Vec<&untis_rs::models::Period>> = vec![vec![]; 5]; // Monday to Friday
|
||||||
|
|
||||||
|
for period in &app.timetable {
|
||||||
|
let weekday = period.start_time.weekday().num_days_from_monday();
|
||||||
|
if weekday < 5 {
|
||||||
|
periods_by_day[weekday as usize].push(period);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let day_chunks = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints(
|
||||||
|
[
|
||||||
|
Constraint::Percentage(20),
|
||||||
|
Constraint::Percentage(20),
|
||||||
|
Constraint::Percentage(20),
|
||||||
|
Constraint::Percentage(20),
|
||||||
|
Constraint::Percentage(20),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(chunks[1]);
|
||||||
|
|
||||||
|
let days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];
|
||||||
|
for (i, day) in days.iter().enumerate() {
|
||||||
|
let day_block = Block::default()
|
||||||
|
.title(*day)
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.style(Style::default().fg(Color::White));
|
||||||
|
|
||||||
|
let mut day_content = Vec::new();
|
||||||
|
for period in &periods_by_day[i] {
|
||||||
|
let start = period.start_time.format("%H:%M").to_string();
|
||||||
|
let end = period.end_time.format("%H:%M").to_string();
|
||||||
|
let subject = period
|
||||||
|
.subjects
|
||||||
|
.first()
|
||||||
|
.map(|s| s.name.clone())
|
||||||
|
.unwrap_or_default();
|
||||||
|
let teacher = period
|
||||||
|
.teachers
|
||||||
|
.first()
|
||||||
|
.map(|t| t.name.clone())
|
||||||
|
.unwrap_or_default();
|
||||||
|
let room = period
|
||||||
|
.rooms
|
||||||
|
.first()
|
||||||
|
.map(|r| r.name.clone())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let period_text = Spans::from(vec![
|
||||||
|
Span::styled(
|
||||||
|
format!("{} - {}: ", start, end),
|
||||||
|
Style::default().fg(Color::Yellow),
|
||||||
|
),
|
||||||
|
Span::styled(
|
||||||
|
format!("{} ", subject),
|
||||||
|
Style::default().fg(Color::Green),
|
||||||
|
),
|
||||||
|
Span::styled(
|
||||||
|
format!("({}) ", teacher),
|
||||||
|
Style::default().fg(Color::Blue),
|
||||||
|
),
|
||||||
|
Span::styled(
|
||||||
|
format!("[{}]", room),
|
||||||
|
Style::default().fg(Color::Magenta),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
day_content.push(period_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if day_content.is_empty() {
|
||||||
|
day_content.push(Spans::from(Span::styled(
|
||||||
|
"No classes",
|
||||||
|
Style::default().fg(Color::Gray),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let day_paragraph = Paragraph::new(day_content).block(day_block);
|
||||||
|
f.render_widget(day_paragraph, day_chunks[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help text at the bottom
|
||||||
|
let help_text = Spans::from(vec![
|
||||||
|
Span::styled("n", Style::default().fg(Color::Yellow)),
|
||||||
|
Span::raw(" - next week, "),
|
||||||
|
Span::styled("p", Style::default().fg(Color::Yellow)),
|
||||||
|
Span::raw(" - previous week, "),
|
||||||
|
Span::styled("q", Style::default().fg(Color::Yellow)),
|
||||||
|
Span::raw(" - quit"),
|
||||||
|
]);
|
||||||
|
let help_paragraph = Paragraph::new(help_text);
|
||||||
|
f.render_widget(
|
||||||
|
help_paragraph,
|
||||||
|
Layout::default()
|
||||||
|
.constraints([Constraint::Length(1)].as_ref())
|
||||||
|
.margin(1)
|
||||||
|
.split(f.size())[0],
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user