Classification model¶
Every piece of annotation work in TRAPPER — AI predictions, human edits, post-approval corrections — lands in one unified Classification table with four types. Understanding the four types and how they relate is the key to understanding almost everything else in the AI pipeline and the classify UI.
The four types¶
graph TD
FINAL["FINAL<br/>(container, no dynamic_attrs of its own)"]
AI["AI<br/>(AI Provider's prediction)"]
USER["USER<br/>(human-edited / forked)"]
FEEDBACK["FEEDBACK<br/>(post-approval correction)"]
AI -->|"final_classification"| FINAL
USER -->|"final_classification"| FINAL
FEEDBACK -->|"final_classification"| FINAL
USER -.->|"source_classification<br/>(forked from)"| AI
FEEDBACK -.->|"source_classification"| USER
FINAL ==>|"source_classification<br/>(once approved)"| AI
FINAL ==>|"source_classification<br/>(once approved)"| USER
| Type | What it is | Has dynamic_attrs? |
|---|---|---|
| FINAL | A container for one resource's approved-or-pending classification state. There's exactly one FINAL per (resource, project) pair. | No — reads observation details via source_classification.dynamic_attrs |
| AI | An AI Provider's prediction: detections, species, distance, etc. | Yes |
| USER | A human-authored or human-edited classification, often forked from an AI classification. | Yes |
| FEEDBACK | A correction reported against an already-approved classification. | Yes |
dynamic_attrs is where the actual observation data lives — species, sex, age, behaviour, count, bounding boxes, per-frame distance — one row per detected object/group, attached to an AI/USER/FEEDBACK classification. FINAL never has its own; it always defers to whichever classification it points at. See Data model reference for the full field list.
Approval flow¶
- The AI pipeline creates an AI Classification with its
dynamic_attrs(species, bboxes, confidence, …), linked to the resource's FINAL viafinal_classification. - A human annotator reviews it. If they need to make changes, they fork it into a USER Classification (
source_classification→ the AI row they forked from). - When a USER Classification is approved:
FINAL.is_approved = TrueFINAL.source_classification→ that USER Classification- The USER Classification becomes locked for further editing.
- Alternatively, the AI Classification can be approved directly without a human edit — same effect, but
FINAL.source_classificationpoints at the AI row instead. - After approval, anyone can still log a FEEDBACK Classification against the approved row —
source_classification→ the approved USER/AI row,final_classification→ FINAL. This does not flipFINAL.is_approvedback to false; it's a permanent quality-control trail, not a re-opening of the case.
This is why "approve" in the classify UI doesn't destroy the AI prediction — the AI Classification row stays in the database (and in child_classifications) even after a USER fork is approved over it. Re-running the AI pipeline overwrites the AI Classification's dynamic_attrs but never touches an approved USER Classification.
Object vs. group observations¶
Within dynamic_attrs, each row has an observation_level:
object(the default) — one row per individually detected/tracked animal, with a bounding box andfirst_frame_index.group— an aggregate row an expert adds by hand to describe a group size that wasn't (or can't be) resolved into per-individual tracks. Group rows carry no bbox and noindividual_id—countis the only meaningful number, and it's enforced at the database level (dynattrs_group_rows_have_no_bboxes/..._null_individual_id/..._positive_countconstraints).
Why a unified table instead of separate models¶
Putting AI/USER/FEEDBACK/FINAL in one Classification table (discriminated by classification_type) rather than four separate Django models means:
- Forking (AI → USER) and approval (USER/AI → FINAL) are just FK rewrites, not cross-table copies.
- The full history of a resource's classification — every AI run, every human edit, every later correction — stays queryable from one place via
child_classifications/derived_classifications. - Permission and visibility logic (who can see what) is written once against
Classification, not duplicated four times.
See also¶
- Data model reference — full field-level reference for
Classification,ClassificationDynamicAttrs, and the AI provider models - AI pipeline architecture — how AI Classifications get created in the first place
- Run & re-run the AI pipeline