Skip to content

Export

Export is a backend capability (architecture 14): an export is reproducible from explicit parameters, generated on the server, and never asks the browser to hold the data. Small exports download at once, large ones run as jobs in the export service (decision D39) and land in MinIO.

Parameters

Both paths take the same parameters, as a JSON body for jobs and as query parameters for direct downloads.

Parameter Meaning
dataset records (one row per moment of a device with its position, measurements and state, see below), positions, measurements (normalized), source_events (raw inbound messages with payload), aggregates (the Data explorer series), movebank_events or movebank_reference (see Movebank below).
format csv, xlsx, json; positions also geojson and gpx.
time_from, time_to Required, with offset. Source events are selected on ingested_at, everything else on the device time.
entity_ids, device_ids, metric_keys, data_source_id Filters. Source events are per device and refuse entity_ids.
timezone IANA name, default UTC. Times in the file are written in this zone with their offset.
include_names Adds entity, device and metric names and units as columns (default on).
bucket, aggregates, group_by, layout Aggregates only; same meaning as in the Data explorer, layout long or wide. A bucket too fine for the range becomes the finest that fits (decision D148).
records_layout Records only: wide (default, one row per moment with a column per metric m_<key> and per state field s_<key>) or long (one row per value).

Records

The records dataset is the records view of the Data explorer as a file (phase 20, decision D144): the selection needs at least one entity or device, and every moment they produced in the window becomes a row, oldest first, with the effective time, the entity and device, the position of the moment where there is one, and the measurements and state fields of that moment. The wide layout learns its metric and state columns from the selection before the first row is written; the long layout writes one row per value. The direct path and the job read the same statement and the same bound: the count of the moments decides.

In the app the export dialog starts from the records view's selection, counts first, downloads directly under the bound and above it starts the job by itself and shows its progress and the download inline. The person asked for the data, not for a choice of mechanism.

Direct download

GET /api/v1/projects/{project_id}/exports/direct?dataset=positions&format=gpx&time_from=...&time_to=...

Streams the file. The row count is checked first; above 100,000 rows the answer is 413 with the advice to create a job (architecture 13.8), and the app's export dialog then creates the job by itself. XLSX is assembled in a temporary file because the format is complete only at the end; every other format streams as it is written.

Jobs

Call Purpose
POST /api/v1/projects/{project_id}/exports Queue a job (permission exports:create). Returns the job with status queued.
GET .../exports List jobs of the project.
GET .../exports/{job_id} Status, progress in rows, and when done: row count, size, SHA-256 and metadata.
GET .../exports/{job_id}/download The file, streamed from MinIO through the API. 409 while the job is not done.
POST .../exports/{job_id}/reproduce A new job with the same parameters, linked through source_job_id.

The export service consumes export.requested, reads rows with a server-side cursor in batches of 2,000, writes them through a streaming writer into a temporary file, uploads the file to the exports bucket and records size and hash. Progress is written every 10,000 rows. A failure is stored on the job with its message; it is not retried, the same parameters would fail again.

Files are kept for seven days (expires_at); the export service removes expired files once an hour and marks the job expired. The job's parameters and metadata stay, download answers 410, and reproduce makes the file again.

Formats

  • CSV: UTF-8, header row, nested values (attributes, payloads) as JSON strings.
  • XLSX: one sheet data; when a sheet would pass the Excel limit of 1,048,576 rows the next rows continue on data_2, data_3 and so on. Nothing is cut off silently (decision D40).
  • JSON: {"metadata": {...}, "columns": [...], "rows": [...]}.
  • GeoJSON: a FeatureCollection of points, the other columns as properties.
  • GPX: one track per entity (per device when unassigned), points in time order with elevation and UTC time.

Movebank

Movebank takes data through arranged live feeds or file import, so Smart Parks Protect offers it as two datasets in Movebank's import format (decision D85) rather than as a push connector. Both are tabular (CSV, XLSX, JSON) and always use the effective (curated) view.

  • movebank_events: one row per position with timestamp (yyyy-MM-dd HH:mm:ss.SSS in UTC), location-long, location-lat, sensor-type (GPS), individual-local-identifier (the entity name), tag-local-identifier (the device name), individual-taxon-canonical-name (the entity type's taxon attribute when set), height-above-msl, ground-speed, heading, gps:satellite-count, location-error-numerical, study-name (the project name) and smart-parks-position-id.
  • movebank_reference: one row per assignment of a device to an entity in the window: animal-id, tag-id, deploy-on-date, deploy-off-date, animal-taxon, animal-comments, tag-manufacturer-name, tag-model, tag-serial-no, study-name.

Import in Movebank as "custom import" with the columns mapped by name; the reference data file matches Movebank's reference data terms.

Reproducibility

Every job stores its parameters and, when done, a metadata snapshot: generator version, timezone, the metric definitions with units, row count, size and SHA-256. JSON exports carry the same metadata inside the file. Reproducing a job later can give a different result when late data arrived; the two jobs' metadata show the difference (architecture 28.13).