annie.dataset.events

Event-annotation domain: interval events on tracks, and their export.

The Event-annotation task lets a reviewer mark events — labelled [start, end] frame intervals — on named tracks (a track is its category) while watching one video. Unlike the CSV-driven tasks, nothing is loaded from a source file: events are authored from scratch and persisted to the review database (see annie.dataset.storage.EventRecord). This module holds the pure pieces around that store:

  • frame ↔ second conversion, since the database is the source of truth in frames (fps-independent) while humans and exports think in seconds;

  • interval clamping, so a typed or dragged boundary can never leave the video;

  • an event’s effective colour and attributes: an event linked to an event category (annie.dataset.storage.EventType) inherits the category’s colour and default attributes, and its own values win;

  • the export shapes. JSON is nested — one object per video, tracks holding their events with attributes inline — for downstream code. CSV and XLSX are flat — one row per event, attributes flattened into attr_<key> columns — for a spreadsheet (XLSX opens in Excel with typed numbers, a frozen header and filters). That split is deliberate: nested attributes do not live cleanly in a spreadsheet cell.

Pure domain: no NiceGUI, no torch. The task UI and the store consume it.

Functions

frames_to_seconds(→ float)

Convert a frame index to seconds at fps.

seconds_to_frame(→ int)

Convert seconds to the nearest frame index at fps.

clamp_event(→ tuple[int, int])

Order and bound an event's frames to a valid interval within the video.

effective_color(→ str | None)

Return the colour an event is drawn and exported with.

effective_attributes(→ dict[str, str])

Return an event's attributes with its event category's defaults filled in.

build_export_tree(→ dict[str, dict[str, object]])

Group events into the nested video_id -> {fps, tracks} export structure.

events_to_json_text(→ str)

Render events as nested JSON text (one object per video).

export_events_json(→ pathlib.Path)

Write events to a nested JSON file (see events_to_json_text()).

event_table(→ tuple[list[str], list[dict[str, object]]])

Flatten events into spreadsheet rows (one per event), shared by CSV and XLSX.

events_to_csv_text(→ str)

Render events as flat CSV text (one row per event; columns as event_table()).

export_events_csv(→ pathlib.Path)

Write events to a flat CSV file (see events_to_csv_text()).

events_to_xlsx_bytes(→ bytes)

Render events as an Excel workbook (same columns as the CSV, see event_table()).

export_events_xlsx(→ pathlib.Path)

Write events to an Excel file (see events_to_xlsx_bytes()).

Module Contents

annie.dataset.events.frames_to_seconds(frame: int, fps: float) → float[source]

Convert a frame index to seconds at fps.

Parameters:
  • frame – The frame index.

  • fps – Frames per second (<= 0 is treated as 0 seconds, since no timebase is known — the caller falls back to frame numbers).

Returns:

The time in seconds, or 0.0 when fps is non-positive.

annie.dataset.events.seconds_to_frame(sec: float, fps: float) → int[source]

Convert seconds to the nearest frame index at fps.

Rounds to the nearest frame; on constant-fps media this is exact, and on a variable-fps file it may be off by one frame (the documented playback limitation).

Parameters:
  • sec – The time in seconds.

  • fps – Frames per second (<= 0 yields frame 0).

Returns:

The nearest non-negative frame index.

annie.dataset.events.clamp_event(start: int, end: int, num_frames: int) → tuple[int, int][source]

Order and bound an event’s frames to a valid interval within the video.

Swaps a reversed pair so start <= end, then clamps both to [0, last] where last = num_frames - 1. A video with no known frame count (num_frames <= 0) is clamped only at the lower bound, so an event can still be authored before metadata loads.

Parameters:
  • start – The proposed start frame.

  • end – The proposed end frame.

  • num_frames – The video’s total frame count (<= 0 if unknown).

Returns:

The ordered, bounded (start, end) pair.

annie.dataset.events.effective_color(event: annie.dataset.storage.EventRecord, types: collections.abc.Mapping[str, annie.dataset.storage.EventType] | None = None, lane_color: str | None = None) → str | None[source]

Return the colour an event is drawn and exported with.

Its own colour wins, then its event category’s, then the participant lane’s.

Parameters:
  • event – The event.

  • types – Event categories by name (None when there are none).

  • lane_color – The participant lane’s colour, the last fallback.

Returns:

A hex colour, or None when nothing sets one.

annie.dataset.events.effective_attributes(event: annie.dataset.storage.EventRecord, types: collections.abc.Mapping[str, annie.dataset.storage.EventType] | None = None) → dict[str, str][source]

Return an event’s attributes with its event category’s defaults filled in.

Parameters:
  • event – The event.

  • types – Event categories by name (None when there are none).

Returns:

{**category defaults, **event's own}, so the event’s own values win.

annie.dataset.events.build_export_tree(events: collections.abc.Iterable[annie.dataset.storage.EventRecord], fps_by_video: collections.abc.Mapping[str, float], meta_by_video: collections.abc.Mapping[str, collections.abc.Mapping[str, str]] | None = None, types: collections.abc.Mapping[str, annie.dataset.storage.EventType] | None = None) → dict[str, dict[str, object]][source]

Group events into the nested video_id -> {fps, tracks} export structure.

Shared by the JSON export and, indirectly, the CSV export’s row order. Videos and their tracks appear in first-seen order; events keep the order they arrive in (the store hands them back sorted by track then start frame).

Parameters:
  • events – The events to export.

  • fps_by_video – Frames per second per video_id, for the derived seconds. A video missing here is exported with fps = 0 and zero seconds (frames still stand).

  • meta_by_video – Optional per-video metadata (e.g. the fields a nested layout captured from the path), exported as a "meta" object when a video has any.

  • types – Event categories by name, so linked events export their effective colour and attributes.

Returns:

A mapping of video_id to {"fps": float, "tracks": {...}} (plus "meta").

annie.dataset.events.events_to_json_text(events: collections.abc.Iterable[annie.dataset.storage.EventRecord], fps_by_video: collections.abc.Mapping[str, float], meta_by_video: collections.abc.Mapping[str, collections.abc.Mapping[str, str]] | None = None, types: collections.abc.Mapping[str, annie.dataset.storage.EventType] | None = None) → str[source]

Render events as nested JSON text (one object per video).

Suits downstream code: attributes stay nested, seconds are derived per video’s fps. A per-video export is this same shape with a single top-level key; a session export has many.

Parameters:
  • events – The events to render.

  • fps_by_video – Frames per second per video_id.

  • meta_by_video – Optional per-video metadata exported as "meta".

  • types – Event categories by name (for effective colour and attributes).

Returns:

The JSON document as a string.

annie.dataset.events.export_events_json(events: collections.abc.Iterable[annie.dataset.storage.EventRecord], fps_by_video: collections.abc.Mapping[str, float], path: str | pathlib.Path) → pathlib.Path[source]

Write events to a nested JSON file (see events_to_json_text()).

Parameters:
  • events – The events to write.

  • fps_by_video – Frames per second per video_id.

  • path – Destination .json path (parent directories are created).

Returns:

The path written.

annie.dataset.events.event_table(events: collections.abc.Iterable[annie.dataset.storage.EventRecord], fps_by_video: collections.abc.Mapping[str, float], meta_by_video: collections.abc.Mapping[str, collections.abc.Mapping[str, str]] | None = None, types: collections.abc.Mapping[str, annie.dataset.storage.EventType] | None = None) → tuple[list[str], list[dict[str, object]]][source]

Flatten events into spreadsheet rows (one per event), shared by CSV and XLSX.

Fixed columns come first, then one meta_<key> column per metadata key seen, then one attr_<key> column per attribute key seen across all events (empty where an event lacks that key), so colleagues can open and sort the file in Excel. Keys are sorted for a stable header. Colour and attributes are the effective ones, so the file is complete without the database.

Parameters:
  • events – The events to flatten.

  • fps_by_video – Frames per second per video_id (for the derived seconds).

  • meta_by_video – Optional per-video metadata.

  • types – Event categories by name (for effective colour and attributes).

Returns:

The column names and the rows (frames as ints, seconds as floats, the rest text).

annie.dataset.events.events_to_csv_text(events: collections.abc.Iterable[annie.dataset.storage.EventRecord], fps_by_video: collections.abc.Mapping[str, float], meta_by_video: collections.abc.Mapping[str, collections.abc.Mapping[str, str]] | None = None, types: collections.abc.Mapping[str, annie.dataset.storage.EventType] | None = None) → str[source]

Render events as flat CSV text (one row per event; columns as event_table()).

Parameters:
  • events – The events to render.

  • fps_by_video – Frames per second per video_id (for the derived seconds).

  • meta_by_video – Optional per-video metadata, as meta_<key> columns.

  • types – Event categories by name (for effective colour and attributes).

Returns:

The CSV document as a string.

annie.dataset.events.export_events_csv(events: collections.abc.Iterable[annie.dataset.storage.EventRecord], fps_by_video: collections.abc.Mapping[str, float], path: str | pathlib.Path) → pathlib.Path[source]

Write events to a flat CSV file (see events_to_csv_text()).

Parameters:
  • events – The events to write.

  • fps_by_video – Frames per second per video_id (for the derived seconds).

  • path – Destination .csv path (parent directories are created).

Returns:

The path written.

annie.dataset.events.events_to_xlsx_bytes(events: collections.abc.Iterable[annie.dataset.storage.EventRecord], fps_by_video: collections.abc.Mapping[str, float], meta_by_video: collections.abc.Mapping[str, collections.abc.Mapping[str, str]] | None = None, types: collections.abc.Mapping[str, annie.dataset.storage.EventType] | None = None) → bytes[source]

Render events as an Excel workbook (same columns as the CSV, see event_table()).

One Events sheet: a bold, frozen header with filters, frames and seconds stored as numbers (so Excel sorts and sums them), and columns sized to their content.

Parameters:
  • events – The events to render.

  • fps_by_video – Frames per second per video_id (for the derived seconds).

  • meta_by_video – Optional per-video metadata, as meta_<key> columns.

  • types – Event categories by name (for effective colour and attributes).

Returns:

The .xlsx file’s bytes.

annie.dataset.events.export_events_xlsx(events: collections.abc.Iterable[annie.dataset.storage.EventRecord], fps_by_video: collections.abc.Mapping[str, float], path: str | pathlib.Path) → pathlib.Path[source]

Write events to an Excel file (see events_to_xlsx_bytes()).

Parameters:
  • events – The events to write.

  • fps_by_video – Frames per second per video_id (for the derived seconds).

  • path – Destination .xlsx path (parent directories are created).

Returns:

The path written.