Topics & Video Statistics
/practice/statistics — a standalone page combining the research-area topic
explorer, a searchable list of every season's debate resolutions, and the
YouTube channel view/upload charts that used to live only in a modal.
- Route:
/practice/statistics(also/practice/statistics) - Package:
debate-videos(the areas explorer comes fromdebate-webview)
What it shows
Three sections, stacked, with Debate Topics by Year at the top:
- Debate Topics by Year (
DebateTopicsExplorer) — a timeline (components/timeline/Timeline.tsx, adapted from the shadcn-space "timeline-02" block): a scrollable rail of every season, newest first, beside an animated pane for the selected year. The pane shows that year's Policy, College (NDT), LD and PF resolutions, each behind a style badge (LD and PF list each month's resolution with its short start month, such as "Sep" or "Nov", under the badge rather than in the resolution text), and — once/api/youtube-statsresolves — that year's video count, total views and average views from itsbyYearrows. Click a year, use the arrow / Home / End keys on the rail, or the up/down buttons beside it to scroll through every season; the selection survives filtering when the year is still shown. A search box filters by year or by resolution text. The data is the sametopicsfield/api/videos/metaalready returns for the video cards' topic tooltips (lib/debate-topics.ts's formatters), passed down fromLecturesPagerather than fetched again here. Every resolution is a link: clicking one opens the CARDS search (/research/cards) for that topic's short name, filtered to that season's year and that format (Policy → CX, College → NDT, LD, PF), listing every card, outline and round that matches. - Research Areas (
TopicAreasExplorer) — every NDT, Policy, LD and PF resolution since 2000, split into 44 focused research areas. Each format is its own collapsible panel with its own selection, so comparing an area's Policy history against its PF history is two clicks rather than a filter toggle that throws the first answer away. Within a panel the areas are ranked by resolution count as a bar chart; selecting one lists its resolutions newest first and draws its year-by-year trend line. The area assignments are heuristic research categories, not official NSDA or NDT classifications — the section says so in its footer. Above the format panels sits a Next season entry (NextSeasonPanel, e.g. "Next season · 2027–28"), shown before any of that season's resolutions are announced. It holds a poll — "Which topic area would you most like to debate next season?" It's ranked-choice: click up to five areas in order of preference (click again to remove one) and press Submit ranking. Each area scores Borda points — 5 for a first choice down to 1 for a fifth — and the list is ordered by points, then first choices. Anyone can see the tally; voting needs a sign-in, and each user has one ballot per season that they can update at any time. - YouTube Channel Statistics (
YouTubeStatsCharts) — the top-channels table and the views/videos/avg-views-over-time chartsYouTubeStatsModalalready drew in its dialog, extracted so they can render inline on a full page instead. The four totals charts (views, videos and average views over time, and average views by elim round) are compact and sit four to a row on wide screens, two on tablets and one on phones. Backed byuseYouTubeStats()(GET /api/youtube-stats, itself optional furniture — a deployment with no YouTube API key just leaves this section off rather than erroring).
Reachable from the sidebar tree's Practice section ("Topics & Video
Statistics", alongside Glossary and Rankings) and from the /videos quick-link
tile grid.
Merged from two pages
This page absorbed the standalone Debate Topics Explorer, which lived at
/research/topics. That address now permanently redirects here. The two were
drawing on the same underlying resolutions — every entry in the areas
explorer's resolutions.json is the same resolution text as
debate-topics.json, filed under an added area tag — so one page now answers
"what has been debated, and in which research domain" and "how much of it is on
the channel" without crossing two routes to join the halves. The areas
explorer's sidebar row was removed from the Research section, where it would
otherwise have been a second entry for the same page.
The 44 area definitions stay in debate-webview
(lib/topic-areas/topic-areas.ts) rather than moving to debate-videos,
where this page renders: debate-videos depends on debate-webview, not the
reverse, so it cannot import the data. The host page mounts the explorer and
hands it down as a topicAreasSlot element, the same way dockSlot carries
the app dock into the video library.
Data flow
panels/LecturesPage.tsx — currentCategory === "statistics" branch
→ panels/statistics/StatisticsPage.tsx
→ components/topic-explorer/DebateTopicsExplorer.tsx (topics, youtubeStats.byYear)
→ components/timeline/Timeline.tsx
→ components/topic-explorer/TopicAreasExplorer.tsx
→ components/youtube-stats-modal/YouTubeStatsCharts.tsx (youtubeStats prop)StatisticsPage takes both as props rather than fetching them itself:
topics comes from the same useVideoMeta() call every other /videos
branch already makes, and youtubeStats from the same useYouTubeStats()
call LecturesVideoGridView's own stats-modal button uses — so landing on
this page makes no request the rest of /videos wasn't already making.
Known gaps
No per-style filter on the topics grid — search matches across every
style's text at once, there's no "Policy only" toggle. Fixed:
DebateTopicsExplorer has an "All / Policy / College / LD / PF" filter
row above the search box (labels match the same DEBATE_STYLE_LABELS the
per-year badges already use — College here is the NDT resolution). Picking a style hides any year with no resolution
for that style, restricts the search box to that style's text (plus the year
itself), and shows only that style's badge in each remaining year's pane — "Policy
only" now works exactly as named. "All" restores the previous behavior.
The topic links search by the topic's short name (e.g. "Water"), not the
full resolution — the cards search is a substring match, so a whole
resolution sentence would match almost nothing. Some older short names
in Fixed for
the 2002–2009 PF topics, which now carry real short names ("National Service",
"War on Terror"). Other seasons' names may still need the same pass; the fix
is better names in the data, not in the link.debate-topics.json were just the resolution's first two words
("All Young", "The United"), so those searches were broad.
The next-season poll has no admin view or reset; ballots for a season stay in
topic_area_rankings after that season starts, and the panel simply moves on to
the following season in July (nextPollSeason).
Topic-area poll internals
- Shared rules:
debate-videoslib/topic-areas/topic-area-poll.ts— which season is polled (nextPollSeason: July starts a new season, labelled by its spring year), ballot validation (parseVoteBody: 1–5 distinct known areas), Borda scoring (pointsForRank), option order and the two client calls. - API:
GET /api/topic-area-poll?season=(points,firstChoices,totalballots, plusmyRankingwhen signed in) andPUT /api/topic-area-poll{ season, ranking }(401 signed out; only next season, and one to five distinct known areas). - Storage:
topic_area_rankings, primary key(season, user_id, rank)— one row per ranked area. A new ballot deletes the user's rows for the season and inserts the new ones in one batch. Declared inschema.ts, created bypackages/debate-videos/migrations/0003_topic_area_rankings.sql, which also copies the old single-choicetopic_area_votesrows in as first choices (that table is left in place, unread).
