Skip to content

Show stats panel in occurrence list sidebar#1308

Draft
mihow wants to merge 6 commits into
feat/human-model-agreement-endpointfrom
feat/occurrence-stats-ui
Draft

Show stats panel in occurrence list sidebar#1308
mihow wants to merge 6 commits into
feat/human-model-agreement-endpointfrom
feat/occurrence-stats-ui

Conversation

@mihow
Copy link
Copy Markdown
Collaborator

@mihow mihow commented May 15, 2026

Summary

Frontend consumer for the /occurrences/stats/model-agreement/ endpoint added in #1307. Adds a Stats panel at the top of the occurrence list sidebar, above the filter sections.

  • New OccurrenceStats component (ui/src/pages/occurrences/occurrence-stats.tsx)
  • Wired into occurrences.tsx, threading the same active filter array the list view sends to useOccurrences — so the stats always match the current result set (taxon, deployment, date, verification status, default filters, etc.)
  • Bars rendered, top to bottom:
    • Verified occurrencesverified_pct with the raw verified_count alongside (e.g. 0% (121)), so a small-but-nonzero set that rounds to 0% still surfaces the count.
    • Agreement (exact taxon)agreed_exact_pct, with agreed_exact_count / verified_with_prediction_count and inline Wilson 95% CI.
    • Agreement (any rank)agreed_any_rank_pct, same shape (exact matches plus disagreements whose LCA is at any real taxonomic rank).
    • Agreement (≥ <RANK>)agreed_coarser_rank_pct, only rendered when the caller passes ?agreement_coarsest_rank=<RANK> and the backend echoes it. No CI in the BE response yet, so the bar shows just the point estimate.
    • Cohen's κ (beyond chance) — signed [-1, 1] bar, zero centred.
  • Loading skeleton; renders nothing on error so it never blocks the list.

Stacked on the backend branch — base is feat/human-model-agreement-endpoint (#1307), not main. Rebase/retarget to main once #1307 merges.

Wilson CI rendered inline (not on its own row)

The Wilson 95% CI is folded into each agreement bar instead of sitting on a separate row. The bar is a single 0–100% track with:

  • a translucent CI band (bg-primary/40) from low to high
  • 2px-wide CI bound caps (whiskers) at the low / high edges
  • a 3px-wide dark vertical marker at the point estimate, slightly taller than the track

This puts the uncertainty visually adjacent to the number it qualifies — the bar is the CI, the marker is the point estimate — so a wide band immediately reads as "shaky number" and a tight band as "confident", without the reader having to cross-reference a separate row.

Filter parity

The panel reuses the list view's filters array verbatim and converts it to query params with the same active/error rules as getFetchUrl (value?.length && !error). The endpoint accepts the full occurrence-list filter set (#1307), so the numbers stay consistent with the visible results.

Test plan

  • tsc --noEmit — no errors in touched files
  • eslint + prettier clean on new/modified files
  • Live browser render verified against project 18 (Vermont Atlas of Life) via the worktree dev server proxied to the Endpoint for stats about verified occurrences #1307 backend. All four core bars render: VERIFIED OCCURRENCES 0% (121), AGREEMENT (EXACT TAXON) 90% (90 of 100) with 95% CI 83–94%, AGREEMENT (ANY RANK) 94% (94 of 100) with 95% CI 88–97%, COHEN'S κ 0.84. The coarser-rank bar is hidden when ?agreement_coarsest_rank is not supplied — verified by direct API call.
  • Live filter reactivity verified: toggling Default Filters off set ?apply_defaults=false and the Stats panel re-queried with the same param. Same filter array drives both list and stats.

Toolchain note for reviewers

The worktree ui/ has no node_modules. Installing under the host's Node 22 breaks the dev server (nova-ui-kit dereferences a React-18 internal removed in React 19 at tailwind-config eval). Use the repo-pinned Node 18 (.nvmrc → 18.12.0): nvm use 18.12.0 && yarn install && yarn start. Under Node 18 it boots cleanly.

Design notes

The "agreement rate" is the share of human-verified occurrences where the human pick matched the model's pick. Three calibration ideas are baked into this panel:

Raw counts beside the percentageVerified occurrences shows 0% (121), making the rounded-to-zero percentage readable as "121 of ~24k" rather than literally zero. Each agreement bar also shows K of N so the reader instantly sees how many verifications the rate is built on.

Hard cutoff vs. confidence interval — rather than a yes/no "enough data" line, the Wilson 95% CI shows how shaky the number is. A Wilson score interval behaves well at small samples, so when few occurrences are verified the band is wide and as more get verified it tightens. This is more honest than picking a magic threshold like "30 verifications" (which is a textbook rule of thumb that only holds if verifications are a random sample — they aren't, people verify the unusual / uncertain / eye-catching ones first).

Plain agreement vs. agreement beyond chance — plain agreement % has a blind spot: if 95% of moths in a project are one common species, human and model "agree" most of the time just by both guessing the common one — that's luck, not skill. Cohen's κ subtracts that expected-by-chance agreement; κ of 1.0 = perfect, 0 = no better than guessing, negative = worse than chance. Same caveat as the CI: it still only describes the occurrences people chose to verify, not the whole project.

🤖 Generated with Claude Code

@netlify
Copy link
Copy Markdown

netlify Bot commented May 15, 2026

Deploy Preview for antenna-preview ready!

Name Link
🔨 Latest commit 237a013
🔍 Latest deploy log https://app.netlify.com/projects/antenna-preview/deploys/6a16f1cdc3f2430008939303
😎 Deploy Preview https://deploy-preview-1308--antenna-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 61 (🔴 down 4 from production)
Accessibility: 81 (🔴 down 8 from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 92 (no change from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 728a5890-7a17-48b2-9497-7b6a8a5615e5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/occurrence-stats-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mihow mihow force-pushed the feat/occurrence-stats-ui branch from 326cd68 to 4ae69ec Compare May 21, 2026 00:52
mihow pushed a commit that referenced this pull request May 21, 2026
…ry params

- Rename `agreed_under_order_*` → `agreed_any_rank_*` to match the endpoint's
  dropped ORDER threshold (0565f06).
- Add optional `agreement_coarsest_rank` + `agreed_coarser_rank_*` fields to
  the response type (not consumed yet — UI follows in #1308).
- Widen `filters` to accept arrays and append repeated query params so
  multi-value filters (e.g. `algorithm`, `not_algorithm` — backend reads via
  `request.query_params.getlist(...)`) survive. Per CodeRabbit review.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow force-pushed the feat/occurrence-stats-ui branch 3 times, most recently from d621ac3 to 3692eba Compare May 21, 2026 01:13
@mihow mihow force-pushed the feat/occurrence-stats-ui branch from 3692eba to d0669ee Compare May 21, 2026 01:18
mihow added a commit that referenced this pull request May 22, 2026
useModelAgreement.ts belongs with the frontend consumer (#1308), not the
backend endpoint PR. Keeps #1307 backend-only.

Co-Authored-By: Claude <noreply@anthropic.com>
mihow added a commit that referenced this pull request May 22, 2026
Typed React Query wrapper for /occurrences/stats/model-agreement/.
Owned by this UI PR (#1308); the backend PR (#1307) is now backend-only.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow force-pushed the feat/occurrence-stats-ui branch from 5e5252d to 50c5ff9 Compare May 22, 2026 04:36
mihow pushed a commit that referenced this pull request May 26, 2026
…ry params

- Rename `agreed_under_order_*` → `agreed_any_rank_*` to match the endpoint's
  dropped ORDER threshold (0565f06).
- Add optional `agreement_coarsest_rank` + `agreed_coarser_rank_*` fields to
  the response type (not consumed yet — UI follows in #1308).
- Widen `filters` to accept arrays and append repeated query params so
  multi-value filters (e.g. `algorithm`, `not_algorithm` — backend reads via
  `request.query_params.getlist(...)`) survive. Per CodeRabbit review.

Co-Authored-By: Claude <noreply@anthropic.com>
mihow added a commit that referenced this pull request May 26, 2026
useModelAgreement.ts belongs with the frontend consumer (#1308), not the
backend endpoint PR. Keeps #1307 backend-only.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow force-pushed the feat/human-model-agreement-endpoint branch from f958a38 to c4a4171 Compare May 26, 2026 01:10
mihow added a commit that referenced this pull request May 26, 2026
Typed React Query wrapper for /occurrences/stats/model-agreement/.
Owned by this UI PR (#1308); the backend PR (#1307) is now backend-only.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow force-pushed the feat/occurrence-stats-ui branch from 50c5ff9 to 1241967 Compare May 26, 2026 01:10
mihow added a commit that referenced this pull request May 26, 2026
Typed React Query wrapper for /occurrences/stats/model-agreement/.
Owned by this UI PR (#1308); the backend PR (#1307) is now backend-only.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow force-pushed the feat/occurrence-stats-ui branch from 3a5e022 to ef2cf01 Compare May 26, 2026 19:58
mihow pushed a commit that referenced this pull request May 27, 2026
…ry params

- Rename `agreed_under_order_*` → `agreed_any_rank_*` to match the endpoint's
  dropped ORDER threshold (0565f06).
- Add optional `agreement_coarsest_rank` + `agreed_coarser_rank_*` fields to
  the response type (not consumed yet — UI follows in #1308).
- Widen `filters` to accept arrays and append repeated query params so
  multi-value filters (e.g. `algorithm`, `not_algorithm` — backend reads via
  `request.query_params.getlist(...)`) survive. Per CodeRabbit review.

Co-Authored-By: Claude <noreply@anthropic.com>
mihow added a commit that referenced this pull request May 27, 2026
useModelAgreement.ts belongs with the frontend consumer (#1308), not the
backend endpoint PR. Keeps #1307 backend-only.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow force-pushed the feat/human-model-agreement-endpoint branch from 9347277 to e476333 Compare May 27, 2026 01:11
mihow added a commit that referenced this pull request May 27, 2026
Typed React Query wrapper for /occurrences/stats/model-agreement/.
Owned by this UI PR (#1308); the backend PR (#1307) is now backend-only.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow force-pushed the feat/occurrence-stats-ui branch from ef2cf01 to 2391505 Compare May 27, 2026 01:12
@mihow mihow changed the title feat(ui): live stats panel in occurrence list sidebar Show stats panel in occurrence list sidebar May 27, 2026
@mihow mihow marked this pull request as draft May 27, 2026 13:20
mihow pushed a commit that referenced this pull request May 27, 2026
…ry params

- Rename `agreed_under_order_*` → `agreed_any_rank_*` to match the endpoint's
  dropped ORDER threshold (0565f06).
- Add optional `agreement_coarsest_rank` + `agreed_coarser_rank_*` fields to
  the response type (not consumed yet — UI follows in #1308).
- Widen `filters` to accept arrays and append repeated query params so
  multi-value filters (e.g. `algorithm`, `not_algorithm` — backend reads via
  `request.query_params.getlist(...)`) survive. Per CodeRabbit review.

Co-Authored-By: Claude <noreply@anthropic.com>
mihow added a commit that referenced this pull request May 27, 2026
useModelAgreement.ts belongs with the frontend consumer (#1308), not the
backend endpoint PR. Keeps #1307 backend-only.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow force-pushed the feat/human-model-agreement-endpoint branch from e476333 to 336c1fe Compare May 27, 2026 13:29
Adds an OccurrenceStats panel above the filter sections on the
occurrence list page. Consumes the /occurrences/stats/model-agreement/
endpoint, threading the same active filter array the list view sends so
the numbers always reflect the current result set.

Shows two metrics: verified occurrences % and human-model agreement
rate % (rank-level / under-order agreement).

Co-Authored-By: Claude <noreply@anthropic.com>
Michael Bunsen and others added 5 commits May 27, 2026 06:29
One-line field rename in the occurrence stats panel to match the backend's
dropped ORDER threshold. Hook type rename + multi-value filter support
landed on the base branch (4a92c0b on #1307).

Co-Authored-By: Claude <noreply@anthropic.com>
`StatBar` takes an optional `count` rendered as "0% (121)". Wired into the
Verified occurrences bar so a small-but-nonzero verified set that rounds to
0% still surfaces the underlying count.

Co-Authored-By: Claude <noreply@anthropic.com>
Typed React Query wrapper for /occurrences/stats/model-agreement/.
Owned by this UI PR (#1308); the backend PR (#1307) is now backend-only.

Co-Authored-By: Claude <noreply@anthropic.com>
Two new horizontal bars below the existing verified / agreement-rate bars:

- 'Agreement 95% CI (Wilson)' — RangeBar showing the Wilson CI as a
  filled segment between low and high (wide bar = shaky number, narrow
  bar = tight). Value reads '87–97%'. '—' when no verified-with-pred set.
- 'Cohen's κ (beyond chance)' — SignedBar over [-1, 1] with the zero
  midpoint marked. Positive fills right, negative fills left. Value
  reads '0.41'. '—' when undefined (empty or single-category set).

Hook type extended with the five new fields (agreed_*_ci_low/high +
cohens_kappa). Loading skeleton bumped to 4 placeholders.

Co-Authored-By: Claude <noreply@anthropic.com>
…nline

Stats panel now renders three agreement bars side-by-side instead of one
generic agreement row plus a separate CI range bar:

- Agreement (exact taxon) — agreed_exact_*
- Agreement (any rank) — agreed_any_rank_* (LCA at any real rank)
- Agreement (≥ <rank>) — agreed_coarser_rank_* (only when the caller passes
  ?agreement_coarsest_rank=<RANK>; otherwise hidden)

Wilson 95% CI is folded into each agreement bar instead of sitting on its
own row. The bar is a single 0–100% track with:

- a translucent CI band (bg-primary/40) from low to high
- 2px-wide CI bound caps (whiskers) at low/high
- a 3px tall dark vertical marker for the point estimate

This puts the uncertainty visually adjacent to the number it qualifies —
the bar IS the CI, the marker IS the point — so the CI is no longer easy
to overlook. Each agreement row also surfaces raw counts ("90 of 100").

Cohen's κ keeps its existing signed bar.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow force-pushed the feat/occurrence-stats-ui branch from 2391505 to 237a013 Compare May 27, 2026 13:29
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