def add_fake_event(db_session, namespace_id, calendar=None, title='title', description='', location='', busy=False, read_only=False, reminders='', recurrence='', start=None, end=None, all_day=False): from inbox.models import Event start = start or datetime.utcnow() end = end or (datetime.utcnow() + timedelta(seconds=1)) calendar = calendar or add_fake_calendar(db_session, namespace_id) event = Event(namespace_id=namespace_id, calendar=calendar, title=title, description=description, location=location, busy=busy, read_only=read_only, reminders=reminders, recurrence=recurrence, start=start, end=end, all_day=all_day, raw_data='', uid=str(uuid.uuid4())) event.sequence_number = 0 db_session.add(event) db_session.commit() return event
def create(namespace, db_session, subject, body, location, reminders, recurrence, start, end, busy, all_day, participants): account = db_session.query(Account).filter( Account.id == namespace.account_id).one() event = Event( calendar=account.default_calendar, account_id=namespace.account_id, uid=uuid.uuid4().hex, provider_name=INBOX_PROVIDER_NAME, raw_data='', subject=subject, body=body, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, busy=busy, all_day=all_day, read_only=False, is_owner=True, source='local') event.participant_list = participants db_session.add(event) db_session.commit() return event
def create(namespace, db_session, subject, body, location, reminders, recurrence, start, end, busy, all_day, participants): event = Event( account_id=namespace.account_id, uid=uuid.uuid4().hex, provider_name=INBOX_PROVIDER_NAME, raw_data='', subject=subject, body=body, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, busy=busy, all_day=all_day, locked=False, time_zone=0, source='local') event.participant_list = participants db_session.add(event) db_session.commit() return event
def event_response(calendar_uid, sync_from_time): if calendar_uid == "first_calendar_uid": return [ Event(uid="first_event_uid", title="Plotting Meeting", **default_params), Event(uid="second_event_uid", title="Scheming meeting", **default_params), Event(uid="third_event_uid", title="Innocent Meeting", **default_params), ] else: return [ Event(uid="second_event_uid", title="Plotting Meeting", **default_params), Event(uid="third_event_uid", title="Scheming meeting", **default_params), ]
def noop_event_update(event, data): # Check whether the update is actually updating fields. # We do this by cloning the event, updating the fields and # comparing them. This is less cumbersome than having to think # about the multiple values of the `when` field. e = Event() e.update(event) e.namespace = event.namespace for attr in Event.API_MODIFIABLE_FIELDS: if attr in data: setattr(e, attr, data[attr]) e1 = encode(event) e2 = encode(e) for attr in Event.API_MODIFIABLE_FIELDS: # We have to handle participants a bit differently because # it's a list which can be permuted. if attr == 'participants': continue event_value = e1.get(attr) e_value = e2.get(attr) if event_value != e_value: return False e_participants = {p['email']: p for p in e.participants} event_participants = {p['email']: p for p in event.participants} if len(e_participants.keys()) != len(event_participants.keys()): return False for email in e_participants: if email not in event_participants: return False p1 = e_participants[email] p2 = event_participants[email] p1_status = p1.get('status') p2_status = p2.get('status') if p1_status != p2_status: return False p1_comment = p1.get('comment') p2_comment = p2.get('comment') if p1_comment != p2_comment: return False return True
def test_api_override_serialization(db, api_client, default_namespace, recurring_event): event = recurring_event override = Event(original_start_time=event.start, master_event_uid=event.uid, namespace_id=default_namespace.id, calendar_id=event.calendar_id) override.update(event) override.uid = event.uid + "_" + event.start.strftime("%Y%m%dT%H%M%SZ") override.master = event override.master_event_uid = event.uid override.cancelled = True db.session.add(override) db.session.commit() filter = 'starts_after={}&ends_before={}'.format( urlsafe(event.start.replace(hours=-1)), urlsafe(event.start.replace(weeks=+1))) events = api_client.get_data('/events?' + filter) # We should have the base event and the override back, but no extras; # this allows clients to do their own expansion, should they ever desire # to experience the joy that is RFC 2445 section 4.8.5.4. assert len(events) == 2 assert events[0].get('object') == 'event' assert events[0].get('recurrence') is not None assert events[1].get('object') == 'event' assert events[1].get('status') == 'cancelled'
def noop_event_update(event, data): # Check whether the update is actually updating fields. # We do this by cloning the event, updating the fields and # comparing them. This is less cumbersome than having to think # about the multiple values of the `when` field. e = Event() e.update(event) for attr in ['title', 'description', 'location', 'when', 'participants']: if attr in data: setattr(e, attr, data[attr]) for attr in ['title', 'description', 'location']: event_value = getattr(event, attr) e_value = getattr(e, attr) if event_value != e_value: return False for attr in ['start', 'end']: # This code can get datetimes and Arrow datetimes # so we convert everything to Arrow datetimes. event_value = arrow.get(getattr(event, attr)) e_value = arrow.get(getattr(e, attr)) if event_value != e_value: return False e_participants = {p['email']: p for p in e.participants} event_participants = {p['email']: p for p in event.participants} if len(e_participants.keys()) != len(event_participants.keys()): return False for email in e_participants: if email not in event_participants: return False p1 = e_participants[email] p2 = event_participants[email] p1_status = p1.get('status') p2_status = p2.get('status') if p1_status != p2_status: return False p1_comment = p1.get('comment') p2_comment = p2.get('comment') if p1_comment != p2_comment: return False return True
def event_response_with_participants_update(calendar_uid, sync_from_time): if calendar_uid == "first_calendar_uid": new_events = [Event(uid="first_event_uid", **default_params)] new_events[0].participants = [ {"name": "Johnny Thunders", "email": "*****@*****.**"} ] return new_events
def supply_event(self, subject, body='', when={'time': 0}, busy=True, location='', read_only=False, owner="", reminders='[]', recurrence="", deleted=False, raw_data='', is_owner=True, participants=[]): from inbox.models import Event self._events.append( Event(account_id=1, calendar_id=1, uid=str(self._next_uid), source='remote', provider_name=self.PROVIDER_NAME, subject=subject, body=body, location=location, when=when, busy=busy, is_owner=is_owner, owner=owner, read_only=read_only, raw_data=raw_data, reminders=reminders, recurrence=recurrence, deleted=deleted, participants=[])) self._next_uid += 1
def add_recurring_event(db, account): rrule = ["RRULE:FREQ=WEEKLY"] ev = db.session.query(Event).filter_by(uid='recurapitest').first() if ev: db.session.delete(ev) cal = db.session.query(Calendar).filter_by( namespace_id=account.namespace.id).order_by('id').first() ev = Event(namespace_id=account.namespace.id, calendar=cal, title='recurring-weekly', description='', uid='recurapitest', location='', busy=False, read_only=False, reminders='', recurrence=rrule, start=arrow.get(2015, 3, 17, 1, 30, 00), end=arrow.get(2015, 3, 17, 1, 45, 00), all_day=False, is_owner=True, participants=[], provider_name='inbox', raw_data='', original_start_tz='America/Los_Angeles', original_start_time=None, master_event_uid=None, source='local') db.session.add(ev) db.session.commit() return ev
def test_update_participant_status2(db, config): """Test the basic logic of the merge() function.""" base = _default_event(db) base.participants = [Participant(email_address="*****@*****.**", status="no")] dest = _default_event(db) dest.participants = [Participant(email_address="*****@*****.**", status="no")] participant1 = Participant(email_address="*****@*****.**", status="yes") remote = Event(account_id=ACCOUNT_ID, calendar=_default_calendar(db), subject='new subject', body='new body', location='new location', busy=True, read_only=True, reminders='', recurrence='', start=2, end=3, all_day=False, source='remote', participants=[participant1]) dest.merge_from(base, remote) assert len(dest.participants) == 1 assert dest.participants[0].status == 'yes'
def test_merge(config, event_sync): """Test the basic logic of the merge() function.""" base = _default_event() remote = Event(account_id=ACCOUNT_ID, subject='new subject', body='new body', location='new location', busy=True, locked=True, reminders='', recurrence='', start=2, end=3, all_day=False, time_zone=0, source='remote') dest = _default_event() event_sync.merge(base, remote, dest) assert dest.subject == 'new subject' assert dest.body == 'new body' assert dest.location == 'new location' assert dest.busy assert dest.locked assert dest.start == 2 assert dest.end == 3
def event_response_with_update(calendar_uid, sync_from_time): if calendar_uid == "first_calendar_uid": return [ Event.create(uid="first_event_uid", title="Top Secret Plotting Meeting", **default_params) ]
def event_response_with_delete(calendar_uid, sync_from_time): if calendar_uid == "first_calendar_uid": return [ Event.create(uid="first_event_uid", status="cancelled", **default_params) ]
def supply_event(self, subject, body, start, end, all_day, busy, location='', time_zone=0, locked=False, reminders='[]', recurrence="", deleted=False, raw_data=''): from inbox.models import Event from datetime import datetime self._events.append( Event(account_id=1, uid=str(self._next_uid), source='remote', provider_name=self.PROVIDER_NAME, subject=subject, body=body, location=location, start=datetime.utcfromtimestamp(start), end=datetime.utcfromtimestamp(end), all_day=all_day, time_zone=time_zone, busy=busy, locked=locked, raw_data=raw_data, reminders=reminders, recurrence=recurrence, deleted=deleted)) self._next_uid += 1
def event_response_with_update(calendar_uid, sync_from_time): if calendar_uid == 'first_calendar_uid': return [ Event(uid='first_event_uid', title='Top Secret Plotting Meeting', **default_params) ]
def test_merge(db, config, event_sync): """Test the basic logic of the merge() function.""" base = _default_event(db) remote = Event(account_id=ACCOUNT_ID, calendar=_default_calendar(db), subject='new subject', body='new body', location='new location', busy=True, read_only=True, reminders='', recurrence='', start=2, end=3, all_day=False, source='remote') dest = _default_event(db) dest.merge_from(base, remote) assert dest.subject == 'new subject' assert dest.body == 'new body' assert dest.location == 'new location' assert dest.busy assert dest.read_only assert dest.start == 2 assert dest.end == 3
def recurring_event(db, default_namespace, request): params = request.param all_day = params.get('all_day', False) rrule = ["RRULE:FREQ=WEEKLY", "EXDATE:20150324T013000,20150331T013000Z"] cal = db.session.query(Calendar).filter_by( namespace_id=default_namespace.id).order_by('id').first() ev = Event(namespace_id=default_namespace.id, calendar=cal, title='recurring-weekly', description='', uid='recurapitest', location='', busy=False, read_only=False, reminders='', recurrence=rrule, start=arrow.get(2015, 3, 17, 1, 30, 00), end=arrow.get(2015, 3, 17, 1, 45, 00), all_day=all_day, is_owner=True, participants=[], provider_name='inbox', raw_data='', original_start_tz='America/Los_Angeles', original_start_time=None, master_event_uid=None, source='local') db.session.add(ev) db.session.commit() return ev
def event_response_with_participants_update(calendar_uid, sync_from_time): if calendar_uid == 'first_calendar_uid': new_events = [Event(uid='first_event_uid', **default_params)] new_events[0].participants = [{ 'name': 'Johnny Thunders', 'email': '*****@*****.**' }] return new_events
def test_initial(db): remote = default_event(db.session) remote.participant_list = [{ 'email': '*****@*****.**', 'status': 'noreply' }, { 'email': '*****@*****.**', 'status': 'noreply' }, { 'email': '*****@*****.**', 'status': 'noreply' }] local = Event(namespace_id=NAMESPACE_ID, calendar=default_calendar(db.session), provider_name='inbox', raw_data='', read_only=False, all_day=False, source='local') local.copy_from(remote) local.source = 'local' assert len(local.participants) == 3 assert len(remote.participants) == 3 db.session.add_all([local, remote]) local.copy_from(remote) assert len(local.participants) == 3 assert len(remote.participants) == 3
def event_create_api(): g.parser.add_argument('notify_participants', type=strict_bool, location='args') args = strict_parse_args(g.parser, request.args) notify_participants = args['notify_participants'] data = request.get_json(force=True) calendar = get_calendar(data.get('calendar_id'), g.namespace, g.db_session) if calendar.read_only: raise InputError("Can't create events on read_only calendar.") valid_event(data) title = data.get('title', '') description = data.get('description') location = data.get('location') when = data.get('when') busy = data.get('busy') # client libraries can send explicit key = None automagically if busy is None: busy = True participants = data.get('participants') if participants is None: participants = [] for p in participants: if 'status' not in p: p['status'] = 'noreply' event = Event(calendar=calendar, namespace=g.namespace, uid=uuid.uuid4().hex, provider_name=g.namespace.account.provider, raw_data='', title=title, description=description, location=location, busy=busy, when=when, read_only=False, is_owner=True, participants=participants, sequence_number=0, source='local') g.db_session.add(event) g.db_session.flush() schedule_action('create_event', event, g.namespace.id, g.db_session, calendar_uid=event.calendar.uid, notify_participants=notify_participants) return g.encoder.jsonify(event)
def parse_event(self, event, extra): try: uid = str(event['guid']) # The entirety of the raw event data in json representation. raw_data = str(event) subject = event.get('title', '')[:SUBJECT_MAX_LEN] body = event.get('description', None) location = event.get('location', None) if location: location = location[:LOCATION_MAX_LEN] all_day = event.get('allDay', False) read_only = event.get('readOnly') # for some reason iCloud gives the date as YYYYMMDD for the first # entry and then the Y, M, D, H, S as later entries. start_date = event['startDate'][1:] end_date = event['endDate'][1:] start = datetime.datetime(*start_date[:-1]) end = datetime.datetime(*end_date[:-1]) recurrence = event['recurrence'] # iCloud doesn't give us busy information busy = True # reminder format is super-funky, punt for now -cg3 reminders = str([]) # and for some reason iCloud isn't giving us participants participants = [] except (KeyError, AttributeError): raise MalformedEventError() return Event(account_id=self.account_id, uid=uid, provider_name=self.PROVIDER_NAME, raw_data=raw_data, subject=subject, body=body, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, busy=busy, all_day=all_day, read_only=read_only, source='remote', is_owner=True, participants=participants)
def create(namespace, db_session, calendar, title, description, location, reminders, recurrence, when, participants): event = Event(calendar=calendar, namespace=namespace, uid=uuid.uuid4().hex, provider_name=INBOX_PROVIDER_NAME, raw_data='', title=title, description=description, location=location, when=when, read_only=False, is_owner=True, source='local') event.participant_list = participants db_session.add(event) db_session.commit() return event
def create(namespace, db_session, subject, body, location, reminders, recurrence, when, participants): account = db_session.query(Account).filter( Account.id == namespace.account_id).one() event = Event(calendar=account.default_calendar, account_id=namespace.account_id, uid=uuid.uuid4().hex, provider_name=INBOX_PROVIDER_NAME, raw_data='', subject=subject, body=body, location=location, when=when, read_only=False, is_owner=True, source='local') event.participant_list = participants db_session.add(event) db_session.commit() return event
def create(namespace, db_session, calendar, title, description, location, reminders, recurrence, when, participants): event = Event( calendar=calendar, account_id=namespace.account_id, uid=uuid.uuid4().hex, provider_name=INBOX_PROVIDER_NAME, raw_data='', title=title, description=description, location=location, when=when, read_only=False, is_owner=True, source='local') event.participant_list = participants db_session.add(event) db_session.commit() return event
def event_response(calendar_uid, sync_from_time): if calendar_uid == 'first_calendar_uid': return [ Event(uid='first_event_uid', title='Plotting Meeting', **default_params), Event(uid='second_event_uid', title='Scheming meeting', **default_params), Event(uid='third_event_uid', title='Innocent Meeting', **default_params) ] else: return [ Event(uid='second_event_uid', title='Plotting Meeting', **default_params), Event(uid='third_event_uid', title='Scheming meeting', **default_params) ]
def test_merge_conflict(db, config, event_sync): """Test that merge() raises an error on conflict.""" base = _default_event(db) remote = Event(account_id=ACCOUNT_ID, calendar=_default_calendar(db), subject='new subject', body='new body', location='new location', busy=False, read_only=True, reminders='', recurrence='', start=2, end=3, all_day=False, source='remote') dest = Event(account_id=ACCOUNT_ID, calendar=_default_calendar(db), subject='subject2', body='body2', location='location2', busy=False, read_only=False, reminders='', recurrence='', start=0, end=1, all_day=False, source='remote') with pytest.raises(MergeError): dest.merge_from(base, remote) # Check no update in case of conflict assert dest.subject == 'subject2' assert dest.body == 'body2' assert dest.location == 'location2'
def _default_event(db): return Event(account_id=ACCOUNT_ID, calendar=_default_calendar(db), subject='subject', body='', location='', busy=False, read_only=False, reminders='', recurrence='', start=0, end=1, all_day=False, source='remote')
def _default_event(): return Event(account_id=ACCOUNT_ID, subject='subject', body='', location='', busy=False, locked=False, reminders='', recurrence='', start=0, end=1, all_day=False, time_zone=0, source='remote')
def event_create_api(): data = request.get_json(force=True) calendar = get_calendar(data.get('calendar_id'), g.namespace, g.db_session) if calendar.read_only: raise InputError("Can't create events on read_only calendar.") valid_event(data) title = data.get('title', '') description = data.get('description') location = data.get('location') when = data.get('when') busy = data.get('busy', True) participants = data.get('participants', []) for p in participants: if 'status' not in p: p['status'] = 'noreply' event = Event(calendar=calendar, namespace=g.namespace, uid=uuid.uuid4().hex, provider_name=g.namespace.account.provider, raw_data='', title=title, description=description, location=location, busy=busy, when=when, read_only=False, is_owner=True, participants=participants, source='local') g.db_session.add(event) g.db_session.flush() schedule_action('create_event', event, g.namespace.id, g.db_session, calendar_uid=event.calendar.uid) return g.encoder.jsonify(event)
def test_handle_offset_all_day_events(): raw_event = { 'created': '2014-01-09T03:33:02.000Z', 'creator': { 'displayName': 'Ben Bitdiddle', 'email': '*****@*****.**', 'self': True }, 'etag': '"2778476764000000"', 'htmlLink': 'https://www.google.com/calendar/event?eid=BAR', 'iCalUID': '*****@*****.**', 'id': '20140615_60o30dr564o30c1g60o30dr4ck', 'kind': 'calendar#event', 'organizer': { 'displayName': 'Ben Bitdiddle', 'email': '*****@*****.**', 'self': True }, 'sequence': 0, 'start': { 'date': '2014-03-15' }, 'end': { u'date': '2014-03-15' }, 'status': 'confirmed', 'summary': 'Ides of March', 'transparency': 'transparent', 'updated': '2014-01-09T03:33:02.000Z', 'visibility': 'public' } expected = Event(uid='20140615_60o30dr564o30c1g60o30dr4ck', title='Ides of March', description=None, read_only=False, busy=False, start=arrow.get(2014, 03, 15), end=arrow.get(2014, 03, 15), all_day=True, owner='Ben Bitdiddle <*****@*****.**>', participants=[]) assert cmp_event_attrs(expected, parse_event_response(raw_event))
def create(namespace, db_session, subject, body, location, reminders, recurrence, start, end, busy, all_day): event = Event(account_id=namespace.account_id, uid=uuid.uuid4().hex, provider_name=INBOX_PROVIDER_NAME, raw_data='', subject=subject, body=body, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, busy=busy, all_day=all_day, locked=False, time_zone=0, source='local') db_session.add(event) db_session.commit() return event
def default_event(db): cal = default_calendar(db) ev = Event(namespace_id=NAMESPACE_ID, calendar=cal, title='title', description='', location='', busy=False, read_only=False, reminders='', recurrence='', start=START, end=END, all_day=False, provider_name='inbox', raw_data='', source='remote') db.session.add(ev) db.session.commit() return ev
def test_initial(db): remote = _default_event() remote.participant_list = [ {'email': '*****@*****.**', 'status': 'awaiting'}, {'email': '*****@*****.**', 'status': 'awaiting'}, {'email': '*****@*****.**', 'status': 'awaiting'}] local = Event() local.copy_from(remote) local.source = 'local' assert len(local.participants) == 3 assert len(remote.participants) == 3 db.session.add_all([local, remote]) local.copy_from(remote) assert len(local.participants) == 3 assert len(remote.participants) == 3
def test_initial(db): remote = _default_event(db) remote.participant_list = [ {'email': '*****@*****.**', 'status': 'noreply'}, {'email': '*****@*****.**', 'status': 'noreply'}, {'email': '*****@*****.**', 'status': 'noreply'}] local = Event(account_id=ACCOUNT_ID, calendar=_default_calendar(db)) local.copy_from(remote) local.source = 'local' assert len(local.participants) == 3 assert len(remote.participants) == 3 db.session.add_all([local, remote]) local.copy_from(remote) assert len(local.participants) == 3 assert len(remote.participants) == 3
def test_initial(db): remote = default_event(db) remote.participant_list = [ {'email': '*****@*****.**', 'status': 'noreply'}, {'email': '*****@*****.**', 'status': 'noreply'}, {'email': '*****@*****.**', 'status': 'noreply'}] local = Event(account_id=ACCOUNT_ID, calendar=default_calendar(db), provider_name='inbox', raw_data='', read_only=False, all_day=False, source='local') local.copy_from(remote) local.source = 'local' assert len(local.participants) == 3 assert len(remote.participants) == 3 db.session.add_all([local, remote]) local.copy_from(remote) assert len(local.participants) == 3 assert len(remote.participants) == 3
def events_from_ics(namespace, calendar, ics_str): try: cal = Calendar.from_ical(ics_str) except ValueError: raise MalformedEventError() events = [] for component in cal.walk(): if component.name == "VEVENT": start = component.get('dtstart').dt end = component.get('dtend').dt subject = component.get('summary') body = str(component.get('description')) if isinstance(start, datetime): all_day = False else: all_day = True start = datetime.combine(start, datetime.min.time()) end = datetime.combine(end, datetime.min.time()) reccur = component.get('rrule') if reccur: reccur = reccur.to_ical() else: reccur = '' participants = [] for attendee in component.get('attendee'): email = str(attendee) # strip mailto: if it exists if email.startswith('mailto:'): email = email[7:] try: name = attendee.params['CN'] except KeyError: name = None status_map = {'NEEDS-ACTION': 'noreply', 'ACCEPTED': 'yes', 'DECLINED': 'no', 'TENTATIVE': 'maybe'} status = 'noreply' try: a_status = attendee.params['PARTSTAT'] status = status_map[a_status] except KeyError: pass notes = None try: guests = attendee.params['X-NUM-GUESTS'] notes = "Guests: {}".format(guests) except KeyError: pass participant = Participant(email_address=email, status=status, name=name, notes=notes) participants.append(participant) location = component.get('location') organizer = component.get('organizer') if(organizer): organizer = str(organizer) if organizer.startswith('mailto:'): organizer = organizer[7:] uid = str(component.get('uid')) event = Event( account_id=namespace.account_id, calendar=calendar, uid=uid, provider_name='ics', raw_data=component.to_ical(), subject=subject, body=body, location=location, reminders=str([]), recurrence=reccur, start=start, end=end, busy=True, all_day=all_day, read_only=True, source='local') event.participants = participants events.append(event) return events