things3_core/database/queries/
projects.rs1use crate::{
2 database::{conversions::safe_timestamp_convert, mappers::map_project_row, ThingsDatabase},
3 error::{Result as ThingsResult, ThingsError},
4 models::{Project, TaskStatus, ThingsId},
5};
6use chrono::{DateTime, Utc};
7use sqlx::Row;
8use tracing::{debug, instrument};
9
10impl ThingsDatabase {
11 #[instrument]
17 pub async fn get_all_projects(&self) -> ThingsResult<Vec<Project>> {
18 let rows = sqlx::query(
19 r"
20 SELECT
21 uuid, title, status,
22 area, notes,
23 creationDate, userModificationDate,
24 startDate, deadline
25 FROM TMTask
26 WHERE type = 1 AND trashed = 0
27 ORDER BY creationDate DESC
28 ",
29 )
30 .fetch_all(&self.pool)
31 .await
32 .map_err(|e| ThingsError::unknown(format!("Failed to fetch projects: {e}")))?;
33
34 let mut projects = Vec::new();
35 for row in rows {
36 let project = Project {
37 uuid: ThingsId::from_trusted(row.get::<String, _>("uuid")),
38 title: row.get("title"),
39 status: TaskStatus::from_i32(row.get("status")).unwrap_or(TaskStatus::Incomplete),
40 area_uuid: row
41 .get::<Option<String>, _>("area")
42 .map(ThingsId::from_trusted),
43 notes: row.get("notes"),
44 deadline: row
45 .get::<Option<i64>, _>("deadline")
46 .and_then(|ts| DateTime::from_timestamp(ts, 0))
47 .map(|dt| dt.date_naive()),
48 start_date: row
49 .get::<Option<i64>, _>("startDate")
50 .and_then(|ts| DateTime::from_timestamp(ts, 0))
51 .map(|dt| dt.date_naive()),
52 tags: Vec::new(), tasks: Vec::new(), created: {
55 let ts_f64 = row.get::<f64, _>("creationDate");
56 let ts = safe_timestamp_convert(ts_f64);
57 DateTime::from_timestamp(ts, 0).unwrap_or_else(Utc::now)
58 },
59 modified: {
60 let ts_f64 = row.get::<f64, _>("userModificationDate");
61 let ts = safe_timestamp_convert(ts_f64);
62 DateTime::from_timestamp(ts, 0).unwrap_or_else(Utc::now)
63 },
64 };
65 projects.push(project);
66 }
67
68 debug!("Fetched {} projects", projects.len());
69 Ok(projects)
70 }
71
72 #[instrument(skip(self))]
78 pub async fn get_projects(&self, limit: Option<usize>) -> ThingsResult<Vec<Project>> {
79 let _ = limit; self.get_all_projects().await
81 }
82
83 #[instrument(skip(self))]
91 pub async fn get_project_by_uuid(&self, id: &ThingsId) -> ThingsResult<Option<Project>> {
92 let row = sqlx::query(
93 r"
94 SELECT
95 uuid, title, status,
96 area, notes,
97 creationDate, userModificationDate,
98 startDate, deadline,
99 trashed, type
100 FROM TMTask
101 WHERE uuid = ? AND type = 1
102 ",
103 )
104 .bind(id.as_str())
105 .fetch_optional(&self.pool)
106 .await
107 .map_err(|e| ThingsError::unknown(format!("Failed to fetch project: {e}")))?;
108
109 if let Some(row) = row {
110 let trashed: i64 = row.get("trashed");
111 if trashed == 1 {
112 return Ok(None);
113 }
114 Ok(Some(map_project_row(&row)))
115 } else {
116 Ok(None)
117 }
118 }
119}