From b937d0bcb50054683102f4e2dd3b7e834938b5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Sun, 8 Sep 2024 22:19:53 +0200 Subject: [PATCH] feat: create a model for tasks with subtasks --- src/models/project.rs | 2 +- src/models/subtask.rs | 7 +++++-- src/models/task.rs | 23 ++++++++++++++++++++++- src/query/mod.rs | 4 +++- src/query/tasks.rs | 28 +++++++++++++++++++++++++++- src/server/tasks.rs | 34 +++++++++++++++++++++++++++++++++- 6 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/models/project.rs b/src/models/project.rs index 6e700d3..fb8da72 100644 --- a/src/models/project.rs +++ b/src/models/project.rs @@ -7,7 +7,7 @@ use validator::Validate; const TITLE_LENGTH_MIN: u64 = 1; const TITLE_LENGTH_MAX: u64 = 255; -#[derive(Queryable, Selectable, Serialize, Deserialize, PartialEq, Clone, Debug)] +#[derive(Queryable, Selectable, Identifiable, Serialize, Deserialize, PartialEq, Clone, Debug)] #[diesel(table_name = crate::schema::projects)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Project { diff --git a/src/models/subtask.rs b/src/models/subtask.rs index fbf9974..7aa9e84 100644 --- a/src/models/subtask.rs +++ b/src/models/subtask.rs @@ -1,5 +1,6 @@ -use chrono::NaiveDateTime; +use crate::models::task::Task; use crate::schema::subtasks; +use chrono::NaiveDateTime; use diesel::prelude::*; use serde::{Deserialize, Serialize}; use validator::Validate; @@ -7,7 +8,9 @@ use validator::Validate; const TITLE_LENGTH_MIN: u64 = 1; const TITLE_LENGTH_MAX: u64 = 255; -#[derive(Queryable, Selectable, Serialize, Deserialize, PartialEq, Clone, Debug)] +#[derive(Queryable, Selectable, Identifiable, Associations, Serialize, Deserialize, PartialEq, + Clone, Debug)] +#[diesel(belongs_to(Task, foreign_key = task_id))] #[diesel(table_name = subtasks)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Subtask { diff --git a/src/models/task.rs b/src/models/task.rs index c75eac9..7b4d7cc 100644 --- a/src/models/task.rs +++ b/src/models/task.rs @@ -4,11 +4,12 @@ use crate::schema::tasks; use diesel::prelude::*; use serde::{Deserialize, Serialize}; use validator::Validate; +use crate::models::subtask::Subtask; const TITLE_LENGTH_MIN: u64 = 1; const TITLE_LENGTH_MAX: u64 = 255; -#[derive(Queryable, Selectable, Serialize, Deserialize, PartialEq, Clone, Debug)] +#[derive(Queryable, Selectable, Identifiable, Serialize, Deserialize, PartialEq, Clone, Debug)] #[diesel(table_name = tasks)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Task { @@ -51,6 +52,26 @@ impl Task { } } +#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] +pub struct TaskWithSubtasks { + task: Task, + subtasks: Vec, +} + +impl TaskWithSubtasks { + pub fn new(task: Task, subtasks: Vec) -> Self { + Self { task, subtasks } + } + + pub fn task(&self) -> &Task { + &self.task + } + + pub fn subtasks(&self) -> &Vec { + &self.subtasks + } +} + #[derive(Insertable, Serialize, Deserialize, Validate, Clone, Debug)] #[diesel(table_name = tasks)] pub struct NewTask { diff --git a/src/query/mod.rs b/src/query/mod.rs index 792d45b..a9462ee 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -3,7 +3,7 @@ use crate::errors::error_vec::ErrorVec; use crate::models::category::Category; use crate::models::project::Project; use crate::models::subtask::Subtask; -use crate::models::task::Task; +use crate::models::task::{Task, TaskWithSubtasks}; pub(crate) mod tasks; pub(crate) mod projects; @@ -13,6 +13,7 @@ pub(crate) mod subtasks; pub(crate) enum QueryValue { Projects(Vec), Tasks(Vec), + TasksWithSubtasks(Vec), Subtasks(Vec), } @@ -26,5 +27,6 @@ pub(crate) enum QueryKey { Projects, Tasks, TasksInCategory(Category), + TasksWithSubtasksInCategory(Category), SubtasksOfTaskId(i32), } diff --git a/src/query/tasks.rs b/src/query/tasks.rs index 1cf22fc..c29a331 100644 --- a/src/query/tasks.rs +++ b/src/query/tasks.rs @@ -2,7 +2,7 @@ use dioxus::prelude::ServerFnError; use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery}; use crate::models::category::Category; use crate::query::{QueryErrors, QueryKey, QueryValue}; -use crate::server::tasks::get_tasks_in_category; +use crate::server::tasks::{get_tasks_in_category, get_tasks_with_subtasks_in_category}; @@ -22,3 +22,29 @@ async fn fetch_tasks_in_category(keys: Vec) -> QueryResult UseQuery { + use_get_query( + [ + QueryKey::TasksWithSubtasksInCategory( + category.clone()), + QueryKey::TasksInCategory(category), + QueryKey::Tasks + ], + fetch_tasks_with_subtasks_in_category + ) +} + +async fn fetch_tasks_with_subtasks_in_category(keys: Vec) + -> QueryResult { + if let Some(QueryKey::TasksWithSubtasksInCategory(category)) = keys.first() { + match get_tasks_with_subtasks_in_category(category.clone()).await { + Ok(tasks) => Ok(QueryValue::TasksWithSubtasks(tasks)), + Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), + Err(error) => panic!("Unexpected error: {:?}", error) + }.into() + } else { + panic!("Unexpected query keys: {:?}", keys); + } +} diff --git a/src/server/tasks.rs b/src/server/tasks.rs index 895551c..fb1d747 100644 --- a/src/server/tasks.rs +++ b/src/server/tasks.rs @@ -1,14 +1,16 @@ use chrono::{Datelike, Days, Months, NaiveDate}; use crate::errors::error::Error; use crate::errors::error_vec::ErrorVec; -use crate::models::task::{NewTask, Task}; +use crate::models::task::{NewTask, Task, TaskWithSubtasks}; use crate::server::database_connection::establish_database_connection; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper}; use dioxus::prelude::*; +use diesel::prelude::*; use time::util::days_in_year_month; use validator::Validate; use crate::errors::task_error::TaskError; use crate::models::category::{Category, ReoccurrenceInterval}; +use crate::models::subtask::Subtask; use crate::server::subtasks::restore_subtasks_of_task; #[server] @@ -72,6 +74,36 @@ pub(crate) async fn get_tasks_in_category(filtered_category: Category) Ok(results) } +#[server] +pub(crate) async fn get_tasks_with_subtasks_in_category(filtered_category: Category) -> Result< + Vec, + ServerFnError> +> { + use crate::schema::tasks; + + let mut connection = establish_database_connection() + .map_err::, _>(|_| vec![Error::ServerInternal].into())?; + + let tasks_in_category = tasks::table + .filter(filtered_category.eq_sql_predicate()) + .select(Task::as_select()).load(&mut connection) + .map_err::, _>(|_| vec![Error::ServerInternal].into())?; + + let subtasks = Subtask::belonging_to(&tasks_in_category) + .select(Subtask::as_select()) + .load(&mut connection) + .map_err::, _>(|_| vec![Error::ServerInternal].into())?; + + let tasks_with_subtasks = subtasks + .grouped_by(&tasks_in_category) + .into_iter() + .zip(tasks_in_category) + .map(|(pages, book)| TaskWithSubtasks::new(book, pages)) + .collect(); + + Ok(tasks_with_subtasks) +} + #[server] pub(crate) async fn edit_task(task_id: i32, new_task: NewTask) -> Result>> {