feat: implement recursive task progress and subtask completion count
This commit is contained in:
parent
50cc8de6a2
commit
c848a4797c
12
README.md
12
README.md
|
@ -69,14 +69,16 @@ An active tag or state filter will also set that attribute for newly created tas
|
||||||
- `description` - accumulated notes on the task
|
- `description` - accumulated notes on the task
|
||||||
- `path` - name including parent tasks
|
- `path` - name including parent tasks
|
||||||
- `rpath` - name including parent tasks up to active task
|
- `rpath` - name including parent tasks up to active task
|
||||||
- `time` - time tracked
|
- `time` - time tracked on this task
|
||||||
- `rtime` - time tracked including subtasks
|
- `rtime` - time tracked on this tasks and all recursive subtasks
|
||||||
- TBI: `progress` - how many subtasks are complete
|
- `progress` - recursive subtask completion in percent
|
||||||
- TBI: `progressp` - subtask completion in percent
|
- `subtasks` - how many direct subtasks are complete
|
||||||
|
|
||||||
For debugging: `props`, `alltags`, `descriptions`
|
For debugging: `props`, `alltags`, `descriptions`
|
||||||
|
|
||||||
TODO: Combined formatting and recursion specifiers
|
TBI: Combined formatting and recursion specifiers -
|
||||||
|
for example progress count/percentage and recursive or not.
|
||||||
|
Subtask progress immediate/all/leafs.
|
||||||
|
|
||||||
## Plans
|
## Plans
|
||||||
|
|
||||||
|
|
|
@ -224,6 +224,13 @@ impl TryFrom<Kind> for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl State {
|
impl State {
|
||||||
|
pub(crate) fn is_open(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
State::Open | State::Active => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn kind(&self) -> Kind {
|
pub(crate) fn kind(&self) -> Kind {
|
||||||
match self {
|
match self {
|
||||||
State::Open => Kind::from(1630),
|
State::Open => Kind::from(1630),
|
||||||
|
|
39
src/tasks.rs
39
src/tasks.rs
|
@ -1,6 +1,8 @@
|
||||||
use std::collections::{BTreeSet, HashMap};
|
use std::collections::{BTreeSet, HashMap};
|
||||||
|
use std::fmt::{Display, Formatter, write};
|
||||||
use std::io::{Error, stdout, Write};
|
use std::io::{Error, stdout, Write};
|
||||||
use std::iter::once;
|
use std::iter::{once, Sum};
|
||||||
|
use std::ops::Add;
|
||||||
|
|
||||||
use chrono::{Local, TimeZone};
|
use chrono::{Local, TimeZone};
|
||||||
use chrono::LocalResult::Single;
|
use chrono::LocalResult::Single;
|
||||||
|
@ -43,6 +45,7 @@ impl Tasks {
|
||||||
tasks: Default::default(),
|
tasks: Default::default(),
|
||||||
properties: vec![
|
properties: vec![
|
||||||
"state".into(),
|
"state".into(),
|
||||||
|
"progress".into(),
|
||||||
"rtime".into(),
|
"rtime".into(),
|
||||||
"hashtags".into(),
|
"hashtags".into(),
|
||||||
"rpath".into(),
|
"rpath".into(),
|
||||||
|
@ -80,6 +83,22 @@ impl Tasks {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn total_progress(&self, id: &EventId) -> Option<f32> {
|
||||||
|
self.tasks.get(id).and_then(|t| match t.pure_state() {
|
||||||
|
State::Closed => None,
|
||||||
|
State::Done => Some(1.0),
|
||||||
|
_ => {
|
||||||
|
let count = t.children.len() as f32;
|
||||||
|
Some(
|
||||||
|
t.children
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| self.total_progress(e).map(|p| p / count))
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Parents
|
// Parents
|
||||||
|
|
||||||
pub(crate) fn get_parent(&self, id: Option<EventId>) -> Option<EventId> {
|
pub(crate) fn get_parent(&self, id: Option<EventId>) -> Option<EventId> {
|
||||||
|
@ -236,6 +255,24 @@ impl Tasks {
|
||||||
self.properties
|
self.properties
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| match p.as_str() {
|
.map(|p| match p.as_str() {
|
||||||
|
"subtasks" => {
|
||||||
|
let mut total = 0;
|
||||||
|
let mut done = 0;
|
||||||
|
for subtask in task.children.iter().filter_map(|id| self.get_by_id(id))
|
||||||
|
{
|
||||||
|
let state = subtask.pure_state();
|
||||||
|
total += &(state != State::Closed).into();
|
||||||
|
done += &(state == State::Done).into();
|
||||||
|
}
|
||||||
|
if total > 0 {
|
||||||
|
format!("{done}/{total}")
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"progress" => self
|
||||||
|
.total_progress(&task.event.id)
|
||||||
|
.map_or(String::new(), |p| format!("{:2}%", p * 100.0)),
|
||||||
"path" => self.get_task_path(Some(task.event.id)),
|
"path" => self.get_task_path(Some(task.event.id)),
|
||||||
"rpath" => self.relative_path(task.event.id),
|
"rpath" => self.relative_path(task.event.id),
|
||||||
"rtime" => {
|
"rtime" => {
|
||||||
|
|
Loading…
Reference in New Issue