Items 1–12 of the interface review: - Larger UI font (UI_FONT_SIZE) across widgets, menus, dialogs, Treeview rows. - DRK-red header bar; window title shows entry count; runtime-drawn red-cross icon (also written to data/icon.png for the launcher). - Status bar colour-coded by severity; transient messages auto-clear after 8 s, queue-count messages stay. - Tab 1: more form spacing, digits-only PLZ, explicit "Auswahl entfernen" + Delete key for queue rows, confirm on "Warteschlange leeren", modal on duplicate save. - Tab 2: raw lat/lon columns replaced by one "Karte" column (checkmark / red "fehlt"); ▲/▼ arrow on the active sort column; hint line always shows the saved-entry count. - "Karte öffnen" with no coordinates shows a dialog, not just a status line. - Window size/position remembered between sessions (data/window.json). Verified by building and driving the window under Homebrew Python 3.12 / Tk 9; non-GUI smoke tests still pass. Docs updated (changelog, overview, improvements, data-model, setup, dev-notes). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.9 KiB
Overview – what the app does
The app is a single window: a DRK-red header bar, two tabs, and a
colour-coded status bar at the bottom (grey = neutral, green = success,
orange = warning, red = error; transient messages fade back to neutral after
~8 s). The window title shows the total entry count, and the window remembers
its size and position between sessions (data/window.json). The UI font is
enlarged for readability (UI_FONT_SIZE).
Tab 1 – "Neuer Eintrag" (new entry)
Workflow: build up a queue of entries, let them geocode in the background, then save the whole batch at once.
- Entry form – date (a calendar picker,
ttkbootstrap.DateEntry, pre-filled with today; typed input is accepted inJJJJ-MM-TTorTT.MM.JJJJand validated – an invalid date is rejected with a dialog), city (autocomplete combobox), postal code (optional). PressingReturnin any field, or the + Hinzufügen button, adds the row to the queue. The PLZ field only accepts digits. - Queue table – shows date, city, PLZ, and a live coordinate column that
updates from
⏳ wird gesucht…to eitherlat / lonor⚠ nicht gefundenas the background geocoder works through the queue.- Select a row and press Delete, use the "Auswahl entfernen"
button, or click the
✕cell. - Right-click a row for Löschen / Koordinaten erneut suchen.
- Select a row and press Delete, use the "Auswahl entfernen"
button, or click the
- Action bar
- Alle speichern – appends every queued row to
orte.csv. If any row looks like a duplicate (same date + city + PLZ), a modal lists them and asks whether to save anyway. - Warteschlange leeren – discards the queue (asks for confirmation).
- Karte öffnen – regenerates
karte.htmland opens it in the browser.
- Alle speichern – appends every queued row to
Tab 2 – "Einträge verwalten" (manage entries)
A table view of everything in orte.csv.
- Columns: Datum, Ort, PLZ, and Karte – a status column showing
✓when the row has coordinates or a redfehltwhen it doesn't (the whole row is red too). The raw lat/lon numbers live in the edit dialog, not this table. - Search box – live filter across date, city, and PLZ (substring match).
- Sortable columns – click a header to sort; clicking again reverses. The active column shows a ▲/▼ arrow. Default sort is by date, newest first. Sorting by Karte groups the rows without coordinates together.
- "Nur ohne Koordinaten" toggle – filters to entries that have no coordinates yet. The hint line always shows the total count, plus how many are missing coordinates.
- Edit – double-click a row (or Bearbeiten) opens a modal dialog to change date (calendar picker, validated) / city / PLZ. Two ways to fix coordinates: a "Koordinaten automatisch neu suchen" toggle re-runs geocoding after saving, or the Breitengrad / Längengrad fields let you type them in by hand (both empty = no map marker).
- Right-click → Koordinaten suchen – runs geocoding for that one row (handy for rows that failed the first time).
- Delete – Löschen removes the selected row(s) after a confirmation
dialog. This is irreversible, but a timestamped copy of the file is
written to
data/backups/before every change (see data-model.md). - Karte öffnen – same as on Tab 1.
The map (karte.html)
Generated by generate_map():
- Base layer:
CartoDB positron, initial view centred on[49.0, 9.0], zoom 8 (roughly Baden-Württemberg). - One
CircleMarkerper unique(city, lat, lon)group. Radius scales linearly with the visit count for that location (5 + count / max_count * 20px), colour is DRK red (#CC0000). - Popup shows the city and the visit count; tooltip shows the city.
- Rows with missing/blank coordinates are silently excluded.
- If there are no usable coordinates at all, a dialog says so (and the status bar shows a warning).
The generated HTML pulls Leaflet, jQuery and Bootstrap from CDNs, so the map needs an internet connection to render even though the data is local.
Geocoding behaviour
geocode_city(city, postal_code) tries a series of queries in order and returns
the first hit:
"<PLZ> <city>, Germany"(only if a PLZ was given)"<city>, Baden-Württemberg, Germany""<city>, Hessen, Germany""<city>, Germany"
The regional bias is the GEOCODE_REGIONS constant (Baden-Württemberg,
Hessen) near the top of app.py. Results are rounded to five decimal places.
Nominatim's usage policy (max 1 req/s, identifying user_agent) is respected by
a manual time.sleep(1) in the worker loop.
If the geocoding service is unreachable (no internet), entries still save –
without coordinates – and after a few consecutive failures a one-time dialog
explains that the coordinates can be added later via Bearbeiten. The worker
thread logs failures to data/app.log and keeps running.