task_manager/
dao.rs

1use crate::{
2    datetime_str_is_past, display_message, get_connection, get_tasks, select_board,
3    user_input::{get_user_confirmation, get_user_date, get_user_input, get_user_selection},
4    Board, Color, Comment, Record, Task, DATETIME_FORMAT, SAMPLE_TITLE, TABLE_BOARDS,
5    TABLE_COMMENTS, TABLE_TASKS,
6};
7use chrono::Utc;
8use rusqlite::{params, Result};
9use tabled::{Disable, Style, Table};
10
11pub fn get_records_qtd(table: &str) -> Result<u16> {
12    let conn = get_connection();
13    let query = format!("SELECT COUNT(*) FROM {table}");
14    let mut stmt = conn.prepare(&query)?;
15    let query_result = stmt.query_map([], |row| Ok(Record { qtd: row.get(0)? }))?;
16    let qtd_records = query_result.last().unwrap()?.qtd;
17    Ok(qtd_records)
18}
19
20///Create database table if not exists
21pub fn create_database() -> Result<()> {
22    let conn = get_connection();
23
24    //boards
25    conn.execute(
26        &format!(
27            "CREATE TABLE IF NOT EXISTS {TABLE_BOARDS} (
28                  id              INTEGER PRIMARY KEY,
29                  title           VARCHAR(255) NOT NULL
30                  );"
31        ),
32        [],
33    )?;
34
35    //tasks
36    conn.execute(
37        &format!(
38            "CREATE TABLE IF NOT EXISTS {TABLE_TASKS} (
39                  id              INTEGER PRIMARY KEY,
40                  title           VARCHAR(255) NOT NULL,
41                  done              INTEGER NOT NULL,
42                  board_id          INTEGER NOT NULL,
43                  created_at           VARCHAR(255) NOT NULL,
44                  reminder           VARCHAR(255) NOT NULL,
45                  FOREIGN KEY(board_id) REFERENCES {TABLE_BOARDS}(id)
46                );"
47        ),
48        [],
49    )?;
50
51    //comments
52    conn.execute(
53        &format!(
54            "CREATE TABLE IF NOT EXISTS {TABLE_COMMENTS} (
55                  id              INTEGER PRIMARY KEY,
56                  title           VARCHAR(255) NOT NULL,
57                  task_id          INTEGER NOT NULL,
58                  created_at           VARCHAR(255) NOT NULL,
59                  FOREIGN KEY(task_id) REFERENCES {TABLE_TASKS}(id)
60                  );"
61        ),
62        [],
63    )?;
64
65    Ok(())
66}
67
68pub fn edit_board(title: &str, id: u16) -> Result<()> {
69    let conn = get_connection();
70    let title = get_user_input("New Board title", title, true).unwrap();
71
72    conn.execute(
73        &format!("UPDATE {TABLE_BOARDS} SET title = ?1 WHERE id = ?2"),
74        params![title, id],
75    )
76    .unwrap();
77    Ok(())
78}
79
80pub fn get_comments_by_task_id(task_id: u16) -> Result<Vec<Comment>> {
81    let conn = get_connection();
82    let query = format!("SELECT * FROM {TABLE_COMMENTS} WHERE task_id = {task_id}");
83    let mut comments: Vec<Comment> = Vec::new();
84
85    let mut stmt = conn.prepare(&query)?;
86
87    let result_iter = stmt.query_map([], |row| {
88        Ok(Comment {
89            id: row.get(0)?,
90            title: row.get(1)?,
91            created_at: row.get(3)?,
92        })
93    })?;
94
95    for i in result_iter {
96        comments.push(i?);
97    }
98    Ok(comments)
99}
100
101///title, id
102pub fn select_task(done: u8) -> Option<(String, u16)> {
103    let records_qtd = get_records_qtd(TABLE_TASKS).unwrap();
104    if records_qtd == 0 {
105        display_message("info", "No Tasks found in database", Color::Cyan);
106        return None;
107    }
108    let mut query = format!("SELECT * FROM {TABLE_TASKS} WHERE done = {done}");
109
110    let all_boards = get_user_confirmation("From all boards");
111
112    if !all_boards {
113        let (_, board_id) = select_board().unwrap();
114        query.push_str(&format!(" AND board_id = {board_id}"));
115    }
116
117    let tasks_raw = get_tasks(&query).unwrap();
118    if tasks_raw.len() == 0 {
119        display_message(
120            "info",
121            "No Tasks found in database with these criteria",
122            Color::Cyan,
123        );
124        return None;
125    }
126
127    let mut tasks: Vec<String> = tasks_raw
128        .iter()
129        .map(|x| format!("{} - {}", &x.id, &x.title))
130        .collect();
131
132    tasks.sort();
133
134    let (selected_task, _) = get_user_selection(&tasks, "Task");
135
136    let task_id: u16 = selected_task
137        .split_whitespace()
138        .next()
139        .unwrap()
140        .parse()
141        .unwrap();
142
143    let selected_task = tasks_raw.iter().find(|x| x.id == task_id).unwrap();
144    let selected_task = (selected_task.title.to_string(), selected_task.id);
145    Some(selected_task)
146}
147
148pub fn list_delayed_tasks() {
149    let query = format!("SELECT * FROM {TABLE_TASKS} WHERE done = 0");
150    let tasks_raw = get_tasks(&query).unwrap();
151
152    if tasks_raw.len() == 0 {
153        return;
154    }
155
156    let delayed_tasks: Vec<&Task> = tasks_raw
157        .iter()
158        .filter(|x| datetime_str_is_past(&x.reminder))
159        .collect();
160
161    if delayed_tasks.len() == 0 {
162        return;
163    }
164    println!("Delayed Tasks: {}", &delayed_tasks.len());
165
166    let table = Table::new(delayed_tasks)
167        .with(Style::modern())
168        .with(Disable::Column(2..4));
169
170    println!("{}", table);
171}
172
173//Delete database records
174pub fn delete_record_by_id(table: &str, id: u16) -> Result<()> {
175    let conn = get_connection();
176    conn.execute(&format!("DELETE FROM {table} WHERE id = ?1"), params![id])?;
177    Ok(())
178}
179
180pub fn get_boards() -> Result<Vec<Board>> {
181    let conn = get_connection();
182    let query = format!("SELECT * FROM {TABLE_BOARDS}");
183    let mut records: Vec<Board> = Vec::new();
184
185    let mut stmt = conn.prepare(&query)?;
186
187    let result_iter = stmt.query_map([], |row| {
188        Ok(Board {
189            id: row.get(0)?,
190            title: row.get(1)?,
191        })
192    })?;
193
194    for i in result_iter {
195        records.push(i?);
196    }
197    Ok(records)
198}
199
200pub fn create_comment(task_id: u16) -> Result<()> {
201    let conn = get_connection();
202
203    let title = get_user_input("Comment title", SAMPLE_TITLE, true).unwrap();
204    let created_at = Utc::now().to_rfc2822();
205
206    conn.execute(
207        &format!("INSERT INTO {TABLE_COMMENTS} (title, task_id, created_at) VALUES (?1, ?2, ?3)"),
208        params![title, task_id, created_at],
209    )?;
210    Ok(())
211}
212
213pub fn set_reminder(task_id: u16) -> Result<()> {
214    let conn = get_connection();
215
216    let reminder = get_user_date(true, false);
217    if reminder.is_none() {
218        return Ok(());
219    }
220
221    conn.execute(
222        &format!("UPDATE {TABLE_TASKS} SET reminder = ?1 WHERE id = ?2"),
223        params![reminder, task_id],
224    )?;
225    Ok(())
226}
227
228pub fn create_task() -> Result<()> {
229    let conn = get_connection();
230    let boards_qtd = get_records_qtd(TABLE_BOARDS).unwrap();
231
232    if boards_qtd == 0 {
233        display_message("info", "Create a initial Board", Color::Cyan);
234        create_board().unwrap();
235    }
236
237    let title = get_user_input("Task title", SAMPLE_TITLE, true).unwrap();
238    let board_id = select_board().unwrap().1;
239    let created_at = Utc::now().format(DATETIME_FORMAT).to_string();
240    let with_reminder = get_user_confirmation("Set reminder");
241
242    let reminder = match with_reminder {
243        true => get_user_date(true, true).unwrap(),
244        false => "".to_string(),
245    };
246
247    conn.execute(
248        &format!("INSERT INTO {TABLE_TASKS} (title, done, board_id, created_at, reminder) VALUES (?1, ?2, ?3, ?4, ?5)"),
249        params![title, 0, board_id, created_at, reminder],
250    )
251    .unwrap();
252    Ok(())
253}
254
255pub fn create_board() -> Result<()> {
256    let conn = get_connection();
257    let title = get_user_input("Board title", SAMPLE_TITLE, true).unwrap();
258    let board = Board { id: 0, title };
259
260    conn.execute(
261        &format!("INSERT INTO {TABLE_BOARDS} (title) VALUES (?1)"),
262        params![board.title],
263    )
264    .unwrap();
265    Ok(())
266}
267
268pub fn switch_task_status(task_id: u16) -> Result<()> {
269    let conn = get_connection();
270
271    let done: u8 = match get_user_confirmation("Done") {
272        true => 1,
273        false => 0,
274    };
275
276    conn.execute(
277        &format!("UPDATE {TABLE_TASKS} SET done = ?1 WHERE id = ?2"),
278        params![done, task_id],
279    )
280    .unwrap();
281    Ok(())
282}