valis_core/modules/tasks/todoist/
core.rs1use std::error::Error;
2use std::result::Result;
3
4use rusqlite::{Connection, params, Row};
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7
8use db::DatabaseOperations;
9
10use crate::modules::db;
11use crate::modules::db::get_connection;
12use crate::modules::projects::agile::core::Sprint;
13
14#[derive(Debug, Deserialize, Serialize)]
15pub struct Task {
16 pub id: String,
17 pub content: Option<String>,
18 pub labels: Vec<String>,
19 }
21
22#[derive(Debug, Serialize, Deserialize)]
23pub struct SprintTask {
24 pub sprint_id: Uuid,
25 pub todoist_task_id: String,
26}
27
28impl Task {
29 pub fn add_to_sprint(&self, db: &str, sprint: Sprint) -> Result<(), rusqlite::Error> {
30 add_task_to_sprint(db, &sprint.id, self.id.to_string())
31 }
32}
33
34impl DatabaseOperations<String> for Task {
35 fn save(&self, db: &str) -> Result<(), std::fmt::Error> {
36 todo!()
37 }
38
39 fn get(id: String, db: &str) -> Result<Self, rusqlite::Error> where Self: Sized {
40 todo!()
41 }
42
43 fn get_all(db: &str) -> Result<Vec<Task>, rusqlite::Error> {
44 let conn = get_connection(db);
45
46 let mut stmt = conn.prepare("SELECT * FROM todoist_tasks").ok().unwrap();
47 let tasks_iter = stmt
48 .query_map([], |row| {
49 Ok(Task {
50 id: row.get(0).ok().unwrap(),
51 content: row.get(1).ok().unwrap(),
52 labels: vec![], })
54 })
55 .ok()
56 .unwrap();
57
58 let mut tasks = Vec::new();
59 for task_result in tasks_iter {
60 let mut task = task_result.ok().unwrap();
61 let mut stmt = conn
62 .prepare(
63 "
64 SELECT todoist_labels.label FROM todoist_labels
65 JOIN todoist_task_labels ON todoist_labels.id = todoist_task_labels.todoist_label_id
66 WHERE todoist_task_labels.todoist_task_id = ?
67 ",
68 )
69 .ok()
70 .unwrap();
71 let labels_iter = stmt
72 .query_map(params![task.id], |row| row.get(0))
73 .ok()
74 .unwrap();
75
76 for label_result in labels_iter {
77 task.labels.push(label_result.ok().unwrap());
78 }
79
80 tasks.push(task);
81 }
82
83 Ok(tasks)
84 }
85
86 fn map(row: &Row<'_>) -> Result<Self, rusqlite::Error> where Self: Sized {
87 todo!()
88 }
89}
90
91fn get_task_by_id(db: &str, id: String) -> Result<Task, std::fmt::Error> {
92 let conn = get_connection(db);
93
94 let mut stmt = conn
95 .prepare("SELECT * FROM tasks WHERE id = ?")
96 .ok()
97 .unwrap();
98 let mut task = stmt
99 .query_row(params![id], |row| {
100 Ok(Task {
101 id: row.get(0).ok().unwrap(),
102 content: row.get(1).ok().unwrap(),
103 labels: vec![], })
105 })
106 .ok()
107 .unwrap();
108
109 let mut stmt = conn
110 .prepare(
111 "
112 SELECT todoist_labels.label FROM todoist_labels
113 JOIN todoist_task_labels ON todoist_labels.id = todoist_task_labels.label_id
114 WHERE todoist_task_labels.task_id = ?
115 ",
116 )
117 .ok()
118 .unwrap();
119 let labels_iter = stmt.query_map(params![id], |row| row.get(0)).ok().unwrap();
120
121 for label_result in labels_iter {
122 task.labels.push(label_result.ok().unwrap());
123 }
124
125 Ok(task)
126}
127
128pub fn get_all_tasks(db: &str) -> Result<Vec<Task>, rusqlite::Error> {
129 let conn = get_connection(db);
130
131 let mut stmt = conn.prepare("SELECT * FROM todoist_tasks").ok().unwrap();
132 let tasks_iter = stmt
133 .query_map([], |row| {
134 Ok(Task {
135 id: row.get(0).ok().unwrap(),
136 content: row.get(1).ok().unwrap(),
137 labels: vec![], })
139 })
140 .ok()
141 .unwrap();
142
143 let mut tasks = Vec::new();
144 for task_result in tasks_iter {
145 let mut task = task_result.ok().unwrap();
146 let mut stmt = conn
147 .prepare(
148 "
149 SELECT todoist_labels.label FROM todoist_labels
150 JOIN todoist_task_labels ON todoist_labels.id = todoist_task_labels.todoist_label_id
151 WHERE todoist_task_labels.todoist_task_id = ?
152 ",
153 )
154 .ok()
155 .unwrap();
156 let labels_iter = stmt
157 .query_map(params![task.id], |row| row.get(0))
158 .ok()
159 .unwrap();
160
161 for label_result in labels_iter {
162 task.labels.push(label_result.ok().unwrap());
163 }
164
165 tasks.push(task);
166 }
167
168 Ok(tasks)
169}
170
171async fn get_todoist_tasks(todoist_token: &str) -> Result<Vec<Task>, reqwest::Error> {
172 let client = reqwest::Client::new();
173 let response = client
174 .get("https://api.todoist.com/rest/v2/tasks")
175 .header("Authorization", format!("Bearer {}", todoist_token))
176 .send()
177 .await?
178 .text()
179 .await?;
180
181 Ok(serde_json::from_str::<Vec<Task>>(&response).ok().unwrap())
182}
183
184fn sync_to_db(tasks: &Vec<Task>, db: &str) -> rusqlite::Result<()> {
185 let conn = Connection::open(db)?;
186
187 for task in tasks {
188 let result: Result<String, rusqlite::Error> = conn.query_row(
189 "SELECT id FROM todoist_tasks WHERE id = ?1",
190 params![task.id],
191 |row| row.get(0),
192 );
193
194 match result {
195 Ok(_) => {
196 conn.execute(
198 "UPDATE todoist_tasks SET content = ?1 WHERE id = ?2",
199 params![task.content, task.id],
200 )?;
201 }
202 Err(_) => {
203 conn.execute(
205 "INSERT INTO todoist_tasks (id, content) VALUES (?1, ?2)",
206 params![task.id, task.content],
207 )?;
208 }
209 }
210 for label in &task.labels {
211 conn.execute(
213 "INSERT OR IGNORE INTO todoist_labels (label) VALUES (?)",
214 [label],
215 )?;
216
217 let label_id: i32 = conn.query_row(
219 "SELECT id FROM todoist_labels WHERE label = ?",
220 [label],
221 |row| row.get(0),
222 )?;
223
224 conn.execute(
226 "INSERT OR IGNORE INTO todoist_task_labels (todoist_task_id, todoist_label_id) VALUES (?, ?)",
227 [&task.id, &(label_id.to_string())],
228 )?;
229 }
230 }
231
232 let db_tasks: Vec<String> = conn
234 .prepare("SELECT id FROM todoist_tasks")?
235 .query_map([], |row| row.get(0))?
236 .collect::<Result<_, _>>()?;
237
238 for db_task in db_tasks {
239 if !tasks.iter().any(|task| task.id == db_task) {
240 conn.execute("DELETE FROM todoist_tasks WHERE id = ?1", params![db_task])?;
241 }
242 }
243
244 Ok(())
245}
246
247pub fn add_task_to_sprint(db: &str, sprint_id: &Uuid, task_id: String) -> Result<(), rusqlite::Error> {
248 let conn = get_connection(db);
249 conn.execute(
250 "INSERT INTO sprint_todoist_task (sprint_id, todoist_task_id) VALUES (?1, ?2)",
251 [&sprint_id.to_string(), &task_id],
252 )?;
253
254 Ok(())
255}
256
257pub async fn sync(token: &str, db: &str) -> Result<(), std::fmt::Error> {
258 let tasks = get_todoist_tasks(token).await.ok().unwrap();
259 sync_to_db(&tasks, db).ok().unwrap();
260
261 Ok(())
262}