You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat: add contributions outside work hours endpoint under v1-alpha - #2237
summary is the share of contributions made outside work hours, in percent, for the current period vs the previous one (changeValue in percentage points). The two percentages split that share into weekdays 18:00 to 08:00 and weekends. data is the heatmap for the current period.
Two concurrent calls to activity_heatmap_by_weekday_and_2hours_blocks (current and previous period), slug sent as project, dates as YYYY-MM-DD 00:00:00, both flags forwarded.
Changes vs the Nuxt handler
Route renamed from contribution-outside.
Heatmap cell day (0 = Monday) becomes weekday with ISO numbering (1 = Monday to 7 = Sunday). The - 1 in Nuxt existed for the ECharts axis index; the API passes the pipe value through.
percentageChange is signed and null when the previous share is 0 and the current is not (shared toPeriodSummary).
periodFrom/periodTo are always present as ISO-8601 UTC; the current call uses the same defaulted range (2010-01-01 to today) so the summary covers exactly the reported period.
Unknown slug returns 200 with zero shares and an empty heatmap (the client's TinybirdProjectNotFoundError is caught by class; every other Tinybird failure is 503 upstream_unavailable).
Decisions
Timezone, stated as a fact in the route description: the hour buckets are in each contributor's local time. The pipe reads contributions_with_local_time_ds, built daily by contributions_with_local_time.pipe in linuxfoundation/crowd.dev as toDayOfWeek(addHours(timestamp, offset)) / intDiv(toHour(...), 2) * 2, with offset looked up from the contributor's profile country or location. Contributors without a mappable location are excluded from the dataset. The startDate/endDate filter is on the original UTC timestamp.
The pipe declares neither includeCollaborations nor includeCodeContributions; Nuxt forwards both and Tinybird ignores unknown params. Exposed as the ticket asks and forwarded; flagged for the Tinybird side.
summary uses a local OutsideWorkHoursSummary schema (spread of PeriodSummary.properties) so current, previous and changeValue carry percent descriptions; schemas/common.ts stays untouched in this PR.
Tests
api/tests/development-contributions-outside-work-hours.test.ts: 44 cases with fetch stubbed at the HTTP boundary and routed by pipe and startDate. Covers the response shape and exact keys, both pipe calls and their params, concurrency (a gated stub times out a sequential implementation), the flag defaults and coercion, every work-hours boundary block, signed and null percentageChange, empty and zero-count results, unknown slug, date validation and defaults, the 503 mapping for pipe, bucket lookup, network and non-JSON failures, Cache-Control, and the OpenAPI listing (tag, description, field descriptions, params, absence from /v1).
pnpm test, pnpm tsc-check, pnpm lint, pnpm format:check in api/: green (14 files, 206 tests). Includes the routing-test parser fix from the base branch (62b136b).
Helpers to consolidate later
toTinybirdDateTime/tinybirdRange, the fetchHeatmap 503-and-unknown-slug wrapper, share (zero-guarded percent), and the PeriodSummary description override; siblings define the same shapes.
Based on main, which includes #2241 (IN-1348, directory-based route registration) as dd37a7a. Jira: IN-1335
the strict > startDate / < endDate compare lives in the tinybird pipe and this route sends the same YYYY-MM-DD 00:00:00 values nuxt and the sibling endpoints send -- out of scope here, tracked with the shared date helpers. wording fixed in 41fd2e9 👍
The empty-state description does not match the upstream pipe, which always returns the complete 7×12 grid for a known project and fills missing cells with zero. Clarify that only an unknown project returns an empty array; otherwise clients may implement an empty state that never appears.
This issue also appears in the following locations of the same file:
The reason will be displayed to describe this comment to others. Learn more.
Traced the weekday pass-through against the Nuxt handler and the pipe's filter logic. The raw pipe value is already ISO numbered, 1 for Monday through 7 for Sunday, so dropping the Nuxt -1 step is correct, not a reindex bug. The percentage null guard, the weekday/weekend split, and the concurrent calls all hold up, and the tests assert concrete weekday numbers instead of just checking for a key.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
GET /v1-alpha/projects/{slug}/development/contributions-outside-work-hours, the API port of the Nuxtcontribution-outsidewidget handler.Query:
repos[],startDate,endDate(UTC calendar days),includeCollaborations(boolean, defaultfalse),includeCodeContributions(boolean, defaulttrue). No granularity.Response:
{ "summary": { "current": 50, "previous": 40, "percentageChange": 25, "changeValue": 10, "periodFrom": "2025-01-01T00:00:00Z", "periodTo": "2025-03-31T00:00:00Z" }, "weekdayOutsideHoursPercentage": 40, "weekendOutsideHoursPercentage": 10, "data": [ { "weekday": 1, "hour": 20, "contributions": 30 } ] }summaryis the share of contributions made outside work hours, in percent, for the current period vs the previous one (changeValuein percentage points). The two percentages split that share into weekdays 18:00 to 08:00 and weekends.datais the heatmap for the current period.Two concurrent calls to
activity_heatmap_by_weekday_and_2hours_blocks(current and previous period), slug sent asproject, dates asYYYY-MM-DD 00:00:00, both flags forwarded.Changes vs the Nuxt handler
contribution-outside.day(0 = Monday) becomesweekdaywith ISO numbering (1 = Monday to 7 = Sunday). The- 1in Nuxt existed for the ECharts axis index; the API passes the pipe value through.percentageChangeis signed andnullwhen the previous share is 0 and the current is not (sharedtoPeriodSummary).periodFrom/periodToare always present as ISO-8601 UTC; the current call uses the same defaulted range (2010-01-01to today) so the summary covers exactly the reported period.TinybirdProjectNotFoundErroris caught by class; every other Tinybird failure is 503upstream_unavailable).Decisions
contributions_with_local_time_ds, built daily bycontributions_with_local_time.pipeinlinuxfoundation/crowd.devastoDayOfWeek(addHours(timestamp, offset))/intDiv(toHour(...), 2) * 2, withoffsetlooked up from the contributor's profile country or location. Contributors without a mappable location are excluded from the dataset. ThestartDate/endDatefilter is on the original UTC timestamp.includeCollaborationsnorincludeCodeContributions; Nuxt forwards both and Tinybird ignores unknown params. Exposed as the ticket asks and forwarded; flagged for the Tinybird side.summaryuses a localOutsideWorkHoursSummaryschema (spread ofPeriodSummary.properties) socurrent,previousandchangeValuecarry percent descriptions;schemas/common.tsstays untouched in this PR.Tests
api/tests/development-contributions-outside-work-hours.test.ts: 44 cases withfetchstubbed at the HTTP boundary and routed by pipe andstartDate. Covers the response shape and exact keys, both pipe calls and their params, concurrency (a gated stub times out a sequential implementation), the flag defaults and coercion, every work-hours boundary block, signed and nullpercentageChange, empty and zero-count results, unknown slug, date validation and defaults, the 503 mapping for pipe, bucket lookup, network and non-JSON failures,Cache-Control, and the OpenAPI listing (tag, description, field descriptions, params, absence from/v1).pnpm test,pnpm tsc-check,pnpm lint,pnpm format:checkinapi/: green (14 files, 206 tests). Includes the routing-test parser fix from the base branch (62b136b).Helpers to consolidate later
toTinybirdDateTime/tinybirdRange, thefetchHeatmap503-and-unknown-slug wrapper,share(zero-guarded percent), and thePeriodSummarydescription override; siblings define the same shapes.Based on main, which includes #2241 (IN-1348, directory-based route registration) as dd37a7a. Jira: IN-1335
main