Scan transcript files and aggregate one row per top-level session
(&self)
| 101 | |
| 102 | /// Scan transcript files and aggregate one row per top-level session |
| 103 | fn scan_sessions(&self) -> Result<Vec<(Key, DataRow)>> { |
| 104 | let files = discover_transcript_files(&self.config); |
| 105 | |
| 106 | // Subagent file counts keyed by (project, parent session id) |
| 107 | let mut subagent_counts: HashMap<(Option<String>, String), i64> = HashMap::new(); |
| 108 | for file in &files { |
| 109 | if file.agent_id.is_some() { |
| 110 | *subagent_counts |
| 111 | .entry((file.project.clone(), file.session_id.clone())) |
| 112 | .or_insert(0) += 1; |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | let mut rows = Vec::new(); |
| 117 | let mut row_id: i64 = 0; |
| 118 | |
| 119 | for file in files.iter().filter(|f| f.agent_id.is_none()) { |
| 120 | let Ok(handle) = fs::File::open(&file.path) else { |
| 121 | continue; |
| 122 | }; |
| 123 | |
| 124 | let mut agg = SessionAggregate::default(); |
| 125 | let reader = BufReader::new(handle); |
| 126 | for line in reader.lines().map_while(Result::ok) { |
| 127 | if let Ok(json) = serde_json::from_str::<JsonValue>(&line) { |
| 128 | agg.observe(&json); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | let subagent_count = subagent_counts |
| 133 | .get(&(file.project.clone(), file.session_id.clone())) |
| 134 | .copied() |
| 135 | .unwrap_or(0); |
| 136 | |
| 137 | rows.push(( |
| 138 | Key::I64(row_id), |
| 139 | session_aggregate_to_data_row(agg, file, subagent_count), |
| 140 | )); |
| 141 | row_id += 1; |
| 142 | } |
| 143 | |
| 144 | Ok(rows) |
| 145 | } |
| 146 | |
| 147 | /// Scan todos directory and return all rows |
| 148 | fn scan_todos(&self) -> Result<Vec<(Key, DataRow)>> { |
no test coverage detected