diff --git a/README.md b/README.md index 461ec20..8944f57 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ client.query_offered_capacity(country_code_from, country_code_to, start, end, co client.query_contracted_reserve_prices(country_code, start, end, type_marketagreement_type, psr_type=None) client.query_contracted_reserve_amount(country_code, start, end, type_marketagreement_type, psr_type=None) client.query_procured_balancing_capacity(country_code, start, end, process_type, type_marketagreement_type=None) +client.query_aggregate_water_reservoirs_and_hydro_storage(country_code, start, end) # methods that return ZIP (bytes) client.query_imbalance_prices(country_code, start, end, psr_type=None) @@ -109,6 +110,7 @@ client.query_net_transfer_capacity_monthahead(country_code_from, country_code_to client.query_net_transfer_capacity_yearahead(country_code_from, country_code_to, start, end) client.query_intraday_offered_capacity(country_code_from, country_code_to, start, end,implicit=True) client.query_offered_capacity(country_code_from, country_code_to, start, end, contract_marketagreement_type, implicit=True) +client.query_aggregate_water_reservoirs_and_hydro_storage(country_code, start, end) # methods that return Pandas DataFrames client.query_load(country_code, start=start,end=end) diff --git a/entsoe/decorators.py b/entsoe/decorators.py index 78c017b..dec270d 100644 --- a/entsoe/decorators.py +++ b/entsoe/decorators.py @@ -59,6 +59,7 @@ def decorator(func): def documents_wrapper(*args, **kwargs): frames = [] for offset in range(0, 4800 + n, n): + print(offset) try: frame = func(*args, offset=offset, **kwargs) frames.append(frame) diff --git a/entsoe/entsoe.py b/entsoe/entsoe.py index 49013f3..138f5ef 100644 --- a/entsoe/entsoe.py +++ b/entsoe/entsoe.py @@ -13,11 +13,12 @@ from .parsers import parse_prices, parse_loads, parse_generation, \ parse_installed_capacity_per_plant, parse_crossborder_flows, \ parse_unavailabilities, parse_contracted_reserve, parse_imbalance_prices_zip, \ - parse_imbalance_volumes_zip, parse_netpositions, parse_procured_balancing_capacity + parse_imbalance_volumes_zip, parse_netpositions, parse_procured_balancing_capacity, \ + parse_water_hydro from .decorators import retry, paginated, year_limited, day_limited, documents_limited __title__ = "entsoe-py" -__version__ = "0.5.3" +__version__ = "0.5.4" __author__ = "EnergieID.be, Frank Boerman" __license__ = "MIT" @@ -408,6 +409,30 @@ def query_installed_generation_capacity_per_unit( response = self._base_request(params=params, start=start, end=end) return response.text + def query_aggregate_water_reservoirs_and_hydro_storage(self, country_code: Union[Area, str], start: pd.Timestamp, + end: pd.Timestamp) -> str: + """ + Parameters + ---------- + country_code : Area|str + start : pd.Timestamp + end : pd.Timestamp + offset : int + offset for querying more than 100 documents + + Returns + ------- + str + """ + area = lookup_area(country_code) + params = { + 'documentType': 'A72', + 'processType': 'A16', + 'in_Domain': area.code + } + response = self._base_request(params=params, start=start, end=end) + return response.text + def query_crossborder_flows( self, country_code_from: Union[Area, str], country_code_to: Union[Area, str], start: pd.Timestamp, @@ -1246,6 +1271,21 @@ def query_installed_generation_capacity_per_unit( df = parse_installed_capacity_per_plant(text) return df + @year_limited + @paginated + def query_aggregate_water_reservoirs_and_hydro_storage(self, country_code: Union[Area, str], start: pd.Timestamp, + end: pd.Timestamp) -> pd.DataFrame: + area = lookup_area(country_code) + text = super( + EntsoePandasClient, + self).query_aggregate_water_reservoirs_and_hydro_storage( + country_code=area, start=start, end=end) + + df = parse_water_hydro(text, area.tz) + + return df + + @year_limited def query_crossborder_flows( self, country_code_from: Union[Area, str], diff --git a/entsoe/parsers.py b/entsoe/parsers.py index 8cc09ae..67508da 100644 --- a/entsoe/parsers.py +++ b/entsoe/parsers.py @@ -220,6 +220,25 @@ def parse_installed_capacity_per_plant(xml_text): return df +def parse_water_hydro(xml_text, tz): + """ + Parameters + ---------- + xml_text : str + + Returns + ------- + pd.Series + """ + all_series = [] + for soup in _extract_timeseries(xml_text): + all_series.append(_parse_water_hydro_timeseries(soup, tz=tz)) + + series = pd.concat(all_series) + + return series + + def parse_crossborder_flows(xml_text): """ Parameters @@ -658,6 +677,35 @@ def _parse_generation_timeseries(soup, per_plant: bool = False, include_eic: boo return series +def _parse_water_hydro_timeseries(soup, tz): + """ + Parses timeseries for water reservoirs and hydro storage plants + + Parameters + ---------- + soup : bs4.element.tag + + Returns + ------- + pd.Series + """ + + positions = [] + quantities = [] + for point in soup.find_all('point'): + positions.append(int(point.find('position').text)) + quantity = point.find('quantity') + if quantity is None: + raise LookupError( + f'No quantity found in this point, it should have one: {point}') + quantities.append(float(quantity.text)) + series = pd.Series(index=positions, data=quantities) + series = series.sort_index() + series.index = _parse_datetimeindex(soup, tz) + + return series + + def _parse_installed_capacity_per_plant(soup): """ Parameters