Files
drk-blutspende-orte/docs/data-model.md
T
Paddy 37c9642877 Initial commit: Arbeitsorte-Logger + data-safety/robustness/input work
Existing app (single-file Tkinter/ttkbootstrap desktop tool for logging
blood-drive work assignments and mapping them) plus the first round of
improvements:

- Data safety: atomic CSV writes (tmp + fsync + os.replace), rotating
  backups in data/backups/ (startup + before every change, keep 20),
  fallback to empty/backup on missing/empty/corrupt orte.csv.
- Geocoder robustness: per-item try/except so the worker thread survives
  failures; GeocodingUnavailable + one-time "service unreachable" dialog.
- Input: DateEntry calendar picker with parse_date() validation; manual
  lat/lon fields in the edit dialog; Tab 2 highlights/filters rows without
  coordinates and adds a right-click "Koordinaten suchen".
- Logging to data/app.log; shared autocomplete helpers; config constants;
  map fit_bounds.

docs/ describes current state, architecture, data model, setup (Linux Mint),
and the full improvement roadmap. data/orte.csv is gitignored for now.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 18:28:28 +02:00

62 lines
2.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Data model
## `data/orte.csv`
Plain CSV, comma-separated, UTF-8, with a header row. Created automatically with
just the header if it does not exist.
| Column | Type on disk | Meaning | Notes |
|--------|--------------|---------|-------|
| `date` | string | Assignment date | Always stored as `YYYY-MM-DD`. Input is validated and normalized by `parse_date()` (accepts `YYYY-MM-DD`, `DD.MM.YYYY`, `DD.MM.YY`, `YYYY/MM/DD`); invalid input is rejected before saving. Rows written before this change may still hold non-ISO strings. |
| `city` | string | Place name | Free text. Used for autocomplete and duplicate detection (trimmed, case-insensitive). |
| `postal_code` | string | German PLZ | Optional. Kept as a string so leading zeros survive. Regex elsewhere accepts 45 digits. |
| `lat` | string | Latitude | Blank until geocoded. Rounded to 5 dp. Parsed with `pd.to_numeric(errors="coerce")` when building the map. |
| `lon` | string | Longitude | As above. |
Every column is read as a string (`dtype={"postal_code": str, "lat": str, "lon": str}`
plus `city`/`date` default object). Numeric conversion happens only where needed.
### Row identity
There is **no ID column**. Rows are identified by their pandas `DataFrame`
index, which is reset to `0..n-1` after every delete. UI tables use that index
as the Treeview `iid`.
Implication: a geocode callback that is in flight while the user deletes a
different row can land on the wrong row, because indices shift. `update_row` /
`_apply_edit_geocode` guard against a *missing* index but not against a
*reused* one. Still open — see [improvements.md](improvements.md#7-stable-row-identity-).
The manual lat/lon fields in the edit dialog give a way to correct any row
that ends up wrong.
### Duplicate detection
`find_duplicates` flags a queued row when an existing row matches on all three
of: `date` (string-equal), `city` (trimmed, lower-cased), `postal_code`
(trimmed). It only produces a status-bar hint; duplicates are still written.
### Map aggregation
`get_map_data` groups by `(city, lat, lon)` and counts rows. Two entries for the
same city with different coordinates (e.g. Karlsruhe geocoded once to `76133`
and once to `76185`) produce **two separate markers**.
## `data/karte.html`
Regenerated from scratch every time the user opens the map. Safe to delete; it
is a build artefact, not data. Should be git-ignored if the project is ever put
under version control.
## `data/backups/`
Automatic timestamped copies of `orte.csv`, named `orte-YYYYMMDD-HHMMSS.csv`.
One is written at startup and one before every change (skipped when nothing
changed since the last backup). The newest 20 are kept; older ones are pruned.
Git-ignored. To restore, copy a backup over `data/orte.csv` while the app is
closed.
## `data/app.log`
Rotating log file (512 KB × 3 generations). Git-ignored. Records saves, map
generation, geocoding failures, and uncaught exceptions.