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 ondata_2,data_3and 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 withtimestamp(yyyy-MM-dd HH:mm:ss.SSSin 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'staxonattribute when set),height-above-msl,ground-speed,heading,gps:satellite-count,location-error-numerical,study-name(the project name) andsmart-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).