mirror of
https://github.com/tiennm99/ghstats.git
synced 2026-05-14 18:58:43 +00:00
feat(card): add contributions area chart card
New 5-contributions.svg renders the last year's contribution calendar as a monthly smooth-filled area chart. Pure Go SVG; no extra API calls — one additional contributionCalendar.weeks block in the existing profile GraphQL query carries the data. - Y-axis mirrored on both sides with nice ticks. - X-axis labels in YY/MM format, every other month to avoid overlap. - Smooth curve via Catmull-Rom interpolation converted to cubic Bezier (d3.curveCatmullRom default tension 0.5). - Missing months between first and last are inserted as zero-count so the chart stays time-continuous.
This commit is contained in:
@@ -38,11 +38,22 @@ type Profile struct {
|
||||
// Commit counts grouped by hour-of-day (0-23) in the configured timezone.
|
||||
Productive [24]int
|
||||
|
||||
// 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
|
||||
|
||||
// TopRepos are owned repos sorted by stargazer count desc. Populated by
|
||||
// FetchProfile and consumed by FetchProductive.
|
||||
TopRepos []RepoInfo
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -36,6 +36,12 @@ type profileGQL struct {
|
||||
RestrictedContributionsCount int `json:"restrictedContributionsCount"`
|
||||
ContributionCalendar struct {
|
||||
TotalContributions int `json:"totalContributions"`
|
||||
Weeks []struct {
|
||||
ContributionDays []struct {
|
||||
ContributionCount int `json:"contributionCount"`
|
||||
Date string `json:"date"`
|
||||
} `json:"contributionDays"`
|
||||
} `json:"weeks"`
|
||||
} `json:"contributionCalendar"`
|
||||
} `json:"contributionsCollection"`
|
||||
|
||||
@@ -101,6 +107,20 @@ func (c *Client) FetchProfile(login string) (*Profile, error) {
|
||||
p.TotalCommits = cc.TotalCommitContributions
|
||||
p.TotalReviews = cc.TotalPullRequestReviewContributions
|
||||
p.TotalContributions = cc.ContributionCalendar.TotalContributions + cc.RestrictedContributionsCount
|
||||
|
||||
// Flatten week → day into a linear daily series sorted by date.
|
||||
for _, w := range cc.ContributionCalendar.Weeks {
|
||||
for _, d := range w.ContributionDays {
|
||||
t, err := time.Parse("2006-01-02", d.Date)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
p.DailyContributions = append(p.DailyContributions, DailyContribution{
|
||||
Date: t,
|
||||
Count: d.ContributionCount,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range u.Repositories.Nodes {
|
||||
|
||||
@@ -30,7 +30,15 @@ query($login: String!, $after: String) {
|
||||
totalPullRequestReviewContributions
|
||||
totalRepositoryContributions
|
||||
restrictedContributionsCount
|
||||
contributionCalendar { totalContributions }
|
||||
contributionCalendar {
|
||||
totalContributions
|
||||
weeks {
|
||||
contributionDays {
|
||||
contributionCount
|
||||
date
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories(
|
||||
first: 100
|
||||
|
||||
Reference in New Issue
Block a user