task.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. use std::str::FromStr;
  2. use askama::Template;
  3. use serde::{Deserialize, Serialize};
  4. use sqlx::pool::PoolConnection;
  5. use sqlx::Postgres;
  6. use sqlx::postgres::PgQueryResult;
  7. use strum::IntoEnumIterator;
  8. use strum_macros::EnumIter;
  9. use uuid::Uuid;
  10. use tide::{http::mime, Request};
  11. use crate::home::NotFoundTemplate;
  12. use crate::{State, user};
  13. use crate::matrix::post_task_create;
  14. // SQL STUFF
  15. pub async fn get_task( conn: &mut PoolConnection<Postgres>, key: uuid::Uuid) -> Task {
  16. let task = sqlx::query!(
  17. "select key, owner_key, organization_key, project_key, assignee_key, name, description, tags, status, estimated_quarter_days, start, due, created, updated from tasks where key = $1",
  18. key
  19. )
  20. .fetch_one(conn)
  21. .await
  22. .expect("Select task by key");
  23. let t_status: TaskStatus = task.status.expect("status exists").into();
  24. Task {
  25. key: task.key.expect("key exists"),
  26. organization_key: task.organization_key.expect("organization_key"),
  27. project_key: task.project_key.expect("project_key"),
  28. owner_key: task.owner_key.expect("owner_key exists"),
  29. assignee_key: task.assignee_key.expect("assignee_key exists"),
  30. name: task.name.expect("name exists"),
  31. description: task.description.expect("description exists"),
  32. tags: task.tags.expect("tags exists"),
  33. status: t_status,
  34. estimated_quarter_days: task.estimated_quarter_days.expect("estimated_quarter_days exists"),
  35. start: task.start.expect("start"),
  36. due:task.due.expect("due"),
  37. created: task.created.expect("created exists"),
  38. updated: task.updated.expect("updated exists"),
  39. }
  40. }
  41. pub async fn get_tasks_by_organization(conn: &mut PoolConnection<Postgres>,organization_key: uuid::Uuid) -> Vec<Task> {
  42. let records = sqlx::query!(
  43. "select key, owner_key, organization_key, project_key, assignee_key, name, description, tags, status, estimated_quarter_days, start, due, created, updated from tasks where organization_key = $1",
  44. organization_key
  45. )
  46. .fetch_all(conn)
  47. .await
  48. .unwrap_or_default();
  49. let mut tasks = Vec::<Task>::new();
  50. for task in records {
  51. let t_status: TaskStatus = task.status.expect("status exists").into();
  52. let tsk = Task {
  53. key: task.key.expect("key exists"),
  54. organization_key: task.organization_key.expect("organization_key"),
  55. project_key: task.project_key.expect("project_key"),
  56. owner_key: task.owner_key.expect("owner_key exists"),
  57. assignee_key: task.assignee_key.expect("assignee_key exists"),
  58. name: task.name.expect("name exists"),
  59. description: task.description.expect("description exists"),
  60. tags: task.tags.expect("tags exists"),
  61. status: t_status,
  62. estimated_quarter_days: task.estimated_quarter_days.expect("estimated_quarter_days exists"),
  63. start: task.start.expect("start"),
  64. due:task.due.expect("due"),
  65. created: task.created.expect("created exists"),
  66. updated: task.updated.expect("updated exists"),
  67. };
  68. tasks.push(tsk);
  69. }
  70. return tasks
  71. }
  72. pub async fn get_tasks_by_project(conn: &mut PoolConnection<Postgres>,project_key: uuid::Uuid) -> Vec<Task> {
  73. let records = sqlx::query!(
  74. "select key, owner_key, organization_key, project_key, assignee_key, name, description, tags, status, estimated_quarter_days, start, due, created, updated from tasks where project_key = $1",
  75. project_key
  76. )
  77. .fetch_all(conn)
  78. .await
  79. .unwrap_or_default();
  80. let mut tasks = Vec::<Task>::new();
  81. for task in records {
  82. let t_status: TaskStatus = task.status.expect("status exists").into();
  83. let tsk = Task {
  84. key: task.key.expect("key exists"),
  85. organization_key: task.organization_key.expect("organization_key"),
  86. project_key: task.project_key.expect("project_key"),
  87. owner_key: task.owner_key.expect("owner_key exists"),
  88. assignee_key: task.assignee_key.expect("assignee_key exists"),
  89. name: task.name.expect("name exists"),
  90. description: task.description.expect("description exists"),
  91. tags: task.tags.expect("tags exists"),
  92. status: t_status,
  93. estimated_quarter_days: task.estimated_quarter_days.expect("estimated_quarter_days exists"),
  94. start: task.start.expect("start"),
  95. due:task.due.expect("due"),
  96. created: task.created.expect("created exists"),
  97. updated: task.updated.expect("updated exists"),
  98. };
  99. tasks.push(tsk);
  100. }
  101. return tasks
  102. }
  103. async fn delete_task( conn: &mut PoolConnection<Postgres>, key: uuid::Uuid, owner_key: uuid::Uuid) -> Result<PgQueryResult, sqlx::Error> {
  104. return sqlx::query!("DELETE FROM tasks where owner_key=$1 AND key=$2", owner_key, key)
  105. .execute( conn)
  106. .await;
  107. }
  108. async fn insert_task( conn: &mut PoolConnection<Postgres>, new_task: &Task) {
  109. sqlx::query!("INSERT INTO tasks (key, owner_key, organization_key, project_key, assignee_key, name, description, tags, status, estimated_quarter_days, start, due, created, updated) values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)",
  110. new_task.key,
  111. new_task.owner_key,
  112. new_task.organization_key,
  113. new_task.project_key,
  114. new_task.assignee_key,
  115. &new_task.name,
  116. &new_task.description,
  117. &new_task.tags,
  118. new_task.status as i16,
  119. new_task.estimated_quarter_days,
  120. new_task.start,
  121. new_task.due,
  122. new_task.created,
  123. new_task.updated,
  124. )
  125. .execute( conn)
  126. .await
  127. .expect("Insert Success");
  128. }
  129. async fn update_task(conn: &mut PoolConnection<Postgres>, task: &Task) {
  130. sqlx::query!("UPDATE tasks SET
  131. organization_key=$2, project_key=$3, owner_key=$4, assignee_key=$5, name=$6,
  132. description=$7, tags=$8, status=$9, estimated_quarter_days=$10, start=$11, due=$12,
  133. updated=extract(epoch from now()) where key = $1",
  134. task.key, task.organization_key, task.project_key, task.owner_key, task.assignee_key, task.name,task.description,
  135. task.tags, task.status as i16, task.estimated_quarter_days, task.start, task.due)
  136. .execute(conn)
  137. .await
  138. .expect("Insert Success");
  139. }
  140. // Route Stuff
  141. pub async fn delete(req: Request<State>) -> tide::Result {
  142. let u = match crate::user::user_or_error(&req) {
  143. Ok(value) => value,
  144. Err(_) => return Ok(tide::Redirect::new("/login").into()),
  145. };
  146. match req.param("task_id") {
  147. Ok(key) => {
  148. let mut conn = req.state().db_pool.acquire().await?;
  149. let s_uuid = uuid::Uuid::from_str(key).expect("Task uuid parse");
  150. match delete_task(&mut conn, s_uuid, u.key).await {
  151. Ok(_) => Ok(tide::Response::builder(tide::StatusCode::Ok)
  152. .content_type(mime::HTML)
  153. .build()),
  154. Err(_) => Ok(tide::Response::builder(tide::StatusCode::InternalServerError)
  155. .content_type(mime::HTML)
  156. .body(NotFoundTemplate::new().render_string())
  157. .build()),
  158. }
  159. }
  160. Err(e) => {
  161. println!("{:?}", e);
  162. Ok(tide::Response::builder(tide::StatusCode::NotFound)
  163. .content_type(mime::HTML)
  164. .body(NotFoundTemplate::new().render_string())
  165. .build())
  166. }
  167. }
  168. }
  169. pub async fn get(req: Request<State>) -> tide::Result {
  170. let u = match crate::user::user_or_error(&req) {
  171. Ok(value) => value,
  172. Err(_) => return Ok(tide::Redirect::new("/login").into()),
  173. };
  174. match req.param("task_id") {
  175. Ok(key) => {
  176. let s_uuid = uuid::Uuid::from_str(key).expect("Task uuid parse");
  177. let mut conn = req.state().db_pool.acquire().await?; // .await? needs to be a real connection pool error handler!!!!!!!!
  178. let task = get_task(&mut conn, s_uuid).await;
  179. let users = crate::user::get_users_by_organization(&mut conn, u.organization_key).await;
  180. Ok(tide::Response::builder(tide::StatusCode::Ok)
  181. .content_type(mime::HTML)
  182. .body(
  183. TaskTemplate::new(
  184. task,
  185. u,
  186. users,
  187. )
  188. .render_string(),
  189. )
  190. .build())
  191. }
  192. Err(e) => {
  193. println!("{:?}", e);
  194. Ok(tide::Response::builder(tide::StatusCode::NotFound)
  195. .content_type(mime::HTML)
  196. .body(NotFoundTemplate::new().render_string())
  197. .build())
  198. }
  199. }
  200. }
  201. pub async fn insert(mut req: Request<State>) -> tide::Result {
  202. let umd: Result<Task, tide::Error> = req.body_json().await;
  203. let claims: user::UserJwtState = match user::read_jwt_cookie(req.cookie("token")) {
  204. Some(c) => c,
  205. None => {
  206. return Ok(tide::Redirect::new("/login").into());
  207. },
  208. };
  209. match umd {
  210. Ok(task) => {
  211. let mut conn = match req.state().db_pool.acquire().await {
  212. Ok(c) => c,
  213. Err(e) => {
  214. return Ok(
  215. tide::Response::builder(tide::StatusCode::InternalServerError)
  216. .content_type(mime::PLAIN)
  217. .body(e.to_string())
  218. .build(),
  219. )
  220. }
  221. };
  222. if task.key == uuid::Uuid::nil() {
  223. let s = Task::new(task.organization_key, task.project_key, task.owner_key, task.assignee_key, task.name, task.description, task.tags,TaskStatus::Todo, task.estimated_quarter_days, task.start, task.due);
  224. insert_task(&mut conn, &s).await;
  225. let organization_key = uuid::Uuid::from_str(claims.organization_key.as_str()).expect("organization key");
  226. post_task_create(&mut conn, claims.matrix_home_server, claims.matrix_user_id, organization_key, claims.matrix_access_token, &s).await.expect("Posting to matrix");
  227. let j = serde_json::to_string(&s).expect("To JSON");
  228. return Ok(tide::Response::builder(tide::StatusCode::Ok)
  229. .content_type(mime::JSON)
  230. .body(j)
  231. .build());
  232. }
  233. update_task(&mut conn, &task).await;
  234. let j = serde_json::to_string(&task).expect("To JSON");
  235. Ok(tide::Response::builder(tide::StatusCode::Ok)
  236. .content_type(mime::JSON)
  237. .body(j)
  238. .build())
  239. }
  240. Err(e) => {
  241. println!("{:?}", e);
  242. Ok(tide::Response::builder(tide::StatusCode::BadRequest)
  243. .content_type(mime::JSON)
  244. .body("{'error': 'invalid json body'}")
  245. .build())
  246. }
  247. }
  248. }
  249. // data types
  250. #[derive(PartialEq, Debug, Deserialize, Serialize, Clone, Copy, sqlx::Type, EnumIter)]
  251. pub enum TaskStatus {
  252. Wishlist,
  253. Todo,
  254. PlanningAndEstimating,
  255. InQueue,
  256. InProgress,
  257. ToReview,
  258. InReviewal,
  259. Complete,
  260. }
  261. impl Into<TaskStatus> for i16 {
  262. fn into(self) -> TaskStatus {
  263. match self {
  264. 0 => TaskStatus::Wishlist,
  265. 1 => TaskStatus::Todo,
  266. 2 => TaskStatus::PlanningAndEstimating,
  267. 3 => TaskStatus::InQueue,
  268. 4 => TaskStatus::InProgress,
  269. 5 => TaskStatus::ToReview,
  270. 6 => TaskStatus::InReviewal,
  271. 7 => TaskStatus::Complete,
  272. _ => TaskStatus::Wishlist
  273. }
  274. }
  275. }
  276. impl FromStr for TaskStatus {
  277. type Err = ();
  278. fn from_str(input: &str) -> Result<TaskStatus, Self::Err> {
  279. match input {
  280. "Wishlist" => Ok(TaskStatus::Wishlist),
  281. "Todo" => Ok(TaskStatus::Todo),
  282. "PlanningAndEstimating" => Ok(TaskStatus::PlanningAndEstimating),
  283. "InQueue" => Ok(TaskStatus::InQueue),
  284. "InProgress" => Ok(TaskStatus::InProgress),
  285. "ToReview" => Ok(TaskStatus::ToReview),
  286. "InReviewal" => Ok(TaskStatus::InReviewal),
  287. "Complete" => Ok(TaskStatus::Complete),
  288. _ => Ok(TaskStatus::Wishlist),
  289. }
  290. }
  291. }
  292. impl ToString for TaskStatus {
  293. fn to_string(&self) -> String {
  294. match self {
  295. TaskStatus::Wishlist => "Wishlist".to_owned(),
  296. TaskStatus::Todo => "Todo".to_owned(),
  297. TaskStatus::PlanningAndEstimating => "Planning And Estimating".to_owned(),
  298. TaskStatus::InQueue => "In Queue".to_owned(),
  299. TaskStatus::InProgress => "In Progress".to_owned(),
  300. TaskStatus::ToReview =>"To Review".to_owned(),
  301. TaskStatus::InReviewal => "In Reviewal".to_owned(),
  302. TaskStatus::Complete => "Complete".to_owned(),
  303. }
  304. }
  305. }
  306. impl From<TaskStatus> for i16 {
  307. fn from(t: TaskStatus) -> Self {
  308. match t {
  309. TaskStatus::Wishlist => 0,
  310. TaskStatus::Todo => 1,
  311. TaskStatus::PlanningAndEstimating => 2,
  312. TaskStatus::InQueue => 3,
  313. TaskStatus::InProgress => 4,
  314. TaskStatus::ToReview => 5,
  315. TaskStatus::InReviewal => 6,
  316. TaskStatus::Complete => 7,
  317. }
  318. }
  319. }
  320. #[derive(Debug, Deserialize, Serialize)]
  321. pub struct Task {
  322. pub key: uuid::Uuid,
  323. pub organization_key: uuid::Uuid,
  324. pub project_key: uuid::Uuid,
  325. pub owner_key: uuid::Uuid,
  326. pub assignee_key: uuid::Uuid,
  327. pub name: String,
  328. pub description: String,
  329. pub tags: String,
  330. pub status: TaskStatus,
  331. pub estimated_quarter_days: i32,
  332. pub start: i64,
  333. pub due: i64,
  334. pub created: i64,
  335. pub updated: i64,
  336. }
  337. impl Task {
  338. pub fn new(
  339. organization_key: uuid::Uuid,
  340. project_key: uuid::Uuid,
  341. owner_key: uuid::Uuid,
  342. assignee_key: uuid::Uuid,
  343. name: String,
  344. description: String,
  345. tags: String,
  346. status: TaskStatus,
  347. estimated_quarter_days:i32,
  348. start: i64,
  349. due: i64,
  350. ) -> Self {
  351. let key = Uuid::new_v4();
  352. let created = chrono::Utc::now().timestamp();
  353. let updated = 0;
  354. Self {
  355. key,
  356. organization_key,
  357. project_key,
  358. owner_key,
  359. assignee_key,
  360. name,
  361. description,
  362. tags,
  363. status,
  364. estimated_quarter_days,
  365. start,
  366. due,
  367. created,
  368. updated,
  369. }
  370. }
  371. }
  372. pub async fn add(req: Request<State>) -> tide::Result {
  373. let u = match crate::user::user_or_error(&req) {
  374. Ok(value) => value,
  375. Err(_) => return Ok(tide::Redirect::new("/login").into()),
  376. };
  377. match req.param("project_id") {
  378. Ok(project_id) => {
  379. let project_id = uuid::Uuid::from_str(project_id).expect("Project uuid parse");
  380. let mut conn = match req.state().db_pool.acquire().await {
  381. Ok(c) => c,
  382. Err(e) => {
  383. return Ok(
  384. tide::Response::builder(tide::StatusCode::InternalServerError)
  385. .content_type(mime::PLAIN)
  386. .body(e.to_string())
  387. .build(),
  388. )
  389. }
  390. };
  391. let users = crate::user::get_users_by_organization(&mut conn, u.organization_key).await;
  392. let mut task= Task::new( u.organization_key, project_id, u.key, uuid::Uuid::nil(), "".to_owned(), "".to_owned(), "".to_owned(), TaskStatus::Todo, 0, 0, 0);
  393. task.key = uuid::Uuid::nil();
  394. Ok(tide::Response::builder(tide::StatusCode::Ok)
  395. .content_type(mime::HTML)
  396. .body(
  397. TaskTemplate::new(
  398. task,
  399. u,
  400. users,
  401. )
  402. .render_string(),
  403. )
  404. .build())
  405. }
  406. Err(e) => {
  407. println!("{:?}", e);
  408. Ok(tide::Response::builder(tide::StatusCode::NotFound)
  409. .content_type(mime::HTML)
  410. .body(NotFoundTemplate::new().render_string())
  411. .build())
  412. }
  413. }
  414. }
  415. #[derive(Template)]
  416. #[template(path = "task.html")]
  417. pub struct TaskTemplate {
  418. task: Task,
  419. user: crate::user::User,
  420. users: Vec::<crate::user::User>,
  421. }
  422. impl<'a> TaskTemplate {
  423. pub fn new(
  424. task: Task,
  425. user: crate::user::User,
  426. users: Vec::<crate::user::User>,
  427. ) -> Self {
  428. return Self {
  429. task,
  430. user,
  431. users,
  432. };
  433. }
  434. pub fn render_string(&self) -> String {
  435. return self.render().unwrap();
  436. }
  437. }