brainwires_agents/task_manager/
query_ops.rs1use std::collections::HashMap;
6
7use super::TaskManager;
8use super::time_tracking::{TaskStats, TaskTimeInfo, TimeStats};
9use brainwires_core::{Task, TaskPriority, TaskStatus};
10
11impl TaskManager {
12 pub async fn get_ready_tasks(&self) -> Vec<Task> {
14 let tasks = self.tasks.read().await;
15 let mut ready = Vec::new();
16
17 for task in tasks.values() {
18 if task.status == TaskStatus::Pending || task.status == TaskStatus::Blocked {
19 let deps_complete = task.depends_on.iter().all(|dep_id| {
21 tasks
22 .get(dep_id)
23 .map(|t| {
24 t.status == TaskStatus::Completed || t.status == TaskStatus::Skipped
25 })
26 .unwrap_or(false)
27 });
28
29 if deps_complete {
30 ready.push(task.clone());
31 }
32 }
33 }
34
35 ready.sort_by(|a, b| b.priority.cmp(&a.priority));
37 ready
38 }
39
40 pub async fn get_root_tasks(&self) -> Vec<Task> {
42 let tasks = self.tasks.read().await;
43 tasks.values().filter(|t| t.is_root()).cloned().collect()
44 }
45
46 pub async fn get_task_tree(&self, root_id: Option<&str>) -> Vec<Task> {
48 let tasks = self.tasks.read().await;
49 let mut result = Vec::new();
50
51 match root_id {
52 Some(id) => {
53 if let Some(task) = tasks.get(id) {
54 Self::collect_tree_recursive(&tasks, task, &mut result);
55 }
56 }
57 None => {
58 for task in tasks.values().filter(|t| t.is_root()) {
60 Self::collect_tree_recursive(&tasks, task, &mut result);
61 }
62 }
63 }
64
65 result
66 }
67
68 fn collect_tree_recursive(tasks: &HashMap<String, Task>, task: &Task, result: &mut Vec<Task>) {
70 result.push(task.clone());
71 for child_id in &task.children {
72 if let Some(child) = tasks.get(child_id) {
73 Self::collect_tree_recursive(tasks, child, result);
74 }
75 }
76 }
77
78 pub async fn get_all_tasks(&self) -> Vec<Task> {
80 let tasks = self.tasks.read().await;
81 tasks.values().cloned().collect()
82 }
83
84 pub async fn get_tasks_by_status(&self, status: TaskStatus) -> Vec<Task> {
86 let tasks = self.tasks.read().await;
87 tasks
88 .values()
89 .filter(|t| t.status == status)
90 .cloned()
91 .collect()
92 }
93
94 pub async fn get_stats(&self) -> TaskStats {
96 let tasks = self.tasks.read().await;
97 let mut stats = TaskStats::default();
98
99 for task in tasks.values() {
100 stats.total += 1;
101 match task.status {
102 TaskStatus::Pending => stats.pending += 1,
103 TaskStatus::InProgress => stats.in_progress += 1,
104 TaskStatus::Completed => stats.completed += 1,
105 TaskStatus::Failed => stats.failed += 1,
106 TaskStatus::Blocked => stats.blocked += 1,
107 TaskStatus::Skipped => stats.skipped += 1,
108 }
109 }
110
111 stats
112 }
113
114 pub async fn get_task_time_info(&self, task_id: &str) -> Option<TaskTimeInfo> {
116 let tasks = self.tasks.read().await;
117 tasks.get(task_id).map(|task| TaskTimeInfo {
118 task_id: task.id.clone(),
119 description: task.description.clone(),
120 status: task.status.clone(),
121 started_at: task.started_at,
122 completed_at: task.completed_at,
123 duration_secs: task.duration_secs(),
124 elapsed_secs: task.elapsed_secs(),
125 })
126 }
127
128 pub async fn get_time_stats(&self) -> TimeStats {
130 let tasks = self.tasks.read().await;
131
132 let mut total_duration: i64 = 0;
133 let mut completed_count: usize = 0;
134 let mut total_elapsed: i64 = 0;
135 let mut in_progress_count: usize = 0;
136
137 for task in tasks.values() {
138 if let Some(duration) = task.duration_secs() {
139 total_duration += duration;
140 completed_count += 1;
141 }
142 if task.status == TaskStatus::InProgress
143 && let Some(elapsed) = task.elapsed_secs()
144 {
145 total_elapsed += elapsed;
146 in_progress_count += 1;
147 }
148 }
149
150 TimeStats {
151 total_duration_secs: total_duration,
152 completed_tasks: completed_count,
153 average_duration_secs: if completed_count > 0 {
154 Some(total_duration / completed_count as i64)
155 } else {
156 None
157 },
158 current_elapsed_secs: total_elapsed,
159 in_progress_tasks: in_progress_count,
160 }
161 }
162
163 pub async fn get_progress(&self, task_id: &str) -> f64 {
167 let tasks = self.tasks.read().await;
168
169 if let Some(task) = tasks.get(task_id) {
170 if task.children.is_empty() {
171 match task.status {
173 TaskStatus::Completed => 1.0,
174 TaskStatus::InProgress => 0.5,
175 _ => 0.0,
176 }
177 } else {
178 let completed = task
180 .children
181 .iter()
182 .filter(|id| {
183 tasks
184 .get(*id)
185 .map(|t| t.status == TaskStatus::Completed)
186 .unwrap_or(false)
187 })
188 .count();
189 let in_progress = task
190 .children
191 .iter()
192 .filter(|id| {
193 tasks
194 .get(*id)
195 .map(|t| t.status == TaskStatus::InProgress)
196 .unwrap_or(false)
197 })
198 .count();
199
200 let total = task.children.len() as f64;
201 if total == 0.0 {
202 return 0.0;
203 }
204
205 (completed as f64 + (in_progress as f64 * 0.5)) / total
206 }
207 } else {
208 0.0
209 }
210 }
211
212 pub async fn get_overall_progress(&self) -> f64 {
214 let stats = self.get_stats().await;
215 if stats.total == 0 {
216 return 0.0;
217 }
218
219 let completed = stats.completed as f64;
220 let in_progress = stats.in_progress as f64 * 0.5;
221 let total = stats.total as f64;
222
223 (completed + in_progress) / total
224 }
225
226 pub async fn get_average_duration(&self) -> Option<i64> {
228 let tasks = self.tasks.read().await;
229 let durations: Vec<i64> = tasks.values().filter_map(|t| t.duration_secs()).collect();
230
231 if durations.is_empty() {
232 None
233 } else {
234 Some(durations.iter().sum::<i64>() / durations.len() as i64)
235 }
236 }
237
238 pub async fn estimate_remaining_time(&self) -> Option<i64> {
240 let avg_duration = self.get_average_duration().await?;
241 let stats = self.get_stats().await;
242 let remaining = stats.pending + stats.blocked;
243
244 Some(avg_duration * remaining as i64)
245 }
246
247 pub async fn format_tree(&self) -> String {
249 let tasks = self.tasks.read().await;
250 let mut output = String::new();
251
252 let mut roots: Vec<_> = tasks.values().filter(|t| t.is_root()).collect();
254 roots.sort_by(|a, b| b.priority.cmp(&a.priority));
255
256 let root_count = roots.len();
257 for (idx, root) in roots.iter().enumerate() {
258 let is_last = idx == root_count - 1;
259 Self::format_task_recursive(&tasks, root, 0, is_last, "", &mut output);
260 }
261
262 if output.is_empty() {
263 output = "No tasks".to_string();
264 }
265
266 output
267 }
268
269 fn format_task_recursive(
270 tasks: &HashMap<String, Task>,
271 task: &Task,
272 depth: usize,
273 is_last: bool,
274 parent_prefix: &str,
275 output: &mut String,
276 ) {
277 let status_icon = match task.status {
278 TaskStatus::Pending => "○",
279 TaskStatus::InProgress => "◐",
280 TaskStatus::Completed => "●",
281 TaskStatus::Failed => "✗",
282 TaskStatus::Blocked => "◌",
283 TaskStatus::Skipped => "⊘",
284 };
285 let priority_icon = match task.priority {
286 TaskPriority::Urgent => "🔴 ",
287 TaskPriority::High => "🟠 ",
288 TaskPriority::Normal => "",
289 TaskPriority::Low => "🔵 ",
290 };
291
292 let current_prefix = if depth == 0 {
294 String::new()
295 } else if is_last {
296 format!("{}└── ", parent_prefix)
297 } else {
298 format!("{}├── ", parent_prefix)
299 };
300
301 output.push_str(&format!(
302 "{}{} {}{}\n",
303 current_prefix, status_icon, priority_icon, task.description
304 ));
305
306 let child_prefix = if depth == 0 {
308 String::new()
309 } else if is_last {
310 format!("{} ", parent_prefix)
311 } else {
312 format!("{}│ ", parent_prefix)
313 };
314
315 let child_count = task.children.len();
317 for (idx, child_id) in task.children.iter().enumerate() {
318 if let Some(child) = tasks.get(child_id) {
319 let child_is_last = idx == child_count - 1;
320 Self::format_task_recursive(
321 tasks,
322 child,
323 depth + 1,
324 child_is_last,
325 &child_prefix,
326 output,
327 );
328 }
329 }
330 }
331}