@@ -3,6 +3,7 @@ package stats
33
44import (
55 "database/sql"
6+ "fmt"
67 "time"
78)
89
@@ -20,6 +21,15 @@ type APIStats struct {
2021 DailyActivity map [string ]DailyStat `json:"daily_activity"`
2122 WeeklyHeatmap []HeatmapDay `json:"weekly_heatmap"`
2223 GeneratedAt time.Time `json:"generated_at"`
24+ Meta APIMeta `json:"_meta"` // NEW: Performance metadata
25+ }
26+
27+ // APIMeta contains performance and diagnostic information
28+ type APIMeta struct {
29+ LoadedActivities int `json:"loaded_activities"`
30+ TotalActivities int `json:"total_activities"`
31+ QueryTimeMs float64 `json:"query_time_ms"`
32+ DataWindow string `json:"data_window"` // e.g., "last_365_days"
2333}
2434
2535// APIPeriodStats is simplified version of PeriodStats for API responses
@@ -90,22 +100,50 @@ type APISession struct {
90100 EndTime time.Time `json:"end_time"`
91101 Duration float64 `json:"duration"`
92102 Projects []string `json:"projects"`
93- Languages []string `json:"languages,omitempty"` // Top languages used in session
103+ Languages []string `json:"languages,omitempty"`
94104 IsActive bool `json:"is_active"`
95- BreakAfter float64 `json:"break_after,omitempty"` // Duration until next session
105+ BreakAfter float64 `json:"break_after,omitempty"`
96106}
97107
98108type APIComparisonResult struct {
99109 Trend string `json:"trend"`
100110}
101111
102- // GetAPIStats returns simplified stats optimized for API consumers
112+ const LOOKBACK_DAYS = 180
113+
114+ // GetAPIStats with smart data loading
103115func GetAPIStats (db * sql.DB ) (* APIStats , error ) {
104- activities , err := loadActivitiesFromDB (db )
116+ return GetAPIStatsWithOptions (db , APIStatsOptions {
117+ LoadRecentDays : LOOKBACK_DAYS , // Default: only load last x days
118+ })
119+ }
120+
121+ // APIStatsOptions controls how much data to load
122+ type APIStatsOptions struct {
123+ LoadRecentDays int // Load only last N days (0 = all time)
124+ IncludeAllTime bool // Include all-time stats
125+ }
126+
127+ // GetAPIStatsWithOptions allows custom data loading
128+ func GetAPIStatsWithOptions (db * sql.DB , opts APIStatsOptions ) (* APIStats , error ) {
129+ startTime := time .Now ()
130+
131+ if opts .LoadRecentDays == 0 {
132+ opts .LoadRecentDays = LOOKBACK_DAYS
133+ }
134+
135+ now := time .Now ()
136+ cutoffDate := now .AddDate (0 , 0 , - opts .LoadRecentDays )
137+
138+ // Only load recent activities
139+ activities , err := loadActivitiesSince (db , cutoffDate )
105140 if err != nil {
106141 return nil , err
107142 }
108143
144+ // Get total count for metadata
145+ totalCount , _ := getTotalActivityCount (db )
146+
109147 calc := NewCalculator (time .Local )
110148 fullStats , err := calc .Calculate (activities )
111149 if err != nil {
@@ -114,9 +152,71 @@ func GetAPIStats(db *sql.DB) (*APIStats, error) {
114152
115153 apiStats := convertToAPIStats (fullStats )
116154
155+ // Add performance metadata
156+ queryTime := time .Since (startTime ).Seconds () * 1000 // Convert to ms
157+ apiStats .Meta = APIMeta {
158+ LoadedActivities : len (activities ),
159+ TotalActivities : totalCount ,
160+ QueryTimeMs : queryTime ,
161+ DataWindow : fmt .Sprintf ("last_%d_days" , opts .LoadRecentDays ),
162+ }
163+
117164 return apiStats , nil
118165}
119166
167+ // Load only activities since a specific date
168+ func loadActivitiesSince (db * sql.DB , since time.Time ) ([]Activity , error ) {
169+ query := `
170+ SELECT id, timestamp, lines, language, project,
171+ editor, file, COALESCE(branch, ''), is_write
172+ FROM activities
173+ WHERE timestamp >= ?
174+ ORDER BY timestamp ASC
175+ `
176+
177+ rows , err := db .Query (query , since .Unix ())
178+ if err != nil {
179+ return nil , err
180+ }
181+ defer rows .Close ()
182+
183+ activities := []Activity {}
184+ for rows .Next () {
185+ var a Activity
186+ var timestampUnix int64
187+ var isWriteInt int
188+
189+ err := rows .Scan (
190+ & a .ID ,
191+ & timestampUnix ,
192+ & a .Lines ,
193+ & a .Language ,
194+ & a .Project ,
195+ & a .Editor ,
196+ & a .File ,
197+ & a .Branch ,
198+ & isWriteInt ,
199+ )
200+ if err != nil {
201+ return nil , err
202+ }
203+
204+ a .Timestamp = time .Unix (timestampUnix , 0 )
205+ a .IsWrite = isWriteInt == 1
206+
207+ activities = append (activities , a )
208+ }
209+
210+ return activities , rows .Err ()
211+ }
212+
213+ // Get total activity count (for metadata)
214+ func getTotalActivityCount (db * sql.DB ) (int , error ) {
215+ var count int
216+ err := db .QueryRow ("SELECT COUNT(*) FROM activities" ).Scan (& count )
217+ return count , err
218+ }
219+
120220// convertToAPIStats converts full Stats to simplified APIStats
121221func convertToAPIStats (s * Stats ) * APIStats {
122222 return & APIStats {
@@ -263,49 +363,3 @@ func convertComparisonResultToAPI(comp ComparisonResult) APIComparisonResult {
263363 Trend : comp .Trend ,
264364 }
265365}
266-
267- // loadActivitiesFromDB loads all activities from the database
268- func loadActivitiesFromDB (db * sql.DB ) ([]Activity , error ) {
269- query := `
270- SELECT id, timestamp, lines, language, project,
271- editor, file, COALESCE(branch, ''), is_write
272- FROM activities
273- ORDER BY timestamp ASC
274- `
275-
276- rows , err := db .Query (query )
277- if err != nil {
278- return nil , err
279- }
280- defer rows .Close ()
281-
282- activities := []Activity {}
283- for rows .Next () {
284- var a Activity
285- var timestampUnix int64
286- var isWriteInt int
287-
288- err := rows .Scan (
289- & a .ID ,
290- & timestampUnix ,
291- & a .Lines ,
292- & a .Language ,
293- & a .Project ,
294- & a .Editor ,
295- & a .File ,
296- & a .Branch ,
297- & isWriteInt ,
298- )
299- if err != nil {
300- return nil , err
301- }
302-
303- // Convert Unix timestamp to time.Time
304- a .Timestamp = time .Unix (timestampUnix , 0 )
305- a .IsWrite = isWriteInt == 1
306-
307- activities = append (activities , a )
308- }
309-
310- return activities , rows .Err ()
311- }
0 commit comments