feat: collections v2 aggregate Tinybird pipes and toggle column (IN-1193)#4336
Conversation
Adds the showAggregateTabs toggle column on collections, and new Tinybird pipes aggregating project_insights and activity-relations data across an arbitrary list of projects for the LFX Insights Collections v2 UI (IN-1193, IN-1195). Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
There was a problem hiding this comment.
Pull request overview
Adds Collections v2 backend support through a database toggle and collection-wide Tinybird metrics.
Changes:
- Adds aggregate insights, popularity, development, and contributor leaderboard pipes.
- Adds the
showAggregateTabscollection column. - Deduplicates collection-wide contributor and organization counts.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
collection_popularity_aggregate.pipe |
Aggregates stars and forks. |
collection_insights_aggregate.pipe |
Computes collection header metrics. |
collection_development_aggregate.pipe |
Counts active contributors and organizations. |
collection_contributors_leaderboard_aggregate.pipe |
Produces a collection-wide contributor leaderboard. |
V1782500000__addShowAggregateTabsColumnToCollectionsTable.sql |
Adds the aggregate-tabs toggle. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…project ids - Takes collectionSlug directly instead of a caller-supplied project-id array, resolving collection membership via insights_projects_populated_ds.collectionsSlugs (has() filter, same pattern as insightsProjects_filtered.pipe). - Fixes segmentId resolution: project_insights_copy_ds has no segmentId column at all (confirmed by the Tinybird deploy error); insights_projects_populated_ds has both segmentId and healthScore. Verified live against Tinybird - returns correct projectCount, uniqueContributorCount, and avgHealthScore for a real collection. Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Contributors/Popularity/Development collection tabs are hidden in the insights UI while IN-1195 is being reworked - removing the corresponding Tinybird pipes so the PR doesn't ship dead backend code. The showAggregateTabs migration and collection_insights_aggregate.pipe (IN-1193's header metrics, already deployed and working) stay. Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
…ll curated - Replaces the earlier hand-written migration with one scaffolded via ./scripts/cli scaffold create-migration, per repo convention. - showAggregateTabs now defaults to false for all collections, then backfills to true for curated (LF Foundation) collections only (ssoUserId IS NULL) - the tab-gating logic lives entirely in this column now, not split between it and a CURATED type check on the insights frontend. - Adds an editable "Show aggregate tabs" toggle to the CDP admin collection add/edit form, defaulting to true for new collections (admin-created collections are always curated), following the exact same DAL/model/form pattern already used for the sibling starred field. Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Frontend dev only needs api running; the packages_worker workers (npm/pypi/maven/osv/dockerhub/cargo/go/nuget/rubygems/security-contacts, bq-dataset-ingest, github-repos-enricher) plus automatic-projects-discovery-worker, pcc-sync-worker, and projects-evaluation-worker were being built/started unnecessarily on every clean-start-fe-dev / service-restart-fe-dev. Claude-Session: https://claude.ai/code/session_01J17dWfDoruiB5xn5iNM89V Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Pairs with V1784026542 - DROP COLUMN reverses the ADD COLUMN + backfill, following this repo's occasional U<version> undo-migration convention. Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
PR SummaryLow Risk Overview Updates Reviewed by Cursor Bugbot for commit cc3b759. Bugbot is set up for automated code reviews on this repo. Configure here. |
- Filter uniqueContributorCount in collection_insights_aggregate.pipe to code contributions/collaborations only, matching the existing segmentId_aggregates_snapshot.pipe convention (was inflated by stars, joins, and other non-contribution activity types). - Default showAggregateTabs based on collection type in createCollection (community collections with ssoUserId now default to false, matching the migration's backfill policy, instead of always defaulting true). - Fix accessible name association for the "Show aggregate tabs" switch by moving its description text into LfSwitch's default slot instead of a sibling span outside the label. Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
collection.page.vue's featured-star toggle builds a CollectionRequest object literal that omits showAggregateTabs (and other optional fields), relying on the backend to preserve omitted values on partial update. The field was typed as required, which breaks that existing call site. Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
| SELECT | ||
| (SELECT count(distinct id) FROM collection_insights_aggregate_projects) AS projectCount, | ||
| uniq(ar.memberId) AS uniqueContributorCount, | ||
| (SELECT round(avg(healthScore)) FROM collection_insights_aggregate_projects) AS avgHealthScore | ||
| FROM activityRelations_deduplicated_cleaned_bucket_union AS ar | ||
| WHERE | ||
| ar.memberId != '' | ||
| AND ar.segmentId IN (SELECT segmentId FROM collection_insights_aggregate_projects) | ||
| AND (ar.type, ar.platform) IN ( | ||
| SELECT activityType, platform | ||
| FROM activityTypes | ||
| WHERE isCodeContribution = 1 OR isCollaboration = 1 | ||
| ) |
| ;; | ||
| service-restart-fe-dev) | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker") | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker" "automatic-projects-discovery-worker" "pcc-sync-worker" "projects-evaluation-worker" "bq-dataset-ingest" "cargo-worker" "dockerhub-sync" "github-repos-enricher" "go-worker" "maven-worker" "npm-worker" "nuget-worker" "osv-worker" "pypi-worker" "rubygems-worker" "security-contacts-worker") |
| ;; | ||
| clean-start-fe-dev) | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker" "cache-worker" "categorization-worker" "cron-service" "data-sink-worker" "git-integration" "integration-run-worker" "integration-stream-worker" "nango-webhook-api" "nango-worker" "script-executor-worker" "search-sync-api" "search-sync-worker" "security-best-practices-worker" "snowflake-connectors-worker") | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker" "cache-worker" "categorization-worker" "cron-service" "data-sink-worker" "git-integration" "integration-run-worker" "integration-stream-worker" "nango-webhook-api" "nango-worker" "script-executor-worker" "search-sync-api" "search-sync-worker" "security-best-practices-worker" "snowflake-connectors-worker" "automatic-projects-discovery-worker" "pcc-sync-worker" "projects-evaluation-worker" "bq-dataset-ingest" "cargo-worker" "dockerhub-sync" "github-repos-enricher" "go-worker" "maven-worker" "npm-worker" "nuget-worker" "osv-worker" "pypi-worker" "rubygems-worker" "security-contacts-worker") |
| -- ssoUserId is only set for community/user-curated collections (see V1772438175), so | ||
| -- ssoUserId IS NULL identifies curated collections. | ||
| UPDATE collections | ||
| SET "showAggregateTabs" = true |
There was a problem hiding this comment.
Why do we need this column? Can't we simply infer this from wether or not ssoUserId is null?
| <!-- Show aggregate tabs --> | ||
| <article class="mb-6"> | ||
| <lf-field label-text="Show aggregate tabs"> | ||
| <div class="flex items-center gap-2"> | ||
| <lf-switch v-model="form.showAggregateTabs" size="small"> | ||
| <span class="text-2xs text-gray-400">Show in-depth aggregate metric tabs on the public collection page</span> | ||
| </lf-switch> | ||
| </div> | ||
| </lf-field> | ||
| </article> |
There was a problem hiding this comment.
Again, I don't think this is needed.
| // Only LF Foundation (curated) collections get aggregate tabs by default. | ||
| // ssoUserId is only set for community/user-curated collections, so | ||
| // ssoUserId being unset identifies curated collections (see V1784026542). | ||
| showAggregateTabs: !collection.ssoUserId, |
| ALTER TABLE collections | ||
| ADD COLUMN "showAggregateTabs" boolean DEFAULT false NOT NULL; |
| ;; | ||
| service-restart-fe-dev) | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker") | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker" "automatic-projects-discovery-worker" "pcc-sync-worker" "projects-evaluation-worker" "bq-dataset-ingest" "cargo-worker" "dockerhub-sync" "github-repos-enricher" "go-worker" "maven-worker" "npm-worker" "nuget-worker" "osv-worker" "pypi-worker" "rubygems-worker" "security-contacts-worker") |
…ement Per product decision: which collections show the in-depth aggregate tabs doesn't need to be independently manageable per foundation - the insights frontend can derive this directly from collection type (curated vs community), which it already knows. Removes the migration, the DAL/admin UI wiring, and the per-foundation CDP toggle entirely. Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit cc3b759. Configure here.
| AND has( | ||
| collectionsSlugs, | ||
| {{ String(collectionSlug, description="Filter by collection slug", required=True) }} | ||
| ) |
There was a problem hiding this comment.
Missing empty segmentId filter
Medium Severity
The projects node does not exclude rows where segmentId is an empty string before those ids feed the activity IN filter. Elsewhere (e.g. leaderboards_members, health_score_copy) empty segmentId values are dropped. Matching on '' can pull unrelated activity rows and inflate uniqueContributorCount.
Reviewed by Cursor Bugbot for commit cc3b759. Configure here.
| SELECT | ||
| (SELECT count(distinct id) FROM collection_insights_aggregate_projects) AS projectCount, | ||
| uniq(ar.memberId) AS uniqueContributorCount, | ||
| (SELECT round(avg(healthScore)) FROM collection_insights_aggregate_projects) AS avgHealthScore | ||
| FROM activityRelations_deduplicated_cleaned_bucket_union AS ar | ||
| WHERE | ||
| ar.memberId != '' | ||
| AND ar.segmentId IN (SELECT segmentId FROM collection_insights_aggregate_projects) | ||
| AND (ar.type, ar.platform) IN ( | ||
| SELECT activityType, platform | ||
| FROM activityTypes | ||
| WHERE isCodeContribution = 1 OR isCollaboration = 1 | ||
| ) |
| ;; | ||
| service-restart-fe-dev) | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker") | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker" "automatic-projects-discovery-worker" "pcc-sync-worker" "projects-evaluation-worker" "bq-dataset-ingest" "cargo-worker" "dockerhub-sync" "github-repos-enricher" "go-worker" "maven-worker" "npm-worker" "nuget-worker" "osv-worker" "pypi-worker" "rubygems-worker" "security-contacts-worker") |
| ;; | ||
| clean-start-fe-dev) | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker" "cache-worker" "categorization-worker" "cron-service" "data-sink-worker" "git-integration" "integration-run-worker" "integration-stream-worker" "nango-webhook-api" "nango-worker" "script-executor-worker" "search-sync-api" "search-sync-worker" "security-best-practices-worker" "snowflake-connectors-worker") | ||
| IGNORED_SERVICES=("frontend" "python-worker" "job-generator" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker" "cache-worker" "categorization-worker" "cron-service" "data-sink-worker" "git-integration" "integration-run-worker" "integration-stream-worker" "nango-webhook-api" "nango-worker" "script-executor-worker" "search-sync-api" "search-sync-worker" "security-best-practices-worker" "snowflake-connectors-worker" "automatic-projects-discovery-worker" "pcc-sync-worker" "projects-evaluation-worker" "bq-dataset-ingest" "cargo-worker" "dockerhub-sync" "github-repos-enricher" "go-worker" "maven-worker" "npm-worker" "nuget-worker" "osv-worker" "pypi-worker" "rubygems-worker" "security-contacts-worker") |


Summary
Backend support for the LFX Insights Collections v2 UI epic (IN-1191, subtickets IN-1193 and IN-1195):
showAggregateTabsboolean column oncollections(defaultfalse, backfilledtruefor curated/LF Foundation collections wheressoUserId IS NULL) — per-collection toggle for the new in-depth aggregate tabs.collection_insights_aggregate.pipe— resolves a collection's member projects viainsights_projects_populated_ds.collectionsSlugs(has()filter, same pattern asinsightsProjects_filtered.pipe) and aggregates Project count / unique Contributors (deduplicated across all projects, filtered to code contributions or collaborations) / Avg. Health Score for a givencollectionSlug(IN-1193's header metrics row).starredfield's pattern.Note:
collection_contributors_leaderboard_aggregate.pipe,collection_popularity_aggregate.pipe, andcollection_development_aggregate.pipewere removed from this PR — those tabs (leaderboard, popularity, development) are being reworked separately and will ship in a follow-up PR. This PR only covers the header metrics row aggregate (IN-1193).Deploy order
This PR must merge and
collection_insights_aggregate.pipemust be deployed to Tinybird before the companioninsightsPR's aggregate features will show real data — until then, the insights UI degrades gracefully to empty/fallback states (verified, does not crash).