def test_to_UNIXtime(self): unix = 1378459200 iso = "2013-09-06 09:20:00+00" date = datetime(2013, 9, 6, 9, 20, 0) self.assertEqual(unix, timeformatutils.to_UNIXtime(unix)) self.assertEqual(unix, timeformatutils.to_UNIXtime(iso)) self.assertEqual(unix, timeformatutils.to_UNIXtime(date))
def create_trigger(self, start, end, conditions, area, alert_channels=None): """ Create a new trigger on the Alert API with the given parameters :param start: time object representing the time when the trigger begins to be checked :type start: int, ``datetime.datetime`` or ISO8601-formatted string :param end: time object representing the time when the trigger ends to be checked :type end: int, ``datetime.datetime`` or ISO8601-formatted string :param conditions: the `Condition` objects representing the set of checks to be done on weather variables :type conditions: list of `pyowm.utils.alertapi30.Condition` instances :param area: the geographic are over which conditions are checked: it can be composed by multiple geoJSON types :type area: list of geoJSON types :param alert_channels: the alert channels through which alerts originating from this `Trigger` can be consumed. Defaults to OWM API polling :type alert_channels: list of `pyowm.utils.alertapi30.AlertChannel` instances :returns: a *Trigger* instance :raises: *ValueError* when start or end epochs are `None` or when end precedes start or when conditions or area are empty collections """ assert start is not None assert end is not None # prepare time period unix_start = timeformatutils.to_UNIXtime(start) unix_end = timeformatutils.to_UNIXtime(end) unix_current = timeutils.now(timeformat='unix') if unix_start >= unix_end: raise ValueError("The start timestamp must precede the end timestamp") delta_millis_start = timeutils.millis_offset_between_epochs(unix_current, unix_start) delta_millis_end = timeutils.millis_offset_between_epochs(unix_current, unix_end) the_time_period = { "start": { "expression": "after", "amount": delta_millis_start }, "end": { "expression": "after", "amount": delta_millis_end } } assert conditions is not None if len(conditions) == 0: raise ValueError('A trigger must contain at least one condition: you provided none') the_conditions = [dict(name=c.weather_param, expression=c.operator, amount=c.amount) for c in conditions] assert area is not None if len(area) == 0: raise ValueError('The area for a trigger must contain at least one geoJSON type: you provided none') the_area = [a.as_dict() for a in area] # >>> for the moment, no specific handling for alert channels status, payload = self.http_client.post( TRIGGERS_URI, params={'appid': self.API_key}, data=dict(time_period=the_time_period, conditions=the_conditions, area=the_area), headers={'Content-Type': 'application/json'}) return self.trigger_parser.parse_dict(payload)
def weather_history_at_coords(self, lat, lon, start=None, end=None): """ Queries the OWM web API for weather history for the specified at the specified geographic (eg: 51.503614, -0.107331). A list of *Weather* objects is returned. It is possible to query for weather history in a closed time period, whose boundaries can be passed as optional parameters. :param lat: the location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: the location's longitude, must be between -180.0 and 180.0 :type lon: int/float :param start: the object conveying the time value for the start query boundary (defaults to ``None``) :type start: int, ``datetime.datetime`` or ISO8601-formatted string :param end: the object conveying the time value for the end query boundary (defaults to ``None``) :type end: int, ``datetime.datetime`` or ISO8601-formatted string :returns: a list of *Weather* instances or ``None`` if history data is not available for the specified location """ pass assert type(lon) is float or type(lon) is int, "'lon' must be a float" if lon < -180.0 or lon > 180.0: raise ValueError("'lon' value must be between -180 and 180") assert type(lat) is float or type(lat) is int, "'lat' must be a float" if lat < -90.0 or lat > 90.0: raise ValueError("'lat' value must be between -90 and 90") params = {'lon': lon, 'lat': lat, 'lang': self._language} if start is not None: unix_start = timeformatutils.to_UNIXtime(start) current_time = time() if unix_start > current_time: raise ValueError("Error: the start time boundary must " "precede the current time!") params['start'] = str(unix_start) else: unix_start = None if end is not None: unix_end = timeformatutils.to_UNIXtime(end) params['end'] = str(unix_end) else: unix_end = None if unix_start is not None and unix_end is not None: if unix_start >= unix_end: raise ValueError("Error: the start time boundary must " "precede the end time!") json_data = self._httpclient.call_API(CITY_WEATHER_HISTORY_URL, params) return self._parsers['weather_history'].parse_JSON(json_data)
def weather_history_at_id(self, id, start=None, end=None): """ Queries the OWM web API for weather history for the specified city ID. A list of *Weather* objects is returned. It is possible to query for weather history in a closed time period, whose boundaries can be passed as optional parameters. :param id: the city ID :type id: int :param start: the object conveying the time value for the start query boundary (defaults to ``None``) :type start: int, ``datetime.datetime`` or ISO8601-formatted string :param end: the object conveying the time value for the end query boundary (defaults to ``None``) :type end: int, ``datetime.datetime`` or ISO8601-formatted string :returns: a list of *Weather* instances or ``None`` if history data is not available for the specified location :raises: *ParseResponseException* when OWM web API responses' data cannot be parsed, *APICallException* when OWM web API can not be reached, *ValueError* if the time boundaries are not in the correct chronological order, if one of the time boundaries is not ``None`` and the other is or if one or both of the time boundaries are after the current time """ assert type(id) is int, "'id' must be an int" if id < 0: raise ValueError("'id' value must be greater than 0") params = {'id': id, 'lang': self._language} if start is None and end is None: pass elif start is not None and end is not None: unix_start = timeformatutils.to_UNIXtime(start) unix_end = timeformatutils.to_UNIXtime(end) if unix_start >= unix_end: raise ValueError("Error: the start time boundary must " \ "precede the end time!") current_time = time() if unix_start > current_time: raise ValueError("Error: the start time boundary must " \ "precede the current time!") params['start'] = str(unix_start) params['end'] = str(unix_end) else: raise ValueError("Error: one of the time boundaries is None, " \ "while the other is not!") json_data = self._httpclient.call_API(CITY_WEATHER_HISTORY_URL, params) return self._parsers['weather_history'].parse_JSON(json_data)
def weather_history_at_id(self, id, start=None, end=None): """ Queries the OWM web API for weather history for the specified city ID. A list of *Weather* objects is returned. It is possible to query for weather history in a closed time period, whose boundaries can be passed as optional parameters. :param id: the city ID :type id: int :param start: the object conveying the time value for the start query boundary (defaults to ``None``) :type start: int, ``datetime.datetime`` or ISO8601-formatted string :param end: the object conveying the time value for the end query boundary (defaults to ``None``) :type end: int, ``datetime.datetime`` or ISO8601-formatted string :returns: a list of *Weather* instances or ``None`` if history data is not available for the specified location :raises: *ParseResponseException* when OWM web API responses' data cannot be parsed, *APICallException* when OWM web API can not be reached, *ValueError* if the time boundaries are not in the correct chronological order, if one of the time boundaries is not ``None`` and the other is or if one or both of the time boundaries are after the current time """ assert type(id) is int, "'id' must be an int" if id < 0: raise ValueError("'id' value must be greater than 0") params = {'id': id, 'lang': self._language} if start is None and end is None: pass elif start is not None and end is not None: unix_start = timeformatutils.to_UNIXtime(start) unix_end = timeformatutils.to_UNIXtime(end) if unix_start >= unix_end: raise ValueError("Error: the start time boundary must " "precede the end time!") current_time = time() if unix_start > current_time: raise ValueError("Error: the start time boundary must " "precede the current time!") params['start'] = str(unix_start) params['end'] = str(unix_end) else: raise ValueError("Error: one of the time boundaries is None, " "while the other is not!") json_data = self._httpclient.call_API(CITY_WEATHER_HISTORY_URL, params) return self._parsers['weather_history'].parse_JSON(json_data)
def get_alerts_since(self, timestamp): """ Returns all the `Alert` objects of this `Trigger` that were fired since the specified timestamp. :param timestamp: time object representing the point in time since when alerts have to be fetched :type timestamp: int, ``datetime.datetime`` or ISO8601-formatted string :return: list of `Alert` instances """ unix_timestamp = timeformatutils.to_UNIXtime(timestamp) result = [] for alert in self.alerts: if alert.last_update >= unix_timestamp: result.append(alert) return result
def get_weather_at(self, timeobject): """ Gives the *Weather* item in the forecast that is closest in time to the time value conveyed by the parameter :param timeobject: may be a UNIX time, a ``datetime.datetime`` object or an ISO8601-formatted string in the format ``YYYY-MM-DD HH:MM:SS+00`` :type timeobject: long/int, ``datetime.datetime`` or str) :returns: a *Weather* object """ return weatherutils. \ find_closest_weather(self._forecast.get_weathers(), timeformatutils.to_UNIXtime(timeobject))
def will_be_foggy_at(self, timeobject): """ Tells if at the specified time the condition is fog. The check is performed on the *Weather* item of the forecast which is closest to the time value conveyed by the parameter :param timeobject: may be a UNIX time, a ``datetime.datetime`` object or an ISO8601-formatted string in the format ``YYYY-MM-DD HH:MM:SS+00`` :type timeobject: long/int, ``datetime.datetime`` or str) :returns: boolean """ time = timeformatutils.to_UNIXtime(timeobject) closest_weather = weatherutils.find_closest_weather( self._forecast.get_weathers(), time) return weatherutils.status_is(closest_weather, "fog", weather_code_registry)
def test_last_year(self): result = timeutils.last_year() expected = datetime.now() + timedelta(days=-365) self.assertAlmostEqual( float(timeformatutils.to_UNIXtime(expected)), float(timeformatutils.to_UNIXtime(result)))
def test_last_year(self): result = timeutils.last_year() expected = datetime.now() + timedelta(days=-365) self.assertAlmostEqual(float(timeformatutils.to_UNIXtime(expected)), float(timeformatutils.to_UNIXtime(result)))
def test_next_week(self): result = timeutils.next_week() expected = datetime.now() + timedelta(days=7) self.assertAlmostEqual( float(timeformatutils.to_UNIXtime(expected)), float(timeformatutils.to_UNIXtime(result)))
def test_last_three_hours(self): result = timeutils.last_three_hours() expected = datetime.now() + timedelta(hours=-3) self.assertAlmostEqual(float(timeformatutils.to_UNIXtime(expected)), float(timeformatutils.to_UNIXtime(result)))
def test_next_week(self): result = timeutils.next_week() expected = datetime.now() + timedelta(days=7) self.assertAlmostEqual(float(timeformatutils.to_UNIXtime(expected)), float(timeformatutils.to_UNIXtime(result)))
def weather_history_at_coords(self, lat, lon, start=None, end=None, type_=None, cnt=None): """ Queries the OWM web API for weather history for the specified at the specified geographic (eg: 51.503614, -0.107331). A list of *Weather* objects is returned. It is possible to query for weather history in a closed time period, whose boundaries can be passed as optional parameters. :param lat: the location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: the location's longitude, must be between -180.0 and 180.0 :type lon: int/float :param start: the object conveying the time value for the start query boundary (defaults to ``None``) :type start: int, ``datetime.datetime`` or ISO8601-formatted string :param end: the object conveying the time value for the end query boundary (defaults to ``None``) :type end: int, ``datetime.datetime`` or ISO8601-formatted string :param type_:'daily', 'hour', and 'tick' :param cnt: :returns: a list of *Weather* instances or ``None`` if history data is not available for the specified location :rtype: list[pyowm.webapi25.weather.Weather] """ pass assert type(lon) is float or type(lon) is int, "'lon' must be a float" if lon < -180.0 or lon > 180.0: raise ValueError("'lon' value must be between -180 and 180") assert type(lat) is float or type(lat) is int, "'lat' must be a float" if lat < -90.0 or lat > 90.0: raise ValueError("'lat' value must be between -90 and 90") params = {'lon': lon, 'lat': lat, 'lang': self._language} if start is not None: unix_start = timeformatutils.to_UNIXtime(start) current_time = time.time() if unix_start > current_time: raise ValueError("Error: the start time boundary must " "precede the current time!") params['start'] = str(unix_start) else: unix_start = None if end is not None: unix_end = timeformatutils.to_UNIXtime(end) params['end'] = str(unix_end) else: unix_end = None if unix_start is not None and unix_end is not None: if unix_start >= unix_end: raise ValueError("Error: the start time boundary must " "precede the end time!") if type_ is not None: params['type'] = type_ if cnt is not None: params['cnt'] = cnt json_data = self._httpclient.call_API(CITY_WEATHER_HISTORY_URL, params) return self._parsers['weather_history'].parse_JSON(json_data)
def test_last_three_hours(self): result = timeutils.last_three_hours() expected = datetime.now() + timedelta(hours=-3) self.assertAlmostEqual( float(timeformatutils.to_UNIXtime(expected)), float(timeformatutils.to_UNIXtime(result)))
def create_trigger(self, start, end, conditions, area, alert_channels=None): """ Create a new trigger on the Alert API with the given parameters :param start: time object representing the time when the trigger begins to be checked :type start: int, ``datetime.datetime`` or ISO8601-formatted string :param end: time object representing the time when the trigger ends to be checked :type end: int, ``datetime.datetime`` or ISO8601-formatted string :param conditions: the `Condition` objects representing the set of checks to be done on weather variables :type conditions: list of `pyowm.utils.alertapi30.Condition` instances :param area: the geographic are over which conditions are checked: it can be composed by multiple geoJSON types :type area: list of geoJSON types :param alert_channels: the alert channels through which alerts originating from this `Trigger` can be consumed. Defaults to OWM API polling :type alert_channels: list of `pyowm.utils.alertapi30.AlertChannel` instances :returns: a *Trigger* instance :raises: *ValueError* when start or end epochs are `None` or when end precedes start or when conditions or area are empty collections """ assert start is not None assert end is not None # prepare time period unix_start = timeformatutils.to_UNIXtime(start) unix_end = timeformatutils.to_UNIXtime(end) unix_current = timeutils.now(timeformat='unix') if unix_start >= unix_end: raise ValueError( "The start timestamp must precede the end timestamp") delta_millis_start = timeutils.millis_offset_between_epochs( unix_current, unix_start) delta_millis_end = timeutils.millis_offset_between_epochs( unix_current, unix_end) the_time_period = { "start": { "expression": "after", "amount": delta_millis_start }, "end": { "expression": "after", "amount": delta_millis_end } } assert conditions is not None if len(conditions) == 0: raise ValueError( 'A trigger must contain at least one condition: you provided none' ) the_conditions = [ dict(name=c.weather_param, expression=c.operator, amount=c.amount) for c in conditions ] assert area is not None if len(area) == 0: raise ValueError( 'The area for a trigger must contain at least one geoJSON type: you provided none' ) the_area = [a.as_dict() for a in area] # >>> for the moment, no specific handling for alert channels status, payload = self.http_client.post( TRIGGERS_URI, params={'appid': self.API_key}, data=dict(time_period=the_time_period, conditions=the_conditions, area=the_area), headers={'Content-Type': 'application/json'}) return self.trigger_parser.parse_dict(payload)