//! Request bodies and path parameters, kept apart from the domain models so the public //! contract does not change every time a domain model does. use chrono::{DateTime, NaiveTime, Utc}; use schemars::JsonSchema; use serde::Deserialize; use crate::core::models::{ course::{CourseData, CourseId, CourseKind}, course_file::CourseFileId, focus::TimetaggerRecord, grade::{GradeData, GradeId}, tag::TagId, task::{TaskData, TaskId}, time_slot::{TimeSlotData, TimeSlotId}, }; #[derive(Deserialize, JsonSchema)] pub struct CoursePath { pub id: CourseId, } #[derive(Deserialize, JsonSchema)] pub struct SlotPath { /// Id of the course pub id: CourseId, pub slot_id: TimeSlotId, } #[derive(Deserialize, JsonSchema)] pub struct GradePath { pub id: GradeId, } #[derive(Deserialize, JsonSchema)] pub struct TaskPath { pub id: TaskId, } #[derive(Deserialize, JsonSchema)] pub struct LectureNotePath { pub id: CourseFileId, } /// Period `[from, to[`, at most 62 days #[derive(Deserialize, JsonSchema)] pub struct PeriodQuery { pub from: DateTime, pub to: DateTime, } /// A course or an association, without its slots: they are added and removed one by one #[derive(Deserialize, JsonSchema)] pub struct CourseBody { /// `course` by default. Cannot be changed once created. pub kind: Option, pub name: String, pub code: Option, /// At least one for a course #[serde(default)] pub teachers: Vec, /// http(s) url of the main page of the course pub main_page_url: Option, } #[derive(Deserialize, JsonSchema)] pub struct TimeSlotBody { pub tag_id: TagId, /// ISO weekday: 1 = monday ... 5 = friday pub weekday: u8, /// Between 08:00 and 19:00, before `end_time` pub start_time: NaiveTime, pub end_time: NaiveTime, } /// A grade on any scale, e.g. 20 out of 100 #[derive(Deserialize, JsonSchema)] pub struct GradeBody { pub name: String, /// Between 0 and `max_score` pub score: f64, /// Positive pub max_score: f64, /// Positive, relative to the other grades of the course pub weight: f64, } #[derive(Deserialize, JsonSchema)] pub struct TaskBody { pub name: String, pub description: Option, /// http(s) urls #[serde(default)] pub links: Vec, pub deadline: Option>, #[serde(default)] pub done: bool, /// At least one for a task, optional for a subtask #[serde(default)] pub tag_ids: Vec, } #[derive(Deserialize, JsonSchema)] pub struct CreateTaskBody { /// Creates a subtask of this task, which must be a task of the same course pub parent_id: Option, #[serde(flatten)] pub task: TaskBody, } /// End (or pause) of a focus session #[derive(Deserialize, JsonSchema)] pub struct FocusStopBody { /// Length of the session, at most 24 hours pub seconds: u32, /// Record returned when the session started, to stop it pub timetagger: Option, } #[derive(Deserialize, JsonSchema)] pub struct LectureNoteBody { /// Subtitle of the pdf, e.g. "Lecture 2 Notes: Memory subsystem" pub title: String, /// Markdown, without front matter: it is generated from the course pub content: String, } impl From for CourseData { fn from(value: CourseBody) -> Self { CourseData { kind: value.kind.unwrap_or(CourseKind::Course), name: value.name, code: value.code, teachers: value.teachers, main_page_url: value.main_page_url, } } } impl From for TimeSlotData { fn from(value: TimeSlotBody) -> Self { TimeSlotData { tag_id: value.tag_id, weekday: value.weekday, start_time: value.start_time, end_time: value.end_time, } } } impl From for GradeData { fn from(value: GradeBody) -> Self { GradeData { name: value.name, score: value.score, max_score: value.max_score, weight: value.weight, } } } impl From for TaskData { fn from(value: TaskBody) -> Self { TaskData { name: value.name, description: value.description, links: value.links, deadline: value.deadline, done: value.done, tag_ids: value.tag_ids, } } }