Task_10: Queries auslagern

This commit is contained in:
Marco Schmid 2026-05-12 16:58:33 +02:00
parent 24a7051632
commit bddf5a30cf
3 changed files with 46 additions and 32 deletions

63
TASK.md
View File

@ -1,41 +1,46 @@
# Task 9 — PoiType als StrEnum
# Task 10 — Queries auslagern
## Rückblick Task 8: Python Package
## Rückblick Task 9: PoiType als StrEnum
Ihr habt das Projekt in ein Python-Package umgewandelt. Die wichtigsten Punkte:
Ihr habt den Magic String `"bergbahn"` durch eine `StrEnum` ersetzt. Die wichtigsten Punkte:
- **Modul vs. Package:** Ein einzelnes `.py`-File ist ein Modul. Ein Ordner mit
`__init__.py` ist ein Package — er kann mehrere Module bündeln und nach aussen
eine saubere API exponieren.
- **Relative Imports** (`from .models import POI`): Beziehen sich auf das eigene
Package, sind refactoring-sicher und machen klar, dass etwas intern ist.
Absolute Imports (`from overpass_project.models import POI`) funktionieren auch,
sind aber abhängig vom Paketnamen.
- **`from overpass import *`** ist problematisch, weil unklar ist, welche Namen
importiert werden — das kann zu Namenskollisionen führen und erschwert das Lesen
des Codes stark.
- **Magic Strings** sind rohe Strings, die eine bestimmte Bedeutung haben, aber nirgends
formal definiert oder geprüft werden. Tippfehler wie `"Bergbahn"` oder `"berg_bahn"`
fallen erst zur Laufzeit auf — oder gar nicht.
- **`PoiType("bergbahn")`** funktioniert bei `StrEnum` — Python findet den passenden
Member anhand des Wertes. Das ist nützlich, wenn Werte aus einer Config oder
Datenbank kommen.
- **Grenzen:** Enums sind statisch — neue POI-Typen erfordern eine Code-Änderung.
Für sehr dynamische Systeme (Typen kommen aus der DB) wäre eine andere Lösung nötig.
## Aufgabe
In `main.py` steht aktuell:
```python
poi_type = "bergbahn"
```
Das ist ein sogenannter **Magic String** — ein roher String, dem man nicht ansieht,
welche Werte gültig sind, und der nirgends geprüft wird.
In `main.py` steht aktuell die Overpass-Query als langer hardcodierter String direkt
im Code. Das hat mehrere Nachteile: Der String ist schwer lesbar, nicht wiederverwendbar,
und für jeden neuen POI-Typ muss `main.py` angefasst werden.
**Konkret:**
1. Füge in `models.py` eine `PoiType`-Klasse hinzu, die von `StrEnum` erbt,
mit den Werten `BERGBAHN` und `RESTAURANT`.
2. Ersetze in `main.py` den String `"bergbahn"` durch `PoiType.BERGBAHN`.
3. Passe den Import in `main.py` entsprechend an.
1. Lege einen Unterordner `queries/` im Package an.
2. Verschiebe die Bergbahn-Query in eine Datei `queries/bergbahn.overpassql`.
Der Platzhalter `{bbox}` bleibt erhalten — ergänze zusätzlich `{timeout}` und
`{maxsize}` als Platzhalter (statt Hardcoding im Query-String).
3. Schreibe in `fetcher.py` eine neue Funktion `load_query()`:
```python
def load_query(poi_type: PoiType, bbox: tuple, timeout: int, maxsize: int) -> str:
...
```
Sie soll die passende `.overpassql`-Datei laden und alle Platzhalter befüllen.
4. Passe `_fetch_overpass()` an: Sie bekommt nun die **fertige Query** (kein
`bbox`-Parameter mehr) und sendet sie direkt.
5. Passe `load_pois()` und `main.py` entsprechend an.
**Fragen zum Nachdenken:**
- Was ist der Unterschied zwischen `Enum` und `StrEnum`?
- Warum ist `PoiType.BERGBAHN` besser als der String `"bergbahn"`?
- Was passiert, wenn jemand `PoiType("bergbahn")` schreibt — funktioniert das?
- Wo könnte ein Enum an seine Grenzen stossen?
- Welche Vorteile hat es, Queries in eigenen Dateien zu speichern?
- Warum ist `Path(__file__).parent / "queries"` besser als ein relativer Pfad `"queries/"`?
- Was passiert, wenn die `.overpassql`-Datei für einen bestimmten `PoiType` fehlt —
wie sollte `load_query()` damit umgehen?
- Welches Design-Prinzip steckt hinter dieser Änderung?
(Stichwort: *Separation of Concerns*)

View File

@ -1,6 +1,6 @@
import logging
from .fetcher import load_pois, OverpassApiError
from .models import POI
from .models import POI, PoiType
logging.basicConfig(
level=logging.INFO,
@ -9,11 +9,13 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
poi_type = PoiType.BERGBAHN
BBOXEN = {
"davos": (46.72, 9.70, 46.92, 10.00),
"schweiz": (45.8, 5.9, 47.8, 10.5),
}
poi_type = "bergbahn"
QUERY = """
[out:json][timeout:2][maxsize:500000];
(

View File

@ -1,4 +1,6 @@
from dataclasses import dataclass, field
from enum import StrEnum
@dataclass
class POI:
@ -13,4 +15,9 @@ class POI:
# REMARK:
# Wann eine eigene Dataclass für tags?
# Nur wenn die tags strukturiert und vorhersehbar sind, was bei OSM-Daten nicht der Fall ist...
# Nur wenn die tags strukturiert und vorhersehbar sind, was bei OSM-Daten nicht der Fall ist...
class PoiType(StrEnum):
BERGBAHN = "bergbahn"
RESTAURANT = "restaurant"