From f55199ad7c66bdc2b2430ecc790fafe830ed666d Mon Sep 17 00:00:00 2001 From: ottjk Date: Fri, 23 Aug 2024 15:24:59 -0400 Subject: added homework management it now manages homeworks; I also changed some lecture things as well --- src/homework.rs | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lecture.rs | 7 ++-- src/main.rs | 65 +++++++++++++++++++++++------- 3 files changed, 176 insertions(+), 17 deletions(-) create mode 100644 src/homework.rs (limited to 'src') diff --git a/src/homework.rs b/src/homework.rs new file mode 100644 index 0000000..f67f9e4 --- /dev/null +++ b/src/homework.rs @@ -0,0 +1,121 @@ +use std::{ + fs, + io, + path::PathBuf, +}; +use tinytemplate::TinyTemplate; +use regex::Regex; +use serde::Serialize; + +use crate::{Course, Config, rofi_picker}; + +#[derive(Serialize)] +struct HomeworkContext<'a> { + course: &'a String, + number: u32, +} + +fn display_name(name: &String) -> String { + let pattern = Regex::new(r"([a-z]+)(\d{3})").unwrap(); + let caps = pattern.captures(name).unwrap(); + let dept = caps.get(1).unwrap().as_str().to_uppercase(); + let number = caps.get(2).unwrap().as_str(); + let display_name = format!("{dept} {number}"); + println!("{display_name}"); + + return display_name; +} + +fn init_homework(course: &Course, config: &Config, context: &HomeworkContext) -> (PathBuf, String) { + let template_stream = fs::read(&config.homework_template) + .expect("Couldn't open template."); + let template_string = String::from_utf8_lossy(&template_stream); + + let mut template = TinyTemplate::new(); + template.add_template("main", &template_string) + .expect("Failed initializing template."); + + let rendered = template.render("main", &context) + .expect("Failed applying template"); + + let homework_file = format!("homework{}.tex", context.number); + let homework_directory = config.root.join(&course.semester).join(&course.name) + .join(format!("homework{}", context.number)); + fs::create_dir(&homework_directory).unwrap(); + fs::write(homework_directory.join(homework_file.clone()), rendered) + .expect("Failed making main tex for course."); + + return (homework_directory, homework_file); +} + +fn detect_homeworks(path: &PathBuf) -> io::Result> { + let mut homework_numbers: Vec = Vec::new(); + let dir_iter = fs::read_dir(path)?; + let pattern = Regex::new(r"homework(\d+)").unwrap(); + + for entry in dir_iter { + let entry_path = entry?.path(); + let name = entry_path.file_name().unwrap().to_string_lossy(); + if let Some(caps) = pattern.captures(&name) { + let homework_number = caps[1].parse::().unwrap(); + homework_numbers.push(homework_number); + } + } + + homework_numbers.sort(); + + return Ok(homework_numbers); +} + +pub fn new_homework(course: &Course, config: &Config) -> (PathBuf, String) { + let course_directory = config.root.join(&course.semester).join(&course.name); + + if !course_directory.try_exists().unwrap() { + fs::create_dir_all(&course_directory).unwrap(); + } + + let homeworks = detect_homeworks(&course_directory) + .expect("Could not make sense of the homeworks situation."); + let new_homework = homeworks.last().unwrap_or(&0).clone() + 1; + + let name = display_name(&course.name); + let homework_context = HomeworkContext { course: &name, number: new_homework }; + return init_homework(course, config, &homework_context); +} + +pub fn recent_homework(course: &Course, config: &Config) -> (PathBuf, String) { + let course_directory = config.root.join(&course.semester).join(&course.name); + + let homeworks = detect_homeworks(&course_directory) + .expect("Could not make sense of the homeworks situation."); + let most_recent = homeworks.last().expect("No homeworks."); + + let homework_directory = course_directory.join(format!("homework{most_recent}")); + let homework_file = format!("homework{most_recent}.tex"); + + return (homework_directory, homework_file); +} + +pub fn view_homeworks(course: &Course, config: &Config) -> Result<(PathBuf, String), &'static str> { + let course_directory = config.root.join(&course.semester).join(&course.name); + let homeworks = detect_homeworks(&course_directory) + .expect("Could not make sense of the homeworks situation."); + + let mut homeworks_string = String::new(); + + for (idx, hw) in homeworks.iter().enumerate() { + if idx != 0 { + homeworks_string.push_str("\n"); + } + homeworks_string.push_str(&format!("Homework {hw}").to_string()); + } + + let hw_idx = rofi_picker("Homeworks", homeworks_string)?; + + let picked_homework = homeworks[hw_idx as usize]; + + let homework_directory = course_directory.join(format!("homework{picked_homework}")); + let homework_file = format!("homework{picked_homework}.tex"); + + return Ok((homework_directory, homework_file)); +} diff --git a/src/lecture.rs b/src/lecture.rs index 89acea0..3cb054f 100644 --- a/src/lecture.rs +++ b/src/lecture.rs @@ -9,7 +9,7 @@ use regex::Regex; use crate::{Course, CourseContext, Config}; fn init_lecture(course: &Course, config: &Config) { - let template_stream = fs::read(&config.template) + let template_stream = fs::read(&config.lecture_template) .expect("Couldn't open template."); let template_string = String::from_utf8_lossy(&template_stream); @@ -59,7 +59,7 @@ fn update_main(main_path: &PathBuf, new_lessons: String) -> io::Result<()> { Ok(()) } -pub fn new_lesson(course: &Course, config: &Config) -> String { +pub fn new_lesson(course: &Course, config: &Config) -> (PathBuf, String) { let lecture_directory = config.root.join(&course.semester).join(&course.name) .join("lecture"); @@ -85,7 +85,8 @@ pub fn new_lesson(course: &Course, config: &Config) -> String { lessons_string.push_str(format!(" \\input{{les{num}.tex}}\n").as_str()); } + println!("{lecture_directory:?}"); update_main(&lecture_directory.join("main.tex"), lessons_string) .expect("Unable to update main.tex"); - return lesson_file; + return (lecture_directory, lesson_file); } diff --git a/src/main.rs b/src/main.rs index 847d096..0a02206 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,12 @@ use std::{ fs, }; use lecture::new_lesson; +use homework::{new_homework, recent_homework, view_homeworks}; use serde::{Deserialize, Serialize}; use clap::{Parser, Subcommand, Args}; mod lecture; +mod homework; #[derive(Parser)] struct Cli { @@ -54,7 +56,8 @@ struct OpenArgs { #[derive(Deserialize, Serialize)] struct Config { root: PathBuf, - template: PathBuf, + lecture_template: PathBuf, + homework_template: PathBuf, } #[derive(Deserialize, Serialize)] @@ -110,8 +113,9 @@ fn create_config(path: &PathBuf) -> Config { root = PathBuf::from(input.trim()); resolve_home(&mut root); - let template = path.join("lecture_template.tex"); - let config = Config { root, template }; + let lecture_template = path.join("lecture_template.tex"); + let homework_template = path.join("lecture_template.tex"); + let config = Config { root, lecture_template, homework_template }; let toml = toml::to_string(&config) .expect("Unable to convert config struct to toml string."); @@ -208,6 +212,13 @@ fn launch_tex(directory: &PathBuf, file_name: &String) { xoppdog.kill().expect("Couldn't kill xoppdog."); } +fn launch_pdf(directory: &PathBuf, file_name: &String) { + Command::new("zathura") + .arg(directory.join(file_name).as_os_str()) + .spawn() + .expect("Failed to start zathura."); +} + fn init_command(args: InitArgs, config: &Config) -> Course { let course = Course { name: args.name, @@ -216,8 +227,7 @@ fn init_command(args: InitArgs, config: &Config) -> Course { semester: args.semester }; - let lecture_directory = config.root.join(&course.semester).join(&course.name) - .join("lecture"); + let lecture_directory = config.root.join(&course.semester).join(&course.name); fs::create_dir_all(&lecture_directory) .expect("Failed creating course dir."); @@ -230,15 +240,43 @@ fn open_command(args: OpenArgs, courses: &HashMap, config: &Conf None => pick_course(courses)?, }; - let in_lecture = rofi_picker("Type", String::from("Lecture\nHomework"))? == 0; - let new_file = rofi_picker("Action", String::from("New\nEdit"))? == 0; - - let course_directory = config.root.join(&course.semester).join(&course.name).join("lecture"); - let file_name = match (in_lecture, new_file) { - (true, true) => new_lesson(&course, &config), - (_, _) => String::from("main.tex"), + let in_lecture = rofi_picker("Type", String::from("Lecture\nHomework"))?; + + let _ = match in_lecture { + 0 => open_lecture(&course, &config), + _ => open_homework(&course, &config), }; - + + Ok(()) +} + +fn open_lecture(course: &Course, config: &Config) -> Result<(), &'static str>{ + let action = rofi_picker("Action", String::from("New Lesson\nEdit Notes\nView"))?; + + let course_directory = config.root.join(&course.semester).join(&course.name); + let (course_directory, file_name) = match action { + 0 => new_lesson(&course, &config), + 1 => (course_directory.join("lecture"), String::from("main.tex")), + _ => (course_directory.join("lecture"), String::from("main.pdf")), + }; + + match action { + 2 => launch_pdf(&course_directory, &file_name), + _ => launch_tex(&course_directory, &file_name), + }; + + Ok(()) +} + +fn open_homework(course: &Course, config: &Config) -> Result<(), &'static str>{ + let action = rofi_picker("Action", String::from("Edit Recent\nNew Homework\nView Previous"))?; + + let (course_directory, file_name) = match action { + 0 => recent_homework(&course, &config), + 1 => new_homework(&course, &config), + _ => view_homeworks(&course, &config)?, + }; + launch_tex(&course_directory, &file_name); Ok(()) @@ -266,6 +304,5 @@ fn main() { save_courses(&courses, &config_path); }, Commands::Open(args) => open_command(args, &courses, &config).unwrap(), - }; } -- cgit v1.3