feat: implement recursive task progress and subtask completion count

This commit is contained in:
xeruf 2024-07-30 17:11:43 +03:00
parent 50cc8de6a2
commit c848a4797c
3 changed files with 52 additions and 6 deletions

View File

@ -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

View File

@ -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),

View File

@ -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" => {