From 310112297580f8e9dd3ab081a357a9d9dc8bc854 Mon Sep 17 00:00:00 2001 From: Marco Schmid Date: Thu, 23 Apr 2026 20:33:39 +0200 Subject: [PATCH] Task_5: logging, new module queries --- TASK.md | 24 ++++++++--- __pycache__/models.cpython-313.pyc | Bin 0 -> 647 bytes __pycache__/overpass.cpython-313.pyc | Bin 3655 -> 5880 bytes main.py | 13 +++--- models.py | 15 +++++++ overpass.py | 62 +++++++++++++++++++++++++-- 6 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 __pycache__/models.cpython-313.pyc create mode 100644 models.py diff --git a/TASK.md b/TASK.md index 6ac122f..27b6588 100644 --- a/TASK.md +++ b/TASK.md @@ -1,8 +1,20 @@ -# TASK 4: +# TASK 5: -Wir arbeiten nun intern direkt mit den Daten von Overpass. Macht das Sinn? Warum vielleicht nicht? +* Jetzt wäre doch ein guter Zeitpunkt um anstelle der print-Statements das Logging einzubauen -> verwendet das + Logging-Modul in Python und ersetzt die print-statements! + Hier haben wir einen weiteren Query für restaurants -> bildet ein neues Modul 'queries' und baut dort sowohl den + Bergbahn- als auch den Restaurant-Query ein: -* Versucht einen Adapter zu bauen, wir wollen intern mit einer eigenen Dataclass `POI` arbeiten. Wir bauen also dazu eine - Funktion `load_pois`, welche einerseits die Daten fetched und andererseits auch parsed. Den Fetching-Teil haben wir - bereits (`fetch_overpass`), den Pasing-Teil haben wir noch nicht. - Schreibt bitte eine eigene Dataclass`Poi` in welche die gefetchten Daten 'abgefüllt' werden können. \ No newline at end of file +``` + RESTAURANT_QUERY = """ + [out:json][timeout:5][maxsize:500000]; + ( + node["amenity"="restaurant"]({bbox}); + way["amenity"="restaurant"]({bbox}); + node["amenity"="cafe"]({bbox}); + way["amenity"="cafe"]({bbox}); + ); + out center body; + """ +``` + \ No newline at end of file diff --git a/__pycache__/models.cpython-313.pyc b/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..100127136ff22855a215acb5be25383a38c5bd77 GIT binary patch literal 647 zcmX|9&ubGw6n?Wm(#^)S^q^EdD8-@+)rFQG1o2`;6Cp}B-WG;&cQ!6;cH+!L$W6#W z|AF4U`sa9&Iu{E))l(%0J^E&oc3{5!-uu4ycr&w$ZZ`y6{|3LyGXijCgUjWw$YKx4 zF(i@Pn`RxG|X*S3v=ET^BcCd4QY_H9uwJ41Ju`SHVI@n zYDb}Q-@NXfyO=RfnHx{fCfv9c(+HxDh}p<_!KRg_1ucnt>U+X;q#-T-m{m(SbXGi zf)^a)fV@(VCNtT>$_DJY)egdEI1M`U7bjtN-aBcp&-)Gh+`REsf8X5wbok}o4{uj) zUDgdE&vdSyjw@*!HN!7^+xeF6o5)P?WO+GHe06~qi6sC literal 0 HcmV?d00001 diff --git a/__pycache__/overpass.cpython-313.pyc b/__pycache__/overpass.cpython-313.pyc index 069e044b139714a1a7120dc06ff496ef4a46968b..54e910528b1bbd31b7df6549f3e8fc409167a741 100644 GIT binary patch delta 2911 zcmZ`*O>7&-6`t8YE|)8c`lDn^kk_)~nsOwkwPabeWrx&HY{`~?)@)OVfUqH#A`@!K z&MxbSJ&-8S!fp%WDyr_NJIeAP9uZV&FZ!^+ z5vgg?17Z(|IU=ia)C2152&c91@O^|dFkfvWKDC{+MWq20 z^+ns%j&X*xM=^}rM{&&@1ufwv9O(Qv3_H+#0NX+~OHzgf9DC{Fxtp75>@B^8BUmi` z3cuSgfF7a53!h1i|1GjPkA}{feNX!&j*3rLJRei5PBc0c$_hqh5A(>6k!^6 z97F*ocMulLJbI8MmNKJTv80kS;>snBn7WeBncDKQZYWyL9KOpCAE=TxAOXg1T})8z z#mp~tlGO^PX?qs4bk$-ulQIMBOJ$oS+BK6NuP6#>h2D0jGg{nHBt0;0htKQ=QbgZM zdp7zXOOwUPZI6G|tay6MT(9G3i~)s4qjBN5`v-6o;-b{eJpG9Qzb+!2geeV-TPT_d9l2z-^`b?jyLj^e^}2{kA~#WsQ84@4o1`XPV08 zoK9ATX7prQCqbojT)K*d(kD`%=&E~K`dm72i1flKCbbTHrOok|bb-Jj)LZje>A%v_ zaT&>-pHAGE_~cA+swyDSw>naBb#Ht7%ll7kc5Wu`_m@wcE$=&5@y?dT*>60p#km(d z-6hL2TL_D2OYEHVv0Hs|_%OS5xSR549h{O zvy|Gb0!d-$;s+n(`7tHO=>I39 zlpF_=@14=pacXGj(m7>F2^rS43?!2TK*gHR548 zs&EfT5mjXmKaaOt+n=;{tWMp?KWXc_-E*^N?a=x_rR}Y4U)O5vG}sv^NHz1Z;r*qd7y$##4O+aHJ%xjHdRjvV8FJz7g3z>U2w-gk#56Zj2 zhS&=cu-xGico$%WI>5F;n6?q2W5z2v%D2X!WubGo=qp zwKSj95?0f?YdVPot{y!oe4rCEOLE2%4C%@xJ)K!vvW6vF)N8mkbAB)ZkO%6`+J{r| z2?t6xK9gqyL1iAGH3q7-pk=eV5qF}s7V~`VNx(Tj)#hkfwOw@o0>HBt@)R~uWa?Fh z9!V(q1$<@>2t>PxWN&fmNr(UT*v+xE;5zxLWAM7G%CPPW_=)WMbneF7XVUsurRT_3 z^4s6>sPjlwLb8t*8y@)-h)Yo1mfOmmgBxR?pS*kW3u}B+tsDuJ2g45!Jd8bzl-uXN zme2oHK2PHnl)pD=xx2G>oVhQBj`Cl!`$L2LmxDa;_x|o##svb;Boyc%G^_~?PFq;c zKr%F)G(ZkfiF^9vn4YCs&AALRK!v=O%cgakOQ%fBc{tb{O?|cUN@4DJPiV~B-thF5 z_rjsG^NW`w(^r1(JX>g4H*65X-%#r_ zwC5S}f75!RDDSjm>|R~jLC{r27Wb{istCG`E~ooqWRv~l_zsm+eVWu{aS3t>8pbh_E8~pYw`7CcTzi)t@a^KzMikJd#aq|7mM@N k9wh&$IQ5Sf|GKd`^0?*Q;>=S{x_zG^D)u);@d7ANoCPK^GK(|Fun(xhBAw6=lw^QLZDmwBJi`c8?I?}e zD%I@R8I{H}uu7O_$EiMk9bhR9Kn*sgQVq@%4(v0Pa^0-tEi3i~`C8Q~=*u6aUNJO%md zjx=|b54=KNqOq$)M^sGlyfKVve$$xad&advDdMN*ojVQ3^@{hFow`@4t@~uvt*vKO zmXyjVe$^ZpcBNH)VX%ZemAK`2%eNP6cO6z=a$Pngn!;Xi-|**VIi-V{eme1Z;>pw= z$sDxfJZH_En9jlbT)J>hX(?tQskD;EWqr~tlMk5|DQ87gJonh>NV None: for name, bbox in BBOXEN.items(): try: - result = fetch_overpass(overpass_query=QUERY, bbox=bbox) + pois: list[POI] = load_pois(overpass_query=QUERY, bbox=bbox) except OverpassApiError as exc: - print(f" Fehler : {exc}") + print(f"Fehler bei '{name}': {exc}") continue - elements = result.get("elements", []) - print(elements) + print(f"\n{name}: {len(pois)} POIs gefunden") + for poi in pois: + print(f" {poi.id}: ({poi.lat}, {poi.lon})") + if __name__ == "__main__": main() \ No newline at end of file diff --git a/models.py b/models.py new file mode 100644 index 0000000..1435a60 --- /dev/null +++ b/models.py @@ -0,0 +1,15 @@ +from dataclasses import dataclass, field + +@dataclass +class POI: + id: str + type: str + lat: float + lon: float + tags: dict = field(default_factory=dict) # weil mutable defaults in Dataclasses eine bekannte Python-Falle sind + # (alle Instanzen würden dasselbe Dict teilen...) + + +# REMARK: +# Wann eine eigene Dataclass für tags? +# Nur wenn die tags strukturiert und vorhersehbar sind, was bei OSM-Daten nicht der Fall ist... \ No newline at end of file diff --git a/overpass.py b/overpass.py index 803d0e2..e6892c1 100644 --- a/overpass.py +++ b/overpass.py @@ -1,15 +1,27 @@ import requests from pprint import pprint - +from models import POI OVERPASS_URL = "https://overpass-api.de/api/interpreter" +# REMARK: +# zwei Strategien: +# Fail-fast: Ein Fehler bricht alles ab → sinnvoll, wenn jedes Element kritisch ist +# Best-effort: Fehlerhafte Elemente überspringen, Rest verarbeiten → sinnvoll bei OSM-Daten, wo einzelne Einträge unvollständig sein können + + class OverpassApiError(Exception): pass -def fetch_overpass(overpass_query: str, bbox: tuple) -> dict: +def load_pois(overpass_query: str, bbox: tuple) -> list[POI]: + """Führt Fetch und Parse zusammen aus.""" + raw = _fetch_overpass(overpass_query=overpass_query, bbox=bbox) + return _parse_pois(raw) + + +def _fetch_overpass(overpass_query: str, bbox: tuple) -> dict: """ Fragt die Overpass API nach Bergbahnen in der angegebenen Bounding Box ab. Sendet einen HTTP-POST-Request an die Overpass API und gibt die geparste @@ -67,7 +79,49 @@ def fetch_overpass(overpass_query: str, bbox: tuple) -> dict: raise OverpassApiError("Overpass-API Timeout") from exc except requests.RequestException as exc: raise OverpassApiError("Overpass-API Request fehlgeschlagen") from exc - return response.json() + + data = response.json() + + # zusätzliche Fehlermöglichkeit -> Status ist zwar 200, aber Liste mit Ergebnissen ist leer... + if "remark" in data: + raise OverpassApiError(f"Overpass Query-Fehler: {data['remark']}") + + return data + + +def _parse_poi(data: dict) -> POI: + """ Wandelt ein einzelnes Overpass-Element in ein POI-Objekt um. + + :param data: dictionary mit Daten für ein POI-Objekt + :return: POI-Objekt + """ + try: + return POI( + id=data['id'], + type=data.get('type', ''), + lat=float(data.get("lat") or data["center"]["lat"]), + lon=float(data.get("lon") or data["center"]["lon"]), + tags=data.get('tags', {}), + ) + except KeyError as exc: + raise OverpassApiError("Feld in API - Antwort fehlt") from exc + except (TypeError, ValueError) as exc: + raise OverpassApiError("API - Antwort hat falsches Format ") from exc + + + +def _parse_pois(raw: dict) -> list[POI]: + """Extrahiert alle Elemente aus der API-Antwort und parst sie. + Fehlerhafte Elemente werden übersprungen und geloggt. + """ + pois = [] + for element in raw.get("elements", []): + try: + pois.append(_parse_poi(element)) + except OverpassApiError as exc: + print(f"POI übersprungen (id={element.get('id', '?')}): {exc}") + return pois + if __name__ == "__main__": @@ -86,5 +140,5 @@ if __name__ == "__main__": bbox = (46.72, 9.70, 46.92, 10.00) # bbox = (45.8, 5.9, 47.8, 10.5) - result = fetch_overpass(overpass_query=BERGBAHN_QUERY, bbox=bbox) + result = load_pois(overpass_query=BERGBAHN_QUERY, bbox=bbox) pprint(result) \ No newline at end of file