Add install.sh / run.sh / bundled icon; fully pin requirements

- install.sh: one-time Linux Mint setup (apt, .venv, requirements, .desktop
  entry with absolute paths). Re-runnable.
- run.sh: launches the app from .venv (called by the menu entry).
- icon.png: bundled 256px red-cross launcher/window icon; app prefers it over
  the runtime-drawn fallback (data/icon.png).
- requirements.txt: fully pinned incl. transitive deps, tested on Python 3.12;
  numpy held at 2.2.6 so it installs on Python 3.10 too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-07 22:16:52 +02:00
parent 1bed723363
commit 08232bda9b
10 changed files with 173 additions and 129 deletions
+8 -4
View File
@@ -40,7 +40,8 @@ CSV_PATH = DATA_DIR / "orte.csv"
MAP_PATH = DATA_DIR / "karte.html" MAP_PATH = DATA_DIR / "karte.html"
BACKUP_DIR = DATA_DIR / "backups" BACKUP_DIR = DATA_DIR / "backups"
LOG_PATH = DATA_DIR / "app.log" LOG_PATH = DATA_DIR / "app.log"
ICON_PATH = DATA_DIR / "icon.png" ICON_PATH = BASE_DIR / "icon.png" # mitgeliefert (für Fenster + Startmenü)
ICON_FALLBACK_PATH = DATA_DIR / "icon.png" # zur Laufzeit gezeichnet, falls obiges fehlt
WINDOW_STATE_PATH = DATA_DIR / "window.json" WINDOW_STATE_PATH = DATA_DIR / "window.json"
CSV_COLUMNS = ["date", "city", "postal_code", "lat", "lon"] CSV_COLUMNS = ["date", "city", "postal_code", "lat", "lon"]
@@ -735,8 +736,12 @@ class App(ttk.Window):
) )
def _install_icon(self) -> None: def _install_icon(self) -> None:
"""Zeichnet ein einfaches rotes Kreuz als Fenster-Icon (ohne Extra-Abhängigkeit).""" """Setzt das Fenster-Icon: mitgelieferte icon.png, sonst ein gezeichnetes Kreuz."""
try: try:
if ICON_PATH.exists():
self._icon_img = tk.PhotoImage(file=str(ICON_PATH))
self.iconphoto(True, self._icon_img)
return
size = 64 size = 64
img = tk.PhotoImage(width=size, height=size) img = tk.PhotoImage(width=size, height=size)
img.put("white", to=(0, 0, size, size)) img.put("white", to=(0, 0, size, size))
@@ -745,9 +750,8 @@ class App(ttk.Window):
img.put(DRK_RED, to=(7, arm, size - 7, size - arm)) img.put(DRK_RED, to=(7, arm, size - 7, size - arm))
self.iconphoto(True, img) self.iconphoto(True, img)
self._icon_img = img # Referenz halten self._icon_img = img # Referenz halten
if not ICON_PATH.exists():
try: try:
img.write(str(ICON_PATH), format="png") img.write(str(ICON_FALLBACK_PATH), format="png")
except tk.TclError: except tk.TclError:
pass # Tk < 8.6 kann kein PNG schreiben nicht kritisch pass # Tk < 8.6 kann kein PNG schreiben nicht kritisch
except tk.TclError: except tk.TclError:
+1 -1
View File
@@ -16,7 +16,7 @@ code) and plots them on an interactive map.
| [overview.md](overview.md) | What the app does, feature by feature | | [overview.md](overview.md) | What the app does, feature by feature |
| [architecture.md](architecture.md) | Code structure, threading model, data flow | | [architecture.md](architecture.md) | Code structure, threading model, data flow |
| [data-model.md](data-model.md) | The `orte.csv` schema and how it is read/written | | [data-model.md](data-model.md) | The `orte.csv` schema and how it is read/written |
| [setup.md](setup.md) | Install & run on a fresh Linux Mint machine, plus a desktop launcher | | [setup.md](setup.md) | `git clone` + `./install.sh` on Linux Mint; how updates work |
| [improvements.md](improvements.md) | Prioritized review findings and suggested changes | | [improvements.md](improvements.md) | Prioritized review findings and suggested changes |
| [changelog.md](changelog.md) | What has changed, newest first | | [changelog.md](changelog.md) | What has changed, newest first |
| [dev-notes.md](dev-notes.md) | Assumptions that turned out wrong — read before editing | | [dev-notes.md](dev-notes.md) | Assumptions that turned out wrong — read before editing |
+10
View File
@@ -2,6 +2,16 @@
Notable changes to the app. Newest first. Notable changes to the app. Newest first.
## 2026-09-07 — one-command install + launcher
- `install.sh` one-time Linux Mint setup: apt packages, `.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 ## 2026-09-07 — built-in self-update
New **Hilfe** menu: New **Hilfe** menu:
+4 -3
View File
@@ -66,7 +66,8 @@ generation, geocoding failures, and uncaught exceptions.
and restored on start. Git-ignored, per-machine. Safe to delete (window opens at and restored on start. Git-ignored, per-machine. Safe to delete (window opens at
its default size). its default size).
## `data/icon.png` ## `icon.png` (repo root) and `data/icon.png`
The red-cross window icon, drawn at first run and written here so the `.desktop` `icon.png` in the repo root is the **bundled** launcher/window icon (committed).
launcher can point at it. Git-ignored (regenerated). Delete to force a redraw. `data/icon.png` is only a runtime-drawn fallback the app writes if the bundled
one is missing — git-ignored.
+13 -18
View File
@@ -127,20 +127,14 @@ example file.
Gives you history, a rollback path, and a clean way to push updates to the Gives you history, a rollback path, and a clean way to push updates to the
laptop (`git pull`). laptop (`git pull`).
## 5. Packaging & distribution 🟡 ## 5. Packaging & distribution 🟡 — mostly done
Today install is: `apt install python3-tk`, create a venv, `pip install`. That's -`requirements.txt` fully pinned (direct + transitive), tested on 3.12.
a one-time terminal session, which is acceptable but fragile (loose version -`run.sh` + `install.sh` (generates the `.desktop` entry) shipped in the repo.
pins mean a future `pip install` could pull an incompatible pandas/folium). - ✅ Bundled `icon.png`.
- ⬜ Optional: a [PyInstaller](https://pyinstaller.org/) one-file bundle would
**Suggested changes** drop the apt/venv step entirely, at the cost of building per release. Not
needed while `install.sh` works.
- Pin all four dependencies to exact versions and regenerate deliberately.
- Ship the `run.sh` + `.desktop` launcher from [setup.md](setup.md) in the repo.
- Optional: build a [PyInstaller](https://pyinstaller.org/) 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 ## 6. Diagnostics / logging 🟡 — ✅ done
@@ -223,8 +217,9 @@ Open follow-ups: no rollback if a pushed update is broken (mitigated by
## Suggested order of remaining work ## Suggested order of remaining work
1. Initial git commit (item 4), then decide on committing `orte.csv`. 1. Deploy to the laptop: `git clone` + `./install.sh` (see [setup.md](setup.md)).
2. **Set up the git remote** the updater pulls from, and pin all deps (item 5). 2. Stable `id` column (item 7 of the first review).
3. `run.sh` + `.desktop` launcher in the repo (item 5). 3. Formalize tests (item 10) — a non-GUI `tests/` suite already exists in
4. Stable `id` column (item 7 of the first review). spirit (dev smoke scripts); move it into the repo.
5. Formalize tests (item 10); offline map (item 9) only if offline use becomes real. 4. Offline map (item 9) only if offline use becomes real.
5. Optional PyInstaller bundle (item 5).
+52 -97
View File
@@ -1,116 +1,63 @@
# Setup & running # Setup & running
Target machine: **Linux Mint laptop**, single non-technical user. The goal is Target machine: **Linux Mint laptop**, single non-technical user. Day-to-day use
that day-to-day use is a **double-click**, with the terminal only needed once is a **double-click from the menu**; the terminal is needed once, for install.
during install.
## 1. System packages ## Quick install (Linux Mint)
Tkinter is not bundled with the system Python on Mint and must be installed
separately. `git` is needed for the in-app updater:
```bash
sudo apt update
sudo apt install python3-tk python3-venv python3-pip git
```
## 2. Get the code
**Clone it** (don't download a zip) the in-app "Nach Update suchen" only works
from a git clone:
```bash ```bash
mkdir -p ~/Apps && cd ~/Apps mkdir -p ~/Apps && cd ~/Apps
git clone <repo-url> DRK_Blutspende_Orte git clone https://git.plutodev.de/Paddy/drk-blutspende-orte DRK_Blutspende_Orte
cd DRK_Blutspende_Orte
./install.sh
``` ```
Put it somewhere stable, e.g. `~/Apps/DRK_Blutspende_Orte`. `install.sh` does everything:
## 3. Create a virtual environment and install dependencies 1. `sudo apt install python3-tk python3-venv python3-pip git` (asks for the
password),
2. creates `.venv` and installs the pinned `requirements.txt`,
3. writes `~/.local/share/applications/drk-blutspende-orte.desktop` with the
correct absolute paths and the bundled `icon.png`.
After it finishes, **"DRK Blutspende Arbeitsorte"** is in the application menu
(log out/in once if it doesn't show immediately). First manual test: `./run.sh`.
> Clone it — don't download a zip. The in-app updater and `run.sh` both rely on
> it being a real git checkout.
## What the pieces are
| File | Purpose |
|------|---------|
| `install.sh` | one-time setup (above). Safe to re-run. |
| `run.sh` | launches the app from `.venv`; the `.desktop` entry calls this. |
| `icon.png` | bundled launcher/window icon (red cross). Replace with the real DRK logo if wanted. |
| `requirements.txt` | fully pinned, tested on Python 3.12. |
## Running manually
```bash ```bash
cd ~/Apps/DRK_Blutspende_Orte cd ~/Apps/DRK_Blutspende_Orte
python3 -m venv .venv ./run.sh # or: .venv/bin/python app.py
.venv/bin/pip install -r requirements.txt
``` ```
`requirements.txt`: `data/orte.csv` is created automatically on first run if missing.
```
ttkbootstrap==1.10.1
geopy>=2.4.1
folium>=0.17.0
pandas>=2.2.0
```
> Only `ttkbootstrap` is pinned exactly. For a machine you hand to someone else,
> consider pinning all four (see [improvements.md](improvements.md#5-packaging--distribution)).
## 4. Run it
```bash
.venv/bin/python app.py
```
The window should open. `data/orte.csv` is created automatically on first run if
it is missing.
## 5. Make it a double-click launcher
### Launcher script
Create `run.sh` in the project root:
```bash
#!/usr/bin/env bash
cd "$(dirname "$0")"
exec .venv/bin/python app.py
```
```bash
chmod +x run.sh
```
### Desktop entry
Create `~/.local/share/applications/drk-blutspende-orte.desktop`:
```ini
[Desktop Entry]
Type=Application
Name=DRK Blutspende Arbeitsorte
Comment=Einsatzorte protokollieren und auf der Karte anzeigen
Exec=/home/USER/Apps/DRK_Blutspende_Orte/run.sh
Icon=/home/USER/Apps/DRK_Blutspende_Orte/data/icon.png
Terminal=false
Categories=Utility;
```
Replace `USER` with the real username. The app writes a simple red-cross
`data/icon.png` on first run; drop in the real DRK logo at that path if you have
one. The entry then shows up in the Mint menu and can be pinned to the panel or
the desktop.
## Python / Tk version ## Python / Tk version
Python 3.9+ is fine (the code uses `from __future__ import annotations`). The Python 3.103.12 (Mint's `python3`). `ttkbootstrap` needs **Tk 8.6+**, which
system Python on current Mint releases is well above that. Mint's `python3-tk` provides. The macOS CommandLineTools Python 3.9 ships Tk 8.5
and can't run the GUI — for dev on macOS use a Homebrew Python, see
`ttkbootstrap` needs **Tk 8.6 or newer**. Linux Mint's `python3-tk` provides [dev-notes.md](dev-notes.md).
that. The macOS CommandLineTools Python used during development ships Tk 8.5 and
**cannot run the GUI** — see [dev-notes.md](dev-notes.md).
## Updating ## Updating
**From inside the app:** menu **Hilfe ▸ Nach Update suchen**. The app also **From inside the app:** menu **Hilfe ▸ Nach Update suchen**. It also checks
checks quietly on start and offers the update if there is one. It fast-forwards quietly on start and offers the update if there is one. It fast-forwards to the
to the server version, reinstalls dependencies if `requirements.txt` changed, server version, reinstalls dependencies if `requirements.txt` changed, and
and restarts itself. `data/` is never touched. restarts itself. `data/` is never touched. The updater only fast-forwards — if
the local copy was hand-edited it refuses and says to get in touch.
For this to work the app must run from a **git clone** (step 2) whose `.venv`
was made with the same Python it runs on, and `git` must be installed. The
updater only ever fast-forwards if the local copy has been changed by hand it
refuses and says to get in touch.
**Manually** (equivalent): **Manually** (equivalent):
@@ -120,8 +67,16 @@ git pull --ff-only
.venv/bin/pip install -r requirements.txt .venv/bin/pip install -r requirements.txt
``` ```
## Data location Publishing an update, from the dev machine: `git push` to
`https://git.plutodev.de/Paddy/drk-blutspende-orte` (branch `main`).
All state is in `data/orte.csv` next to `app.py`. To back up the app, copy that ## Data location & backup
one file (plus `data/backups/` if you want the history). To move to a new
laptop, clone the repo again and redo steps 1 & 3; copy `data/orte.csv` across. All state is in `data/` next to `app.py`:
- `orte.csv` — the data (git-ignored).
- `backups/` — automatic timestamped copies, newest 20 kept.
- `app.log`, `window.json`, `icon.png` (runtime fallback) — git-ignored.
To back up: copy `data/orte.csv` (and `data/backups/` for history). To move to a
new laptop: clone the repo, run `./install.sh`, copy `data/orte.csv` across.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Executable
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Einmalige Einrichtung auf einem Linux-Mint-Rechner:
# - benötigte System-Pakete
# - virtuelle Python-Umgebung + Abhängigkeiten
# - Eintrag im Startmenü
#
# Aufruf: ./install.sh
set -euo pipefail
cd "$(dirname "$(readlink -f "$0")")"
APPDIR="$(pwd)"
echo "==> System-Pakete (fragt nach dem Passwort)…"
sudo apt update
sudo apt install -y python3-tk python3-venv python3-pip git
echo
echo "==> Virtuelle Umgebung unter .venv …"
python3 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -r requirements.txt
echo
echo "==> Startmenü-Eintrag …"
DESKTOP_DIR="$HOME/.local/share/applications"
mkdir -p "$DESKTOP_DIR"
cat > "$DESKTOP_DIR/drk-blutspende-orte.desktop" <<EOF
[Desktop Entry]
Type=Application
Name=DRK Blutspende Arbeitsorte
Comment=Einsatzorte protokollieren und auf der Karte anzeigen
Exec=$APPDIR/run.sh
Icon=$APPDIR/icon.png
Terminal=false
Categories=Utility;
StartupWMClass=DRK Blutspende Arbeitsorte
EOF
chmod +x "$APPDIR/run.sh"
update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
echo
echo "Fertig."
echo "Die App ist jetzt im Menü als \"DRK Blutspende Arbeitsorte\" zu finden"
echo "(ggf. einmal ab- und wieder anmelden, damit sie auftaucht)."
echo "Direkt testen: ./run.sh"
+27 -3
View File
@@ -1,4 +1,28 @@
# Vollständig gepinnt für reproduzierbare Installationen (Linux Mint, Python 3.103.12).
# Getestet mit Python 3.12. Nach Änderungen: in einem frischen venv
# pip install -r requirements.txt && python app.py
# und die Versionen mit pip freeze hier aktualisieren.
# direkte Abhängigkeiten
ttkbootstrap==1.10.1 ttkbootstrap==1.10.1
geopy>=2.4.1 geopy==2.4.1
folium>=0.17.0 folium==0.19.5
pandas>=2.2.0 pandas==2.2.3
# transitiv (mitgepinnt, damit auf allen Rechnern dasselbe ankommt)
numpy==2.2.6
branca==0.8.2
Jinja2==3.1.6
MarkupSafe==3.0.3
xyzservices==2026.9.1
requests==2.34.2
certifi==2026.7.22
charset-normalizer==3.5.1
idna==3.19
urllib3==2.7.0
geographiclib==2.1
python-dateutil==2.9.0.post0
pytz==2026.3.post1
tzdata==2026.3
six==1.17.0
pillow==12.3.0
Executable
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Startet die App aus ihrer virtuellen Umgebung.
set -euo pipefail
cd "$(dirname "$(readlink -f "$0")")"
if [ ! -x .venv/bin/python ]; then
echo "Die App ist noch nicht eingerichtet. Bitte einmal ./install.sh ausführen." >&2
exit 1
fi
exec .venv/bin/python app.py