Skip to content
Merged
Show file tree
Hide file tree
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
124 changes: 124 additions & 0 deletions services/libs/tinybird/pipes/median_time_to_merge.pipe
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
DESCRIPTION >
- `median_time_to_merge.pipe` serves the "Median time to merge" widget in the Development tab.
- Calculates the median time in seconds from PR opening to merge for the selected time period.
- **When `granularity` is NOT provided, returns a single KPI value** (median) across the entire time range.
- **When `granularity` is provided, returns time-series data** showing median time to merge aggregated by different time periods (daily, weekly, monthly, quarterly, yearly).
- Uses `generate_timeseries` pipe to create consistent time periods and left joins PR data to handle periods with no merged PRs.
- Only includes PRs that have been merged (`isNotNull(pra.mergedAt)`) to ensure accurate merge time calculations.
- Replaces the deprecated `averageTimeToMerge` widget with more robust median-based statistics.
- Available for projects connected with Gerrit, GitHub, or GitLab platforms.
- Primary use case: monitoring code review and merge cycle time, identifying bottlenecks in the merge process.
- Parameters:
- `project`: Required string for project slug (e.g., 'k8s', 'tensorflow') - inherited from `segments_filtered`
- `repos`: Optional array of repository URLs for filtering (e.g., ['https://github.com/kubernetes/kubernetes'])
- `startDate`: Optional DateTime filter for PRs opened after timestamp (e.g., '2024-01-01 00:00:00')
- `endDate`: Optional DateTime filter for PRs opened before timestamp (e.g., '2024-12-31 23:59:59')
- `platform`: Optional string filter for source platform (e.g., 'gerrit', 'github', 'gitlab')
- `granularity`: Optional string for time aggregation ('daily', 'weekly', 'monthly', 'quarterly', 'yearly')
- Response:
- Without granularity: `medianTimeToMergeSeconds` (median value in seconds)
- With granularity: `startDate`, `endDate`, and `medianTimeToMergeSeconds` for each time period

TAGS "Widget", "Pull requests", "Development metrics", "Merge time"

NODE timeseries_generation_for_median_time_to_merge
SQL >
%
{% if defined(granularity) %}
SELECT
ds."startDate",
ds."endDate",
ifNull(round(median(prf.mergedInSeconds)), 0) AS "medianTimeToMergeSeconds"
FROM generate_timeseries ds
LEFT JOIN
pull_requests_filtered prf
ON CASE
WHEN {{ granularity }} = 'daily'
THEN toDate(prf.openedAt)
WHEN {{ granularity }} = 'weekly'
THEN toStartOfWeek(prf.openedAt)
WHEN {{ granularity }} = 'monthly'
THEN toStartOfMonth(prf.openedAt)
WHEN {{ granularity }} = 'quarterly'
THEN toStartOfQuarter(prf.openedAt)
WHEN {{ granularity }} = 'yearly'
THEN toStartOfYear(prf.openedAt)
END
= ds."startDate"
AND isNotNull(prf.mergedAt)
AND isNotNull(prf.mergedInSeconds)
{% if defined(platform) %}
AND prf.platform
= {{
String(
platform,
description="Filter by platform (e.g., 'gerrit', 'github', 'gitlab')",
required=False,
)
}}
{% end %}
{% if defined(startDate) %}
AND prf.openedAt
>= {{
DateTime(
startDate,
description="Filter PRs opened after this timestamp",
required=False,
)
}}
{% end %}
{% if defined(endDate) %}
AND prf.openedAt
<= {{
DateTime(
endDate,
description="Filter PRs opened before this timestamp",
required=False,
)
}}
{% end %}
Copy link

Choose a reason for hiding this comment

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

Bug: Time window uses opened date, not merge

The time bucketing and startDate/endDate filtering use prf.openedAt while the metric is “time to merge”. This makes results depend on when PRs were opened rather than when they merged, so PRs opened in-range but merged later can appear, and PRs opened earlier but merged in-range are excluded, changing the meaning vs the widget being replaced.

Additional Locations (1)

Fix in Cursor Fix in Web

GROUP BY ds."startDate", ds."endDate"
ORDER BY ds."startDate"
{% else %} SELECT 1
{% end %}

NODE median_time_to_merge_merged
SQL >
%
{% if not defined(granularity) %}
SELECT round(median(prf.mergedInSeconds)) AS "medianTimeToMergeSeconds"
FROM pull_requests_filtered prf
WHERE
isNotNull(prf.mergedAt) AND isNotNull(prf.mergedInSeconds)
{% if defined(platform) %}
AND prf.platform
= {{
String(
platform,
description="Filter by platform (e.g., 'gerrit', 'github', 'gitlab')",
required=False,
)
}}
{% end %}
{% if defined(startDate) %}
AND prf.openedAt
>= {{
DateTime(
startDate,
description="Filter PRs opened after this timestamp",
required=False,
)
}}
{% end %}
{% if defined(endDate) %}
AND prf.openedAt
<= {{
DateTime(
endDate,
description="Filter PRs opened before this timestamp",
required=False,
)
}}
{% end %}
{% else %} SELECT * FROM timeseries_generation_for_median_time_to_merge
{% end %}
124 changes: 124 additions & 0 deletions services/libs/tinybird/pipes/median_time_to_review.pipe
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
DESCRIPTION >
- `median_time_to_review.pipe` serves the "Median time to review" widget in the Development tab.
- Calculates the median time in seconds from PR opening to first review for the selected time period.
- **When `granularity` is NOT provided, returns a single KPI value** (median) across the entire time range.
- **When `granularity` is provided, returns time-series data** showing median time to review aggregated by different time periods (daily, weekly, monthly, quarterly, yearly).
- Uses `generate_timeseries` pipe to create consistent time periods and left joins PR data to handle periods with no reviewed PRs.
- Only includes PRs that have been reviewed (`isNotNull(pra.reviewedAt)`) to ensure accurate review time calculations.
- Replaces the deprecated `waitTimeFor1stReview` widget with more robust median-based statistics.
- Available for projects connected with Gerrit, GitHub, or GitLab platforms.
- Primary use case: monitoring code review responsiveness and identifying delays in the review process.
- Parameters:
- `project`: Required string for project slug (e.g., 'k8s', 'tensorflow') - inherited from `segments_filtered`
- `repos`: Optional array of repository URLs for filtering (e.g., ['https://github.com/kubernetes/kubernetes'])
- `startDate`: Optional DateTime filter for PRs opened after timestamp (e.g., '2024-01-01 00:00:00')
- `endDate`: Optional DateTime filter for PRs opened before timestamp (e.g., '2024-12-31 23:59:59')
- `platform`: Optional string filter for source platform (e.g., 'gerrit', 'github', 'gitlab')
- `granularity`: Optional string for time aggregation ('daily', 'weekly', 'monthly', 'quarterly', 'yearly')
- Response:
- Without granularity: `medianTimeToReviewSeconds` (median value in seconds)
- With granularity: `startDate`, `endDate`, and `medianTimeToReviewSeconds` for each time period

TAGS "Widget", "Pull requests", "Development metrics", "Review time"

NODE timeseries_generation_for_median_time_to_review
SQL >
%
{% if defined(granularity) %}
SELECT
ds."startDate",
ds."endDate",
ifNull(round(median(prf.reviewedInSeconds)), 0) AS "medianTimeToReviewSeconds"
FROM generate_timeseries ds
LEFT JOIN
pull_requests_filtered prf
ON CASE
WHEN {{ granularity }} = 'daily'
THEN toDate(prf.openedAt)
WHEN {{ granularity }} = 'weekly'
THEN toStartOfWeek(prf.openedAt)
WHEN {{ granularity }} = 'monthly'
THEN toStartOfMonth(prf.openedAt)
WHEN {{ granularity }} = 'quarterly'
THEN toStartOfQuarter(prf.openedAt)
WHEN {{ granularity }} = 'yearly'
THEN toStartOfYear(prf.openedAt)
END
= ds."startDate"
AND isNotNull(prf.reviewedAt)
AND isNotNull(prf.reviewedInSeconds)
{% if defined(platform) %}
AND prf.platform
= {{
String(
platform,
description="Filter by platform (e.g., 'gerrit', 'github', 'gitlab')",
required=False,
)
}}
{% end %}
{% if defined(startDate) %}
AND prf.openedAt
>= {{
DateTime(
startDate,
description="Filter PRs opened after this timestamp",
required=False,
)
}}
{% end %}
{% if defined(endDate) %}
AND prf.openedAt
<= {{
DateTime(
endDate,
description="Filter PRs opened before this timestamp",
required=False,
)
}}
{% end %}
Copy link

Choose a reason for hiding this comment

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

Bug: Time window uses opened date, not review

The time bucketing and startDate/endDate filtering use prf.openedAt while the metric is “time to first review”. This makes results depend on when PRs were opened rather than when they were first reviewed, so PRs opened in-range but reviewed later can appear, and PRs opened earlier but reviewed in-range are excluded, shifting the widget’s meaning.

Additional Locations (1)

Fix in Cursor Fix in Web

GROUP BY ds."startDate", ds."endDate"
ORDER BY ds."startDate"
{% else %} SELECT 1
{% end %}

NODE median_time_to_review_merged
SQL >
%
{% if not defined(granularity) %}
SELECT round(median(prf.reviewedInSeconds)) AS "medianTimeToReviewSeconds"
FROM pull_requests_filtered prf
WHERE
isNotNull(prf.reviewedAt) AND isNotNull(prf.reviewedInSeconds)
{% if defined(platform) %}
AND prf.platform
= {{
String(
platform,
description="Filter by platform (e.g., 'gerrit', 'github', 'gitlab')",
required=False,
)
}}
{% end %}
{% if defined(startDate) %}
AND prf.openedAt
>= {{
DateTime(
startDate,
description="Filter PRs opened after this timestamp",
required=False,
)
}}
{% end %}
{% if defined(endDate) %}
AND prf.openedAt
<= {{
DateTime(
endDate,
description="Filter PRs opened before this timestamp",
required=False,
)
}}
{% end %}
{% else %} SELECT * FROM timeseries_generation_for_median_time_to_review
{% end %}
113 changes: 113 additions & 0 deletions services/libs/tinybird/pipes/patchsets_per_review.pipe
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
DESCRIPTION >
- `patchsets_per_review.pipe` serves the "Patchsets per review" widget in the Development tab.
- Calculates median or average number of patchsets per review for Gerrit changesets in the selected time period.
- **When `granularity` is NOT provided, returns a single KPI value** (median or average) across the entire time range.
- **When `granularity` is provided, returns time-series data** showing patchsets per review aggregated by different time periods (daily, weekly, monthly, quarterly, yearly).
- Uses `generate_timeseries` pipe to create consistent time periods and left joins PR data to handle periods with no data.
- Only includes Gerrit changesets with patchset data (`platform = 'gerrit'` and `isNotNull(numberOfPatchsets)`) to ensure accurate calculations.
- Primary use case: analyzing code review iteration patterns and review efficiency specifically for Gerrit changesets.
- Parameters:
- `project`: Required string for project slug (e.g., 'k8s', 'tensorflow') - inherited from `segments_filtered`
- `repos`: Optional array of repository URLs for filtering (e.g., ['https://gerrit.example.com/repo'])
- `startDate`: Optional DateTime filter for changesets opened after timestamp (e.g., '2024-01-01 00:00:00')
- `endDate`: Optional DateTime filter for changesets opened before timestamp (e.g., '2024-12-31 23:59:59')
- `granularity`: Optional string for time aggregation ('daily', 'weekly', 'monthly', 'quarterly', 'yearly')
- `dataType`: Optional string to select calculation method ('median' or 'average'), defaults to 'median'
- Response:
- Without granularity: `patchsetsPerReview` (median or average value)
- With granularity: `startDate`, `endDate`, and `patchsetsPerReview` for each time period

TAGS "Widget", "Pull requests", "Development metrics", "Code review", "Gerrit"

NODE timeseries_generation_for_patchsets_per_review
SQL >
%
{% if defined(granularity) %}
{% set dataType_val = 'median' %}
{% if defined(dataType) %} {% set dataType_val = dataType %} {% end %}
SELECT
ds."startDate",
ds."endDate",
{% if dataType_val == 'average' %}
ifNull(round(avg(prf.numberOfPatchsets), 2), 0) AS "patchsetsPerReview"
{% else %} ifNull(round(median(prf.numberOfPatchsets), 2), 0) AS "patchsetsPerReview"
{% end %}
FROM generate_timeseries ds
LEFT JOIN
pull_requests_filtered prf
ON CASE
WHEN {{ granularity }} = 'daily'
THEN toDate(prf.openedAt)
WHEN {{ granularity }} = 'weekly'
THEN toStartOfWeek(prf.openedAt)
WHEN {{ granularity }} = 'monthly'
THEN toStartOfMonth(prf.openedAt)
WHEN {{ granularity }} = 'quarterly'
THEN toStartOfQuarter(prf.openedAt)
WHEN {{ granularity }} = 'yearly'
THEN toStartOfYear(prf.openedAt)
END
= ds."startDate"
AND prf.platform = 'gerrit'
AND isNotNull(prf.numberOfPatchsets)
{% if defined(startDate) %}
AND prf.openedAt
>= {{
DateTime(
startDate,
description="Filter changesets opened after this timestamp",
required=False,
)
}}
{% end %}
{% if defined(endDate) %}
AND prf.openedAt
<= {{
DateTime(
endDate,
description="Filter changesets opened before this timestamp",
required=False,
)
}}
{% end %}
GROUP BY ds."startDate", ds."endDate"
ORDER BY ds."startDate"
{% else %} SELECT 1
{% end %}

NODE patchsets_per_review_merged
SQL >
%
{% if not defined(granularity) %}
{% set dataType_val = 'median' %}
{% if defined(dataType) %} {% set dataType_val = dataType %} {% end %}
SELECT
{% if dataType_val == 'average' %}
round(avg(prf.numberOfPatchsets), 2) AS "patchsetsPerReview"
{% else %} round(median(prf.numberOfPatchsets), 2) AS "patchsetsPerReview"
{% end %}
FROM pull_requests_filtered prf
WHERE
prf.platform = 'gerrit' AND isNotNull(prf.numberOfPatchsets)
{% if defined(startDate) %}
AND prf.openedAt
>= {{
DateTime(
startDate,
description="Filter changesets opened after this timestamp",
required=False,
)
}}
{% end %}
{% if defined(endDate) %}
AND prf.openedAt
<= {{
DateTime(
endDate,
description="Filter changesets opened before this timestamp",
required=False,
)
}}
{% end %}
{% else %} SELECT * FROM timeseries_generation_for_patchsets_per_review
{% end %}
2 changes: 1 addition & 1 deletion services/libs/tinybird/pipes/review_efficiency.pipe
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ DESCRIPTION >
- Without granularity: `openedCount` and `mergedCount` (single values)
- With granularity: `startDate`, `endDate`, `openedCount`, and `mergedCount` for each time period

TAGS "" Development metrics", Widget", "Pull requests", "Review efficiency"
TAGS "Widget", "Pull requests", "Development metrics", "Review efficiency"

NODE review_efficiency_timeseries
SQL >
Expand Down