Skip to content

workouts: JSON keys diverge from CSV headers (startdate vs start, deviceid vs device_id, nested data{} vs flat columns) #27

Open

Description

@DTTerastar

Severity

minor

Summary

Sibling of #20 but for the workouts subcommand. Cross-format consumers maintain two name maps and a flat-vs-nested shape difference.

Reproduce

HEAD b58068351a2edeb90f0d26b9098f04e85db225ee.

$ /tmp/qa-bin/withings-export workouts --since 90d --format csv | head -1
date,start,end,category,category_code,timezone,duration_min,eff_duration_min,calories,steps,distance_m,elevation_m,hr_avg,hr_min,hr_max,hr_zone_0,hr_zone_1,hr_zone_2,hr_zone_3,device_id

$ /tmp/qa-bin/withings-export workouts --since 90d --format json | jq '.[0] | keys'
[
  "attrib", "category", "data", "date", "deviceid",
  "enddate", "id", "model", "startdate", "timezone"
]

Drift:

CSV JSON
start startdate (epoch seconds)
end enddate (epoch seconds)
device_id deviceid
category (name string), category_code (int) category (int only)
duration_min, eff_duration_min, calories, steps, distance_m, elevation_m, hr_*, hr_zone_* (flat) data.{effduration, calories, steps, distance, elevation, hr_average, hr_min, hr_max, hr_zone_0..3, manual_distance, manual_calories, intensity, pause_duration, ...} (nested)
(none) attrib, id, model (extra metadata)

The prime text documents the JSON shape, so it's intentional in spirit, but the divergence with CSV is significant: a downstream tool can't pick one schema and use it for both. `measurements` and `activity` have only minor unit-suffix differences (distance_m/`distance`, `hr_avg`/`hr_average`); `workouts` is the most divergent.

Expected

Either:

  • Pick canonical field names per row and use them in both serializers (preferred — flat snake_case with unit suffixes, e.g. `device_id`, `start_epoch`, `category` (string), `category_code` (int)).
  • Or, document the mapping in `prime` (currently only the JSON shape is documented).

If the intent is to keep raw Withings shape in JSON and mapped/flat shape in CSV, prime should say so explicitly. (#20 covers the same problem for `intraday`.)

Activity

  1. added
    kind:bugObserved behavior diverges from documented behavior.
    area:format--format markdown/json/csv and codec behavior.
    priority:mediumReproducible bug or gap with a workaround, or a well-scoped enhancement clearly in charter.
    on May 19, 2026
  2. Terastar-Paperclip commented on May 19, 2026

    @Terastar-Paperclip
    Contributor

    Thanks — and noting #20 explicitly in the body saves a triage hop.

    Triage: kind:bug · area:format · priority:medium per TRIAGE.md. Same class of CSV/JSON schema drift as #20 (intraday) but with the additional flat-vs-nested wrinkle on workouts.data{}. A decision is needed (flat snake_case both sides vs. document the raw-Withings-shape-in-JSON intent in prime) — flagging that explicitly so the maintainer can pick rather than having triage default it.

    Routing: Lead Go Engineer.

  3. DTTerastar commented on Jul 21, 2026

    @DTTerastar
    ContributorAuthor

    Adding a data point from a cross-repo sweep, since it widens this slightly beyond workouts.

    The date/timestamp representation is inconsistent across five subcommands, not just between JSON and CSV in one:

    So jq '.[].date' returns three different shapes depending on the subcommand.

    Counter-evidence worth weighing before treating it as a straight bug: cmd/prime.go documents the choice explicitly ("Times are LOCAL; JSON epoch seconds are zone-agnostic"), so it's deliberate, not an oversight. But it does sit against common/CONTRACT.md §2, which says JSON output preserves RFC3339 with whatever offset Go's default time.Time marshaler emits.

    Filing this as a comment rather than a new issue since it overlaps this and #20. Related org-level gap: quantcli/common#27 proposes a contract clause requiring one JSON type per logical field across subcommands, which would make this decidable instead of a judgment call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:format--format markdown/json/csv and codec behavior.kind:bugObserved behavior diverges from documented behavior.priority:mediumReproducible bug or gap with a workaround, or a well-scoped enhancement clearly in charter.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions