package store import "encoding/json" // AverageEstimateForProject returns the rounded average estimated_minutes // across all user-estimated (estimated_minutes > 0) tasks under projectID. // ok is false when no such task exists -- there's no signal to infer from. func (s *Store) AverageEstimateForProject(projectID string) (int, bool, error) { var sum, count int rows, err := s.db.Query(`SELECT estimated_minutes FROM native_tasks WHERE project_id = ? AND estimated_minutes > 0`, projectID) if err != nil { return 0, false, err } defer func() { _ = rows.Close() }() for rows.Next() { var minutes int if err := rows.Scan(&minutes); err != nil { return 0, false, err } sum += minutes count++ } if err := rows.Err(); err != nil { return 0, false, err } if count == 0 { return 0, false, nil } return sum / count, true, nil } // AverageEstimateForLabel returns the rounded average estimated_minutes // across all user-estimated tasks carrying the given label. Labels are // stored as a JSON array column, not a joinable table, so this scans every // estimated task and filters in Go rather than risking a SQL substring // false-positive (e.g. LIKE '%"run"%' matching a task labeled "running"). func (s *Store) AverageEstimateForLabel(label string) (int, bool, error) { rows, err := s.db.Query(`SELECT labels, estimated_minutes FROM native_tasks WHERE estimated_minutes > 0`) if err != nil { return 0, false, err } defer func() { _ = rows.Close() }() var sum, count int for rows.Next() { var labelsJSON string var minutes int if err := rows.Scan(&labelsJSON, &minutes); err != nil { return 0, false, err } var labels []string if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil { continue } for _, l := range labels { if l == label { sum += minutes count++ break } } } if err := rows.Err(); err != nil { return 0, false, err } if count == 0 { return 0, false, nil } return sum / count, true, nil }