| 641 |
641 |
|
})
|
| 642 |
642 |
|
}
|
| 643 |
643 |
|
|
| 644 |
|
- |
/// Whether the task says how long it should take.
|
| 645 |
|
- |
fn has_estimate(task: &Task) -> bool {
|
| 646 |
|
- |
task.estimated_minutes.is_some()
|
| 647 |
|
- |
}
|
| 648 |
|
- |
|
| 649 |
|
- |
/// That estimate, in words.
|
| 650 |
|
- |
fn estimate_words(task: &Task) -> String {
|
| 651 |
|
- |
spans(task.estimated_minutes.unwrap_or(0))
|
| 652 |
|
- |
}
|
| 653 |
|
- |
|
| 654 |
|
- |
/// Whether any time has been recorded against the task.
|
| 655 |
|
- |
fn has_tracked(task: &Task) -> bool {
|
| 656 |
|
- |
task.actual_minutes > 0
|
|
644 |
+ |
/// The row's one trailing fact: the project, and the estimate and the tracked
|
|
645 |
+ |
/// total when the task carries them.
|
|
646 |
+ |
///
|
|
647 |
+ |
/// One string rather than three settings because `meta` sets rather than
|
|
648 |
+ |
/// appends, so three of them left only the last: a task with tracked time never
|
|
649 |
+ |
/// showed its project.
|
|
650 |
+ |
fn task_meta(task: &Task) -> String {
|
|
651 |
+ |
let project = task.project_name.clone();
|
|
652 |
+ |
let estimate = task
|
|
653 |
+ |
.estimated_minutes
|
|
654 |
+ |
.map(|minutes| format!("{} est", spans(minutes)));
|
|
655 |
+ |
let tracked =
|
|
656 |
+ |
(task.actual_minutes > 0).then(|| format!("{} tracked", spans(task.actual_minutes)));
|
|
657 |
+ |
let said = [project, estimate, tracked]
|
|
658 |
+ |
.into_iter()
|
|
659 |
+ |
.flatten()
|
|
660 |
+ |
.collect::<Vec<_>>()
|
|
661 |
+ |
.join(" · ");
|
|
662 |
+ |
// What the row said when the project was its only fact, kept for the task
|
|
663 |
+ |
// that has none of the three. A leading dash in front of an estimate would
|
|
664 |
+ |
// be saying "no project" louder than the estimate it sits next to.
|
|
665 |
+ |
if said.is_empty() {
|
|
666 |
+ |
task.project_name_or_dash().to_owned()
|
|
667 |
+ |
} else {
|
|
668 |
+ |
said
|
|
669 |
+ |
}
|
| 657 |
670 |
|
}
|
| 658 |
671 |
|
|
| 659 |
672 |
|
/// Today's date, which is what a retroactive log opens on.
|
| 681 |
694 |
|
/// momentarily refused, and a row that lost its buttons would read as a
|
| 682 |
695 |
|
/// task that cannot be tracked at all.
|
| 683 |
696 |
|
///
|
| 684 |
|
- |
/// **`meta` sets rather than appends**, so the three below are one trailing
|
| 685 |
|
- |
/// fact and the last one placed wins. That is what the hand-written row did
|
| 686 |
|
- |
/// too, and it is a defect rather than a decision: a task with tracked time
|
| 687 |
|
- |
/// never shows its project. Reported rather than repaired here, because a
|
| 688 |
|
- |
/// conversion is the wrong place to change what a screen says.
|
|
697 |
+ |
/// The project, the estimate and the tracked total are one `meta`, joined by
|
|
698 |
+ |
/// [`task_meta`]. `meta` sets rather than appends, so writing them as three
|
|
699 |
+ |
/// settings left only the last, which is what the hand-written row did and
|
|
700 |
+ |
/// what meant a task with tracked time never showed its project.
|
| 689 |
701 |
|
shape row_for(task: &Task, offered: &Offered, view: View) -> Row;
|
| 690 |
702 |
|
|
| 691 |
703 |
|
row &task.title {
|
| 692 |
|
- |
meta task.project_name_or_dash().to_owned();
|
| 693 |
|
- |
meta "{estimate_words(task)} est" when has_estimate(task);
|
| 694 |
|
- |
meta "{spans(task.actual_minutes)} tracked" when has_tracked(task);
|
|
704 |
+ |
meta task_meta(task);
|
| 695 |
705 |
|
|
| 696 |
706 |
|
for marker in super::Availability::of(task).marker().into_iter() {
|
| 697 |
707 |
|
token marker;
|
| 802 |
812 |
|
project.estimate_accuracy_percent.is_some()
|
| 803 |
813 |
|
}
|
| 804 |
814 |
|
|
|
815 |
+ |
/// The project row's one trailing fact: what the window tracked, and the
|
|
816 |
+ |
/// estimate against the actual when the project carries estimates.
|
|
817 |
+ |
///
|
|
818 |
+ |
/// Joined for the same reason [`task_meta`] is: two `meta` settings left only
|
|
819 |
+ |
/// the second, so a project with estimates lost its tracked total and the meter
|
|
820 |
+ |
/// beside it was the only thing still saying it.
|
|
821 |
+ |
fn project_meta(project: &TimeReportProject) -> String {
|
|
822 |
+ |
let against = has_accuracy(project).then(|| against_estimate(project));
|
|
823 |
+ |
[Some(spans(project.tracked_minutes)), against]
|
|
824 |
+ |
.into_iter()
|
|
825 |
+ |
.flatten()
|
|
826 |
+ |
.collect::<Vec<_>>()
|
|
827 |
+ |
.join(" · ")
|
|
828 |
+ |
}
|
|
829 |
+ |
|
| 805 |
830 |
|
/// The estimate against the actual, in words.
|
| 806 |
831 |
|
fn against_estimate(project: &TimeReportProject) -> String {
|
| 807 |
832 |
|
format!(
|
| 855 |
880 |
|
list {
|
| 856 |
881 |
|
for project in report.read.projects.iter() {
|
| 857 |
882 |
|
row &project.name {
|
| 858 |
|
- |
meta spans(project.tracked_minutes);
|
|
883 |
+ |
meta project_meta(project);
|
| 859 |
884 |
|
meter Meter::new(counted(project.tracked_minutes), report.total)
|
| 860 |
885 |
|
.label("minutes");
|
| 861 |
886 |
|
|
| 862 |
|
- |
meta against_estimate(project) when has_accuracy(project);
|
| 863 |
887 |
|
token Tag::badge(accuracy(project)).tone(overran(project))
|
| 864 |
888 |
|
when has_accuracy(project);
|
| 865 |
889 |
|
token Tag::badge("no estimates") unless has_accuracy(project);
|