mirror of
https://github.com/tiennm99/ghglance.git
synced 2026-10-11 03:13:20 +00:00
3.0 KiB
3.0 KiB
phase, title, status, priority, effort, dependencies
| phase | title | status | priority | effort | dependencies |
|---|---|---|---|---|---|
| 1 | Records computation helpers | completed | P1 | 1h |
Phase 1: Records computation helpers
Overview
Add pure functions that derive the 6 records from Profile data already in memory. Live in internal/card/records.go next to the card itself — no new public package surface needed.
Requirements
Functional
peakDay(days []DailyContribution) (count int, date time.Time)— argmax; ties → earliest datepeakMonth(days []DailyContribution) (count int, ym time.Time)— sum perYYYY-MM, argmax; ties → earliest monthfirstActiveDay(days []DailyContribution) time.Time— first day withCount > 0; zero time if noneactiveDaysCount(days []DailyContribution) int— count of days whereCount > 0accountAgeYears(createdAt, now time.Time) float64— fractional years, 1 decimallanguagesUsed(stats []LangStat) int—len(stats)(already deduped upstream)
Non-functional
- O(n) over
DailyContributionsAllTimefor all daily-derived records (single pass acceptable) - Zero allocations for argmax helpers (just iterate)
- All helpers package-private (
unexported) — only the card uses them
Architecture
Single file internal/card/records.go exporting the recordsCard struct (Phase 2) and these helpers. No mutation of Profile. Empty-input handling: return zero values; the card layer decides what to display.
Related Code Files
- Create:
internal/card/records.go(helpers section) - Read for context:
internal/github/model.go(Profile fields),internal/card/stats.go(card pattern)
Implementation Steps
- Create
internal/card/records.gowith package-private helpers. - Implement
peakDay: iterate, track(maxCount, earliestDate); tie-break on earlier date. - Implement
peakMonth: bucket sum into amap[time.Time]intkeyed bytime.Date(year,month,1,...); argmax with same tie-break. - Implement
firstActiveDay: first index whereCount > 0; assumes input is chronological (matches existing fetcher). - Implement
activeDaysCount: counter ofCount > 0. - Implement
accountAgeYears:now.Sub(createdAt).Hours() / 24 / 365.25, round to 1 decimal. - Implement
languagesUsed: trivial wrapper for symmetry/testability.
Success Criteria
- All 6 helpers compile (
go build ./...) - Helpers produce correct results on hand-rolled fixtures
- Empty-data inputs return zero values without panic
Risk Assessment
- Risk:
peakMonthmap ordering non-deterministic — Go map iteration is randomized, so two months tied at the same count may produce different "winners" across runs. Mitigation: scan keys, sort by date ascending, then argmax on first occurrence. Alternative: track(count, ym)during the bucket-fill pass instead of a second pass. - Risk:
accountAgeYearsrounding inconsistent with existing profile-card "age" string. Mitigation: matchinternal/card/profile.goconvention (check there before implementing).