Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
165ae9b
feat(occurrence-stats): add lca_rank_between helper
mihow May 14, 2026
7b1660c
feat(occurrence-stats): aggregate human-model agreement over filtered…
mihow May 14, 2026
3110418
feat(occurrence-stats): wire human-model-agreement action
mihow May 14, 2026
ba9c901
test(occurrence-stats): HTTP coverage for human-model-agreement action
mihow May 14, 2026
5b1bde7
feat(ui): useHumanModelAgreement hook for occurrence stats
mihow May 14, 2026
e050a1f
docs(prompts): handoff for PR #1307 rework — rename + SQL push-down +…
mihow May 14, 2026
f49c9ca
refactor(occurrence-stats): rename to model-agreement + push aggregat…
mihow May 15, 2026
da2a232
docs(plan): add text lang to fenced block (markdownlint MD040)
mihow May 15, 2026
7ba8689
perf(occurrence-stats): scope agreement subqueries to verified set
mihow May 15, 2026
6ad1885
feat(occurrence-stats): drop ORDER threshold; add coarsest_rank query…
May 21, 2026
6f51da5
feat(ui): align model-agreement hook with BE rename + multi-value que…
May 21, 2026
7c144b0
chore(docs): drop NEXT_SESSION_PROMPT.md from PR
May 21, 2026
34aace5
chore(docs): drop session-scratchpad planning docs from PR
May 21, 2026
36cc677
test(occurrence-stats): make any-rank bucket test deterministic
mihow May 22, 2026
b74b3cd
chore(occurrence-stats): move FE hook to UI PR #1308
mihow May 22, 2026
2c65cce
feat(occurrence-stats): add Wilson CI + Cohen's kappa to model-agreement
mihow May 26, 2026
336c1fe
refactor(stats): move wilson_interval + cohens_kappa to ami/utils/stats
mihow May 26, 2026
6748631
feat(stats): expose response schema via OPTIONS metadata
mihow May 28, 2026
8bea80f
fix(stats): exclude taxon-less verifications from agreement denominator
mihow May 28, 2026
75b21c3
fix(stats): validate agreement_coarsest_rank via ChoiceField
mihow May 28, 2026
b65100f
fix(stats): wilson_interval rejects successes outside [0, total]
mihow May 28, 2026
f0ea7d3
feat(ui): live stats panel in occurrence list sidebar
mihow May 15, 2026
7cb9042
feat(ui): switch stats panel to agreed_any_rank_pct (BE rename)
May 21, 2026
8852651
feat(ui): show raw verified count next to percentage
May 21, 2026
047bd40
feat(ui): add useModelAgreement hook for occurrence stats
mihow May 22, 2026
f4c3873
feat(ui): add Wilson CI + Cohen's kappa bars to stats panel
mihow May 26, 2026
3ecc891
feat(ui): split agreement bars by match scope + integrate Wilson CI i…
mihow May 26, 2026
c87499a
Merge remote-tracking branch 'origin/main' into feat/occurrence-stats-ui
mihow Jun 18, 2026
3331b1f
feat(ui): address stats panel review feedback from Anna
mihow Jun 18, 2026
2fa5bf9
feat(ui): simplify stats panel bars and tooltips
mihow Jun 18, 2026
562a1dc
style(ui): prettier-format occurrence-stats panel
mihow Jun 18, 2026
d3c6acf
fix(ui): keep CI hatch visible at any point estimate
mihow Jun 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions ui/src/data-services/hooks/occurrences/stats/useModelAgreement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { API_ROUTES, API_URL } from 'data-services/constants'
import { useAuthorizedQuery } from '../../auth/useAuthorizedQuery'

interface ModelAgreementResponse {
project_id: number
total_occurrences: number
verified_count: number
verified_pct: number
verified_with_prediction_count: number
no_prediction_count: number
agreed_exact_count: number
agreed_exact_pct: number
agreed_exact_ci_low: number | null
agreed_exact_ci_high: number | null
agreed_any_rank_count: number
agreed_any_rank_pct: number
agreed_any_rank_ci_low: number | null
agreed_any_rank_ci_high: number | null
// Cohen's kappa (exact-taxon) — agreement beyond chance. Range [-1, 1];
// null when denominator is 0 or expected agreement is 1.0.
cohens_kappa: number | null
// Only populated when the caller passes ?agreement_coarsest_rank=<RANK>.
agreement_coarsest_rank: string | null
agreed_coarser_rank_count: number | null
agreed_coarser_rank_pct: number | null
}

type FilterPrimitive = string | number | boolean
type FilterValue = FilterPrimitive | FilterPrimitive[] | null | undefined

// Accepts an arbitrary filter map so the occurrence list page's filter state
// can be threaded through unchanged (deployment, event, taxon, score
// thresholds, apply_defaults, etc). Arrays are appended as repeated query
// params so multi-select filters (e.g. `algorithm`, `not_algorithm`, which
// the backend reads via `request.query_params.getlist(...)`) survive.
export const useModelAgreement = (
projectId?: string,
filters?: Record<string, FilterValue>
) => {
const url = `${API_URL}/${API_ROUTES.OCCURRENCES}/stats/model-agreement/`

const params = new URLSearchParams()
if (projectId) params.set('project_id', projectId)
if (filters) {
Object.entries(filters).forEach(([key, value]) => {
if (value === undefined || value === null || value === '') return
if (Array.isArray(value)) {
value.forEach((item) => {
if (item !== undefined && item !== null && item !== '') {
params.append(key, String(item))
}
})
return
}
params.set(key, String(value))
})
}
const queryString = params.toString()

const { data, isLoading, isFetching, error } =
useAuthorizedQuery<ModelAgreementResponse>({
queryKey: [
API_ROUTES.OCCURRENCES,
'stats',
'model-agreement',
projectId,
queryString,
],
url: `${url}?${queryString}`,
})

return {
data,
isLoading,
isFetching,
error,
}
}
Loading