def mongo_to_ics(events, sub: db.Subscription = None): """ creates the iCal based on the MongoDb database and events submitted """ # initialize calendar object cal = Calendar() cal.add('PRODID', 'ABE') cal.add('VERSION', '2.0') for event in events: new_event = create_ics_event( event, sub=sub) # create the base event fields in ics format recurrence = event['recurrence'] if recurrence: new_event = create_ics_recurrence( new_event, recurrence) # create the rrule field if event['sub_events']: for sub_event in event['sub_events']: full_sub_event = sub_event_to_full(mongo_to_dict(sub_event), event) new_sub_event = create_ics_event(full_sub_event, True) cal.add_component(new_sub_event) new_event.add('EXDATE', sub_event['rec_id']) # vevent.add('attendee', 'MAILTO:[email protected]') cal.add_component(new_event) response = cal.to_ical() return response
def get_events(self): query_dict = get_to_event_search(request) query_time_period = query_dict['end'] - query_dict['start'] if query_time_period > timedelta(days=366): return "Too wide of date range in query. Max date range of 1 year allowed.", 404 if not request_has_scope(request, 'read:all_events'): query_dict['visibility'] = 'public' query = event_query(query_dict) results = db.Event.objects( __raw__=query) # {'start': new Date('2017-06-14')}) logging.debug('found %s events for query', len(results)) # date range for query start = query_dict['start'] end = query_dict['end'] events_list = [] for event in results: if 'recurrence' in event: # checks for recurrent events # expands a recurring event defintion into a json response with individual events events_list = recurring_to_full(event, events_list, start, end) else: # appends the event information as a dictionary events_list.append(mongo_to_dict(event)) return events_list
def recurring_to_full(event, events_list, start, end): """Expands recurring events in MongoDb to multiple placeholder objects event mongoDB event events_list list of events to be returned start, end start and end indicating the query date range """ if 'sub_events' in event: # if there are sub_events in event for sub_event in event['sub_events']: if 'start' in sub_event: # if the sub_event fits into the date range and is not deleted if sub_event['start'] <= end and sub_event['start'] >= start \ and not sub_event['deleted']: events_list.append( sub_event_to_full(mongo_to_dict(sub_event), event)) # generate a list of all datetimes a recurring event would occur rule_list = instance_creation(event, start, end) # for each instance create a full event definition based on its parent event for instance in rule_list: def convert_timezone(a): return a.replace(tzinfo=pytz.UTC) if isinstance(a, datetime) else a if convert_timezone(instance) >= convert_timezone( start) and convert_timezone(instance) < convert_timezone(end): events_list = placeholder_recurring_creation( instance, events_list, event) return (events_list)
def update_sub_event(received_data, parent_event, sub_event_id, ics=False): """edits a sub_event that has already been created sub_event_id if the update is not coming from an ics feed: - will be an objectid if the update is coming from an ics feed: - will be a rec_id (datetime object) """ def convert_timezone(a): return a.replace(tzinfo=pytz.UTC) if isinstance(a, datetime) else a for sub_event in parent_event.sub_events: if not ics: # if this update is not coming from an ics feed # if the sub_event to be updated's id matches the id of the received_data if sub_event['_id'] == sub_event_id: updated_sub_event_dict = create_new_sub_event_defintion( mongo_to_dict(sub_event), received_data, parent_event) updated_sub_event = db.RecurringEventExc( **updated_sub_event_dict) parent_event.update(pull__sub_events___id=sub_event_id) parent_event.update( add_to_set__sub_events=updated_sub_event_dict) if not updated_sub_event_dict['forever']: parent_event.recurrence_end = find_recurrence_end( parent_event) parent_event.save() parent_event.reload() return (updated_sub_event) else: # if this update is coming from an ics feed sub_event_compare = convert_timezone(sub_event["rec_id"]) if sub_event_compare == sub_event_id: updated_sub_event_dict = create_new_sub_event_defintion( mongo_to_dict(sub_event), received_data, parent_event) updated_sub_event = db.RecurringEventExc( **updated_sub_event_dict) parent_event.update(pull__sub_events__rec_id=sub_event_id) parent_event.update( add_to_set__sub_events=updated_sub_event_dict) if not updated_sub_event_dict['forever']: parent_event.recurrence_end = find_recurrence_end( parent_event) parent_event.save() parent_event.reload() return (updated_sub_event)
def get_event_by_id(self, event_id, rec_id): # This doesn't check the scope for viewing non-public events. These # events are protected by the obscurity of their ids: the event id is as # well-protected as the rest of its data. logging.debug('Event requested: %s', event_id) result = db.Event.objects(id=event_id).first() if not result: # if there are no events with the event_id given # search for sub_events with that id and save the parent event cur_parent_event = db.Event.objects( __raw__={ 'sub_events._id': objectid.ObjectId(event_id) }).first() if cur_parent_event: # if a sub_event was found # access the information of the sub_event cur_sub_event = access_sub_event( mongo_to_dict(cur_parent_event), objectid.ObjectId(event_id)) # expand the sub_event to inherit from the parent event return sub_event_to_full(cur_sub_event, cur_parent_event) else: logging.debug("No sub_event found") abort(404) # if an event was found and there is a rec_id given, a sub_event needs to be returned elif rec_id: # the json response will be used to display the information of the sub_event before it is edited logging.debug('Sub_event requested: %s', rec_id) # return a json response with the parent event information filled in with # the start and end datetimes updated from the rec_id result = placeholder_recurring_creation(rec_id, [], result, True) if not result: return f"Subevent not found with identifier '{rec_id}'", 404 return result if not result: return f"Event not found with identifier '{event_id}'", 404 return mongo_to_dict(result)
def duplicate_query_check(sub_event_dict, parent_event): """checks whether a dictionary has the same field-value pair as a parent event used to check for duplicate information in sub_events and their parent events """ parent_event_dict = mongo_to_dict(parent_event) fields_to_pop = [] for field in sub_event_dict: if field in parent_event_dict: if sub_event_dict[field] == parent_event_dict[field]: fields_to_pop.append(field) for field in fields_to_pop: sub_event_dict.pop(field) return (sub_event_dict)
def post(self): """ Create new event with parameters passed in through args or form """ received_data = request_to_dict(request) logging.debug("Received POST data: %s", received_data) # combines args and form new_event = db.Event(**received_data) if not request_has_scope(request, 'create:protected_events'): check_protected_labels(new_event.labels) if 'recurrence' in new_event: # if this is a recurring event if not new_event.recurrence.forever: # if it doesn't recur forever # find the end of the recurrence new_event.recurrence_end = find_recurrence_end(new_event) new_event.save() return mongo_to_dict(new_event), 201
def cal_to_event(cal): """ Creates an event from a calendar object """ received_data, sender = ical_to_dict(cal) try: new_event = db.Event(**received_data) if new_event.labels == []: # if no labels were given new_event.labels = ['unlabeled'] if 'recurrence' in new_event: # if this is a recurring event if not new_event.recurrence.forever: # if it doesn't recurr forever # find the end of the recurrence new_event.recurrence_end = find_recurrence_end(new_event) new_event.save() except ValidationError as error: error_reply(sender, error) return { 'error_type': 'validation', 'validation_errors': [str(err) for err in error.errors], 'error_message': error.message }, 400 else: # return success new_event_dict = mongo_to_dict(new_event) reply_email(sender, new_event_dict) return new_event_dict, 201
def delete(self, event_id, rec_id=None): """ Delete individual event event_id the id of the event to be deleted rec_id the rec_id of a sub_event to be deleted """ # TODO: call check_protected_labels(result.labels) # if not request_has_scope(request, 'delete:protected_events') logging.debug('Event requested: %s', event_id) result = db.Event.objects(id=event_id).first() if not result: # if no event is found with the id given # try finding a sub_event with that id cur_parent_event = db.Event.objects( __raw__={ 'sub_events._id': objectid.ObjectId(event_id) }).first() if cur_parent_event: # if found update the deleted field of the sub_event received_data = {'deleted': True} result = update_sub_event(received_data, cur_parent_event, objectid.ObjectId(event_id)) logging.debug("Edited sub_event deleted") else: abort(404) elif rec_id: # if this is a sub_event of a recurring event that has not been created yet sub_event_dummy = placeholder_recurring_creation( rec_id, [], result, True) sub_event_dummy['deleted'] = True # create a sub_event with the deleted field set to true create_sub_event(sub_event_dummy, result) logging.debug("Deleted sub_event for the first time") else: # if a normal event is to be deleted received_data = request_to_dict(request) logging.debug("Received DELETE data: %s", received_data) result.delete() return mongo_to_dict(result)
def put(self, event_id): """ Modify individual event event_id id of the event to modify """ received_data = request_to_dict(request) logging.debug("Received PUT data: %s", received_data) result = db.Event.objects(id=event_id).first() if not result: # if no event was found # try finding a sub_event with the id and save the parent event it is stored under cur_parent_event = db.Event.objects( __raw__={ 'sub_events._id': objectid.ObjectId(event_id) }).first() if cur_parent_event: # if a sub_event was found, updated it with the received_data result = update_sub_event(received_data, cur_parent_event, objectid.ObjectId(event_id)) else: abort(404) else: # if event was found if not request_has_scope(request, 'edit:protected_events'): check_protected_labels(result.labels) # TODO: also check the new labels # if the received data is a new sub_event if 'sid' in received_data and received_data['sid'] is not None: # create a new sub_event document if 'rec_id' in received_data and received_data[ 'rec_id'] is not None: received_data['rec_id'] = dateutil.parser.parse( str(received_data['rec_id'])) result = create_sub_event(received_data, result) else: # if this a normal event to be updated result.update(**received_data) result.reload() return mongo_to_dict(result)
def extract_ics(cal, ics_url, labels=None): """ Extracts the ics components and stores them cal the ics calendar ics_url the ics feed url labels labels to assign to the events """ results = db.ICS.objects(url=ics_url).first() logging.debug("ics feeds: %s", mongo_to_dict(results)) if results: # if this feed has already been inputted for component in cal.walk(): if component.name == "VEVENT": last_modified = component.get('LAST-MODIFIED').dt now = datetime.datetime.now(timezone.utc) difference = now - last_modified # if an event has been modified in the last two hours if difference.total_seconds() < 7200: update_ics_to_mongo(component, results.labels) else: # if this is the first time this ics feed has been input # save the ics url feed as an ICS object ics_object = db.ICS(**{'url': ics_url, 'labels': labels}).save() temporary_dict = [] for component in cal.walk(): if component.name == "VEVENT": # convert the event to a dictionary com_dict = ics_to_dict(component, labels, ics_object.id) if 'rec_id' in com_dict: # if this is a sub_event # search for another event with the same UID normal_event = db.Event.objects(__raw__={ 'UID': com_dict['UID'] }).first() if normal_event is not None: # if its parent event has already been created create_sub_event(com_dict, normal_event) logging.debug("sub event created in new instance") else: # if its parent event has not been created # store the dict in a list to come back to later temporary_dict.append(com_dict) logging.debug( "temporarily saved recurring event as dict") else: # if this is a regular event try: new_event = db.Event(**com_dict).save() except: # FIXME: bare except # noqa: E722 logging.exception("com_dict: %s", com_dict) continue if not new_event.labels: # if the event has no labels new_event.labels = ['unlabeled'] if 'recurrence' in new_event: # if the event has no recurrence_end if not new_event.recurrence.forever: new_event.recurrence_end = find_recurrence_end( new_event) logging.debug("made end_recurrence: %s", new_event.recurrence_end) new_event.save() # cycle through all the events in the temporary list for sub_event_dict in temporary_dict: normal_event = db.Event.objects(__raw__={ 'UID': sub_event_dict['UID'] }).first() create_sub_event(sub_event_dict, normal_event) logging.debug( "temporarily deferred sub_event now saved as mongodb object")