Răsfoiți Sursa

bin/taud: finish internal functions for TaskInfo and MonthTasks

ghassmo 4 ani în urmă
părinte
comite
ba1c841315
4 a modificat fișierele cu 198 adăugiri și 82 ștergeri
  1. 21 7
      bin/taud/src/main.rs
  2. 55 21
      bin/taud/src/month_tasks.rs
  3. 87 22
      bin/taud/src/task_info.rs
  4. 35 32
      bin/taud/src/util.rs

+ 21 - 7
bin/taud/src/main.rs

@@ -2,7 +2,6 @@ use std::{fs::create_dir_all, path::PathBuf, sync::Arc};
 
 use async_executor::Executor;
 use async_trait::async_trait;
-use chrono::{TimeZone, Utc};
 use clap::{IntoApp, Parser};
 use log::debug;
 use serde_json::{json, Value};
@@ -26,7 +25,7 @@ mod util;
 
 use crate::{
     task_info::TaskInfo,
-    util::{CliTaud, Settings, TauConfig, Timestamp, CONFIG_FILE_CONTENTS},
+    util::{get_current_time, CliTaud, Settings, TauConfig, Timestamp, CONFIG_FILE_CONTENTS},
 };
 struct JsonRpcInterface {
     settings: Settings,
@@ -66,12 +65,27 @@ impl JsonRpcInterface {
             (Some(title), Some(desc), Some(rank)) => {
                 let due: Option<Timestamp> = if args[4].is_i64() {
                     let timestamp = args[4].as_i64().unwrap();
-                    Some(Timestamp(Utc.timestamp(timestamp, 0).to_string()))
+                    let timestamp = Timestamp(timestamp);
+
+                    if timestamp < get_current_time() {
+                        return JsonResult::Err(jsonerr(
+                            InvalidParams,
+                            Some("invalid due date".into()),
+                            id,
+                        ))
+                    }
+
+                    Some(timestamp)
                 } else {
                     None
                 };
 
-                task = TaskInfo::new(title, desc, due, rank as u32);
+                match TaskInfo::new(title, desc, due, rank as u32, &self.settings) {
+                    Ok(t) => task = t,
+                    Err(e) => {
+                        return JsonResult::Err(jsonerr(InternalError, Some(e.to_string()), id))
+                    }
+                }
             }
             (None, _, _) => {
                 return JsonResult::Err(jsonerr(InvalidParams, Some("invalid title".into()), id))
@@ -87,18 +101,18 @@ impl JsonRpcInterface {
         let assign = args[2].as_array();
         if assign.is_some() && assign.unwrap().len() > 0 {
             for a in assign.unwrap() {
-                task.assign(a.as_str().unwrap().into());
+                task.assign(a.as_str().unwrap());
             }
         }
 
         let project = args[3].as_array();
         if project.is_some() && project.unwrap().len() > 0 {
             for p in project.unwrap() {
-                task.project(p.as_str().unwrap().into());
+                task.project(p.as_str().unwrap());
             }
         }
 
-        match task.save(&self.settings) {
+        match task.save() {
             Ok(()) => JsonResult::Resp(jsonresp(json!(true), id)),
             Err(e) => JsonResult::Err(jsonerr(ServerError(-32603), Some(e.to_string()), id)),
         }

+ 55 - 21
bin/taud/src/month_tasks.rs

@@ -1,55 +1,89 @@
-use chrono::Utc;
+use std::path::PathBuf;
+
+use chrono::{TimeZone, Utc};
 use serde::{Deserialize, Serialize};
 
 use darkfi::Result;
 
 use crate::{
     task_info::TaskInfo,
-    util::{Settings, Timestamp},
+    util::{get_current_time, Settings, Timestamp},
 };
 
 // XXX
 #[allow(dead_code)]
-#[derive(Clone, Debug, Serialize, Deserialize)]
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 pub struct MonthTasks {
-    pub created_at: Timestamp,
-    #[serde(skip_serializing, skip_deserializing)]
-    pub settings: Settings,
-    pub task_tks: Vec<String>,
+    created_at: Timestamp,
+    settings: Settings,
+    task_tks: Vec<String>,
 }
 
 impl MonthTasks {
-    pub fn add(&mut self, tk_hash: &str) {
-        self.task_tks.push(tk_hash.into());
+    pub fn new(task_tks: &Vec<String>, settings: &Settings) -> Self {
+        Self {
+            created_at: get_current_time(),
+            settings: settings.clone(),
+            task_tks: task_tks.clone(),
+        }
+    }
+
+    pub fn add(&mut self, ref_id: &str) {
+        self.task_tks.push(ref_id.into());
     }
 
     pub fn objects(&self) -> Result<Vec<TaskInfo>> {
         let mut tks: Vec<TaskInfo> = vec![];
 
-        for tk_hash in self.task_tks.iter() {
-            tks.push(TaskInfo::load(&tk_hash, &self.settings)?);
+        for ref_id in self.task_tks.iter() {
+            tks.push(TaskInfo::load(&ref_id, &self.settings)?);
         }
 
         Ok(tks)
     }
 
-    pub fn remove(&mut self, tk_hash: &str) {
-        if let Some(index) = self.task_tks.iter().position(|t| *t == tk_hash) {
+    pub fn remove(&mut self, ref_id: &str) {
+        if let Some(index) = self.task_tks.iter().position(|t| *t == ref_id) {
             self.task_tks.remove(index);
         }
     }
 
-    fn load(_date: Timestamp, _settings: Settings) -> Result<Timestamp> {
-        Ok(Timestamp(Utc::now().to_string()))
+    pub fn set_settings(&mut self, settings: &Settings) {
+        self.settings = settings.clone();
     }
 
-    fn load_or_create(_date: Timestamp, _settings: Settings) -> Result<Timestamp> {
-        Ok(Timestamp(Utc::now().to_string()))
+    pub fn set_date(&mut self, date: &Timestamp) {
+        self.created_at = date.clone();
+    }
+
+    fn get_path(date: &Timestamp, settings: &Settings) -> PathBuf {
+        settings
+            .dataset_path
+            .join("month")
+            .join(Utc.timestamp(date.0, 0).format("%m%y").to_string())
+    }
+
+    pub fn save(&self) -> Result<()> {
+        crate::util::save::<Self>(&Self::get_path(&self.created_at, &self.settings), self)
+    }
+
+    pub fn load_or_create(date: &Timestamp, settings: &Settings) -> Result<Self> {
+        match crate::util::load::<Self>(&Self::get_path(date, settings)) {
+            Ok(mut mt) => {
+                mt.set_settings(settings);
+                Ok(mt)
+            }
+            Err(_) => {
+                let mut mt = Self::new(&vec![], settings);
+                mt.set_date(date);
+                mt.save()?;
+                return Ok(mt)
+            }
+        }
     }
-}
 
-impl PartialEq for MonthTasks {
-    fn eq(&self, other: &Self) -> bool {
-        self.created_at == other.created_at && self.task_tks == other.task_tks
+    pub fn load_current_open_tasks(settings: &Settings) -> Result<Vec<TaskInfo>> {
+        let mt = Self::load_or_create(&get_current_time(), settings)?;
+        Ok(mt.objects()?.into_iter().filter(|t| t.get_state() != "stop").collect())
     }
 }

+ 87 - 22
bin/taud/src/task_info.rs

@@ -1,31 +1,47 @@
-use chrono::Utc;
-use rand::Rng;
+use std::path::PathBuf;
+
 use serde::{Deserialize, Serialize};
 
 use darkfi::Result;
 
-use crate::util::{random_ref_id, Settings, Timestamp};
+use crate::{
+    month_tasks::MonthTasks,
+    util::{find_free_id, get_current_time, random_ref_id, Settings, Timestamp},
+};
 
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
+
 struct TaskEvent {
     action: String,
     timestamp: Timestamp,
 }
 
+impl TaskEvent {
+    fn new(action: String) -> Self {
+        Self { action, timestamp: get_current_time() }
+    }
+}
+
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-struct Comment {
+pub struct Comment {
     content: String,
     author: String,
     timestamp: Timestamp,
 }
 
+impl Comment {
+    pub fn new(content: &str, author: &str) -> Self {
+        Self { content: content.into(), author: author.into(), timestamp: get_current_time() }
+    }
+}
+
 // XXX
 #[allow(dead_code)]
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 pub struct TaskInfo {
     ref_id: String,
     id: u32,
-    pub title: String,
+    title: String,
     desc: String,
     assign: Vec<String>,
     project: Vec<String>,
@@ -34,23 +50,28 @@ pub struct TaskInfo {
     created_at: Timestamp,
     events: Vec<TaskEvent>,
     comments: Vec<Comment>,
+    #[serde(skip_serializing, skip_deserializing)]
+    settings: Settings,
 }
 
 impl TaskInfo {
-    pub fn new(title: &str, desc: &str, due: Option<Timestamp>, rank: u32) -> Self {
-        // TODO
-        // check due date
-
+    pub fn new(
+        title: &str,
+        desc: &str,
+        due: Option<Timestamp>,
+        rank: u32,
+        settings: &Settings,
+    ) -> Result<Self> {
         // generate ref_id
         let ref_id = random_ref_id();
 
-        // XXX must find the next free id
-        let mut rng = rand::thread_rng();
-        let id: u32 = rng.gen();
+        let created_at: Timestamp = get_current_time();
 
-        let created_at: Timestamp = Timestamp(Utc::now().to_string());
+        let task_ids: Vec<u32> =
+            MonthTasks::load_current_open_tasks(&settings)?.into_iter().map(|t| t.id).collect();
+        let id: u32 = find_free_id(&task_ids);
 
-        Self {
+        Ok(Self {
             ref_id,
             id,
             title: title.into(),
@@ -62,22 +83,66 @@ impl TaskInfo {
             created_at,
             comments: vec![],
             events: vec![],
+            settings: settings.clone(),
+        })
+    }
+
+    pub fn assign(&mut self, n: &str) {
+        self.assign.push(n.into());
+    }
+
+    pub fn project(&mut self, p: &str) {
+        self.project.push(p.into());
+    }
+
+    pub fn set_comment(&mut self, c: Comment) {
+        self.comments.push(c);
+    }
+
+    pub fn load(ref_id: &str, settings: &Settings) -> Result<Self> {
+        let mut task = crate::util::load::<Self>(&Self::get_path(ref_id, settings))?;
+        task.set_settings(settings);
+        Ok(task)
+    }
+
+    pub fn save(&self) -> Result<()> {
+        crate::util::save::<Self>(&Self::get_path(&self.ref_id, &self.settings), self)
+    }
+
+    pub fn get_state(&self) -> String {
+        if let Some(ev) = self.events.last() {
+            return ev.action.clone()
+        } else {
+            return "open".into()
         }
     }
 
-    pub fn assign(&mut self, n: String) {
-        self.assign.push(n);
+    pub fn activate(&self) -> Result<()> {
+        let mut mt = MonthTasks::load_or_create(&self.created_at, &self.settings)?;
+        mt.add(&self.ref_id);
+        mt.save()
+    }
+
+    pub fn set_settings(&mut self, settings: &Settings) {
+        self.settings = settings.clone();
     }
 
-    pub fn project(&mut self, p: String) {
-        self.project.push(p);
+    fn get_path(ref_id: &str, settings: &Settings) -> PathBuf {
+        settings.dataset_path.join("task").join(ref_id)
     }
 
-    pub fn load(_tk_hash: &str, _settings: &Settings) -> Result<Self> {
-        Ok(Self::new("test", "test", None, 0))
+    pub fn get_ref_id(&self) -> String {
+        self.ref_id.clone()
     }
 
-    pub fn save(&self, _settings: &Settings) -> Result<()> {
-        Ok(())
+    pub fn set_title(&mut self, title: &str) {
+        self.title = title.into();
+    }
+
+    pub fn set_state(&mut self, action: &str) {
+        if self.get_state() == action {
+            return
+        }
+        self.events.push(TaskEvent::new(action.into()));
     }
 }

+ 35 - 32
bin/taud/src/util.rs

@@ -1,5 +1,6 @@
 use std::{fs::File, io::BufReader, path::PathBuf};
 
+use chrono::Utc;
 use clap::Parser;
 use rand::{distributions::Alphanumeric, thread_rng, Rng};
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
@@ -12,9 +13,13 @@ pub fn random_ref_id() -> String {
     thread_rng().sample_iter(&Alphanumeric).take(30).map(char::from).collect()
 }
 
-pub fn find_free_id(tasks_ids: &Vec<u32>) -> u32 {
+pub fn get_current_time() -> Timestamp {
+    Timestamp(Utc::now().timestamp())
+}
+
+pub fn find_free_id(task_ids: &Vec<u32>) -> u32 {
     for i in 1.. {
-        if !tasks_ids.contains(&i) {
+        if !task_ids.contains(&i) {
             return i
         }
     }
@@ -35,7 +40,7 @@ pub fn save<T: Serialize>(path: &PathBuf, value: &T) -> Result<()> {
     Ok(())
 }
 
-#[derive(Clone, Debug, Serialize, Deserialize)]
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 pub struct Settings {
     pub dataset_path: PathBuf,
 }
@@ -46,8 +51,8 @@ impl Default for Settings {
     }
 }
 
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct Timestamp(pub String);
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, PartialOrd)]
+pub struct Timestamp(pub i64);
 
 /// taud cli
 #[derive(Parser)]
@@ -75,8 +80,6 @@ pub struct TauConfig {
 mod tests {
     use std::fs::create_dir_all;
 
-    use chrono::Utc;
-
     use crate::{month_tasks::MonthTasks, task_info::TaskInfo};
 
     use super::*;
@@ -110,47 +113,47 @@ mod tests {
         create_dir_all(path.join("month"))?;
         create_dir_all(path.join("task"))?;
 
-        // test with MonthTasks
+        let settings = Settings { dataset_path: path.clone() };
+
+        // test with TaskInfo
         ///////////////////////
-        let mt_path = path.join("month");
-        let mt_path = mt_path.join("022");
 
-        let settings = Settings { dataset_path: path.clone() };
-        let task_tks = vec![];
-        let created_at = Timestamp(Utc::now().to_string());
+        let mut task = TaskInfo::new("test_title", "test_desc", None, 0, &settings)?;
 
-        let mut mt = MonthTasks { created_at, task_tks, settings };
+        task.save()?;
 
-        save::<MonthTasks>(&mt_path, &mt)?;
+        let t_load = TaskInfo::load(&task.get_ref_id(), &settings)?;
 
-        let mt_load = load::<MonthTasks>(&mt_path)?;
-        assert_eq!(mt, mt_load);
+        assert_eq!(task, t_load);
 
-        mt.add("test_hash");
+        task.set_title("test_title_2");
 
-        save::<MonthTasks>(&mt_path, &mt)?;
+        task.save()?;
 
-        let mt_load = load::<MonthTasks>(&mt_path)?;
-        assert_eq!(mt, mt_load);
+        let t_load = TaskInfo::load(&task.get_ref_id(), &settings)?;
 
-        // test with TaskInfo
+        assert_eq!(task, t_load);
+
+        // test with MonthTasks
         ///////////////////////
-        let t_path = path.join("task");
-        let t_path = t_path.join("test_hash");
 
-        let mut task = TaskInfo::new("test_title", "test_desc", None, 0);
+        let task_tks = vec![];
 
-        save::<TaskInfo>(&t_path, &task)?;
+        let mut mt = MonthTasks::new(&task_tks, &settings);
 
-        let t_load = load::<TaskInfo>(&t_path)?;
-        assert_eq!(task, t_load);
+        mt.save()?;
 
-        task.title = "test_title_2".into();
+        let mt_load = MonthTasks::load_or_create(&get_current_time(), &settings)?;
 
-        save::<TaskInfo>(&t_path, &task)?;
+        assert_eq!(mt, mt_load);
 
-        let t_load = load::<TaskInfo>(&t_path)?;
-        assert_eq!(task, t_load);
+        mt.add(&task.get_ref_id());
+
+        mt.save()?;
+
+        let mt_load = MonthTasks::load_or_create(&get_current_time(), &settings)?;
+
+        assert_eq!(mt, mt_load);
 
         Ok(())
     }