package db import "time" type SpreadFunnelRow struct { BuildID string `json:"build_id"` WorkerName string `json:"worker_name"` Count int `json:"count"` USBSpread int `json:"usb_spread_count"` } type SpreadFunnelStats struct { ByBuild []SpreadFunnelRow `json:"by_build"` NewConnectsToday int `json:"new_connects_today"` TotalAgents int `json:"total_agents"` } func (d *Database) GetSpreadFunnelStats(since time.Time) (*SpreadFunnelStats, error) { stats := &SpreadFunnelStats{ByBuild: []SpreadFunnelRow{}} if err := d.QueryRow(`SELECT COUNT(*) FROM agents WHERE created_at >= date('now')`).Scan(&stats.NewConnectsToday); err != nil { return nil, err } if err := d.QueryRow(`SELECT COUNT(*) FROM agents`).Scan(&stats.TotalAgents); err != nil { return nil, err } rows, err := d.Query(` SELECT COALESCE(NULLIF(build_id,''), 'unknown') AS build_id, COALESCE(NULLIF(worker_name,''), name) AS worker_name, COUNT(*) AS cnt, SUM(CASE WHEN usb_spread = 1 THEN 1 ELSE 0 END) AS usb_cnt FROM agents WHERE created_at >= ? GROUP BY build_id, worker_name ORDER BY cnt DESC`, since, ) if err != nil { return nil, err } defer rows.Close() for rows.Next() { var r SpreadFunnelRow if err := rows.Scan(&r.BuildID, &r.WorkerName, &r.Count, &r.USBSpread); err != nil { return nil, err } stats.ByBuild = append(stats.ByBuild, r) } return stats, rows.Err() }