focus-flow/lib/src/task_statistics.dart

180 lines
6.8 KiB
Dart

// Quiet per-task history counters.
//
// The app design calls for recovery/reporting features that can notice repeated
// pushes, backlog moves, burnout skips, and locked-hour leakage. This file keeps
// that history as immutable counters attached to each task.
/// Internal counters used for future filtering, reporting, and scheduling hints.
///
/// These counters answer "what has happened to this task over time?" without
/// changing the primary task state. They are intentionally quiet metadata: daily
/// task cards should not show all of this by default, but reports and filters can
/// use it to identify patterns such as tasks repeatedly pushed, skipped during
/// burnout, or completed inside locked hours.
///
/// Like [Task], this is immutable. Increment helpers return a new
/// [TaskStatistics] value so calling code can update a task through `copyWith`
/// while retaining predictable before/after behavior.
class TaskStatistics {
const TaskStatistics({
this.skippedDuringBurnoutCount = 0,
this.manuallyPushedCount = 0,
this.autoPushedCount = 0,
this.movedToBacklogCount = 0,
this.restoredFromBacklogCount = 0,
this.missedCount = 0,
this.cancelledCount = 0,
this.completedLateCount = 0,
this.completedDuringLockedHoursCount = 0,
this.completedDuringLockedHoursMinutes = 0,
this.completedAfterShieldCount = 0,
this.completedAfterPushCount = 0,
this.totalPushesBeforeCompletion = 0,
});
/// Number of times this task was skipped during Shield/recovery behavior.
final int skippedDuringBurnoutCount;
/// Number of times the user explicitly pushed this task.
final int manuallyPushedCount;
/// Number of times the scheduler moved this task to make room for something.
final int autoPushedCount;
/// Number of times this task was moved from schedule/today into backlog.
final int movedToBacklogCount;
/// Number of times this task came back from backlog into a planned slot.
final int restoredFromBacklogCount;
/// Number of times this task missed its intended timing.
final int missedCount;
/// Number of times this task was intentionally cancelled.
final int cancelledCount;
/// Number of times this task was completed after its scheduled window.
final int completedLateCount;
/// Number of completion events that overlapped locked hours.
final int completedDuringLockedHoursCount;
/// Total minutes completed while overlapping locked hours.
final int completedDuringLockedHoursMinutes;
/// Dormant V2-compatible counter for future Shield completion workflows.
final int completedAfterShieldCount;
/// Number of completions that happened after at least one push.
final int completedAfterPushCount;
/// Sum of push counts present when completion was recorded.
final int totalPushesBeforeCompletion;
/// Return a copy with selected counters changed.
///
/// Counters default to their current values when omitted, which keeps small
/// increment helpers concise and avoids direct mutation.
TaskStatistics copyWith({
int? skippedDuringBurnoutCount,
int? manuallyPushedCount,
int? autoPushedCount,
int? movedToBacklogCount,
int? restoredFromBacklogCount,
int? missedCount,
int? cancelledCount,
int? completedLateCount,
int? completedDuringLockedHoursCount,
int? completedDuringLockedHoursMinutes,
int? completedAfterShieldCount,
int? completedAfterPushCount,
int? totalPushesBeforeCompletion,
}) {
return TaskStatistics(
skippedDuringBurnoutCount:
skippedDuringBurnoutCount ?? this.skippedDuringBurnoutCount,
manuallyPushedCount: manuallyPushedCount ?? this.manuallyPushedCount,
autoPushedCount: autoPushedCount ?? this.autoPushedCount,
movedToBacklogCount: movedToBacklogCount ?? this.movedToBacklogCount,
restoredFromBacklogCount:
restoredFromBacklogCount ?? this.restoredFromBacklogCount,
missedCount: missedCount ?? this.missedCount,
cancelledCount: cancelledCount ?? this.cancelledCount,
completedLateCount: completedLateCount ?? this.completedLateCount,
completedDuringLockedHoursCount: completedDuringLockedHoursCount ??
this.completedDuringLockedHoursCount,
completedDuringLockedHoursMinutes: completedDuringLockedHoursMinutes ??
this.completedDuringLockedHoursMinutes,
completedAfterShieldCount:
completedAfterShieldCount ?? this.completedAfterShieldCount,
completedAfterPushCount:
completedAfterPushCount ?? this.completedAfterPushCount,
totalPushesBeforeCompletion:
totalPushesBeforeCompletion ?? this.totalPushesBeforeCompletion,
);
}
/// Record that the task was removed from active planning and stored for later.
TaskStatistics incrementMovedToBacklog() {
return copyWith(movedToBacklogCount: movedToBacklogCount + 1);
}
/// Record a recovery/Shield skip. This is distinct from manual cancellation.
TaskStatistics incrementSkippedDuringBurnout() {
return copyWith(skippedDuringBurnoutCount: skippedDuringBurnoutCount + 1);
}
/// Record an explicit user push action.
TaskStatistics incrementManualPush() {
return copyWith(manuallyPushedCount: manuallyPushedCount + 1);
}
/// Record scheduler-driven movement caused by another placement.
TaskStatistics incrementAutoPush() {
return copyWith(autoPushedCount: autoPushedCount + 1);
}
/// Record that a backlog item was scheduled again.
TaskStatistics incrementRestoredFromBacklog() {
return copyWith(restoredFromBacklogCount: restoredFromBacklogCount + 1);
}
/// Record a missed intended time or required task handling event.
TaskStatistics incrementMissed() {
return copyWith(missedCount: missedCount + 1);
}
/// Record that the task was deliberately cancelled.
TaskStatistics incrementCancelled() {
return copyWith(cancelledCount: cancelledCount + 1);
}
/// Record that the task was completed after its planned end.
TaskStatistics incrementCompletedLate() {
return copyWith(completedLateCount: completedLateCount + 1);
}
/// Record completion that overlapped locked time by [minutes].
///
/// Both count and minutes are tracked because reports may want either "how
/// often did this happen?" or "how much time leaked into locked blocks?"
TaskStatistics incrementCompletedDuringLockedHours(int minutes) {
return copyWith(
completedDuringLockedHoursCount: completedDuringLockedHoursCount + 1,
completedDuringLockedHoursMinutes:
completedDuringLockedHoursMinutes + minutes,
);
}
/// Record that completion happened after [pushCount] prior pushes.
TaskStatistics recordPushesBeforeCompletion(int pushCount) {
if (pushCount <= 0) {
return this;
}
return copyWith(
completedAfterPushCount: completedAfterPushCount + 1,
totalPushesBeforeCompletion: totalPushesBeforeCompletion + pushCount,
);
}
}