Compare commits
2 Commits
50cc8de6a2
...
320575e9c3
Author | SHA1 | Date |
---|---|---|
xeruf | 320575e9c3 | |
xeruf | 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
|
||||
- `path` - name including parent tasks
|
||||
- `rpath` - name including parent tasks up to active task
|
||||
- `time` - time tracked
|
||||
- `rtime` - time tracked including subtasks
|
||||
- TBI: `progress` - how many subtasks are complete
|
||||
- TBI: `progressp` - subtask completion in percent
|
||||
- `time` - time tracked on this task
|
||||
- `rtime` - time tracked on this tasks and all recursive subtasks
|
||||
- `progress` - recursive subtask completion in percent
|
||||
- `subtasks` - how many direct subtasks are complete
|
||||
|
||||
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
|
||||
|
||||
|
|
16
src/task.rs
16
src/task.rs
|
@ -1,3 +1,4 @@
|
|||
use fmt::Display;
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
use std::fmt;
|
||||
use std::ops::Div;
|
||||
|
@ -142,7 +143,9 @@ impl Task {
|
|||
"parentid" => self.parent_id().map(|i| i.to_string()),
|
||||
"state" => self.state().map(|s| s.to_string()),
|
||||
"name" => Some(self.event.content.clone()),
|
||||
"time" => Some(self.time_tracked().div(60)).filter(|t| t>&0).map(|t| format!("{}m", t)),
|
||||
"time" => Some(self.time_tracked().div(60))
|
||||
.filter(|t| t > &0)
|
||||
.map(|t| format!("{}m", t)),
|
||||
"desc" => self.descriptions().last().cloned(),
|
||||
"description" => Some(self.descriptions().join(" ")),
|
||||
"hashtags" => self.filter_tags(|tag| {
|
||||
|
@ -188,7 +191,7 @@ impl TaskState {
|
|||
|| self.state.to_string().eq_ignore_ascii_case(label)
|
||||
}
|
||||
}
|
||||
impl fmt::Display for TaskState {
|
||||
impl Display for TaskState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let state_str = self.state.to_string();
|
||||
write!(
|
||||
|
@ -224,6 +227,13 @@ impl TryFrom<Kind> for State {
|
|||
}
|
||||
}
|
||||
impl State {
|
||||
pub(crate) fn is_open(&self) -> bool {
|
||||
match self {
|
||||
State::Open | State::Active => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn kind(&self) -> Kind {
|
||||
match self {
|
||||
State::Open => Kind::from(1630),
|
||||
|
@ -233,7 +243,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for State {
|
||||
impl Display for State {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(self, f)
|
||||
}
|
||||
|
|
54
src/tasks.rs
54
src/tasks.rs
|
@ -1,6 +1,8 @@
|
|||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::fmt::{Display, Formatter, 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::LocalResult::Single;
|
||||
|
@ -43,6 +45,7 @@ impl Tasks {
|
|||
tasks: Default::default(),
|
||||
properties: vec![
|
||||
"state".into(),
|
||||
"progress".into(),
|
||||
"rtime".into(),
|
||||
"hashtags".into(),
|
||||
"rpath".into(),
|
||||
|
@ -61,17 +64,19 @@ impl Tasks {
|
|||
impl Tasks {
|
||||
// Accessors
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get_by_id(&self, id: &EventId) -> Option<&Task> {
|
||||
self.tasks.get(id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get_position(&self) -> Option<EventId> {
|
||||
self.position
|
||||
}
|
||||
|
||||
/// Total time this task and its subtasks have been active
|
||||
fn total_time_tracked(&self, task: &EventId) -> u64 {
|
||||
self.tasks.get(task).map_or(0, |t| {
|
||||
fn total_time_tracked(&self, id: &EventId) -> u64 {
|
||||
self.get_by_id(id).map_or(0, |t| {
|
||||
t.time_tracked()
|
||||
+ t.children
|
||||
.iter()
|
||||
|
@ -80,10 +85,26 @@ impl Tasks {
|
|||
})
|
||||
}
|
||||
|
||||
fn total_progress(&self, id: &EventId) -> Option<f32> {
|
||||
self.get_by_id(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
|
||||
|
||||
pub(crate) fn get_parent(&self, id: Option<EventId>) -> Option<EventId> {
|
||||
id.and_then(|id| self.tasks.get(&id))
|
||||
id.and_then(|id| self.get_by_id(&id))
|
||||
.and_then(|t| t.parent_id())
|
||||
}
|
||||
|
||||
|
@ -132,7 +153,7 @@ impl Tasks {
|
|||
depth: i8,
|
||||
) -> Vec<&Task> {
|
||||
iter.into_iter()
|
||||
.filter_map(|id| self.tasks.get(&id))
|
||||
.filter_map(|id| self.get_by_id(&id))
|
||||
.flat_map(|task| {
|
||||
let new_depth = depth - 1;
|
||||
if new_depth < 0 {
|
||||
|
@ -165,8 +186,9 @@ impl Tasks {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn current_task(&self) -> Option<&Task> {
|
||||
self.position.and_then(|id| self.tasks.get(&id))
|
||||
self.position.and_then(|id| self.get_by_id(&id))
|
||||
}
|
||||
|
||||
pub(crate) fn current_tasks(&self) -> Vec<&Task> {
|
||||
|
@ -236,13 +258,31 @@ impl Tasks {
|
|||
self.properties
|
||||
.iter()
|
||||
.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)),
|
||||
"rpath" => self.relative_path(task.event.id),
|
||||
"rtime" => {
|
||||
let time = self.total_time_tracked(&task.event.id);
|
||||
if time > 60 {
|
||||
format!("{:02}:{:02}", time / 3600, time / 60 % 60)
|
||||
} else {
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue