def test_get_alerts(self): cond = Condition('humidity', 'LESS_THAN', 10) alert1 = Alert('alert1', 'trigger1', [{ "current_value": 263.576, "condition": cond }], { "lon": 37, "lat": 53 }, 1481802090232) alert2 = Alert('alert2', 'trigger1', [{ "current_value": 111.576, "condition": cond }], { "lon": 37, "lat": 53 }, 1481802100000) alerts = [alert1, alert2] instance = Trigger(1526809375, 1527809375, [cond], [geo.Point(13.6, 46.9)], alerts=alerts, alert_channels=None) result = instance.get_alerts() self.assertTrue(isinstance(result, list)) self.assertTrue(alert1 in result) self.assertTrue(alert2 in result)
def test_to_dict(self): cond = Condition('humidity', 'LESS_THAN', 10) instance = Trigger(1526809375, 1527809375, [cond], [geo.Point(13.6, 46.9)], alerts=[], alert_channels=None, id='myid') result = instance.to_dict() self.assertIsInstance(result, dict) self.assertEqual('myid', result['id']) self.assertEqual(1526809375, result['start_after_millis']) self.assertEqual(1527809375, result['end_after_millis']) self.assertEqual([dict(name='OWM API POLLING')], result['alert_channels']) self.assertEqual(list(), result['alerts']) self.assertEqual([{ 'type': 'Point', 'coordinates': [13.6, 46.9] }], result['area']) self.assertEqual([{ 'id': None, 'weather_param': 'humidity', 'operator': 'LESS_THAN', 'amount': 10 }], result['conditions'])
def test_get_alerts_since(self): cond = Condition('humidity', 'LESS_THAN', 10) alert1 = Alert('alert1', 'trigger1', [{ "current_value": 263.576, "condition": cond }], { "lon": 37, "lat": 53 }, 1000) alert2 = Alert('alert2', 'trigger1', [{ "current_value": 111.576, "condition": cond }], { "lon": 37, "lat": 53 }, 3000) alert3 = Alert('alert3', 'trigger1', [{ "current_value": 119.332, "condition": cond }], { "lon": 37, "lat": 53 }, 9000) alert4 = Alert('alert4', 'trigger1', [{ "current_value": 119.332, "condition": cond }], { "lon": 37, "lat": 53 }, 12000) alerts = [alert1, alert2, alert3, alert4] instance = Trigger(1526809375, 1527809375, [cond], [geo.Point(13.6, 46.9)], alerts=alerts, alert_channels=None) result = instance.get_alerts_since(4000) self.assertEqual(2, len(result)) self.assertTrue(alert3 in result) self.assertTrue(alert4 in result) result = instance.get_alerts_since(3000) self.assertEqual(3, len(result)) self.assertTrue(alert2 in result) self.assertTrue(alert3 in result) self.assertTrue(alert4 in result) result = instance.get_alerts_since(15000) self.assertEqual(0, len(result))
def test_get_alert(self): cond = Condition('humidity', 'LESS_THAN', 10) alert = Alert('alert1', 'trigger1', [{ "current_value": 263.576, "condition": cond }], { "lon": 37, "lat": 53 }, 1481802090232) alerts = [alert] instance = Trigger(1526809375, 1527809375, [cond], [geo.Point(13.6, 46.9)], alerts=alerts, alert_channels=None) self.assertEqual(alert, instance.get_alert('alert1'))
def test_get_alerts_on(self): cond1 = Condition('humidity', 'LESS_THAN', 10) cond2 = Condition('temp', 'GREATER_THAN_EQUAL', 100.6) alert1 = Alert('alert1', 'trigger1', [{ "current_value": 8.576, "condition": cond1 }], { "lon": 37, "lat": 53 }, 1000) alert2 = Alert('alert2', 'trigger1', [{ "current_value": 111.576, "condition": cond2 }], { "lon": 37, "lat": 53 }, 3000) alert3 = Alert('alert3', 'trigger1', [{ "current_value": 119.332, "condition": cond2 }], { "lon": 37, "lat": 53 }, 9000) alert4 = Alert('alert4', 'trigger1', [{ "current_value": 7.332, "condition": cond1 }], { "lon": 37, "lat": 53 }, 12000) alerts = [alert1, alert2, alert3, alert4] instance = Trigger(1526809375, 1527809375, [cond1, cond2], [geo.Point(13.6, 46.9)], alerts=alerts, alert_channels=None) result = instance.get_alerts_on('temp') self.assertEqual(2, len(result)) self.assertTrue(alert2 in result) self.assertTrue(alert3 in result) result = instance.get_alerts_on('humidity') self.assertEqual(2, len(result)) self.assertTrue(alert1 in result) self.assertTrue(alert4 in result) result = instance.get_alerts_on('wind_direction') self.assertEqual(0, len(result))
def test_repr(self): instance = Trigger(1526809375, 1527809375, [Condition('humidity', 'LESS_THAN', 10)], [geo.Point(13.6, 46.9)], alerts=None, alert_channels=None) print(instance)
def test_update_trigger(self): instance = self.factory(MockHttpClient) modified_trigger = Trigger.from_dict( json.loads(MockHttpClient.test_trigger_json)) modified_trigger.id = '5852816a9aaacb00153134a3' modified_trigger.end = self._trigger.end_after_millis + 10000 result = instance.update_trigger(modified_trigger) self.assertIsNone(result)
def test_init(self): alert_channels = [AlertChannelsEnum.items()] instance = Trigger(1526809375, 1527809375, [Condition('humidity', 'LESS_THAN', 10)], [geo.Point(13.6, 46.9)], alerts=None, alert_channels=alert_channels) self.assertEqual(instance.alert_channels, alert_channels)
def get_triggers(self): """ Retrieves all of the user's triggers that are set on the Weather Alert API. :returns: list of `pyowm.alertapi30.trigger.Trigger` objects """ status, data = self.http_client.get_json( TRIGGERS_URI, params={'appid': self.API_key}, headers={'Content-Type': 'application/json'}) return [Trigger.from_dict(item) for item in data]
def test_defaulted_parameters(self): instance = Trigger(1526809375, 1527809375, [Condition('humidity', 'LESS_THAN', 10)], [geo.Point(13.6, 46.9)], alerts=None, alert_channels=None) self.assertIsInstance(instance.alerts, list) self.assertEqual(0, len(instance.alerts)) self.assertIsInstance(instance.alert_channels, list) self.assertEqual(1, len(instance.alert_channels)) self.assertEqual(instance.alert_channels[0], AlertChannelsEnum.OWM_API_POLLING)
def test_get_alert(self): cond = Condition('humidity', 'LESS_THAN', 10) alert = Alert('alert1', 'trigger1', [{ "current_value": 263.576, "condition": cond }], { "lon": 37, "lat": 53 }, 1481802090232) alert_two = copy.deepcopy(alert) alert_two.id = 'alert_two' alerts = [alert_two, alert ] # Second alert has to be 1st element to have full coverage instance = Trigger(1526809375, 1527809375, [cond], [geo.Point(13.6, 46.9)], alerts=alerts, alert_channels=None) self.assertEqual(alert, instance.get_alert('alert1')) # Trigger without alerts instance.alerts = [] self.assertIsNone(instance.get_alert(alert_id='alert1'))
def get_trigger(self, trigger_id): """ Retrieves the named trigger from the Weather Alert API. :param trigger_id: the ID of the trigger :type trigger_id: str :return: a `pyowm.alertapi30.trigger.Trigger` instance """ assert isinstance(trigger_id, str), "Value must be a string" status, data = self.http_client.get_json( NAMED_TRIGGER_URI % trigger_id, params={'appid': self.API_key}, headers={'Content-Type': 'application/json'}) return Trigger.from_dict(data)
def test_from_dict(self): the_dict = json.loads( '''{"_id":"5852816a9aaacb00153134a3","__v":0,"alerts":{"8b48b2cd21c23d2894466caccba1ed1f":{"conditions":[ {"current_value":{"min":263.576,"max":263.576},"condition":{"name":"temp","expression":"$lt","amount":273, "_id":"5852816a9aaacb00153134a5"}}],"last_update":1481802090232,"date":1482181200000,"coordinates":{"lon":37, "lat":53}}},"area":[{"type":"Point","_id":"5852816a9aaacb00153134a4","coordinates":[37,53]}],"conditions": [{"name":"temp","expression":"$lt","amount":273,"_id":"5852816a9aaacb00153134a5"}],"time_period":{"end":{ "amount":432060000,"expression":"after"},"start":{"amount":432000000,"expression":"after"}}}''' ) result = Trigger.from_dict(the_dict) self.assertTrue(isinstance(result, Trigger)) self.assertEqual(432000000, result.start_after_millis) self.assertEqual(432060000, result.end_after_millis) self.assertTrue(isinstance(result.conditions, list)) self.assertTrue(isinstance(result.area, list)) self.assertTrue(isinstance(result.alerts, list)) self.assertTrue(isinstance(result.alert_channels, list)) self.assertEqual('5852816a9aaacb00153134a3', result.id) with self.assertRaises(pyowm.commons.exceptions.ParseAPIResponseError): Trigger.from_dict(None) with self.assertRaises(pyowm.commons.exceptions.ParseAPIResponseError): Trigger.from_dict(dict(nonexistent='key')) with self.assertRaises(pyowm.commons.exceptions.ParseAPIResponseError): Trigger.from_dict( json.loads( '''{"_id":"5852816a9aaacb00153134a3","__v":0,"alerts":{"8b48b2cd21c23d2894466caccba1ed1f":{"conditions":[ {"current_value":{"min":263.576,"max":263.576},"condition":{"name":"temp","expression":"$lt","amount":273, "_id":"5852816a9aaacb00153134a5"}}],"last_update":1481802090232,"date":1482181200000,"coordinates":{"lon":37, "lat":53}}},"area":[{"type":"Point","_id":"5852816a9aaacb00153134a4","coordinates":[37,53]}],"conditions": [{"name":"temp","expression":"$lt","amount":273,"_id":"5852816a9aaacb00153134a5"}],"time_period":{"end":{ "amount":432000000,"expression":"exact"},"start":{"amount":132000000,"expression":"before"}}}''' )) # ValueError check with self.assertRaises(pyowm.commons.exceptions.ParseAPIResponseError): the_dict['time_period']['end']['expression'] = 'before' Trigger.from_dict(the_dict)
def test_trigger_fails_with_wrong_parameters(self): start_after_millis = 450000 end_after_millis = 470000 conditions = [Condition('humidity', 'LESS_THAN', 10)] area = [geo.Point(13.6, 46.9)] self.assertRaises(AssertionError, Trigger, None, end_after_millis, conditions, area, alerts=None, alert_channels=None, id=None) self.assertRaises(AssertionError, Trigger, start_after_millis, None, conditions, area, alerts=None, alert_channels=None, id=None) self.assertRaises(AssertionError, Trigger, 'test', end_after_millis, conditions, area, alerts=None, alert_channels=None, id=None) self.assertRaises(AssertionError, Trigger, start_after_millis, 'test', conditions, area, alerts=None, alert_channels=None, id=None) self.assertRaises(ValueError, Trigger, end_after_millis, start_after_millis, conditions, area, alerts=None, alert_channels=None, id=None) self.assertRaises(AssertionError, Trigger, start_after_millis, end_after_millis, None, area, alerts=None, alert_channels=None, id=None) self.assertRaises(ValueError, Trigger, start_after_millis, end_after_millis, [], area, alerts=None, alert_channels=None, id=None) self.assertRaises(AssertionError, Trigger, start_after_millis, end_after_millis, conditions, None, alerts=None, alert_channels=None, id=None) self.assertRaises(ValueError, Trigger, start_after_millis, end_after_millis, conditions, [], alerts=None, alert_channels=None, id=None) _ = Trigger(start_after_millis, end_after_millis, conditions, area, alerts=None, alert_channels=None)
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 = formatting.to_UNIXtime(start) unix_end = formatting.to_UNIXtime(end) unix_current = timestamps.now(timeformat='unix') if unix_start >= unix_end: raise ValueError( "The start timestamp must precede the end timestamp") delta_millis_start = timestamps.millis_offset_between_epochs( unix_current, unix_start) delta_millis_end = timestamps.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.to_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 Trigger.from_dict(payload)
def test_delete_trigger(self): instance = self.factory(MockHttpClient) trigger = Trigger.from_dict( json.loads(MockHttpClient.test_trigger_json)) result = instance.delete_trigger(trigger) self.assertIsNone(result)
def parse_JSON(self, JSON_string): """ Parses a `pyowm.alertapi30.trigger.Trigger` instance out of raw JSON data. As per OWM documentation, start and end times are expressed with respect to the moment when you create/update the Trigger. By design, PyOWM will only allow users to specify *absolute* datetimes - which is, with the `exact` expression - for start/end timestamps (will otherwise result in a `ParseResponseError` be raised) :param JSON_string: a raw JSON string :type JSON_string: str :return: a `pyowm.alertapi30.trigger.Trigger` instance or ``None`` if no data is available :raises: *ParseResponseError* if it is impossible to find or parse the data needed to build the result """ if JSON_string is None: raise parse_response_error.ParseResponseError('JSON data is None') d = json.loads(JSON_string) try: # trigger id trigger_id = d.get('_id', None) # start timestamp start_dict = d['time_period']['start'] expr = start_dict['expression'] if expr != 'after': raise ValueError('Invalid time expression: "%s" on start timestamp. Only: "after" is supported' % expr) start = start_dict['amount'] # end timestamp end_dict = d['time_period']['end'] expr = end_dict['expression'] if expr != 'after': raise ValueError('Invalid time expression: "%s" on end timestamp. Only: "after" is supported' % expr) end = end_dict['amount'] # conditions conditions = [Condition.from_dict(c) for c in d['conditions']] # alerts alerts_dict = d['alerts'] alerts = list() for key in alerts_dict: alert_id = key alert_data = alerts_dict[alert_id] alert_last_update = alert_data['last_update'] alert_met_conds = [ dict(current_value=c['current_value']['min'], condition=Condition.from_dict(c['condition'])) for c in alert_data['conditions'] ] alert_coords = alert_data['coordinates'] alert = Alert(alert_id, trigger_id, alert_met_conds, alert_coords, last_update=alert_last_update) alerts.append(alert) # area area_list = d['area'] area = [GeometryBuilder.build(a_dict) for a_dict in area_list] # alert channels alert_channels = None # defaulting except ValueError as e: raise parse_response_error.ParseResponseError('Impossible to parse JSON: %s' % e) except KeyError as e: raise parse_response_error.ParseResponseError('Impossible to parse JSON: %s' % e) return Trigger(start, end, conditions, area=area, alerts=alerts, alert_channels=alert_channels, id=trigger_id)
class TestAlertManager(unittest.TestCase): _cond1 = Condition('humidity', 'LESS_THAN', 10) _cond2 = Condition('temp', 'GREATER_THAN_EQUAL', 100.6) _trigger = Trigger(1526809375, 1527809375, [_cond1, _cond2], [geo.Point(13.6, 46.9)], alerts=[], alert_channels=None, id='trigger-id') _alert = Alert('alert1', 'trigger1', [{ "current_value": 263.576, "condition": _cond1 }], { "lon": 37, "lat": 53 }, 1481802090232) def factory(self, _kls): sm = AlertManager('APIKey') sm.http_client = _kls() return sm def test_instantiation_fails_without_api_key(self): self.assertRaises(AssertionError, AlertManager, None) def test_get_alert_api_version(self): instance = AlertManager('APIKey') result = instance.alert_api_version() self.assertIsInstance(result, tuple) self.assertEqual(result, ALERT_API_VERSION) def test_get_triggers(self): instance = self.factory(MockHttpClientTwoTriggers) results = instance.get_triggers() self.assertEqual(2, len(results)) t = results[0] self.assertIsInstance(t, Trigger) def test_get_trigger_fails_with_wrong_input(self): instance = AlertManager('APIKey') with self.assertRaises(AssertionError): instance.get_trigger(None) with self.assertRaises(AssertionError): instance.get_trigger(123) def test_get_trigger(self): instance = self.factory(MockHttpClientOneTrigger) result = instance.get_trigger('any-id') self.assertIsInstance(result, Trigger) def test_create_trigger(self): instance = self.factory(MockHttpClient) result = instance.create_trigger(1526809375, 1527809375, [self._cond1, self._cond2], [geo.Point(13.6, 46.9)], alert_channels=None) self.assertIsInstance(result, Trigger) def test_create_trigger_fails_with_wrong_inputs(self): instance = self.factory(MockHttpClient) with self.assertRaises(AssertionError): instance.create_trigger(None, 1527809375, [self._cond1, self._cond2], [geo.Point(13.6, 46.9)], alert_channels=None) with self.assertRaises(AssertionError): instance.create_trigger(1526809375, None, [self._cond1, self._cond2], [geo.Point(13.6, 46.9)], alert_channels=None) with self.assertRaises(ValueError): instance.create_trigger(1526809375, 1327809375, [self._cond1, self._cond2], [geo.Point(13.6, 46.9)], alert_channels=None) with self.assertRaises(AssertionError): instance.create_trigger(1526809375, 1527809375, None, [geo.Point(13.6, 46.9)], alert_channels=None) with self.assertRaises(ValueError): instance.create_trigger(1526809375, 1527809375, [], [geo.Point(13.6, 46.9)], alert_channels=None) with self.assertRaises(AssertionError): instance.create_trigger(1526809375, 1527809375, [self._cond1, self._cond2], None, alert_channels=None) with self.assertRaises(ValueError): instance.create_trigger(1526809375, 1527809375, [self._cond1, self._cond2], [], alert_channels=None) def test_delete_trigger_fails_with_wrong_input(self): instance = AlertManager('APIKey') with self.assertRaises(AssertionError): instance.delete_trigger(None) with self.assertRaises(AssertionError): self._trigger.id = 123 instance.delete_trigger(self._trigger) def test_delete_trigger(self): instance = self.factory(MockHttpClient) parser = TriggerParser() trigger = parser.parse_JSON(MockHttpClient.test_trigger_json) result = instance.delete_trigger(trigger) self.assertIsNone(result) def test_update_trigger_fails_with_wrong_input(self): instance = AlertManager('APIKey') with self.assertRaises(AssertionError): instance.update_trigger(None) with self.assertRaises(AssertionError): self._trigger.id = 123 instance.update_trigger(self._trigger) def test_update_trigger(self): instance = self.factory(MockHttpClient) parser = TriggerParser() modified_trigger = parser.parse_JSON(MockHttpClient.test_trigger_json) modified_trigger.id = '5852816a9aaacb00153134a3' modified_trigger.end = self._trigger.end_after_millis + 10000 result = instance.update_trigger(modified_trigger) self.assertIsNone(result) def test_get_alerts_for_fails_with_wrong_input(self): instance = AlertManager('APIKey') with self.assertRaises(AssertionError): instance.get_alerts_for(None) with self.assertRaises(AssertionError): self._trigger.id = 123 instance.get_alerts_for(self._trigger) def test_get_alerts_for(self): instance = self.factory(MockHttpClientTwoAlerts) self._trigger.id = 'trigger-id' results = instance.get_alerts_for(self._trigger) self.assertEqual(2, len(results)) self.assertIsInstance(results[0], Alert) self.assertIsInstance(results[1], Alert) def test_get_alert_fails_with_wrong_input(self): instance = AlertManager('APIKey') with self.assertRaises(AssertionError): instance.get_alert(None, self._trigger) with self.assertRaises(AssertionError): instance.get_alert(123, self._trigger) with self.assertRaises(AssertionError): instance.get_alert('alert-id', None) with self.assertRaises(AssertionError): self._trigger.id = 123 instance.get_alert('alert-id', self._trigger) def test_get_alert(self): self._trigger.id = 'trigger-id' instance = self.factory(MockHttpClientOneAlert) result = instance.get_alert('alert-id', self._trigger) self.assertIsInstance(result, Alert) def test_delete_all_alerts_for_fails_with_wrong_input(self): instance = AlertManager('APIKey') with self.assertRaises(AssertionError): instance.delete_all_alerts_for(None) with self.assertRaises(AssertionError): self._trigger.id = 123 instance.delete_all_alerts_for(self._trigger) def test_delete_all_alerts_for(self): instance = self.factory(MockHttpClientTwoAlerts) result = instance.delete_all_alerts_for(self._trigger) self.assertIsNone(result) def test_delete_alert_fails_with_wrong_input(self): instance = AlertManager('APIKey') with self.assertRaises(AssertionError): instance.delete_alert(None) with self.assertRaises(AssertionError): self._alert.id = 123 instance.delete_alert(self._alert) self._alert.id = 'alert-id' self._alert.trigger_id = None with self.assertRaises(AssertionError): instance.delete_alert(self._alert) self._alert.trigger_id = 789 with self.assertRaises(AssertionError): instance.delete_alert(self._alert) def test_delete_alert(self): instance = self.factory(MockHttpClientOneAlert) result = instance.delete_alert(self._alert) self.assertIsNone(result)