add relations
This commit is contained in:
parent
41524a18af
commit
a721556902
5 changed files with 163 additions and 8 deletions
|
@ -5,9 +5,11 @@ mod task;
|
||||||
|
|
||||||
pub use project::Project;
|
pub use project::Project;
|
||||||
pub use task::Comment;
|
pub use task::Comment;
|
||||||
|
pub use task::Relation;
|
||||||
pub use task::Task;
|
pub use task::Task;
|
||||||
|
|
||||||
use moka::sync::Cache;
|
use moka::sync::Cache;
|
||||||
|
use task::TaskRelation;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Label {
|
pub struct Label {
|
||||||
|
@ -356,6 +358,30 @@ impl VikunjaAPI {
|
||||||
serde_json::from_str(&resp).unwrap()
|
serde_json::from_str(&resp).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_relation(&self, task_id: isize, relation: Relation, other_task_id: isize) {
|
||||||
|
self.delete_request(&format!(
|
||||||
|
"/tasks/{task_id}/relations/{}/{other_task_id}",
|
||||||
|
relation.api()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_relation(
|
||||||
|
&self,
|
||||||
|
task_id: isize,
|
||||||
|
relation: Relation,
|
||||||
|
other_task_id: isize,
|
||||||
|
) -> TaskRelation {
|
||||||
|
let resp = self.put_request(
|
||||||
|
&format!("/tasks/{task_id}/relations"),
|
||||||
|
&serde_json::json!({
|
||||||
|
"task_id": task_id,
|
||||||
|
"other_task_id": other_task_id,
|
||||||
|
"relation_kind": relation.api()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
serde_json::from_str(&resp).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_comment(&self, task_id: isize, comment: &str) -> Comment {
|
pub fn new_comment(&self, task_id: isize, comment: &str) -> Comment {
|
||||||
let resp = self.put_request(
|
let resp = self.put_request(
|
||||||
&format!("/tasks/{task_id}/comments"),
|
&format!("/tasks/{task_id}/comments"),
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{Label, User};
|
use super::{Label, User};
|
||||||
|
@ -23,7 +25,7 @@ pub struct Task {
|
||||||
pub percent_done: f64,
|
pub percent_done: f64,
|
||||||
pub identifier: String,
|
pub identifier: String,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
// pub related_tasks
|
pub related_tasks: Option<HashMap<String, Vec<Task>>>,
|
||||||
// pub attachments
|
// pub attachments
|
||||||
pub cover_image_attachment_id: usize,
|
pub cover_image_attachment_id: usize,
|
||||||
pub is_favorite: bool,
|
pub is_favorite: bool,
|
||||||
|
@ -43,3 +45,83 @@ pub struct Comment {
|
||||||
pub id: isize,
|
pub id: isize,
|
||||||
pub updated: String,
|
pub updated: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct TaskRelation {
|
||||||
|
pub created: String,
|
||||||
|
pub created_by: User,
|
||||||
|
pub other_task_id: isize,
|
||||||
|
pub task_id: isize,
|
||||||
|
pub relation_kind: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Relation {
|
||||||
|
Unknown,
|
||||||
|
Subtask,
|
||||||
|
ParentTask,
|
||||||
|
Related,
|
||||||
|
DuplicateOf,
|
||||||
|
Duplicates,
|
||||||
|
Blocking,
|
||||||
|
Blocked,
|
||||||
|
Precedes,
|
||||||
|
Follows,
|
||||||
|
CopiedFrom,
|
||||||
|
CopiedTo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Relation {
|
||||||
|
pub fn try_parse(val: &str) -> Option<Self> {
|
||||||
|
match val {
|
||||||
|
"unknown" => Some(Self::Unknown),
|
||||||
|
"subtask" | "sub" => Some(Self::Subtask),
|
||||||
|
"parenttask" | "parent" => Some(Self::ParentTask),
|
||||||
|
"related" => Some(Self::Related),
|
||||||
|
"duplicateof" => Some(Self::DuplicateOf),
|
||||||
|
"duplicates" => Some(Self::Duplicates),
|
||||||
|
"blocking" => Some(Self::Blocking),
|
||||||
|
"blocked" => Some(Self::Blocked),
|
||||||
|
"precedes" => Some(Self::Precedes),
|
||||||
|
"follows" => Some(Self::Follows),
|
||||||
|
"copiedfrom" => Some(Self::CopiedFrom),
|
||||||
|
"copiedto" => Some(Self::CopiedTo),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn repr(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Relation::Unknown => "Unknown",
|
||||||
|
Relation::Subtask => "Subtask",
|
||||||
|
Relation::ParentTask => "Parent Task",
|
||||||
|
Relation::Related => "Related",
|
||||||
|
Relation::DuplicateOf => "Duplicate of",
|
||||||
|
Relation::Duplicates => "Duplicates",
|
||||||
|
Relation::Blocking => "Blocking",
|
||||||
|
Relation::Blocked => "Blocked by",
|
||||||
|
Relation::Precedes => "Precedes",
|
||||||
|
Relation::Follows => "Follows",
|
||||||
|
Relation::CopiedFrom => "Copied from",
|
||||||
|
Relation::CopiedTo => "Copied to",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn api(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Relation::Unknown => "unknown",
|
||||||
|
Relation::Subtask => "subtask",
|
||||||
|
Relation::ParentTask => "parenttask",
|
||||||
|
Relation::Related => "related",
|
||||||
|
Relation::DuplicateOf => "duplicateof",
|
||||||
|
Relation::Duplicates => "duplicates",
|
||||||
|
Relation::Blocking => "blocking",
|
||||||
|
Relation::Blocked => "blocked",
|
||||||
|
Relation::Precedes => "precedes",
|
||||||
|
Relation::Follows => "follows",
|
||||||
|
Relation::CopiedFrom => "copiedfrom",
|
||||||
|
Relation::CopiedTo => "copiedto",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -81,6 +81,15 @@ pub fn get_args() -> clap::ArgMatches {
|
||||||
.arg(arg!([task_id] "Task ID").required(true))
|
.arg(arg!([task_id] "Task ID").required(true))
|
||||||
.arg(arg!([comment] "Comment").required(true)),
|
.arg(arg!([comment] "Comment").required(true)),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
command!()
|
||||||
|
.name("relation")
|
||||||
|
.about("Set task relations")
|
||||||
|
.arg(arg!(-d --delete "Delete the relation").required(false))
|
||||||
|
.arg(arg!([task_id] "Task ID").required(true))
|
||||||
|
.arg(arg!([relation] "Relation").required(true))
|
||||||
|
.arg(arg!([second_task_id] "Other Task ID").required(true)),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
command!()
|
command!()
|
||||||
.name("fav")
|
.name("fav")
|
||||||
|
|
27
src/main.rs
27
src/main.rs
|
@ -3,10 +3,9 @@ mod args;
|
||||||
mod config;
|
mod config;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use api::{ProjectID, VikunjaAPI};
|
use api::{ProjectID, Relation, VikunjaAPI};
|
||||||
|
|
||||||
// todo : error handling
|
// todo : error handling
|
||||||
// todo : task relations
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let arg = args::get_args();
|
let arg = args::get_args();
|
||||||
|
@ -155,6 +154,30 @@ fn main() {
|
||||||
api.fav_task(task_id.parse().unwrap(), !undo);
|
api.fav_task(task_id.parse().unwrap(), !undo);
|
||||||
ui::task::print_task_info(task_id.parse().unwrap(), &api);
|
ui::task::print_task_info(task_id.parse().unwrap(), &api);
|
||||||
}
|
}
|
||||||
|
Some(("relation", rel_args)) => {
|
||||||
|
let task_id: &String = rel_args.get_one("task_id").unwrap();
|
||||||
|
let relation: &String = rel_args.get_one("relation").unwrap();
|
||||||
|
let sec_task_id: &String = rel_args.get_one("second_task_id").unwrap();
|
||||||
|
let delete = rel_args.get_flag("delete");
|
||||||
|
|
||||||
|
let relation = Relation::try_parse(&relation).unwrap();
|
||||||
|
|
||||||
|
if delete {
|
||||||
|
api.remove_relation(
|
||||||
|
task_id.parse().unwrap(),
|
||||||
|
relation,
|
||||||
|
sec_task_id.parse().unwrap(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
api.add_relation(
|
||||||
|
task_id.parse().unwrap(),
|
||||||
|
relation,
|
||||||
|
sec_task_id.parse().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui::task::print_task_info(task_id.parse().unwrap(), &api);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let done = arg.get_flag("done");
|
let done = arg.get_flag("done");
|
||||||
let fav = arg.get_flag("favorite");
|
let fav = arg.get_flag("favorite");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{Comment, Project, ProjectID, Task, VikunjaAPI},
|
api::{Comment, Project, ProjectID, Relation, Task, VikunjaAPI},
|
||||||
ui::{
|
ui::{
|
||||||
format_html_to_terminal, hex_to_color, is_in_past, parse_datetime, print_color,
|
format_html_to_terminal, hex_to_color, is_in_past, parse_datetime, print_color,
|
||||||
print_label, time_relative,
|
print_label, time_relative,
|
||||||
|
@ -157,10 +157,6 @@ pub fn print_task_info(task_id: isize, api: &VikunjaAPI) {
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
if task.description != "<p></p>" && !task.description.is_empty() {
|
|
||||||
println!("---\n{}", format_html_to_terminal(&task.description));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(assigned) = task.assignees {
|
if let Some(assigned) = task.assignees {
|
||||||
print!("Assigned to: ");
|
print!("Assigned to: ");
|
||||||
for assignee in assigned {
|
for assignee in assigned {
|
||||||
|
@ -169,6 +165,25 @@ pub fn print_task_info(task_id: isize, api: &VikunjaAPI) {
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(related) = task.related_tasks {
|
||||||
|
for relation in related {
|
||||||
|
print_color(
|
||||||
|
crossterm::style::Color::Magenta,
|
||||||
|
&format!("{}: ", Relation::try_parse(&relation.0).unwrap().repr()),
|
||||||
|
);
|
||||||
|
for t in relation.1 {
|
||||||
|
print_color(crossterm::style::Color::Blue, &t.title);
|
||||||
|
print_color(crossterm::style::Color::Yellow, &format!(" ({})", t.id));
|
||||||
|
print!(" ");
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if task.description != "<p></p>" && !task.description.is_empty() {
|
||||||
|
println!("---\n{}", format_html_to_terminal(&task.description));
|
||||||
|
}
|
||||||
|
|
||||||
// pub percent_done: f64,
|
// pub percent_done: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue