- 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>
4.8 KiB
Dev notes — assumptions that turned out wrong
Running list so the same mistakes aren't repeated. Add to it whenever reality contradicts an assumption.
The macOS system Python can't run the GUI; use a Homebrew Python (2026-09-07)
The macOS CommandLineTools Python 3.9 (/Library/Developer/CommandLineTools/ .../python3.9) ships Tk 8.5.9. ttkbootstrap 1.10.1 requires Tk 8.6+.
Symptoms: TclError: couldn't recognize image data (window icon) and
TclError: unknown option "-style" on ttk.Scrollbar.
Fix for local GUI testing: brew install python-tk@3.12 (pulls tcl-tk,
gives Python 3.12 + Tk 9.0), then:
/opt/homebrew/bin/python3.12 -m venv venv
venv/bin/pip install -r requirements.txt
venv/bin/python app.py
ttkbootstrap 1.10.1 runs fine on Tk 9.0 in practice (verified: window builds,
Treeview sort, DateEntry, LabelFrame, custom styles all work).
- The Linux Mint target uses its own
python3-tk(Tk 8.6+) and is unaffected either way. screencapturefrom a non-GUI shell fails ("could not create image from display") without Screen-Recording permission — automated screenshots of the running app aren't available here; verify by building the widget tree and driving it programmatically instead.
pandas reads everything as strings by design
DataStore._load uses dtype={...: str} for postal_code, lat, lon.
Numeric parsing is deliberately deferred to get_map_data (pd.to_numeric(..., errors="coerce")). Don't "fix" columns to float on load — blank coordinates and
leading-zero PLZ both depend on the string representation.
Map tiles: Carto needs a key, OSM 403s file:// — use Esri (2026-09-07)
Two dead ends before landing on Esri:
folium.Map(tiles="CartoDB positron")→ "API KEY REQUIRED" watermark; Carto moved basemaps behind a key.tiles="OpenStreetMap"→ worked incurlfrom the dev machine but the user's browser got HTTP 403 "referer is required by tile usage policy". OSM's tile CDN graylists refererless requests, and akarte.htmlopened as a localfile://sends noReferer. A browser can't add one. Dead end for this app's "open a local HTML file" model.
Now: Esri "World Light Gray" base + reference (labels) overlay
(server.arcgisonline.com/ArcGIS/rest/services/Canvas/…). No key, no Referer
check, light/muted look close to the old positron. MAP_TILES /
MAP_TILES_LABELS constants; comment lists Esri Street/Topo as swaps.
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:
>>> 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: menu.grab_release() is the Windows idiom and misfires on X11: tk_popup
returns immediately, grab_release() drops the menu's grab, and the pending
<ButtonRelease-3> then activates whatever entry is under the cursor (the first
one). Symptom here: right-click a history row → menu flashes → edit dialog opens.
Rules for this codebase:
- bind the release event:
RIGHT_CLICKconstant (<ButtonRelease-3>, or<ButtonRelease-2>on macOS), - build each context menu once (store on
self), don't recreate per click, - just call
menu.tk_popup(...)— nograb_release().
Nominatim query order matters
geocode_city returns the first hit, trying PLZ-qualified first, then each
region in GEOCODE_REGIONS, then a bare ", Germany". Reordering changes which
coordinates ambiguous town names resolve to.