* Add monthly install tracking for per-package install charts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Incremental aggregation with watermark, event pruning, and API fixes Switch from full-recompute to watermark-based incremental aggregation that only processes new events. Add 365-day event retention with pruning. Fix migration to not seed data (avoids double-counting on first run). Cap monthly installs API to 36 months and add error logging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Scott Walkinshaw <scott.walkinshaw@gmail.com>
39 lines
1 KiB
Go
39 lines
1 KiB
Go
package telemetry
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
)
|
|
|
|
// MonthlyInstall holds the install count for a single month.
|
|
type MonthlyInstall struct {
|
|
Month string `json:"month"`
|
|
Installs int `json:"installs"`
|
|
}
|
|
|
|
// GetMonthlyInstalls returns up to 36 months of install counts for a package, ordered ascending.
|
|
func GetMonthlyInstalls(ctx context.Context, db *sql.DB, packageID int64) ([]MonthlyInstall, error) {
|
|
rows, err := db.QueryContext(ctx, `
|
|
SELECT month, installs FROM (
|
|
SELECT month, installs
|
|
FROM monthly_installs
|
|
WHERE package_id = ?
|
|
ORDER BY month DESC
|
|
LIMIT 36
|
|
) ORDER BY month ASC`, packageID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("querying monthly installs: %w", err)
|
|
}
|
|
defer func() { _ = rows.Close() }()
|
|
|
|
var result []MonthlyInstall
|
|
for rows.Next() {
|
|
var m MonthlyInstall
|
|
if err := rows.Scan(&m.Month, &m.Installs); err != nil {
|
|
return nil, fmt.Errorf("scanning monthly install row: %w", err)
|
|
}
|
|
result = append(result, m)
|
|
}
|
|
return result, rows.Err()
|
|
}
|