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>
9.9 KiB
Improvement roadmap
Review of the current code, prioritized for the actual deployment: one non-technical user, Linux Mint laptop, offline some of the time, no one else to fix things when they break. That ordering puts data safety and robustness above features.
Priorities: 🔴 do first · 🟡 worth doing · 🟢 nice to have
Status (2026-09-07): items 1, 2 and 3 are implemented. Items 6 and 10 are
partly done (logging added; shared autocomplete helpers extracted; map now
fit_bounds; region list is now a constant). See
changelog.md.
1. Data safety 🔴 — ✅ done
Implemented: atomic writes (tmp + fsync + os.replace), rotating backups in
data/backups/ (startup + before every change, keep 20), EmptyDataError /
corrupt-file fallback to an empty frame or the newest backup, and all writes
(including append) routed through _save_csv. A "restore from backup" button in
the UI is still open; for now it is a manual file copy.
Original finding
data/orte.csv is the only copy of the data and there is no safety net.
- No backups. A bad edit, a botched delete, or a disk hiccup loses history permanently. Delete is explicitly irreversible and only guarded by a yes/no dialog.
- Non-atomic full rewrites.
update_row/delete_rowsdodf.to_csv(CSV_PATH)directly. A crash or power loss mid-write can leave a truncated file. - External-edit clobber. The CSV is read once at startup. If it is opened in a spreadsheet and changed while the app runs, the next save silently overwrites those changes.
- Empty-file crash. If
orte.csvexists but is 0 bytes (e.g. an interrupted write),pd.read_csvraisesEmptyDataErrorand the app won't start.
Suggested changes
- Write every full rewrite to a temp file in the same directory, then
os.replace()it into place (atomic on POSIX). - On startup, and before each destructive write, copy the current file to
data/backups/orte-YYYYMMDD-HHMMSS.csv; keep the last ~20 and prune the rest. - Add a "Backup jetzt erstellen" / "Aus Backup wiederherstellen" pair in Tab 2, or at least open the backups folder from a menu.
- Handle
EmptyDataError/ missing columns by falling back to an empty frame and logging a warning.
2. Geocoder thread robustness 🔴 — ✅ done
Implemented: every queue item is wrapped in try/except Exception (both the
geocode call and the callback), failures are logged and the loop continues.
geocode_city now raises GeocodingUnavailable when every attempt hit a
service/network error, and after 3 consecutive such failures the app shows a
one-time "Standortdienst nicht erreichbar" dialog. RateLimiter was not
adopted; the manual sleep(1) stays.
Original finding
GeocoderWorker.run has no error boundary around the per-item work. If
geocode_city or a callback raises anything other than
GeocoderTimedOut / GeocoderServiceError (a network stack error, a bug in a
callback, GeocoderQuotaExceeded), the worker thread dies and every
subsequent entry stays stuck on ⏳ wird gesucht… with no error shown. The user
has no way to know geocoding stopped working.
Suggested changes
- Wrap each item's processing in
try/except Exception, log the traceback, mark that row as failed, and keep the loop alive. - Consider
geopy.extra.rate_limiter.RateLimiter(withswallow_exceptions) instead of the manualsleep(1). - Surface a clear one-time status message when geocoding fails repeatedly ("Standortdienst nicht erreichbar – bitte Internetverbindung prüfen").
3. Easier, safer input 🔴 — ✅ done
Implemented: the date field is now ttkbootstrap.DateEntry (calendar picker) in
both the entry form and the edit dialog, with parse_date() validation on save
(a bad date is rejected with a dialog). The edit dialog gained manual
Breitengrad / Längengrad fields. Tab 2 shows rows without coordinates in red,
has a "Nur ohne Koordinaten" filter, a hint line with the count, and a
right-click Koordinaten suchen action.
Original finding
For the target user the free-text date field is the biggest usability risk.
- Date field is unvalidated free text in both the entry form and
EditDialog. A typo like2026-13-05or05.02.2026is stored verbatim, breaks date sorting, and never appears correctly on the timeline. - No recourse when geocoding fails. A row that comes back
⚠ nicht gefundensaves with blank coordinates and then just silently never shows on the map. There is no way to type coordinates by hand or retry from Tab 2.
Suggested changes
- Replace the date
Entrywithttkbootstrap.DateEntry(calendar picker) in both places. If keeping free text, validate on add/save and refuse invalid dates with a clear message. - In
EditDialog, add optionallat/lonfields so a location can be fixed manually. - In Tab 2, highlight rows with missing coordinates (e.g. red text) and add a filter / right-click "Koordinaten suchen" so failed geocodes are visible and fixable after the fact.
4. Put it under version control 🟡 — partly done
git init done and .gitignore added (covers .venv/, __pycache__/,
data/karte.html, data/backups/, data/app.log). Still open: the initial
commit (left to Patrick), and the decision on committing data/orte.csv vs. an
example file.
- Decide on
data/orte.csv: for a personal tool it's reasonable to commit it, or commit a smalldata/orte.example.csvand ignore the real one. - This
docs/folder.
Gives you history, a rollback path, and a clean way to push updates to the
laptop (git pull).
5. Packaging & distribution 🟡
Today install is: apt install python3-tk, create a venv, pip install. That's
a one-time terminal session, which is acceptable but fragile (loose version
pins mean a future pip install could pull an incompatible pandas/folium).
Suggested changes
- Pin all four dependencies to exact versions and regenerate deliberately.
- Ship the
run.sh+.desktoplauncher from setup.md in the repo. - Optional: build a PyInstaller one-file bundle on a matching Linux box. It bundles Python, Tk, and all deps, so install becomes "copy one file + double-click" with no apt/venv step. Trade-off: you build it, and rebuild on dependency updates.
6. Diagnostics / logging 🟡 — ✅ done
_setup_logging() writes a RotatingFileHandler to data/app.log
(512 KB × 3). Saves, map generation, geocode failures and uncaught exceptions
are logged. _on_open_map now logs and narrows its handling. Open: no in-app
"open log folder" shortcut yet.
7. Stable row identity 🟡
Rows are keyed by the pandas index, which is renumbered after every delete. An
in-flight geocode callback for one row can land on another row if the user
deletes something in between. _apply_edit_geocode guards against a deleted
index but not a reused one.
Suggested change: add an immutable id column (uuid or incrementing int)
written to the CSV, and match callbacks on that instead of the positional index.
8. Duplicate handling 🟡
_on_save_all detects likely duplicates but saves them anyway with only a
transient status-bar line the user will probably miss. Consider a modal
"Diese Einträge existieren schon – trotzdem speichern?" with a per-row choice,
or at least skip exact duplicates by default.
9. Offline map 🟢
karte.html loads Leaflet/jQuery/Bootstrap from CDNs and tiles from CartoDB, so
the map is blank without internet even though all the data is local. If offline
use matters, vendor the Leaflet JS/CSS into data/ and post-process the Folium
output to point at local files (tiles would still need caching or an offline
tile pack — larger effort).
10. Small code-quality items 🟢 — partly done
- ✅ Shared autocomplete helpers (
filter_locations,split_city_plz) extracted;EditDialogandAppboth use them. - ✅ Region list is now the
GEOCODE_REGIONSconstant; map centre/zoom areMAP_DEFAULT_CENTER/MAP_DEFAULT_ZOOM. - ✅
generate_mapnowfit_boundsto the markers. - ⬜ Add a
tests/folder with pytest coverage forDataStore(append / update / delete /find_duplicates) and the input helpers. A non-GUI smoke script exists (used during development) but is not in the repo — worth formalizing. - ⬜ Add
ruff/mypyconfig.
UI/UX review (2026-09-07)
Second review, focused on the interface. Items 1–12 implemented; 13 deferred.
| # | Change | Status |
|---|---|---|
| 1 | Larger base font + taller Treeview rows (UI_FONT_SIZE) |
✅ |
| 2 | Colour-coded status bar + 8 s auto-clear to a neutral state | ✅ |
| 3 | "Warteschlange leeren" asks for confirmation; more form spacing | ✅ |
| 4 | ▲/▼ arrow on the active sort column in Tab 2 | ✅ |
| 5 | Dropped the now-redundant (JJJJ-MM-TT) hint |
✅ |
| 6 | Tab 2: single "Karte" column (✓ / red fehlt) instead of raw lat/lon |
✅ |
| 7 | Explicit "Auswahl entfernen" button + Delete key for queue rows | ✅ |
| 8 | Duplicate save shows a modal with the list, not a transient hint | ✅ |
| 9 | Persistent entry count (window title + Tab 2 hint line) | ✅ |
| 10 | "Karte öffnen" with no coordinates shows a dialog | ✅ |
| 11 | Remember window size & position (data/window.json) |
✅ |
| 12 | DRK-red header bar + runtime-drawn red-cross icon (data/icon.png) |
✅ |
| 13 | One-click "Hinzufügen & sofort speichern" | ⬜ deferred |
Full custom DRK-red theme (recolouring primary etc.) was not done — the
red header bar gives the branding without fighting ttkbootstrap's theme system.
Suggested order of remaining work
- Initial git commit (item 4), then decide on committing
orte.csv. - Pinned deps +
run.sh/.desktoplauncher in the repo (item 5). - Stable
idcolumn (item 7 of the first review). - Formalize tests (item 10); offline map (item 9) only if offline use becomes real.