"""Taken from the fantastic Dark Sky library: https://github.com/ZeevG/python-forecast.io in October 2024. Updated in April 2025 using the Pirate Weather library: https://github.com/cloneofghosts/python-pirate-weather.""" import datetime import requests class UnicodeMixin: """Provide string representation for Python 2/3 compatibility.""" def __str__(self): """Return the unicode representation of the object for Python 2/3 compatibility.""" return self.__unicode__() """Models used in the Pirate Weather library.""" class Forecast(UnicodeMixin): """Represent the forecast data and provide methods to access weather blocks.""" def __init__(self, data, response, headers): """Initialize the Forecast with data, HTTP response, and headers.""" self.response = response self.http_headers = headers self.json = data self._alerts = [] for alertJSON in self.json.get("alerts", []): self._alerts.append(Alert(alertJSON)) def update(self): """Update the forecast data by making a new request to the same URL.""" r = requests.get(self.response.url) self.json = r.json() self.response = r def currently(self): """Return the current weather data block.""" return self._pirateweather_data("currently") def minutely(self): """Return the minutely weather data block.""" return self._pirateweather_data("minutely") def hourly(self): """Return the hourly weather data block.""" return self._pirateweather_data("hourly") def day_night(self): """Return the day_night weather data block.""" return self._pirateweather_data("day_night") def daily(self): """Return the daily weather data block.""" return self._pirateweather_data("daily") def flags(self): """Return the flags data block.""" return self._pirateweather_data("flags") def offset(self): """Return the time zone offset for the forecast location.""" return self.json["offset"] def alerts(self): """Return the list of alerts issued for this forecast.""" return self._alerts def _pirateweather_data(self, key): """Fetch and return specific weather data (currently, minutely, hourly, daily, flags and day_night).""" keys = ["minutely", "currently", "hourly", "daily", "flags", "day_night"] try: if key not in self.json: keys.remove(key) url = "{}&exclude={}{}".format( self.response.url.split("&")[0], ",".join(keys), ",alerts", ) response = requests.get(url).json() self.json[key] = response[key] if key == "currently": return PirateWeatherDataPoint(self.json[key]) if key == "flags": return PirateWeatherFlagsBlock(self.json[key]) return PirateWeatherDataBlock(self.json[key]) except KeyError: if key == "currently": return PirateWeatherDataPoint() return PirateWeatherDataBlock() class PirateWeatherDataBlock(UnicodeMixin): """Represent a block of weather data such as minutely, hourly, or daily summaries.""" def __init__(self, d=None): """Initialize the data block with summary and icon information.""" d = d or {} self.summary = d.get("summary") self.icon = d.get("icon") self.data = [ PirateWeatherDataPoint(datapoint) for datapoint in d.get("data", []) ] def __unicode__(self): """Return a string representation of the data block.""" return f"" class PirateWeatherFlagsBlock(UnicodeMixin): """Represent a block of flags data.""" def __init__(self, d=None): """Initialize the data block with flags information.""" d = d or {} self.units = d.get("units") self.version = d.get("version") self.nearestStation = d.get("nearest-station") self.sources = list(d.get("sources")) self.sourceTimes = d.get("sourceTimes") self.processTime = d.get("processTime") self.ingestVersion = d.get("ingestVersion") self.nearestCity = d.get("nearestCity") self.nearestCountry = d.get("nearestCountry") self.nearestSubNational = d.get("nearestSubNational") def __unicode__(self): """Return a string representation of the data block.""" return f"" class PirateWeatherDataPoint(UnicodeMixin): """Represent a single data point in a weather forecast, such as an hourly or daily data point.""" def __init__(self, d={}): """Initialize the data point with timestamp and weather information.""" self.d = d try: self.time = datetime.datetime.fromtimestamp(int(d["time"])) self.utime = d["time"] except KeyError: pass try: sr_time = int(d["sunriseTime"]) self.sunriseTime = datetime.datetime.fromtimestamp(sr_time) except KeyError: self.sunriseTime = None try: ss_time = int(d["sunsetTime"]) self.sunsetTime = datetime.datetime.fromtimestamp(ss_time) except KeyError: self.sunsetTime = None def __getattr__(self, name): """Return the weather property dynamically or return None if missing.""" try: return self.d[name] except KeyError: return None def __unicode__(self): """Return a string representation of the data point.""" return f"" class Alert(UnicodeMixin): """Represent a weather alert, such as a storm warning or flood alert.""" def __init__(self, json): """Initialize the alert with the raw JSON data.""" self.json = json def __getattr__(self, name): """Return the alert property dynamically or return None if missing.""" try: return self.json[name] except KeyError: return None def __unicode__(self): """Return a string representation of the alert.""" return f""