Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/lib/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,20 @@ function calculateStreaks(calendar: { count: number }[]): { longestStreak: numbe
}

function calculateMostActiveDay(calendar: { date: string; count: number }[]): string | null {
if (calendar.length === 0) {
return null;
}

const weekdayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
const weekdayTotals = Array.from({ length: 7 }, () => 0);
const startDay = new Date(`${calendar[0].date}T00:00:00Z`).getUTCDay();

for (const day of calendar) {
for (let i = 0; i < calendar.length; i++) {
const day = calendar[i];
if (day.count === 0) {
continue;
}
const weekday = new Date(`${day.date}T00:00:00Z`).getUTCDay();
const weekday = (startDay + i) % 7;
Comment on lines +98 to +105

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

if command -v ast-grep >/dev/null 2>&1; then
  ast-grep outline src/lib/github.ts --items all --type function
fi

rg -n -C 15 \
  'calculateMostActiveDay|contributionCalendar|contributionDays|calendar' \
  src/lib/github.ts \
  src/lib/__tests__/github/fetchContributions.test.ts

rg -n -C 8 \
  '\bcalculateMostActiveDay\b' \
  --glob '*.{ts,tsx,js,jsx}' .

Repository: Hiroki-org/github-user-summary

Length of output: 23085


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the test fixtures/top-level contributors collection to identify which mock is used by the sort test.
rg -n -C 12 'MOCK_CONTRIBUTIONS|contributionCollection|contributionCalendar|weeks|contributionDays' src/lib/__tests__/github src/lib/github.ts

# Read only the fixture definitions and relevant test body around the sort test.
python3 - <<'PY'
from pathlib import Path
p = Path("src/lib/__tests__/github/fetchContributions.test.ts")
txt = p.read_text()
for needle in ("const MOCK_CONTRIBUTIONS", "describe(", '"カレンダーデータが日付順にソートされている"'):
    print(f"\n--- {needle!r} ---")
    idx = txt.find(needle if needle.startswith(('const MOCK_CONTRIBUTIONS', '"')) else needle[1:])
    # broad context around first occurrence for each
    for s in ("const MOCK_CONTRIBUTIONS", '"カレンダーデータが日付順にソートされている"'):
        i = txt.find(s)
        if i >= 0:
            lo = max(0, i - 300)
            hi = min(len(txt), i + 2000)
            print("\n\nTEXT:")
            print(txt[lo:hi])
PY

# Behavioral probe: compare index-based weekday assignment vs date-based assignment for non-concentric ordered inputs.
python3 - <<'PY'
from datetime import datetime, UTC

calendar = [
    {"date": "2024-01-01", "count": 1},
    {"date": "2024-01-03", "count": 2},
    {"date": "2024-01-05", "count": 3},
    {"date": "2024-01-06", "count": 4},
]

weekdayTotals_index = [0] * 7
startDay = datetime.strptime(calendar[0]["date"], "%Y-%m-%d").utcdate().weekday()
for i, day in enumerate(calendar):
    if day["count"] == 0:
        continue
    weekdayTotals_index[(startDay + i) % 7] += day["count"]

weekdayTotals_date = [0] * 7
for day in calendar:
    if day["count"] == 0:
        continue
    weekdayTotals_date[datetime.strptime(day["date"], "%Y-%m-%d").utcdate().weekday()] += day["count"]

print(calendar)
print("index-based totals:", weekdayTotals_index)
print("date-based totals:", weekdayTotals_date)
print("index-based most-active weekdays:", [d for d, c in enumerate(weekdayTotals_index) if c == max(weekdayTotals_index)])
print("date-day names:", [["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][d for d, c in enumerate(weekdayTotals_date) if c == max(weekdayTotals_date)][0]])
PY

Repository: Hiroki-org/github-user-summary

Length of output: 32996


🌐 Web query:

GitHub GraphQL contributionCalendar contributionDays date order

💡 Result:

In the GitHub GraphQL API, the contribution calendar data is structured hierarchically, and the order of contribution days is deterministic based on the calendar timeline [1][2]. Within the contributionCalendar object, data is organized as follows: 1. Weeks: The contributionCalendar field contains a weeks list (ContributionCalendarWeek) [1][2]. This list is ordered chronologically by time, with each week representing a distinct seven-day period [1][2]. 2. Days: Each ContributionCalendarWeek object contains a contributionDays list (ContributionCalendarDay) [1][2]. Within each week, the days are ordered chronologically, starting from the first day of that week [1][2]. Each day object includes a date field, which allows you to programmatically verify the chronological sequence [1][2]. You can retrieve this information by querying the user's contributionsCollection [3][4]. Since the API returns these objects in an ordered array structure (weeks followed by the days within those weeks), you do not need to perform manual sorting to maintain the chronological order of the calendar [1][4].

Citations:


曜日を day.date から取得してください。

calculateMostActiveDay()startDay + i で実際に存在しない曜日にカウントを加算します。calendar が昇順でも欠落日を含む場合は曜日より大きなズレになります。週境界を超えて連続していない日付での mostActiveDay 検証を追加してください。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/github.ts` around lines 98 - 105, Update calculateMostActiveDay to
derive weekday from each day.date rather than using startDay + i, so zero-count
entries or missing calendar dates cannot shift the weekday calculation. Preserve
the existing count aggregation and add validation covering non-contiguous dates
across a week boundary.

weekdayTotals[weekday] += day.count;
Comment on lines +98 to 106

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Invalid first date poisons 🐞 Bug ≡ Correctness

calculateMostActiveDay now computes all weekdays from calendar[0].date; if that first date is
invalid, startDay becomes NaN and all contributions are accumulated into a non-numeric array
property, causing the function to return null even when later days have valid dates and counts.
Agent Prompt
### Issue description
`calculateMostActiveDay` parses only the first calendar date and derives all subsequent weekdays via `(startDay + i) % 7`. If `calendar[0].date` is malformed/invalid, `new Date(...).getUTCDay()` yields `NaN`, and the modulo arithmetic produces `NaN` for every element. This causes writes to `weekdayTotals[NaN]` (a non-index property) and the function returns `null` despite nonzero contributions.

### Issue Context
Other code in this repo already treats invalid date parsing as a realistic possibility and guards with `Number.isNaN(date.getTime())` before using `getUTCDay()`.

### Fix Focus Areas
- src/lib/github.ts[91-113]

### Suggested fix
1. Parse the first date into a `Date` object and validate it using `Number.isNaN(date.getTime())`.
2. If invalid, fall back to the previous per-entry parsing approach (optionally with per-entry NaN checks), so later valid entries are still counted.
3. Keep the optimized modulo path when the first date is valid.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

}

Expand Down
Loading