German date display + optional street address for precise geocoding

- 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>
This commit is contained in:
2026-09-12 11:15:37 +02:00
parent 0b6ed854dc
commit d418f156fa
7 changed files with 185 additions and 61 deletions
+26
View File
@@ -57,6 +57,32 @@ Lesson: a tile source that works from `curl` or an `http://` page can still fail
from `file://`. Test the map by **opening the generated `karte.html` directly**,
not just by checking the URL in the HTML.
## pandas turns blank CSV cells into NaN even with `dtype=str` (2026-09-12)
Assumption that turned out wrong: forcing a column's dtype (e.g.
`dtype={"street": str}`) does **not** stop `pd.read_csv` from parsing a truly
empty field as `NaN` (a float) instead of `""`. Verified directly:
```python
>>> pd.read_csv(io.StringIO("a\n\n"), dtype={"a": str})["a"][0]
nan # not ''
>>> str(pd.read_csv(io.StringIO("a\n\n"), dtype={"a": str})["a"][0])
'nan' # !!
```
This is *why* `lat`/`lon` already needed `_is_blank()` / `_coord_str()` helpers
— but the new `street` column (added 2026-09-12) hit it immediately and much
harder: it's optional, so *every* existing row has it blank, and without a fix
every one of them would show the literal text `nan` in Tab 2. Fix: after
`pd.read_csv` (all three load paths — normal, empty-file, backup-recovery),
`DataStore._load` runs `self.df["street"] = self.df["street"].fillna("")`.
Rule for this codebase: **any column that can legitimately be blank needs an
explicit `.fillna("")` (or the `_is_blank`/`_coord_str` treatment) right after
`pd.read_csv`** — `dtype=str` alone is not enough. `city`/`postal_code` haven't
needed this only because they're rarely actually blank in practice, not because
they're immune.
## Tk popup menus: bind `<ButtonRelease-3>`, not `<Button-3>` (2026-09-07)
`widget.bind("<Button-3>", …)` + `menu.tk_popup(x, y)` + `finally: