37c9642877
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>
62 lines
2.9 KiB
Markdown
62 lines
2.9 KiB
Markdown
# 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 4–5 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.
|