def test_determine_event_status_far_with_ramp_up(): active_period = { 'dtstart': datetime.now(timezone.utc) + timedelta(seconds=10), 'duration': timedelta(seconds=5), 'ramp_up_period': timedelta(seconds=5) } assert utils.determine_event_status(active_period) == 'far'
async def _event_cleanup(self): """ Periodic task that will clean up completed and cancelled events in our memory. """ for event in self.received_events: if event['event_descriptor']['event_status'] == 'cancelled' or \ utils.determine_event_status(event['active_period']) == 'completed': logger.info(f"Removing event {event} because it is no longer relevant.") self.received_events.pop(self.received_events.index(event))
def __post_init__(self): if self.active_period is None: dtstart = min([ i['dtstart'] if isinstance(i, dict) else i.dtstart for s in self.event_signals for i in s.intervals ]) duration = max([ i['dtstart'] + i['duration'] if isinstance(i, dict) else i.dtstart + i.duration for s in self.event_signals for i in s.intervals ]) - dtstart self.active_period = ActivePeriod(dtstart=dtstart, duration=duration) if self.targets is None and self.targets_by_type is None: raise ValueError( "You must supply either 'targets' or 'targets_by_type'.") elif self.targets_by_type is None: list_of_targets = [ asdict(target) if is_dataclass(target) else target for target in self.targets ] self.targets_by_type = utils.group_targets_by_type(list_of_targets) elif self.targets is None: self.targets = [ Target(**target) for target in utils.ungroup_targets_by_type( self.targets_by_type) ] elif self.targets is not None and self.targets_by_type is not None: list_of_targets = [ asdict(target) if is_dataclass(target) else target for target in self.targets ] if utils.group_targets_by_type( list_of_targets) != self.targets_by_type: raise ValueError( "You assigned both 'targets' and 'targets_by_type' in your event, " "but the two were not consistent with each other. " f"You supplied 'targets' = {self.targets} and " f"'targets_by_type' = {self.targets_by_type}") # Set the event status self.event_descriptor.event_status = utils.determine_event_status( self.active_period)
def test_determine_event_status_active(): active_period = { 'dtstart': datetime.now(timezone.utc) - timedelta(seconds=10), 'duration': timedelta(seconds=15) } assert utils.determine_event_status(active_period) == 'active'
async def _on_event(self, message): logger.debug("The VEN received an event") events = message['events'] try: results = [] for event in message['events']: event_id = event['event_descriptor']['event_id'] event_status = event['event_descriptor']['event_status'] modification_number = event['event_descriptor']['modification_number'] received_event = utils.find_by(self.received_events, 'event_descriptor.event_id', event_id) if received_event: if received_event['event_descriptor']['modification_number'] == modification_number: # Re-submit the same opt type as we already had previously result = self.responded_events[event_id] else: # Replace the event with the fresh copy utils.pop_by(self.received_events, 'event_descriptor.event_id', event_id) self.received_events.append(event) # Wait for the result of the on_update_event handler result = await utils.await_if_required(self.on_update_event(event)) else: # Wait for the result of the on_event self.received_events.append(event) result = self.on_event(event) if asyncio.iscoroutine(result): result = await result results.append(result) if event_status in (enums.EVENT_STATUS.COMPLETED, enums.EVENT_STATUS.CANCELLED) and event_id in self.responded_events: self.responded_events.pop(event_id) else: self.responded_events[event_id] = result for i, result in enumerate(results): if result not in ('optIn', 'optOut') and events[i]['response_required'] == 'always': logger.error("Your on_event or on_update_event handler must return 'optIn' or 'optOut'; " f"you supplied {result}. Please fix your on_event handler.") results[i] = 'optOut' except Exception as err: logger.error("Your on_event handler encountered an error. Will Opt Out of the event. " f"The error was {err.__class__.__name__}: {str(err)}") results = ['optOut'] * len(events) event_responses = [{'response_code': 200, 'response_description': 'OK', 'opt_type': results[i], 'request_id': message['request_id'], 'modification_number': events[i]['event_descriptor']['modification_number'], 'event_id': events[i]['event_descriptor']['event_id']} for i, event in enumerate(events) if event['response_required'] == 'always' and not utils.determine_event_status(event['active_period']) == 'completed'] if len(event_responses) > 0: logger.info(f"Total event_responses: {len(event_responses)}") response = {'response_code': 200, 'response_description': 'OK', 'request_id': message['request_id']} message = self._create_message('oadrCreatedEvent', response=response, event_responses=event_responses, ven_id=self.ven_id) service = 'EiEvent' response_type, response_payload = await self._perform_request(service, message) logger.info(response_type, response_payload) else: logger.info("Not sending any event responses, because a response was not required/allowed by the VTN.")