mirror of
https://github.com/tiennm99/ghstats.git
synced 2026-05-27 12:24:37 +00:00
e3f4d1d121
New -start-of-week CLI flag (and start_of_week action input) rotates the contribution-heatmap rows and the productive-weekday bars so users whose calendars start on Monday (or any other day) get matching output. Default stays Sunday to preserve existing renders.
185 lines
6.0 KiB
Go
185 lines
6.0 KiB
Go
package github
|
|
|
|
import "time"
|
|
|
|
// Profile is the aggregate of data other packages render into cards.
|
|
type Profile struct {
|
|
ID string
|
|
Login string
|
|
Name string
|
|
Bio string
|
|
AvatarURL string
|
|
Company string
|
|
Location string
|
|
Website string
|
|
CreatedAt time.Time
|
|
|
|
Followers int
|
|
Following int
|
|
PublicRepos int
|
|
|
|
// Totals for the stats card.
|
|
TotalStars int
|
|
TotalForks int
|
|
TotalCommits int // last year, from contributionsCollection
|
|
TotalCommitsAllTime int // sum across contributionYears
|
|
TotalPRs int
|
|
TotalIssues int
|
|
TotalReviews int
|
|
TotalContributedTo int
|
|
TotalContributionsLastYear int // contributionCalendar.totalContributions + restrictedContributionsCount (last year)
|
|
|
|
// Count of owned repos grouped by primary language, sorted desc by Value.
|
|
ReposByLanguage []LangStat
|
|
|
|
// Count of commits (last year, by this user) attributed to each repo's
|
|
// primary language, sorted desc. Populated by FetchProductive.
|
|
CommitsByLanguage []LangStat
|
|
|
|
// Commit counts grouped by hour-of-day (0-23) in the configured timezone.
|
|
// Productive is the last-year slice; ProductiveAllTime is the lifetime
|
|
// slice derived from the same paginated commit history so we pay for
|
|
// pagination once.
|
|
Productive [24]int
|
|
ProductiveAllTime [24]int
|
|
|
|
// Same pagination also feeds day-of-week histograms (index 0 = Sunday
|
|
// to match time.Weekday). Last-year and all-time kept separately so the
|
|
// weekday cards mirror the hour-of-day pair.
|
|
Weekday [7]int
|
|
WeekdayAllTime [7]int
|
|
|
|
// CommitsByLanguageAllTime is the lifetime counterpart of
|
|
// CommitsByLanguage, computed from the same commit stream.
|
|
CommitsByLanguageAllTime []LangStat
|
|
|
|
// DailyContributions is the raw per-day contribution calendar covering
|
|
// the most recent year. The area chart aggregates it into monthly
|
|
// buckets; kept granular here so any downstream card can re-bin freely.
|
|
DailyContributions []DailyContribution
|
|
|
|
// DailyContributionsAllTime concatenates contribution calendars across
|
|
// every year the user has been active (user.contributionYears), so the
|
|
// all-time area chart can show history beyond the default 1-year window.
|
|
DailyContributionsAllTime []DailyContribution
|
|
|
|
// TopRepos are owned non-fork repos sorted by stargazer count desc,
|
|
// populated by FetchProfile. Used for the profile's stars/forks totals
|
|
// and the repos-per-language card.
|
|
TopRepos []RepoInfo
|
|
|
|
// SeedRepos are the repos where the user actually committed, unioned
|
|
// across every contribution year via commitContributionsByRepository.
|
|
// Used by FetchProductive so commit-history probes land where there is
|
|
// signal instead of across the long tail of empty owned repos.
|
|
SeedRepos []RepoInfo
|
|
|
|
// ContributionYears lists every calendar year the user has been active
|
|
// on GitHub, newest first. Used by FetchContributionsAllTime to iterate
|
|
// per-year contributionsCollection queries.
|
|
ContributionYears []int
|
|
|
|
// UTCOffsetLabel is the configured timezone rendered as "UTC±N.NN" for
|
|
// display on time-based cards (e.g. "UTC+7.00" for Asia/Saigon). Filled
|
|
// by the CLI after loading -tz.
|
|
UTCOffsetLabel string
|
|
|
|
// WeekStart is the weekday used as row 0 on the heatmap and the first
|
|
// bar on the productive-weekday chart. Defaults to time.Sunday to match
|
|
// GitHub's own contribution calendar; -start-of-week on the CLI can flip
|
|
// it to time.Monday (or any other weekday) without touching the data —
|
|
// only the presentation order changes.
|
|
WeekStart time.Weekday
|
|
}
|
|
|
|
// DailyContribution is a single day in the contributions calendar.
|
|
type DailyContribution struct {
|
|
Date time.Time
|
|
Count int
|
|
}
|
|
|
|
// LangStat is one row in a language breakdown card. Value is repo count or
|
|
// commit count depending on which slice holds it.
|
|
type LangStat struct {
|
|
Name string
|
|
Color string
|
|
Value int64
|
|
}
|
|
|
|
// RepoInfo is the minimal repo summary used for downstream fetches. Owner is
|
|
// kept so the history query can target forks / repos belonging to other users.
|
|
type RepoInfo struct {
|
|
Owner string
|
|
Name string
|
|
Stars int
|
|
IsPrivate bool
|
|
IsFork bool
|
|
PrimaryLanguage string
|
|
PrimaryColor string
|
|
// Languages is the repo's language byte breakdown as reported by GitHub
|
|
// linguist. Used to attribute each commit fractionally across all
|
|
// languages the repo contains, rather than crediting only the primary.
|
|
Languages []LangEdge
|
|
}
|
|
|
|
// LangEdge is a single entry in a repo's language-bytes breakdown.
|
|
type LangEdge struct {
|
|
Name string
|
|
Color string
|
|
Bytes int64
|
|
}
|
|
|
|
// repoNode is the GraphQL shape of one repository node; kept here because
|
|
// it's shared by the profile fetcher and the productive-time fetcher.
|
|
type repoNode struct {
|
|
Name string `json:"name"`
|
|
IsPrivate bool `json:"isPrivate"`
|
|
IsFork bool `json:"isFork"`
|
|
StargazerCount int `json:"stargazerCount"`
|
|
ForkCount int `json:"forkCount"`
|
|
Owner *struct {
|
|
Login string `json:"login"`
|
|
} `json:"owner"`
|
|
PrimaryLanguage *struct {
|
|
Name string `json:"name"`
|
|
Color string `json:"color"`
|
|
} `json:"primaryLanguage"`
|
|
Languages struct {
|
|
Edges []struct {
|
|
Size int64 `json:"size"`
|
|
Node struct {
|
|
Name string `json:"name"`
|
|
Color string `json:"color"`
|
|
} `json:"node"`
|
|
} `json:"edges"`
|
|
} `json:"languages"`
|
|
}
|
|
|
|
// toRepoInfo converts the GraphQL shape into our public RepoInfo, defaulting
|
|
// owner to the given login if the node omits it (profile query doesn't
|
|
// request owner since it's implicit on user.repositories).
|
|
func (r repoNode) toRepoInfo(defaultOwner string) RepoInfo {
|
|
info := RepoInfo{
|
|
Owner: defaultOwner,
|
|
Name: r.Name,
|
|
Stars: r.StargazerCount,
|
|
IsPrivate: r.IsPrivate,
|
|
IsFork: r.IsFork,
|
|
}
|
|
if r.Owner != nil {
|
|
info.Owner = r.Owner.Login
|
|
}
|
|
if r.PrimaryLanguage != nil {
|
|
info.PrimaryLanguage = r.PrimaryLanguage.Name
|
|
info.PrimaryColor = r.PrimaryLanguage.Color
|
|
}
|
|
for _, e := range r.Languages.Edges {
|
|
info.Languages = append(info.Languages, LangEdge{
|
|
Name: e.Node.Name,
|
|
Color: e.Node.Color,
|
|
Bytes: e.Size,
|
|
})
|
|
}
|
|
return info
|
|
}
|