Skip to content

Commit

Permalink
old weather provider and base reverted to align with main
Browse files Browse the repository at this point in the history
  • Loading branch information
orion-junkins committed Feb 14, 2024
1 parent bebbb2b commit 5e8a517
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import time
from typing import List, Optional
import pandas as pd

from pandas import DataFrame
import pytz

Expand All @@ -14,8 +14,8 @@
from rlf.forecasting.data_fetching_utilities.weather_provider.base_weather_provider import (
BaseWeatherProvider
)
from rlf.forecasting.data_fetching_utilities.weather_provider.open_meteo.ecmwf_adapter import (
OpenMeteoECMWFAdapter
from rlf.forecasting.data_fetching_utilities.weather_provider.open_meteo.open_meteo_adapter import (
OpenMeteoAdapter
)
from rlf.forecasting.data_fetching_utilities.weather_provider.weather_datum import (
WeatherDatum
Expand All @@ -25,15 +25,15 @@
DEFAULT_START_DATE = "2022-01-01"
DEFAULT_END_DATE = datetime.now().strftime("%Y-%m-%d")

RESPONSE_TOLERANCE = 0.25
RESPONSE_TOLERANCE = 0.05


class APIWeatherProvider(BaseWeatherProvider):
"""Provides a historical of forecasted weather for a given location and time period."""

def __init__(self,
coordinates: List[Coordinate],
api_adapter: BaseAPIAdapter = OpenMeteoECMWFAdapter()) -> None:
api_adapter: BaseAPIAdapter = OpenMeteoAdapter()) -> None:
"""Create an APIWeatherProvider for the given list of coordinates.
Args:
Expand All @@ -43,29 +43,31 @@ def __init__(self,
self.coordinates = coordinates
self.api_adapter = api_adapter



def _build_hourly_parameters_from_response(self, hourly, ...) -> DataFrame:
# This is the function that needs to be figure out
def _build_hourly_parameters_from_response(self, hourly_parameters_response: dict, tz: str) -> DataFrame:
index_parameter = self.api_adapter.get_index_parameter()
df = DataFrame(hourly_parameters_response)
df.index = df[index_parameter].map(lambda x: datetime.fromisoformat(x).replace(tzinfo=pytz.timezone(tz)).astimezone(pytz.timezone("UTC")))
df.drop(columns=[index_parameter], inplace=True)
return df
def build_datum_from_response(self, response, coordinate: Coordinate, precision = 5) -> WeatherDatum:

def build_datum_from_response(self, response: Response, coordinate: Coordinate, precision: int = 5) -> WeatherDatum:
"""Construct a WeatherDatum from a Response.
Args:
response (WeatherApiResponse): The response from the API.
response (Response): The Response to draw data from.
coordinate (Coordinate): The coordinate that is requested by the user.
precision (int, optional): The number of decimal places to round the response coordinates to. Defaults to 5.
precision (int): The precision to round the response coordinates to. Defaults to 5 decimal places.
Returns:
WeatherDatum: The constructed WeatherDatum instance.
"""
# Issue some query to get a response from the api
assert response.data is not None

requested_lon = coordinate.lon
requested_lat = coordinate.lat

response_lon = response.Longitude()
response_lat = response.Latitude()
response_lon = response.data.get("longitude", None)
response_lat = response.data.get("latitude", None)

response_rounded_lon = round(response_lon, precision)
response_rounded_lat = round(response_lat, precision)
Expand All @@ -81,18 +83,31 @@ def build_datum_from_response(self, response, coordinate: Coordinate, precision
"To change the tolerance, change the RESPONSE_TOLERANCE constant in the APIWeatherProvider class. "
"To change the rounding precision, change the precision argument in the build_datum_from_response method.")

elif abs(difference_rounded_lon) <= RESPONSE_TOLERANCE or abs(difference_rounded_lat) <= RESPONSE_TOLERANCE:
logging.warning(
"The API responded with a location within the requested location tolerance, but not equal. "
f"The requested location is ({requested_lon}, {requested_lat}) vs. the response location ({response_lon}, {response_lat}). "
f"The difference in longitude is {difference_rounded_lon} and the difference in latitude is {difference_rounded_lat}. "
"To change the tolerance, change the RESPONSE_TOLERANCE constant in the APIWeatherProvider class. "
"To change the rounding precision, change the precision argument in the build_datum_from_response method.")
else:
pass

datum = WeatherDatum(
longitude=requested_lon,
latitude=requested_lat,
api_response_longitude=response_lon,
api_response_latitude=response_lat,
elevation=response.Elevation(),
utc_offset_seconds=response.Hourly(),
timezone=response.Timezone(),
hourly_units=None,
hourly_parameters=self._build_hourly_parameters_from_response(response.Hourly())

)
elevation=response.data.get(
"elevation", None),
utc_offset_seconds=response.data.get(
"utc_offset_seconds", None),
timezone=response.data.get(
"timezone", None),
hourly_units=response.data.get(
"hourly_units", None),
hourly_parameters=self._build_hourly_parameters_from_response(
response.data.get("hourly", None), response.data["timezone"]))

return datum

Expand All @@ -117,6 +132,8 @@ def fetch_historical_datum(self,

datum = self.build_datum_from_response(response, coordinate)

datum.hourly_parameters.columns = self._remap_historical_parameters_from_adapter(datum.hourly_parameters.columns)

return datum

def fetch_historical(self,
Expand All @@ -137,6 +154,9 @@ def fetch_historical(self,
"""
datums = {}

if columns:
columns = self._remap_historical_parameters_to_adapter(columns)

for coordinate in self.coordinates:
datum = self.fetch_historical_datum(coordinate=coordinate, start_date=start_date, end_date=end_date, columns=columns)
coord = Coordinate(datum.longitude, datum.latitude)
Expand All @@ -159,6 +179,8 @@ def fetch_current_datum(self, coordinate: Coordinate, columns: Optional[List[str

datum = self.build_datum_from_response(response, coordinate)

datum.hourly_parameters.columns = self._remap_current_parameters_from_adapter(datum.hourly_parameters.columns)

return datum

def fetch_current(self, columns: Optional[List[str]] = None, sleep_duration: float = 0.0) -> List[WeatherDatum]:
Expand All @@ -173,8 +195,11 @@ def fetch_current(self, columns: Optional[List[str]] = None, sleep_duration: flo
"""
datums = []

if columns:
columns = self._remap_current_parameters_to_adapter(columns)

for coordinate in self.coordinates:
datum = self.fetch_current_datum(coordinate=coordinate, columns=columns)
datums.append(datum)
time.sleep(sleep_duration)
return datums
return datums
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@
DEFAULT_START_DATE = "2022-01-01"
DEFAULT_END_DATE = datetime.now().strftime("%Y-%m-%d")

current_parameter_remaps_from_adapter = {
"soil_temperature_0_to_10cm": "soil_temperature_level_1",
"soil_temperature_10_to_40cm": "soil_temperature_level_2",
"soil_temperature_40_to_100cm": "soil_temperature_level_3",
"soil_temperature_100_to_200cm": "soil_temperature_level_4",
"soil_moisture_0_to_10cm": "soil_moisture_level_1",
"soil_moisture_10_to_40cm": "soil_moisture_level_2",
"soil_moisture_40_to_100cm": "soil_moisture_level_3",
"soil_moisture_100_to_200cm": "soil_moisture_level_4",
}

current_parameter_remaps_to_adapter = {value: key for key, value in current_parameter_remaps_from_adapter.items()}

historical_parameter_remaps_from_adapter = {
"soil_temperature_0_to_7cm": "soil_temperature_level_1",
"soil_temperature_7_to_28cm": "soil_temperature_level_2",
"soil_temperature_28_to_100cm": "soil_temperature_level_3",
"soil_temperature_100_to_255cm": "soil_temperature_level_4",
"soil_moisture_0_to_7cm": "soil_moisture_level_1",
"soil_moisture_7_to_28cm": "soil_moisture_level_2",
"soil_moisture_28_to_100cm": "soil_moisture_level_3",
"soil_moisture_100_to_255cm": "soil_moisture_level_4"
}

historical_parameter_remaps_to_adapter = {value: key for key, value in historical_parameter_remaps_from_adapter.items()}


class BaseWeatherProvider(ABC):
"""Provides historical and forecasted weather for a given set of locations. WeatherProviders exist at a single moment in time. Relative to that moment, they provide access to current (recent + forecasted) weather data as well as historical (beginning of collection to some point in the past) weather data."""
Expand Down Expand Up @@ -55,3 +81,47 @@ def fetch_current(self,
list[WeatherDatum]: A list of WeatherDatums containing the weather data about the location.
"""
pass

def _remap_current_parameters_to_adapter(self, params: List[str]) -> List[str]:
"""Remap the parameter names for current data from the consistent names to the adapter's actual names.
Args:
params (List[str]): Initial list of params to remap.
Returns:
List[str]: New list with param names either remapped or left alone (maintains order).
"""
return [current_parameter_remaps_to_adapter.get(param, param) for param in params]

def _remap_current_parameters_from_adapter(self, params: List[str]) -> List[str]:
"""Remap the parameter names for current data from the adapter's actual name to the consistent names.
Args:
params (List[str]): Initial list of params to remap.
Returns:
List[str]: New list with param names either remapped or left alone (maintains order).
"""
return [current_parameter_remaps_from_adapter.get(param, param) for param in params]

def _remap_historical_parameters_to_adapter(self, params: List[str]) -> List[str]:
"""Remap the parameter names for historical data from the consistent names to the adapter's actual names.
Args:
params (List[str]): Initial list of params to remap.
Returns:
List[str]: New list with param names either remapped or left alone (maintains order).
"""
return [historical_parameter_remaps_to_adapter.get(param, param) for param in params]

def _remap_historical_parameters_from_adapter(self, params: List[str]) -> List[str]:
"""Remap the parameter names for historical data from the adapter's actual names to the consistent names.
Args:
params (List[str]): Initial list of params to remap.
Returns:
List[str]: New list with param names either remapped or left alone (maintains order).
"""
return [historical_parameter_remaps_from_adapter.get(param, param) for param in params]

0 comments on commit 5e8a517

Please sign in to comment.