( task: TaskInfo, getActiveSession: (task: TaskInfo) => TimeEntry | null )
| 269 | } |
| 270 | |
| 271 | export function computeTaskTimeData( |
| 272 | task: TaskInfo, |
| 273 | getActiveSession: (task: TaskInfo) => TimeEntry | null |
| 274 | ): TaskTimeDataResult { |
| 275 | const timeEntries = task.timeEntries || []; |
| 276 | const activeSession = getActiveSession(task); |
| 277 | const totalMinutes = calculateTotalTimeSpent(timeEntries); |
| 278 | |
| 279 | const completedSessions = timeEntries.filter((entry) => entry.endTime).length; |
| 280 | const completedEntries = timeEntries.filter((entry) => entry.endTime); |
| 281 | const averageSessionMinutes = |
| 282 | completedEntries.length > 0 |
| 283 | ? Math.round( |
| 284 | (completedEntries.reduce( |
| 285 | (sum, entry) => |
| 286 | sum + |
| 287 | Math.floor( |
| 288 | (new Date(entry.endTime as string).getTime() - |
| 289 | new Date(entry.startTime).getTime()) / |
| 290 | (1000 * 60) |
| 291 | ), |
| 292 | 0 |
| 293 | ) / |
| 294 | completedEntries.length) * |
| 295 | 100 |
| 296 | ) / 100 |
| 297 | : 0; |
| 298 | |
| 299 | return { |
| 300 | task: { |
| 301 | id: task.path, |
| 302 | title: task.title, |
| 303 | status: task.status, |
| 304 | priority: task.priority, |
| 305 | }, |
| 306 | summary: { |
| 307 | totalMinutes, |
| 308 | totalHours: Math.round((totalMinutes / 60) * 100) / 100, |
| 309 | totalSessions: timeEntries.length, |
| 310 | completedSessions, |
| 311 | activeSessions: activeSession ? 1 : 0, |
| 312 | averageSessionMinutes, |
| 313 | }, |
| 314 | activeSession: activeSession |
| 315 | ? { |
| 316 | startTime: activeSession.startTime, |
| 317 | description: activeSession.description, |
| 318 | elapsedMinutes: Math.floor( |
| 319 | (Date.now() - new Date(activeSession.startTime).getTime()) / |
| 320 | (1000 * 60) |
| 321 | ), |
| 322 | } |
| 323 | : null, |
| 324 | timeEntries: timeEntries.map((entry) => ({ |
| 325 | startTime: entry.startTime, |
| 326 | endTime: entry.endTime || null, |
| 327 | description: entry.description || null, |
| 328 | duration: entry.endTime |
no test coverage detected