annie.dataset.storage¶
SQLite-backed review-status store (Browse tab curation state).
This holds curation state per video — a good/bad verdict, an optional note, an
“add to annotator” flag, and the protagonist-track correction — plus, on export,
the protagonist _manual CSV. The store is a real file (by default a per-session
~/.annie/sessions/annie_<timestamp>.db; pin one with ANNIE_DB_PATH) so it
survives restarts, and is keyed by annie.models.VideoEntry.key.
Every video is liked (good) by default: a row only exists once the user interacts, and the UI treats “no row” as good. The store is exportable to CSV/JSON in one call and importable via upsert, so a reviewer’s curation travels with them.
Attributes¶
Classes¶
One persisted review row. |
|
One persisted Event-annotation event: a labelled interval on a track. |
|
One participant category (a timeline lane) in the Event-annotation task. |
|
One event category in the Event-annotation task (e.g. |
|
A thin, connection-per-call wrapper over the |
Module Contents¶
- annie.dataset.storage.Verdict¶
A review verdict.
None(no row) is treated as"good"by the UI.
- annie.dataset.storage.Decision¶
A Segment-review decision on a clip.
Nonemeans the clip is not yet reviewed.
- class annie.dataset.storage.ReviewRecord[source]¶
One persisted review row.
- row_key¶
Stable per-row identity (
annie.models.VideoEntry.key).
- video_id¶
The video this review belongs to.
- annotation_suffix¶
The annotation suffix, or
Nonefor an exact match.
- verdict¶
"good","bad", orNoneif untouched (treated as good).
- note¶
Free-text reviewer note (empty string when none).
- annotate¶
Whether the video is queued for the Annotator tab.
- active_track¶
The session’s protagonist-track override, or
Noneif the reviewer has not corrected this video (the heuristic value then stands).
- decision¶
The Segment-review accept/drop for a clip row, or
Noneif the clip is not (or not a) Segment-review sample.
- updated_at¶
ISO-8601 UTC timestamp of the last change.
- class annie.dataset.storage.EventRecord[source]¶
One persisted Event-annotation event: a labelled interval on a track.
- event_id¶
Stable UUID identity, so an event survives edits to its own fields.
- video_id¶
The video this event belongs to.
- row_key¶
The
annie.core.models.VideoEntry.keyof the queue row, tying the event to the same identity curation uses.
- track¶
The track (category) name the event sits on.
- start_frame¶
Inclusive start frame of the interval.
- end_frame¶
Inclusive end frame of the interval (
>= start_frame).
- label¶
Free-text label shown on the event box (empty when unnamed).
- note¶
Free-text note (empty when none).
- attributes¶
Arbitrary string key/value pairs; stored as a JSON object.
- color¶
Per-event colour override (hex string), or
Noneto inherit the event category’s colour, else the track’s, on the timeline.
- event_type¶
The event category the event was made with (and stays linked to), or
Nonefor a plain event.
- updated_at¶
ISO-8601 UTC timestamp of the last change.
- class annie.dataset.storage.EventCategory[source]¶
One participant category (a timeline lane) in the Event-annotation task.
DB-scoped, not per-video: the categories in a review DB are shared by every video of that dataset. An event belongs to a category when its
trackequals the categoryname.- name¶
The category / participant label (e.g.
"Mother"); unique within the DB.
- ordinal¶
Vertical lane order,
0at the top.
- color¶
Optional hex colour override for the lane;
Noneuses the palette by ordinal.
- updated_at¶
ISO-8601 UTC timestamp of the last change.
- class annie.dataset.storage.EventType[source]¶
One event category in the Event-annotation task (e.g.
"Smile").DB-scoped like
EventCategory. A new event made with it gets its name as the label, and stays linked: renaming the type renames its events; its colour and attributes apply wherever the event sets none of its own (seeannie.dataset.events).- name¶
The event category’s name; unique within the DB.
- ordinal¶
Order in the Category block (
0first; Shift+1 selects it).
- color¶
Hex colour of its events, or
Nonefor the participant lane’s colour.
- attributes¶
Default key/value attributes of its events.
- updated_at¶
ISO-8601 UTC timestamp of the last change.
- class annie.dataset.storage.ReviewStore(db_path: str | pathlib.Path)[source]¶
A thin, connection-per-call wrapper over the
reviewtable.The store opens a fresh connection for each operation, which keeps it safe to call from the render worker threads and the UI thread alike without sharing a connection across threads.
Open (creating if needed) the review database at
db_path.- Parameters:
db_path – Filesystem path to the SQLite file. Parent directories are created as needed.
- has_data() bool[source]¶
Return whether any review, event, participant or event category has been stored.
Used to decide whether switching to another database would leave progress behind (see
copy_to()).
- copy_to(db_path: str | pathlib.Path) ReviewStore[source]¶
Copy this whole database to
db_pathand return a store opened on the copy.Uses SQLite’s online backup API, so the copy is consistent even while this store is in use. An existing file at
db_pathis overwritten; this database is left untouched.- Parameters:
db_path – Destination SQLite file. Parent directories are created as needed.
- Returns:
A
ReviewStorebacked by the copy.
- get(row_key: str) ReviewRecord | None[source]¶
Return the review record for
row_key, orNoneif unreviewed.- Parameters:
row_key – The row identity to look up.
- Returns:
The stored
ReviewRecord, orNone.
- all() list[ReviewRecord][source]¶
Return every review record, ordered by row key.
- Returns:
All stored review records.
- records_by_key() dict[str, ReviewRecord][source]¶
Return every stored review record keyed by row key, in one query.
A filter pass over the whole manifest needs each video’s review state; reading them all at once replaces one connection per video with a single one.
- Returns:
row_key -> ReviewRecordfor every touched video (unreviewed ones are absent).
- list_by_verdict(verdict: Verdict) list[ReviewRecord][source]¶
Return all records with the given verdict (the good or bad list).
- Parameters:
verdict –
"good"or"bad".- Returns:
Matching review records ordered by row key.
- upsert(row_key: str, video_id: str, annotation_suffix: str | None, *, verdict: Verdict | None = None, note: str | None = None, annotate: bool | None = None, active_track: int | None = None, decision: Decision | None = None) ReviewRecord[source]¶
Insert or update a review row, preserving fields left as
None.Passing
verdict=None/note=None/annotate=None/active_track=None/decision=Nonekeeps any existing value, so the good/bad toggle, the note, the annotator flag, the protagonist correction, and the Segment-review accept/drop update independently.- Parameters:
row_key – Stable row identity (primary key).
video_id – The video id, stored for export/grouping.
annotation_suffix – The annotation suffix, or
None.verdict – New verdict, or
Noneto leave unchanged.note – New note, or
Noneto leave unchanged.annotate – New annotator flag, or
Noneto leave unchanged.active_track – New protagonist-track override, or
Noneto leave unchanged.decision – New Segment-review decision, or
Noneto leave unchanged.
- Returns:
The resulting
ReviewRecord.
- set_verdict(row_key: str, video_id: str, annotation_suffix: str | None, verdict: Verdict | None) ReviewRecord[source]¶
Set (or clear) the good/bad verdict for a row.
- Parameters:
row_key – Stable row identity.
video_id – The video id.
annotation_suffix – The annotation suffix, or
None.verdict –
"good","bad", orNoneto clear.
- Returns:
The updated
ReviewRecord.
- set_annotate(row_key: str, video_id: str, annotation_suffix: str | None, value: bool) ReviewRecord[source]¶
Set the “add to annotator” flag for a video.
- Parameters:
row_key – Stable row identity.
video_id – The video id.
annotation_suffix – The annotation suffix, or
None.value – Whether the video is queued for the Annotator tab.
- Returns:
The updated
ReviewRecord.
- set_annotate_many(videos: collections.abc.Iterable[tuple[str, str]], value: bool) int[source]¶
Set the “add to annotator” flag for many videos in one transaction.
Backs Browse’s “Add all to Annotator” action: calling
set_annotate()per video would open a connection and commit for each one, which is slow on a large filtered selection. Only theannotatecolumn is written, so any stored verdict, note, or protagonist correction survives; videos with no row yet get one with the defaults (liked, no note).- Parameters:
videos –
(row_key, video_id)pairs to update.value – Whether the videos are queued for the Annotator tab.
- Returns:
The number of videos written.
- set_active_track(row_key: str, video_id: str, annotation_suffix: str | None, track_id: int) ReviewRecord[source]¶
Persist the reviewer’s protagonist-track correction for a video.
The choice lives only in this session’s database; the
_manualCSV is written separately by the Annotator’s “Export corrected CSV” action.- Parameters:
row_key – Stable row identity.
video_id – The video id.
annotation_suffix – The annotation suffix, or
None.track_id – The chosen active track index.
- Returns:
The updated
ReviewRecord.
- active_tracks() dict[str, int][source]¶
Return every stored protagonist override as
row_key -> track_id.- Returns:
A mapping of row key to corrected track index for the rows a reviewer has changed this session (rows without an override are omitted).
- annotator_keys() set[str][source]¶
Return the row keys flagged for the Annotator tab.
- Returns:
The set of
row_keyvalues whoseannotateflag is set.
- set_decision(row_key: str, video_id: str, decision: Decision) ReviewRecord[source]¶
Persist a Segment-review accept/drop decision for a clip.
The clip is keyed by its composite
{video_id}_{segment_id}row_key, so re-opening the source resumes a half-finished pass from the database.- Parameters:
row_key – The clip identity (
{video_id}_{segment_id}).video_id – The parent video id, stored for export/grouping.
decision –
"accept"or"drop".
- Returns:
The updated
ReviewRecord.
- clear_decision(row_key: str) None[source]¶
Return a clip to the undecided state, dropping any accept/drop.
This is the “Undecided” escape hatch in Segment review: a misclick or a changed mind must be able to put a clip back into the undecided pool that the progress bar counts and “jump to next undecided” walks. It cannot go through
upsert(), wheredecision=Nonemeans “leave unchanged” — this writes theNULLexplicitly. A clip that was never decided is left untouched.- Parameters:
row_key – The clip identity (
{video_id}_{segment_id}).
- decisions() dict[str, Decision][source]¶
Return every stored Segment-review decision as
row_key -> decision.- Returns:
A mapping of clip key to
"accept"/"drop"for the clips a reviewer has decided (undecided clips are omitted).
- list_by_decision(decision: Decision) list[ReviewRecord][source]¶
Return all records with the given Segment-review decision.
- Parameters:
decision –
"accept"or"drop".- Returns:
Matching review records ordered by row key (the accepted or dropped set).
- add_event(video_id: str, row_key: str, track: str, start_frame: int, end_frame: int, *, label: str = '', note: str = '', attributes: dict[str, str] | None = None, color: str | None = None, event_type: str | None = None) EventRecord[source]¶
Insert a new event on a track and return it (with its fresh id).
The event’s track is also registered in
event_trackif absent, so a lane created implicitly by dropping an event onto it persists like an explicit one.- Parameters:
video_id – The video the event belongs to.
row_key – The queue row identity the event ties to.
track – The track (category) name.
start_frame – Inclusive start frame.
end_frame – Inclusive end frame (
>= start_frame).label – Optional event label.
note – Optional event note.
attributes – Optional arbitrary string key/value pairs.
color – Optional per-event colour override (hex), or
Noneto inherit the track.event_type – The event category it is made with, or
Nonefor a plain event.
- Returns:
The stored
EventRecord.
- update_event(event_id: str, **fields: object) EventRecord | None[source]¶
Update named columns of an event, returning the updated record.
Only the columns named in
fieldsare written (plusupdated_at); any others keep their stored value.attributesis accepted as a dict and stored as JSON. Passing an unknown column raises, so a typo fails loudly rather than silently doing nothing.- Parameters:
event_id – The event to update.
**fields – Column/value pairs among
track,start_frame,end_frame,label,note,attributes,color,event_type.
- Returns:
The updated
EventRecord, orNoneif no such event exists.
- delete_event(event_id: str) None[source]¶
Delete one event by id (a no-op if it does not exist).
- Parameters:
event_id – The event to delete.
- get_event(event_id: str) EventRecord | None[source]¶
Return one event by id, or
Noneif it does not exist.- Parameters:
event_id – The event to fetch.
- Returns:
The stored
EventRecord, orNone.
- events_for(row_key: str) list[EventRecord][source]¶
Return a video’s events, ordered by track then start frame.
- Parameters:
row_key – The queue row identity to fetch events for.
- Returns:
The events on that video, grouped by track and sorted within each track.
- all_events() list[EventRecord][source]¶
Return every stored event, ordered by video then track then start frame.
Backs the session-wide export (every annotated video in one file).
- Returns:
All events in the store.
- add_track(row_key: str, track: str) None[source]¶
Create an empty track (lane) for a video, appended after existing ones.
Idempotent: re-adding an existing track leaves its ordinal untouched.
- Parameters:
row_key – The queue row identity.
track – The track (category) name.
- rename_track(row_key: str, old: str, new: str) None[source]¶
Rename a track and move its events onto the new name, keeping the ordinal.
A no-op when
oldandneware equal. Ifnewalready exists, the two lanes merge:old’s events move ontonewand the now-emptyoldlane is removed.- Parameters:
row_key – The queue row identity.
old – The current track name.
new – The new track name.
- delete_track(row_key: str, track: str) None[source]¶
Delete a track and all of its events for a video.
- Parameters:
row_key – The queue row identity.
track – The track (category) name to remove.
- tracks_for(row_key: str) list[str][source]¶
Return a video’s track names in their vertical order.
- Parameters:
row_key – The queue row identity.
- Returns:
The track names ordered by
ordinal(their top-to-bottom lane order).
- event_row_keys() set[str][source]¶
Return the row keys that have at least one event or track.
Lets the tab know which queued videos already carry annotation work.
- Returns:
The set of row keys present in either event table.
- categories() list[EventCategory][source]¶
Return the dataset’s participant categories, in lane order.
- Returns:
The categories ordered by
ordinalthenname(top to bottom).
- add_category(name: str, color: str | None = None) EventCategory[source]¶
Add a category at the end of the lane order (idempotent on the name).
Re-adding an existing name is a no-op that returns the existing category, so a double-click never creates a duplicate.
- Parameters:
name – The category / participant label (must be non-empty; caller validates).
color – Optional hex colour override for the lane.
- Returns:
The stored
EventCategory(existing one if the name was already present).
- rename_category(old: str, new: str) None[source]¶
Rename a category and move every event on it onto the new name.
A no-op when
old == new. Ifnewalready exists, the two lanes merge:old’s events move ontonewand the now-emptyoldcategory is removed. This is the safe way to fix a label everywhere at once — events follow, so nothing is orphaned.- Parameters:
old – The current category name.
new – The new name.
- delete_category(name: str) None[source]¶
Delete a category and all events on it, then close the ordinal gap.
The caller is responsible for confirming when the category still holds events (the UI guards this); at the store level the delete always cascades to keep the data consistent — an event can never reference a category that no longer exists.
- Parameters:
name – The category to remove.
- category_event_count(name: str) int[source]¶
Return how many events currently sit on a category (for the delete guard).
- Parameters:
name – The category name.
- Returns:
The number of events whose
trackequalsname.
- reorder_categories(names: list[str]) None[source]¶
Set the lane order to
names(first = top). Unlisted categories keep going after.- Parameters:
names – The category names in the desired top-to-bottom order. Names not present in the DB are ignored; categories omitted from the list are appended after the listed ones in their previous relative order.
- set_category_color(name: str, color: str | None) None[source]¶
Set (or clear) a category’s lane colour override.
- Parameters:
name – The category name.
color – A hex colour, or
Noneto fall back to the palette by ordinal.
- seed_categories_from_events() int[source]¶
Seed the category table from distinct event track names, if it is empty.
Back-compat for a DB annotated under the old single-lane model: its events sit on the
"events"track (or whatever names were used), but there are no category rows yet. This creates one category per distinct existing track name so those events keep a lane the user can then rename to a participant. Does nothing once any category exists.- Returns:
The number of categories created (
0if the table was already populated or there are no events).
- event_types() list[EventType][source]¶
Return the dataset’s event categories, in their order (Shift+1 is the first).
- Returns:
The event categories ordered by
ordinalthenname.
- add_event_type(name: str, color: str | None = None, attributes: dict[str, str] | None = None) EventType[source]¶
Add an event category at the end of the order (idempotent on the name).
- Parameters:
name – The event category’s name (must be non-empty; caller validates).
color – Optional hex colour of its events.
attributes – Optional default key/value attributes of its events.
- Returns:
The stored
EventType(the existing one if the name was already present).
- rename_event_type(old: str, new: str) None[source]¶
Rename an event category; its events follow.
Events made with it point at the new name, and those whose label is still the old name are relabelled (a label the user changed is kept). If
newalready exists, the two merge:old’s events move ontonewandoldis removed.- Parameters:
old – The current name.
new – The new name.
- delete_event_type(name: str) None[source]¶
Delete an event category without changing how its events look.
Each of its events first takes over what it inherited: the category’s colour (unless the event has its own) and its default attributes (the event’s own values win). Then the link is cleared, so the events stay as they were, just no longer linked.
- Parameters:
name – The event category to remove.
- event_type_event_count(name: str) int[source]¶
Return how many events are linked to an event category (for the delete warning).
- Parameters:
name – The event category’s name.
- Returns:
The number of events whose
event_typeequalsname.
- reorder_event_types(names: list[str]) None[source]¶
Set the event categories’ order to
names(unlisted ones keep going after).- Parameters:
names – Event category names in the desired order; unknown names are ignored.
- set_event_type_color(name: str, color: str | None) None[source]¶
Set (or clear) an event category’s colour; its events follow unless they have their own.
- Parameters:
name – The event category’s name.
color – A hex colour, or
Nonefor the participant lane’s colour.
- set_event_type_attributes(name: str, attributes: dict[str, str]) None[source]¶
Replace an event category’s default attributes.
- Parameters:
name – The event category’s name.
attributes – The new default key/value attributes.
- set_note(row_key: str, video_id: str, annotation_suffix: str | None, note: str) ReviewRecord[source]¶
Set the free-text note for a row.
- Parameters:
row_key – Stable row identity.
video_id – The video id.
annotation_suffix – The annotation suffix, or
None.note – The note text.
- Returns:
The updated
ReviewRecord.
- export_json(path: str | pathlib.Path) pathlib.Path[source]¶
Write all review records to a JSON array file.
- Parameters:
path – Destination JSON path.
- Returns:
The path written.