fuckass shit
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -371,3 +371,8 @@ compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_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