def get_recurring_timeline(self, selected, spiked=False, rescheduled=False, cancelled=False, postponed=False): """Utility method to get all events in the series This splits up the series of events into 3 separate arrays. Historic: event.dates.start < utcnow() Past: utcnow() < event.dates.start < selected.dates.start Future: event.dates.start > selected.dates.start """ excluded_states = [] if not spiked: excluded_states.append(WORKFLOW_STATE.SPIKED) if not rescheduled: excluded_states.append(WORKFLOW_STATE.RESCHEDULED) if not cancelled: excluded_states.append(WORKFLOW_STATE.CANCELLED) if not postponed: excluded_states.append(WORKFLOW_STATE.POSTPONED) query = { '$and': [ {'recurrence_id': selected['recurrence_id']}, {'_id': {'$ne': selected[config.ID_FIELD]}} ] } if excluded_states: query['$and'].append({'state': {'$nin': excluded_states}}) sort = '[("dates.start", 1)]' max_results = get_max_recurrent_events() selected_start = selected.get('dates', {}).get('start', utcnow()) # Make sure we are working with a datetime instance if not isinstance(selected_start, datetime): selected_start = datetime.strptime(selected_start, '%Y-%m-%dT%H:%M:%S%z') historic = [] past = [] future = [] for event in self.get_series(query, sort, max_results): event['dates']['end'] = event['dates']['end'] event['dates']['start'] = event['dates']['start'] for sched in event.get('_planning_schedule', []): sched['scheduled'] = sched['scheduled'] end = event['dates']['end'] start = event['dates']['start'] if end < utcnow(): historic.append(event) elif start < selected_start: past.append(event) elif start > selected_start: future.append(event) return historic, past, future
def _get_series(self, original): query = {'$and': [{'recurrence_id': original['recurrence_id']}]} sort = '[("dates.start", 1)]' max_results = get_max_recurrent_events() events = [] for event in self.get_series(query, sort, max_results): event['dates']['start'] = event['dates']['start'] event['dates']['end'] = event['dates']['end'] events.append(event) return events
def get_expired_items(self, expiry_datetime): """Get the expired items Where end date is in the past """ query = { 'query': { 'bool': { 'must_not': [{ 'term': { 'expired': True } }] } }, 'filter': { 'range': { 'dates.end': { 'lte': date_to_str(expiry_datetime) } } }, 'sort': [{ 'dates.start': 'asc' }], 'size': get_max_recurrent_events() } total_received = 0 total_events = -1 while True: query["from"] = total_received results = self.search(query) # If the total_events has not been set, then this is the first query # In which case we need to store the total hits from the search if total_events < 0: total_events = results.count() # If the search doesn't contain any results, return here if total_events < 1: break # If the last query doesn't contain any results, return here if not len(results.docs): break total_received += len(results.docs) # Yield the results for iteration by the callee yield list(results.docs)
def parse_recurring_rules(self, item, component): """Extracts ICS RRULE into the Event item :param item: The Event item :param component: An ICS VEVENT component """ # parse ics RRULE to fit eventsML recurring_rule r_rule = component.get('rrule') if not isinstance(r_rule, vRecur): return r_rule_dict = vRecur.from_ical(r_rule) recurring_rule: Dict[str, Union[str, int, datetime.date, datetime.datetime]] = {} if r_rule.get('FREQ'): recurring_rule['frequency'] = ''.join(r_rule_dict['FREQ']) if len(r_rule.get('INTERVAL') or []): recurring_rule['interval'] = r_rule_dict['INTERVAL'][0] if len(r_rule.get('UNTIL') or []): recurring_rule['until'] = r_rule_dict['UNTIL'][0] if r_rule.get('COUNT'): recurring_rule['count'] = r_rule_dict['COUNT'] if r_rule.get('BYMONTH'): recurring_rule['bymonth'] = ' '.join(r_rule_dict['BYMONTH']) if r_rule.get('BYDAY'): recurring_rule['byday'] = ' '.join(r_rule_dict['BYDAY']) if r_rule.get('BYHOUR'): recurring_rule['byhour'] = ' '.join(r_rule_dict['BYHOUR']) if r_rule.get('BYMIN'): recurring_rule['bymin'] = ' '.join(r_rule_dict['BYMIN']) if recurring_rule.get('count'): recurring_rule['endRepeatMode'] = 'count' elif recurring_rule.get('until'): recurring_rule['endRepeatMode'] = 'until' # If the `until` attribute is just a date # then copy the time/tzinfo from `dates.end` attribute if isinstance(recurring_rule['until'], datetime.date): end_date = item['dates']['end'] recurring_rule['until'] = datetime.datetime.combine( recurring_rule['until'], end_date.time(), end_date.tzinfo) else: # If the calendar does not provide an end date # then set `count` to MAX_RECURRENT_EVENTS settings recurring_rule['count'] = get_max_recurrent_events() recurring_rule['endRepeatMode'] = 'count' item['dates']['recurring_rule'] = recurring_rule
def generate_recurring_events(event): generated_events = [] setRecurringMode(event) # Get the recurrence_id, or generate one if it doesn't exist recurrence_id = event.get('recurrence_id', generate_guid(type=GUID_NEWSML)) # compute the difference between start and end in the original event time_delta = event['dates']['end'] - event['dates']['start'] # for all the dates based on the recurring rules: for date in itertools.islice( generate_recurring_dates( start=event['dates']['start'], tz=event['dates'].get('tz') and pytz.timezone(event['dates']['tz'] or None), **event['dates']['recurring_rule']), 0, get_max_recurrent_events() ): # set a limit to prevent too many events to be created # create event with the new dates new_event = copy.deepcopy(event) # Remove fields not required by the new events for key in list(new_event.keys()): if key.startswith('_'): new_event.pop(key) elif key.startswith('lock_'): new_event.pop(key) new_event.pop('pubstatus', None) new_event.pop('reschedule_from', None) new_event['dates']['start'] = date new_event['dates']['end'] = date + time_delta # set a unique guid new_event['guid'] = generate_guid(type=GUID_NEWSML) new_event['_id'] = new_event['guid'] # set the recurrence id new_event['recurrence_id'] = recurrence_id # set expiry date overwrite_event_expiry_date(new_event) # the _planning_schedule set_planning_schedule(new_event) generated_events.append(new_event) return generated_events