Skip to content

Add /stats career-stats page - #46

Open
tuggernuts1123 wants to merge 2 commits into
openversus:openversusfrom
tuggernuts1123:stats
Open

Add /stats career-stats page#46
tuggernuts1123 wants to merge 2 commits into
openversus:openversusfrom
tuggernuts1123:stats

Conversation

@tuggernuts1123

@tuggernuts1123 tuggernuts1123 commented Jun 16, 2026

Copy link
Copy Markdown

Summary

  • New public /stats page that resolves the visitor by IP (same flow as /namechange — account picker shown if multiple accounts share an IP) and renders their career stats from PlayerStats.aggregate.
  • Moves table with Used / Hit / Hit % / % of all (sorted by Used desc; computed client-side from the inlined aggregate JSON). Charge Attack shows its own row but is excluded from the % of all denominator since it's an overlay action, not a discrete move type.
  • Summary cards for Defense (parries, dodges, projectiles blocked, armor), Damage & Ringouts, and Movement.
  • "My Stats" tile added to /home.

Test plan

  • Visit /stats from an IP with an existing PlayerStats document → moves table + summary cards render correctly with real data.
  • Visit /stats from a fresh IP / account with no aggregate → friendly "No stats yet" screen.
  • Visit /stats from an IP with multiple accounts → existing account picker is shown.
  • Charge Attack row appears in table with Used/Hit/Hit % but in the % of all column.
  • "My Stats" tile appears on /home and links to /stats.
  • Back button at the top of /stats returns to /home.

🤖 Generated with Claude Code

Summary by Sourcery

Add a public career stats page resolved by IP and link it from the home screen.

New Features:

  • Introduce a /stats route that resolves the caller’s account by IP and serves a career stats page based on aggregated player stats.
  • Add a dedicated career stats HTML template that displays move usage, accuracy, defensive, damage, ringout, and movement summaries, with a friendly empty state when no data exists.
  • Expose a "My Stats" tile on the home page that links to the new /stats career stats view.

Enhancements:

  • Inline aggregated stats JSON into the stats page and compute move usage tables and summary stat cards client-side, including special handling for charge attacks in distribution percentages.

Public IP-resolved page (same flow as /namechange) showing the visitor's
career move usage, accuracy, parries, ringouts, and movement stats from
their PlayerStats.aggregate document.

- New /stats route in server.ts: resolves player by IP via the existing
  resolvePlayerForWeb helper (renders the account picker if multiple
  accounts share an IP), loads PlayerStats by account_id, returns a
  rendered page with the aggregate JSON inlined.
- New static page src/static/my_stats.html with the moves table (sorted
  by Used desc, computed Hit % and % of all client-side) plus defense /
  damage / movement summary cards. Charge Attack shows its own row but
  is excluded from the % of all denominator.
- "My Stats" tile added to /home.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Reviewer's Guide

/stats career stats page added, resolving the caller via the existing IP-based account flow, rendering a new stats HTML template with aggregated player stats and client-side computed move/summary tables, and exposing it via a new home tile.

File-Level Changes

Change Details Files
Expose a new /stats route that resolves a player by IP and renders stats using a Handlebars template.
  • Compile a new my_stats.html Handlebars template alongside the existing home template.
  • Add GET /stats route that uses resolvePlayerForWeb to obtain the player or show the existing account picker.
  • Query PlayerStatsModel by resolved account_id and branch between no-stats and has-stats render paths.
  • Inline the aggregate stats JSON into the page, safely escaped, and pass updated_at to the client.
src/server.ts
src/static/my_stats.html
Add a "My Stats" entry point on the home page.
  • Add a new orange link-card on /home labeled "My Stats" that links to /stats and describes the stats content.
src/static/home.html
Implement the /stats page UI and client-side logic to display career stats.
  • Create a dedicated my_stats.html page reusing the name-change background and styling plus a back-to-home button.
  • Render either a "No stats yet" empty state or, when hasStats is true, the player name, description, move table, and stat sections.
  • Parse the inlined aggregate JSON in a script block and compute move usage, accuracy, and distribution, excluding Charge Attack from the % of all denominator while still showing its Used/Hit/Hit%.
  • Populate defense, damage/ringouts, and movement summary grids from aggregate fields, formatting counts and derived metrics like dodge accuracy and net ringouts.
src/static/my_stats.html

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue, and left some high level feedback:

  • The my_stats.html page reimplements a lot of layout/styling that appears to be shared with the name-change flow (background, wrapper, card styles); consider extracting a shared partial or CSS so future tweaks to that look-and-feel only need to be made in one place.
  • The client-side stats script hardcodes a large set of aggregate field keys (e.g., totalNeutralGroundNormalUsed, totalRingoutsEnemyLowPercent); it may be more maintainable to centralize these mappings (or derive them from a shared schema/module) so changes to PlayerStats.aggregate don’t require updating multiple scattered string literals.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `my_stats.html` page reimplements a lot of layout/styling that appears to be shared with the name-change flow (background, wrapper, card styles); consider extracting a shared partial or CSS so future tweaks to that look-and-feel only need to be made in one place.
- The client-side stats script hardcodes a large set of aggregate field keys (e.g., `totalNeutralGroundNormalUsed`, `totalRingoutsEnemyLowPercent`); it may be more maintainable to centralize these mappings (or derive them from a shared schema/module) so changes to `PlayerStats.aggregate` don’t require updating multiple scattered string literals.

## Individual Comments

### Comment 1
<location path="src/static/my_stats.html" line_range="366-373" />
<code_context>
+      const platDrops    = num("totalPlatformDropThroughs");
+      const taunts       = num("totalTauntsUsed");
+
+      const moveCards = [
+        ["Total Jumps",         fmt(jumps)],
+        ["Double Jumps",        fmt(doubleJumps)],
+        ["Air Time (s)",        fmt(Math.round(airTime))],
+        ["Walk Time (s)",       fmt(Math.round(walkTime))],
+        ["Wall Hang Time (s)",  fmt(Math.round(wallHang))],
+        ["Platform Drops",      fmt(platDrops)],
+        ["Taunts",              fmt(taunts)],
+      ];
+      for (const [label, value] of moveCards) {
</code_context>
<issue_to_address>
**suggestion:** Consider reusing a helper to render stat cards to reduce repetition across grids.

These sections all iterate over `[label, value]` pairs to build identical `.stat_card` elements. Pulling that logic into a helper like `renderCards(containerId, cards)` would centralize the structure and make future layout/style changes easier to maintain.

Suggested implementation:

```
      // Shared helper to render stat cards into a grid container
      function renderStatCards(container, cards) {
        for (const [label, value] of cards) {
          const c = document.createElement("div");
          c.className = "stat_card";
          c.innerHTML = '<div class="label">' + label + '</div><div class="value">' + value + '</div>';
          container.appendChild(c);
        }
      }

      // ─── Movement grid ─────────────────────────────────────────────
      const movementGrid = document.getElementById("movement_grid");
      const jumps        = num("totalJumps");
      const doubleJumps  = num("totalDoubleJumps");
      const airTime      = num("totalAirTime");
      const walkTime     = num("totalWalkTime");
      const wallHang     = num("totalWallHangTime");
      const platDrops    = num("totalPlatformDropThroughs");
      const taunts       = num("totalTauntsUsed");

      const moveCards = [
        ["Total Jumps",         fmt(jumps)],
        ["Double Jumps",        fmt(doubleJumps)],
        ["Air Time (s)",        fmt(Math.round(airTime))],
        ["Walk Time (s)",       fmt(Math.round(walkTime))],
        ["Wall Hang Time (s)",  fmt(Math.round(wallHang))],
        ["Platform Drops",      fmt(platDrops)],
        ["Taunts",              fmt(taunts)],
      ];
      renderStatCards(movementGrid, moveCards);
    })();
  </script>
  {{/if}}
</body>
</html>

```

There are likely other sections in this file that build `.stat_card` grids by looping over `[label, value]` pairs (e.g., for damage, knockouts, etc.). For each of those sections, replace the inline `for (const [label, value] of someCards)` loops that create `.stat_card` elements with calls to `renderStatCards(targetGridElement, someCards)` to fully centralize the rendering logic.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/static/my_stats.html Outdated
Sourcery feedback: the defense / damage / movement grids each duplicated
the same loop that built .stat_card elements. Centralize it in a single
renderStatCards(containerId, cards) helper.

Bonus: switch from innerHTML to textContent for the label and value
elements so future data shifts can't inject markup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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