mirror of
https://github.com/tiennm99/ghglance.git
synced 2026-10-11 12:18:55 +00:00
feat: implement profile summary cards with GraphQL fetch and Action wrapper
- Add GraphQL client fetching profile, stats, language aggregation, and per-repo commit histograms for the productive-time heatmap. - Render real SVG cards (profile details, top languages, stats grid, weekday×hour heatmap) with XML escaping and thousands-formatted numbers. - Expand theme palette to 30 built-ins ported from github-readme-stats; add -list-themes, multi-theme rendering, and 'all' shortcut. - Package as Docker-based GitHub Action (action.yml, Dockerfile, entrypoint.sh) with optional auto-commit of generated cards. - Release workflow publishes GHCR image and cross-platform binaries on v* tags. - Unit tests cover rendering, XML escape, number formatting, language sort.
This commit is contained in:
1 parent
a91d964a1d
commit
ff4975fae2
19 files changed
+1312
-135
No files matched your search
@@ -0,0 +1,81 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// productiveGQL is the response shape for commitHistoryQuery.
|
||||
type productiveGQL struct {
|
||||
Repository *struct {
|
||||
DefaultBranchRef *struct {
|
||||
Target *struct {
|
||||
History struct {
|
||||
PageInfo struct {
|
||||
HasNextPage bool `json:"hasNextPage"`
|
||||
EndCursor string `json:"endCursor"`
|
||||
} `json:"pageInfo"`
|
||||
Nodes []struct {
|
||||
CommittedDate string `json:"committedDate"`
|
||||
} `json:"nodes"`
|
||||
} `json:"history"`
|
||||
} `json:"target"`
|
||||
} `json:"defaultBranchRef"`
|
||||
} `json:"repository"`
|
||||
}
|
||||
|
||||
// FetchProductive fills p.Productive with a [7][24] commit histogram over the
|
||||
// last year, gathered from the user's top-starred owned repos. Each repo is
|
||||
// sampled up to maxPerRepo commits to keep the cost bounded.
|
||||
//
|
||||
// The timezone loc is applied to CommittedDate so the heatmap reflects when the
|
||||
// user actually commits, not UTC.
|
||||
func (c *Client) FetchProductive(p *Profile, repos []string, loc *time.Location, maxPerRepo int) error {
|
||||
if loc == nil {
|
||||
loc = time.UTC
|
||||
}
|
||||
since := time.Now().AddDate(-1, 0, 0).UTC().Format(time.RFC3339)
|
||||
|
||||
for _, repo := range repos {
|
||||
var cursor *string
|
||||
seen := 0
|
||||
for {
|
||||
if seen >= maxPerRepo {
|
||||
break
|
||||
}
|
||||
vars := map[string]any{
|
||||
"login": p.Login,
|
||||
"repo": repo,
|
||||
"userId": p.ID,
|
||||
"since": since,
|
||||
}
|
||||
if cursor != nil {
|
||||
vars["after"] = *cursor
|
||||
}
|
||||
|
||||
var resp productiveGQL
|
||||
if err := c.query(commitHistoryQuery, vars, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Repository == nil || resp.Repository.DefaultBranchRef == nil ||
|
||||
resp.Repository.DefaultBranchRef.Target == nil {
|
||||
break
|
||||
}
|
||||
h := resp.Repository.DefaultBranchRef.Target.History
|
||||
for _, n := range h.Nodes {
|
||||
t, err := time.Parse(time.RFC3339, n.CommittedDate)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
tl := t.In(loc)
|
||||
p.Productive[int(tl.Weekday())][tl.Hour()]++
|
||||
seen++
|
||||
}
|
||||
if !h.PageInfo.HasNextPage {
|
||||
break
|
||||
}
|
||||
end := h.PageInfo.EndCursor
|
||||
cursor = &end
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user