Skip to content

Add public competition pages and scouting analytics - #82

Draft
jackshim415 wants to merge 64 commits into
mainfrom
feature/store-more-frc-data
Draft

jackshim415 wants to merge 64 commits into
mainfrom
feature/store-more-frc-data

Conversation

@jackshim415

@jackshim415 jackshim415 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Add public competition browsing and Lovat scouting analytics to the Website. Season and district pages organize events by week and nest championship divisions. Tournament pages share Schedule, Teams, and Alliances views; team pages show event records, awards, alliance details, robot photos, and configurable scoring/feeding graphs with role breakdowns. Match pages show official results, scouting metrics, adjacent matches, predictions, and video/resource links.

Search provides team/event suggestions and direct team-number navigation. Shared match cards display scores, DQs, role icons, and available predictions. Credited location photos and deterministic FRC fallbacks supply event backgrounds; Worlds uses broadcast-style symbols. The layout follows Dashboard/Magnolia styling and supports mobile screens.

Public Server routes select competition fields and approved scouting aggregates only. All-team public aggregate access was explicitly approved by the maintainer. Raw reports, notes, identities, source identities, and robot paths remain private. Signed-in predictions retain existing source filters and take precedence; unsupported seasons and unavailable values remain hidden. Scouting metrics currently support 2026.

Dependency and deployment

#83 is merged. This PR is now based on main; its diff excludes all shared schema migrations, worker code, worker CI, and API import handoff. The prerequisite was merged with a merge commit, preserving ancestry and avoiding duplicate infrastructure changes.

The existing lovat-pr-82 environment and populated database are retained. Its original sanitized backup remains untouched. A fresh snapshot was also made while splitting the PR. The schema/worker preview uses a separate copy; changes in either preview do not synchronize automatically.

Website/API deploy after the schema and worker. Configure Website API/signing references and Auth0 callback, web-origin, and logout allowlists for the production domain. The maintainer has already allowlisted the #82 Website preview.

Verification

  • Before this history-only split: Server build, both typechecks, all 1,096 tests, and lint passed (80 existing warnings). Website check/lint/build passed (13/19 existing warnings), together with prediction, record, wallpaper, and analytics regression checks.
  • Deployed browser checks cover event/district/season navigation, brackets, records, team event dropdowns, match results/videos, predictions, public metrics, and responsive 390px layouts without page overflow.
  • Team 8033 analytics verified 36 scouted matches across three events, metric/event/chart/rolling-average controls, event-filtered pie geometry, tied-role weighting, and links from graph points.
  • The split merged only prerequisite ancestry and documentation. Website and public API implementation is unchanged; no source histories were rewritten.

Team registration indicators: approved registered teams expose only a public usesLovat boolean. Profiles show a Lovat-mark badge beside the number; event team lists reuse a compact icon with an accessible label and tooltip, retaining centered records and separate award icons. Codes, contacts, and registration records are excluded. No migration is needed. Server build, both typechecks, lint (80 existing warnings), and all 1,101 unit tests pass. Website check/lint/build pass with existing 13/19 warnings. Fresh dependency installs pass with a project-local legacy-peer setting for Magnolia preparation, preserving existing engine/resolution settings and all pinned versions; Docker now copies this configuration before npm ci. Existing Server coverage-threshold failures remain separate from these passing tests.

Scouting context and comparison

Team analytics now mark tournament boundaries, eliminations, and matches reported as broken. Charts restart rolling averages at each tournament; summaries show the arithmetic mean, median, interpolated 25th–75th percentile range, linked best match, and recent-five-match average for the selected metric and event filter. Breakdown matches remain included by default and can be excluded. Only a breakdown flag is public, never the description.

Match cards show how many participants have a scouting report, with “Fully scouted” requiring six distinct teams, three per alliance, and at least one report each. The dedicated /predictor page provides a configurable six-team public predictor using the existing 2026 scouting engine, linked from team analytics with that team prefilled and from the footer. District pages add a sortable Lovat leaderboard from cached season metrics, distinct from official district standings. Matches, Points, Auto, and Teleop headers toggle ascending/descending sorting; approved Collection teams receive the shared subtle icon in the district team list. Existing authenticated source filters are unchanged. No schema migration is required.

Tournament graph boxes show their event key and raw match average. New tournaments use solid red boundaries, reported breakdowns use dashed red lines, and eliminations begin at dashed blue lines. Known unscouted matches retain their timeline positions as gaps and are excluded from statistics. The centered role pie uses purple shades and direct percentage labels.

Validation: Server build and test type-check; all 1,108 Server tests across 55 files pass. Website check, lint, production build, and four analytics tests pass. Existing warning baseline remains (13 Svelte check warnings, 19 Website lint warnings, 80 Server lint warnings).

Tournament points ranking

Tournament pages now include an Analytics tab with a “Total Points Dropoff” graph plotting average total scouting points per match against total-points rank. Every point is labeled with its team number, and a horizontal line marks the mean of the plotted team averages. Explanatory subtitle and hover instructions are removed. Hover or keyboard focus reveals the team, rank, score, and number of scouted matches; each point links to that team. Rankings use only reports from the selected event, retain zero scores, order equal scores by team number, and exclude teams without reports. A cached public projection reuses the existing 2026 scoring engine and returns only aggregate team data. Other seasons show an empty state.

Validation: all 1,110 Server tests pass, including event-source isolation and zero-score coverage. Server build, both typechecks, lint, and Website check/lint/build pass with existing warnings. Browser verification uses the CalGames preview data after an insert-only production report sync. No schema migration is required.

The dropoff graph also fits a decaying exponential by minimizing squared residuals in points, including zero scores, and shows its equation and original-scale R². Its top 32 teams form four consecutive eight-team cohorts with shaded bands and separate team-average lines. Smaller events show only the available cohorts. Tests cover exact exponential recovery, zero-score influence, original-scale R², flat/insufficient data, and cohort boundaries; all eight analytics tests pass. Website check/lint/build pass with the existing warning baseline.

The best-fit curve and equation, overall average, team labels, and each cohort’s shading and average can be toggled independently. All seven controls were verified in the browser; Website check, lint, and production build pass with the existing warning baseline.

@railway-app

railway-app Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

🚅 Deployed to the lovat-pr-82 environment in lovat

Service Status Web Updated
dashboard 😴 Sleeping (View Logs) Web Oct 18, 2026 at 9:29 pm UTC
website ✅ Success (View Logs) Web Oct 18, 2026 at 5:33 am UTC
api ✅ Success (View Logs) Web Oct 18, 2026 at 3:02 am UTC
learn 😴 Sleeping (View Logs) Web Oct 14, 2026 at 5:07 pm UTC
worker ✅ Success (View Logs) Oct 12, 2026 at 3:03 pm UTC

@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 10, 2026 18:11 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 10, 2026 19:39 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 10, 2026 19:47 Destroyed
@jackshim415
jackshim415 marked this pull request as ready for review October 10, 2026 20:55
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-10T20:58:37.607171Z fa0bd35 Draft marked ready
🔒 Security Review ✅ Completed 2026-10-10T20:58:31.486546Z fa0bd35 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa0bd3550c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +271 to +275
if (
existing &&
((existing.matchKey && existing.matchKey !== match.key) ||
(existing.teamNumber !== teamNumber &&
existing._count.scoutReports > 0))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Retire the server's competing match writer

When a TBA station assignment changes after a report exists, this guard correctly rejects reassignment, but the still-active server path calls addTournamentMatches() from apps/server/src/handler/manager/getMatches.ts:48 and unconditionally overwrites the same TeamMatchData.teamNumber in addTournamentMatches.ts:176-191. Because that writer also uses a separate legacy ETag, deploying both services lets a request to the server bypass this reconciliation check and silently attach existing scouting reports to the replacement team; disable or update the server writer as part of this cross-app ingestion change.

AGENTS.md reference: AGENTS.md:L27-L28

Useful? React with 👍 / 👎.

Comment thread apps/worker/src/gaps.ts Outdated
Comment on lines +87 to +91
const ordered = [...matches].sort(
(a, b) =>
levels[a.competitionLevel] - levels[b.competitionLevel] ||
a.setNumber - b.setNumber ||
a.matchNumber - b.matchNumber,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Order bracket games by their scheduled sequence

For historical playoff formats that alternate series—such as qf1m1, qf2m1, …, followed by qf1m2—sorting by setNumber before matchNumber instead groups every game from one series together. The resulting gap rows connect matches that were not adjacent in the event schedule and can classify ordinary intervening matches as long breaks; use the imported display/scheduled order or a format-specific canonical order instead.

Useful? React with 👍 / 👎.

Expose selected public competition data for 2026cancmp and display match results, inferred breaks, and a searchable team roster using Lovat styling.
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 10, 2026 22:36 Destroyed
Use local TBA avatars with sampled gradients and verified event award markers for the fixed 2026cancmp page. Align participants by station, name upper and lower bracket matches, and center alliance-colored scores in each match header.
Show the eight selected alliances and connect upper/lower bracket results using imported match data. Share the tournament loader between pages and use local Material SVGs for reliable award markers.
Switch tournament views inline so schedule, teams, and alliances keep the same URL and event header.
Import alliance selections, awards, season avatars, and regular-season weeks so public pages work with any imported event. Group district events by week and championship, add credited California town backgrounds, and load roster icons near the viewport.
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 10, 2026 23:52 Destroyed
Show disqualified team identities with a strikethrough and size alliance cards for every selected member and backup. Add a basic season page linking imported districts and non-district events through a public competition-only API.
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 15:18 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 15:24 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 15:28 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 15:30 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 15:32 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 16:25 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 16:26 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 16:46 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 16:51 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 16:59 Destroyed
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 15, 2026 17:00 Destroyed
@jackshim415
jackshim415 marked this pull request as draft October 16, 2026 04:07
@railway-app
railway-app Bot temporarily deployed to lovat / lovat-pr-82 October 18, 2026 04:48 Destroyed

This branch was successfully deployed

1 active deployment
lovat / lovat-pr-82 — 4dc52290 Deployed Oct 18, 2026 by railway-app[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant