22
33import logging
44from io import StringIO
5- from typing import Any , Dict , List , Optional
5+ from typing import Optional
66
77import pandas as pd
88import requests
1313
1414logger = logging .getLogger (__name__ )
1515
16+
1617class GermanyBerlinFetcher (base .RiverDataFetcher ):
1718 """Fetches river gauge data from Wasserportal Berlin.
1819
@@ -96,15 +97,15 @@ def get_metadata(self) -> pd.DataFrame:
9697 except Exception as e :
9798 logger .error (f"Failed to fetch metadata: { e } " )
9899 return pd .DataFrame (columns = keep_cols ).set_index (constants .GAUGE_ID )
99-
100+
100101 @staticmethod
101102 def get_available_variables () -> tuple [str , ...]:
102103 return (
103104 constants .STAGE_DAILY_MEAN ,
104105 constants .DISCHARGE_DAILY_MEAN ,
105106 constants .WATER_TEMPERATURE_DAILY_MEAN ,
106107 constants .STAGE_INSTANT ,
107- constants .DISCHARGE_INSTANT
108+ constants .DISCHARGE_INSTANT ,
108109 )
109110
110111 def _download_data (
@@ -117,24 +118,21 @@ def _download_data(
117118 """Downloads CSV data for a gauge and variable."""
118119 thema_map = {
119120 # Daily
120- constants .STAGE_DAILY_MEAN : ("ows" , "tw" ), # Wasserstand (cm)
121- constants .DISCHARGE_DAILY_MEAN : ("odf" , "tw" ), # Durchfluss (m³/s)
122- constants .WATER_TEMPERATURE_DAILY_MEAN : ("owt" , "tw" ), # Wassertemperatur (°C)
123-
121+ constants .STAGE_DAILY_MEAN : ("ows" , "tw" ), # Wasserstand (cm)
122+ constants .DISCHARGE_DAILY_MEAN : ("odf" , "tw" ), # Durchfluss (m³/s)
123+ constants .WATER_TEMPERATURE_DAILY_MEAN : ("owt" , "tw" ), # Wassertemperatur (°C)
124124 # Instantaneous
125125 constants .STAGE_INSTANT : ("ows" , "ew" ),
126126 constants .DISCHARGE_INSTANT : ("odf" , "ew" ),
127-
128127 }
129128
130129 if variable not in thema_map :
131130 raise ValueError (f"Unsupported variable: { variable } " )
132-
131+
133132 thema , frequency = thema_map [variable ]
134133 start_date_fmt = pd .to_datetime (start_date ).strftime ("%d.%m.%Y" )
135134 url = self .BASE_URL .format (id = gauge_id , thema = thema , frequency = frequency , start_date = start_date_fmt )
136135
137-
138136 logger .info (f"Fetching { variable } for { gauge_id } from { url } " )
139137 r = requests .get (url , timeout = 20 )
140138 r .raise_for_status ()
@@ -150,15 +148,17 @@ def _download_data(
150148 except Exception as e :
151149 logger .error (f"Error parsing CSV for { gauge_id } : { e } " )
152150 return pd .DataFrame ()
153-
151+
154152 def _parse_data (self , gauge_id : str , raw_data : pd .DataFrame , variable : str ) -> pd .DataFrame :
155153 """Parses Wasserportal CSV to standardized DataFrame."""
156154 if raw_data .empty :
157155 return pd .DataFrame (columns = [constants .TIME_INDEX , variable ])
158156
159157 raw_data .columns = [c .strip ().lower () for c in raw_data .columns ]
160158 time_col = next ((c for c in raw_data .columns if "datum" in c or "zeit" in c ), raw_data .columns [0 ])
161- val_col = next ((c for c in raw_data .columns if c not in [time_col ] and raw_data [c ].dtype != "O" ), raw_data .columns [1 ])
159+ val_col = next (
160+ (c for c in raw_data .columns if c not in [time_col ] and raw_data [c ].dtype != "O" ), raw_data .columns [1 ]
161+ )
162162
163163 raw_data [constants .TIME_INDEX ] = pd .to_datetime (raw_data [time_col ], dayfirst = True , errors = "coerce" )
164164 raw_data [variable ] = pd .to_numeric (raw_data [val_col ], errors = "coerce" )
@@ -173,7 +173,7 @@ def _parse_data(self, gauge_id: str, raw_data: pd.DataFrame, variable: str) -> p
173173 .set_index (constants .TIME_INDEX )
174174 )
175175 return df
176-
176+
177177 def get_data (
178178 self ,
179179 gauge_id : str ,
0 commit comments