Skip to content

Commit 8a47711

Browse files
authored
docs(export): correct the bundle layout, the CSV import claim and the tracked-day loss (#999)
* docs(export): correct the bundle layout, the CSV import claim and the tracked-day loss Checked against origin/release/2.2.0. The CSV column tables are all still exact -- 41 intake columns, 13 activity, 9 tracked-day, in order -- as is the dialect section. The layout and the round-trip section were not. - **The bundle is format-exclusive.** Settings -> Export offers a JSON / CSV toggle and writes one or the other; no bundle the app produces has ever carried a CSV next to its JSON counterpart. The doc described a single zip with both side by side, each CSV a "flat companion" -- so a tool author unzipping expecting six files finds three. Split into a JSON table and a CSV table. - **Four artefacts were missing:** weight_log.json and custom_activity_templates.json from the JSON bundle, and the recipe_images/ and meal_images/ directories carrying user photos. - **CSV import shipped.** ImportDataUsecase.importDataCsv reads all three CSVs and is wired to the same toggle. The doc said the importer "reads the JSON files and ignores the CSV companions" and offered CSV import as future work "if there's demand". - **Tracked days do not round-trip, and the doc promised they did.** trackedDayColumns has 9 entries against TrackedDayDBO's 19 fields: the ten per-nutrient goals added by #173 have no column and are dropped. The cited test does not pin what the doc claimed, because its tracked-day group never sets them. Now stated, with a pointer to use JSON for restores. - **"User profile (height, weight, birthday, PAL, goal) is not included"** read as "your weight data is not in the bundle". The profile box is excluded, but weight history is exported as weight_log.json. Separated. Refs #991 * docs(export): photos ship with the JSON bundle only Copilot caught it, and the code agrees: `_addUserImageIfPresent` sits inside the `format == ExportFormat.json` branch of `exportData`, and `importDataCsv` has no image-restore step at all. The page said either bundle carries them, which would have cost someone their photos on a CSV round trip. Also prefixes the profile-box path with `lib/`, matching every other path on the page.
1 parent 9a9282b commit 8a47711

1 file changed

Lines changed: 51 additions & 28 deletions

File tree

docs/export-format.md

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,49 @@ issue #40 asked for it: people who want to keep their nutrition history in a
66
plaintext format they can sync via Syncthing, open in a spreadsheet, or feed
77
into other tooling deserve a stable schema they can read against. Issue #132
88
asked specifically for CSV export so the import / export round trip would be
9-
symmetric, and that lives alongside the JSON in the same bundle.
9+
symmetric. CSV is a second bundle format rather than an addition to the JSON one.
1010

1111
## Zip layout
1212

1313
The export is a single `.zip` file (default filename
14-
`opennutritracker-export.zip`). It contains:
15-
16-
| File | Format | Notes |
17-
| --------------------- | ------ | ------------------------------------------------------------------ |
18-
| `user_intake.json` | JSON | Canonical format the app re-imports from. |
19-
| `user_intake.csv` | CSV | Flat companion to `user_intake.json` — same rows, flattened meal. |
20-
| `user_activity.json` | JSON | Canonical format the app re-imports from. |
21-
| `user_activity.csv` | CSV | Flat companion to `user_activity.json`. |
22-
| `user_tracked_day.json` | JSON | Canonical format the app re-imports from. |
23-
| `user_tracked_day.csv` | CSV | Flat companion to `user_tracked_day.json`. |
24-
| `user_recipes.json` | JSON | Recipes only. Nested-ingredient shape, no CSV counterpart. |
25-
26-
User profile (height, weight, birthday, PAL, goal) is intentionally **not**
27-
included — see `core/data/data_source/user_data_source.dart` for the box that
28-
stores it.
29-
30-
The user can re-import the same zip via **Settings → Import**. The importer
31-
reads the JSON files and ignores the CSV companions. The CSV files exist so a
32-
spreadsheet, a Syncthing-style backup, or external tooling can read the same
33-
data without going through Hive.
14+
`opennutritracker-export.zip`).
15+
16+
**You pick one format, and the bundle carries only that one.** Settings → Export
17+
offers a JSON / CSV toggle, and the bundle contains either the JSON files or the
18+
CSV files — never both side by side. A tool that unzips expecting a JSON file
19+
next to its CSV counterpart will not find one.
20+
21+
**JSON bundle**
22+
23+
| File | Notes |
24+
| -------------------------------- | -------------------------------------------------------- |
25+
| `user_intake.json` | Canonical format the app re-imports from. |
26+
| `user_activity.json` | Canonical format the app re-imports from. |
27+
| `user_tracked_day.json` | Canonical format the app re-imports from. |
28+
| `user_recipes.json` | Recipes only. Nested-ingredient shape, no CSV counterpart. |
29+
| `weight_log.json` | Weight history. |
30+
| `custom_activity_templates.json` | Saved custom activities — name and typical kcal. |
31+
32+
**CSV bundle**
33+
34+
| File | Notes |
35+
| ----------------------- | ------------------------------------------------------------ |
36+
| `user_intake.csv` | Same rows as the JSON, with the meal and its nutriments flattened. |
37+
| `user_activity.csv` | Flat activities. |
38+
| `user_tracked_day.csv` | Flat tracked days — **lossy, see [Round-trip guarantee](#round-trip-guarantee)**. |
39+
40+
**Photos travel with the JSON bundle only.** Any user-attached recipe and custom-meal images are
41+
added under `recipe_images/` and `meal_images/` when you export as JSON. A CSV export carries
42+
none, and the CSV importer does not restore them — another reason to use JSON for anything you
43+
intend to restore from.
44+
45+
The **profile box** — height, birthday, PAL, goal — is intentionally not
46+
included; see `lib/core/data/data_source/user_data_source.dart`. Note this is the
47+
profile only: your logged **weight history** *is* exported, as `weight_log.json`.
48+
49+
The user can re-import the same zip via **Settings → Import**, in either format.
50+
The CSV files also exist so a spreadsheet, a Syncthing-style backup, or external
51+
tooling can read the same data without going through Hive.
3452

3553
## JSON schema
3654

@@ -250,18 +268,23 @@ Header lookup is case-insensitive.
250268

251269
## Round-trip guarantee
252270

253-
The CSV files round-trip cleanly through
271+
Intakes and activities round-trip cleanly through
254272
`lib/core/utils/csv_data_exporter.dart` — exporting a list of DBOs and parsing
255273
the resulting CSV yields structurally equal DBOs (modulo
256274
empty-string-vs-null normalisation on nullable fields). The unit test
257275
`test/unit_test/csv_data_exporter_test.dart` pins this behaviour.
258276

259-
The in-app **Import** action still reads the JSON files only, so external
260-
tooling that wants to write data back into the app must produce the JSON
261-
shape above. Future work could surface a "Import CSV (intakes /
262-
activities / tracked days)" entry point if there's demand — for now the
263-
CSV is read-only from the app's perspective and read-write from your
264-
spreadsheet's perspective.
277+
**Tracked days do not round-trip.** `trackedDayColumns` carries nine columns
278+
while `TrackedDayDBO` has nineteen fields: the ten per-nutrient goals — fibre,
279+
saturated fat, sugars, sodium, calcium, iron, potassium, magnesium, vitamin B12
280+
and vitamin D — have no column and are lost on a CSV export. The JSON bundle
281+
keeps them. Use JSON if you intend to restore from the bundle rather than read
282+
it in a spreadsheet.
283+
284+
**CSV import works.** `ImportDataUsecase.importDataCsv` reads `user_intake.csv`,
285+
`user_activity.csv` and `user_tracked_day.csv`, and Settings → Import uses the
286+
same format toggle as the export. External tooling can therefore write either
287+
shape — subject to the tracked-day loss above.
265288

266289
## Syncthing-friendly notes
267290

0 commit comments

Comments
 (0)