summaryrefslogtreecommitdiff
path: root/internal/store/estimate_inference.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/store/estimate_inference.go')
-rw-r--r--internal/store/estimate_inference.go70
1 files changed, 70 insertions, 0 deletions
diff --git a/internal/store/estimate_inference.go b/internal/store/estimate_inference.go
new file mode 100644
index 0000000..d0a0034
--- /dev/null
+++ b/internal/store/estimate_inference.go
@@ -0,0 +1,70 @@
+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
+}