d418f156fa
- Date pickers (entry form + edit dialog) now display TT.MM.JJJJ instead of
ISO (DATE_DISPLAY_FORMAT). Storage stays YYYY-MM-DD; parse_date() already
accepted both formats, so existing data and the self-updater are unaffected.
- New optional "Straße" field (street + house number) in the entry form and
edit dialog, backed by a new `street` CSV column. geocode_city() and
GeocoderWorker.enqueue() gained a street parameter: when set, a full-address
query is tried first for a much more precise map point, falling back
automatically to the existing city/PLZ search if it doesn't resolve.
- Tab 2 and the entry queue show a Straße column; Tab 2 search now also
matches on street.
- Fix: pandas turns a blank CSV cell into NaN even for a dtype=str column, so
every existing (blank-street) row would have shown literal "nan" in Tab 2.
DataStore._load now does street.fillna("") after every read.
Verified with a non-GUI test suite (date parsing, query construction, CSV
round-trip incl. the NaN case) and a full GUI build/drive test on Python
3.12/Tk 9. Docs updated (changelog, overview, architecture, data-model,
dev-notes, improvements).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
163 lines
8.1 KiB
Markdown
163 lines
8.1 KiB
Markdown
# Changelog
|
||
|
||
Notable changes to the app. Newest first.
|
||
|
||
## 2026-09-12 — German date display + optional street address for geocoding
|
||
|
||
- **Date fields now display `TT.MM.JJJJ`** (e.g. `12.09.2026`) instead of ISO,
|
||
in both the entry form and the edit dialog (`DATE_DISPLAY_FORMAT`). Storage on
|
||
disk is unchanged (`YYYY-MM-DD`) — `parse_date()` still accepts either format
|
||
when typed, so nothing about existing data changes.
|
||
- **New optional "Straße" field** (street + house number) on the entry form and
|
||
in the edit dialog, and a new `street` CSV column. When set, geocoding tries
|
||
the full address first for a much more precise map point, falling back to the
|
||
existing city/PLZ search if the address doesn't resolve — see
|
||
[overview.md](overview.md#geocoding-behaviour). `geocode_city()` and
|
||
`GeocoderWorker.enqueue()` both gained a `street` parameter.
|
||
- Tab 2 and the entry queue both show a **Straße** column; the Tab 2 search box
|
||
now also matches on street.
|
||
- Fixed a bug this surfaced immediately: pandas turns a **blank CSV cell into
|
||
`NaN`, even for a column forced to `dtype=str`** — every pre-existing row (all
|
||
blank on `street`) would have displayed the literal text `nan`. `DataStore._load`
|
||
now does `self.df["street"] = self.df["street"].fillna("")`. See
|
||
[dev-notes.md](dev-notes.md#pandas-turns-blank-csv-cells-into-nan-even-with-dtypestr).
|
||
- `find_duplicates` intentionally still ignores `street` (unchanged: date + city
|
||
+ PLZ only).
|
||
|
||
## 2026-09-07 — fix: right-click context menu instantly triggered its first item
|
||
|
||
On Linux the Tab 2 (and queue) right-click menu flashed and immediately ran
|
||
"Bearbeiten" / "Löschen", so entries couldn't be deleted. Cause: the menu was
|
||
bound to `<Button-3>` (press) and `finally: menu.grab_release()` dropped the
|
||
grab, so the following `<ButtonRelease-3>` selected the item under the cursor.
|
||
Fix: bind `<ButtonRelease-3>` (`<ButtonRelease-2>` on macOS), build each menu
|
||
once, and drop the premature `grab_release()`.
|
||
|
||
## 2026-09-07 — map tiles → Esri (OSM 403s local files)
|
||
|
||
The switch to `tiles="OpenStreetMap"` worked on the dev machine but the target
|
||
laptop's browser got **HTTP 403 "referer is required by tile usage policy"** —
|
||
OSM's CDN blocks refererless requests, and a `karte.html` opened as `file://`
|
||
sends no `Referer`. Switched to **Esri "World Light Gray"** (base + labels
|
||
overlay, `server.arcgisonline.com`): no key, no `Referer` check, muted look
|
||
similar to the old positron. Constants `MAP_TILES` / `MAP_TILES_LABELS` /
|
||
`MAP_TILES_ATTR`.
|
||
|
||
## 2026-09-07 — install split for non-admin users
|
||
|
||
The target account has no sudo, so install is two scripts:
|
||
|
||
- `install-system.sh` – admin runs once: `apt install python3-tk python3-venv git`.
|
||
- `install.sh` – user runs, **no sudo**: checks the system packages, creates
|
||
`.venv` + installs `requirements.txt`, writes the `.desktop` menu entry. If a
|
||
package is missing and the account can't sudo, it prints the exact admin
|
||
command and stops.
|
||
|
||
## 2026-09-07 — one-command install + launcher
|
||
|
||
- `install.sh` – Linux Mint setup: `.venv` + `requirements.txt`, and a
|
||
`.desktop` menu entry with correct absolute paths.
|
||
- `run.sh` – launches the app from `.venv`; the menu entry calls this.
|
||
- `icon.png` – bundled red-cross launcher/window icon (256×256); the app now
|
||
prefers this over the runtime-drawn one.
|
||
- `requirements.txt` – **fully pinned** (direct + transitive), tested on
|
||
Python 3.12. `numpy` capped at 2.2.6 so it still installs on Python 3.10.
|
||
|
||
## 2026-09-07 — built-in self-update
|
||
|
||
New **Hilfe** menu:
|
||
|
||
- **Nach Update suchen** – `git fetch` + count of new commits on the server.
|
||
- **Version…** – shows the installed commit (`<hash> · <date>`).
|
||
|
||
On start, a quiet background check runs; if updates exist the status bar says so
|
||
and the menu item becomes "Update installieren (N verfügbar)".
|
||
|
||
Installing runs `git merge --ff-only` (fast-forward only – never an automatic
|
||
merge), re-runs `pip install -r requirements.txt` if that file changed, then
|
||
restarts the app via `os.execv`. User data (`data/`) is git-ignored and
|
||
untouched. If the local checkout has diverged or the network is down, it shows a
|
||
clear message and does nothing.
|
||
|
||
Only active when the app runs from a **git clone** with `git` installed;
|
||
otherwise the menu just shows "Version: unbekannt". New `Updater` class,
|
||
`app.py`.
|
||
|
||
## 2026-09-07 — map tiles switched to OpenStreetMap
|
||
|
||
`folium.Map(tiles="CartoDB positron")` began showing an "API KEY REQUIRED"
|
||
watermark (Carto moved its basemaps behind a key). Switched to
|
||
`tiles="OpenStreetMap"` via a new `MAP_TILES` constant — no key required. OSM
|
||
standard tiles are more colourful than positron was; `MAP_TILES` makes swapping
|
||
the provider a one-line change.
|
||
|
||
## 2026-09-07 — UI/UX pass
|
||
|
||
A round of usability improvements aimed at the non-technical user
|
||
(see [improvements.md](improvements.md#uiux-review-2026-09-07)).
|
||
|
||
- **Larger UI font** (`UI_FONT_SIZE = 11`) applied to all default fonts, menus,
|
||
dialogs and the Treeview (taller rows).
|
||
- **DRK-red header bar** with the app name; window title shows the total count
|
||
("… · 142 Einsätze"); simple red-cross window icon drawn at runtime (also
|
||
written to `data/icon.png` for the launcher).
|
||
- **Status bar** is now colour-coded by severity (grey/green/orange/red), and
|
||
transient messages auto-clear after 8 s back to a neutral state; queue-count
|
||
messages stay put.
|
||
- **Tab 1:** more spacing in the form; PLZ field accepts digits only;
|
||
"+ Hinzufügen" is a solid button; explicit **"Auswahl entfernen"** button and
|
||
<kbd>Delete</kbd> key for queue rows; **"Warteschlange leeren"** now asks for
|
||
confirmation; **duplicate save shows a modal** (list + "trotzdem speichern?")
|
||
instead of a transient hint.
|
||
- **Tab 2:** the raw `lat`/`lon` columns are replaced by a single **"Karte"**
|
||
column (`✓` / red `fehlt`); active sort column shows a ▲/▼ arrow; the hint
|
||
line always shows the saved-entry count.
|
||
- **"Karte öffnen"** with no coordinates now shows a dialog, not just a status
|
||
line.
|
||
- **Window size & position** are remembered between sessions
|
||
(`data/window.json`).
|
||
|
||
## 2026-09-07 — data safety, robustness, easier input
|
||
|
||
Addressed the top three findings from [improvements.md](improvements.md).
|
||
|
||
### Data safety (`DataStore`)
|
||
- All writes (append / update / delete) now rebuild the full DataFrame and go
|
||
through `_save_csv`: **rotating backup** to `data/backups/` (startup + before
|
||
every change, keep 20, skip if unchanged) then **atomic write**
|
||
(`tmp` + `fsync` + `os.replace`).
|
||
- `orte.csv` missing / empty (`EmptyDataError`) / unreadable now falls back to an
|
||
empty table or the newest backup instead of crashing.
|
||
|
||
### Geocoder robustness (`GeocoderWorker`, `geocode_city`)
|
||
- Each queue item runs inside `try/except`; a failing lookup or callback logs a
|
||
traceback and the worker keeps going (previously an unexpected error killed the
|
||
thread and silently froze all further geocoding).
|
||
- `geocode_city` raises `GeocodingUnavailable` when every query failed on a
|
||
service/network error. After 3 in a row the app shows a one-time
|
||
"Standortdienst nicht erreichbar" dialog; entries still save without
|
||
coordinates.
|
||
|
||
### Easier, safer input
|
||
- Date fields (entry form + edit dialog) are now `ttkbootstrap.DateEntry`
|
||
calendar pickers, validated/normalized to ISO by `parse_date()` (also accepts
|
||
`TT.MM.JJJJ`). Invalid dates are rejected with a dialog.
|
||
- Edit dialog has manual **Breitengrad / Längengrad** fields for fixing
|
||
coordinates by hand.
|
||
- Tab 2: rows without coordinates shown in red, "Nur ohne Koordinaten" filter,
|
||
count in the hint line, right-click **Koordinaten suchen**.
|
||
|
||
### Housekeeping
|
||
- `git init` + `.gitignore`.
|
||
- Rotating log file at `data/app.log` (`_setup_logging`).
|
||
- Shared helpers `filter_locations` / `split_city_plz` / `parse_coord` replace
|
||
duplicated combobox code.
|
||
- Config constants: `GEOCODE_REGIONS`, `MAP_DEFAULT_CENTER`, `MAP_DEFAULT_ZOOM`,
|
||
`MAX_BACKUPS`.
|
||
- `generate_map` fits the view to the markers.
|
||
|
||
### Verification
|
||
Non-GUI smoke tests (helpers, `DataStore` backup/atomic/recovery, geocoder
|
||
resilience) pass. The GUI now *can* be built and driven on the dev Mac via a
|
||
Homebrew Python with Tk 9 — see [dev-notes.md](dev-notes.md).
|