def test_event_accessor__sync_uid(self): self.request.set('HTTP_HOST', 'nohost') e1 = createContentInContainer( self.portal, 'plone.app.event.dx.event', title='event1' ) acc = IEventAccessor(e1) # setting no sync uid will automatically generate one self.assertTrue(acc.sync_uid, IUUID(e1) + '@nohost') # it's not stored on the object though self.assertEqual(e1.sync_uid, None) # but it's indexed result = self.portal.portal_catalog(sync_uid=IUUID(e1) + '@nohost') self.assertEqual(len(result), 1) # Setting the sync_uid acc.sync_uid = 'okay' e1.reindexObject() self.assertEqual(acc.sync_uid, 'okay') # Now, it's also stored on the object itself self.assertEqual(e1.sync_uid, 'okay') # and indexed result = self.portal.portal_catalog(sync_uid='okay') self.assertEqual(len(result), 1)
def test_event_accessor(self): obj = MockObject() tz = pytz.timezone('Europe/Vienna') obj.start = datetime(2012, 12, 12, 10, 0, tzinfo=tz) obj.end = datetime(2012, 12, 12, 12, 0, tzinfo=tz) zope.interface.alsoProvides(obj, IEvent) # Create accessor acc = IEventAccessor(obj) # Accessor getters self.assertEqual(acc.start, obj.start) self.assertEqual(acc.end, obj.end) self.assertEqual(acc.duration, obj.end - obj.start) # Accessor setters start = datetime(2013, 4, 5, 16, 31, tzinfo=tz) end = datetime(2013, 4, 5, 16, 35, tzinfo=tz) acc.start = start acc.end = end self.assertTrue(acc.start == obj.start == start) self.assertTrue(acc.end == obj.end == end) # Accessor deletor acc.something = True self.assertTrue(acc.something == obj.something is True) del acc.something self.assertTrue(hasattr(acc, 'something') is False) self.assertTrue(hasattr(obj, 'something') is False) del acc.start self.assertTrue(hasattr(acc, 'start') is False) self.assertTrue(hasattr(obj, 'start') is False)
def migrate_schema_fields(self): newacc = IEventAccessor(self.new) newacc.start = self.old.start_date newacc.end = self.old.end_date newacc.timezone = str(self.old.start_date.tzinfo) \ if self.old.start_date.tzinfo \ else default_timezone(fallback='UTC') if hasattr(self.old, 'location'): newacc.location = self.old.location if hasattr(self.old, 'attendees'): newacc.attendees = tuple(self.old.attendees.splitlines()) if hasattr(self.old, 'event_url'): newacc.event_url = self.old.event_url if hasattr(self.old, 'contact_name'): newacc.contact_name = self.old.contact_name if hasattr(self.old, 'contact_email'): newacc.contact_email = self.old.contact_email if hasattr(self.old, 'contact_phone'): newacc.contact_phone = self.old.contact_phone if hasattr(self.old, 'text'): # Copy the entire richtext object, not just it's representation IEventSummary(self.new).text = self.old.text # Trigger ObjectModified, so timezones can be fixed up. notify(ObjectModifiedEvent(self.new))
def test_import_from_ics__no_sync(self): """SYNC_NONE and importing the same file again should create new event objects and give them each a new sync_uid. """ self.portal.invokeFactory('Folder', 'impfolder2') impfolder = self.portal.impfolder2 directory = os.path.dirname(__file__) icsfile = open(os.path.join(directory, 'icaltest.ics'), 'rb').read() res = ical_import(impfolder, icsfile, self.event_type) self.assertEqual(res['count'], 5) e11 = impfolder['e1'] suid1 = IEventAccessor(e11).sync_uid res = ical_import(impfolder, icsfile, self.event_type, sync_strategy=base.SYNC_NONE) self.assertEqual(res['count'], 5) e12 = impfolder['e1-1'] suid2 = IEventAccessor(e12).sync_uid self.assertEqual(len(impfolder.contentIds()), 10) self.assertNotEqual(suid1, suid2)
def test_event_accessor(self): utc = pytz.utc self.portal.invokeFactory('plone.app.event.dx.event', 'event1', start=datetime(2011, 11, 11, 11, 0, tzinfo=utc), end=datetime(2011, 11, 11, 12, 0, tzinfo=utc), timezone='UTC', whole_day=False) e1 = self.portal['event1'] # setting attributes via the accessor acc = IEventAccessor(e1) acc.end = datetime(2011, 11, 13, 10, 0) acc.timezone = TZNAME tz = pytz.timezone(TZNAME) # accessor should return end datetime in the event's timezone self.assertTrue(acc.end == datetime(2011, 11, 13, 11, 0, tzinfo=tz)) # the behavior's end datetime is stored in utc on the content object self.assertTrue(e1.end == datetime(2011, 11, 13, 10, 0, tzinfo=utc)) # accessing the end property via the behavior adapter, returns the # value converted to the event's timezone self.assertTrue(IEventBasic(e1).end == datetime(2011, 11, 13, 11, 0, tzinfo=tz)) # timezone should be the same on the event object and accessor self.assertTrue(e1.timezone == acc.timezone) self.portal.manage_delObjects(['event1'])
def test_event_accessor__sync_uid(self): self.request.set('HTTP_HOST', 'nohost') e1 = createContentInContainer( self.portal, 'plone.app.event.dx.event', title=u'event1' ) acc = IEventAccessor(e1) # setting no sync uid will automatically generate one self.assertTrue(acc.sync_uid, IUUID(e1) + '@nohost') # it's not stored on the object though self.assertEqual(e1.sync_uid, None) # but it's indexed result = self.portal.portal_catalog(sync_uid=IUUID(e1) + '@nohost') self.assertEqual(len(result), 1) # Setting the sync_uid acc.sync_uid = 'okay' e1.reindexObject() self.assertEqual(acc.sync_uid, 'okay') # Now, it's also stored on the object itself self.assertEqual(e1.sync_uid, 'okay') # and indexed result = self.portal.portal_catalog(sync_uid='okay') self.assertEqual(len(result), 1)
def test_import_from_ics__sync_keep_mine(self): """SYNC_KEEP_MINE and importing the same file again should do nothing. """ self.portal.invokeFactory('Folder', 'impfolder3') impfolder = self.portal.impfolder3 directory = os.path.dirname(__file__) icsfile = open(os.path.join(directory, 'icaltest.ics'), 'rb').read() res = ical_import(impfolder, icsfile, self.event_type) self.assertEqual(res['count'], 5) e1a = IEventAccessor(impfolder.e1) mod1 = e1a.last_modified suid1 = e1a.sync_uid res = ical_import(impfolder, icsfile, self.event_type, sync_strategy=base.SYNC_KEEP_MINE) self.assertEqual(res['count'], 0) e1a = IEventAccessor(impfolder.e1) mod2 = e1a.last_modified suid2 = e1a.sync_uid self.assertEqual(len(impfolder.contentIds()), 5) self.assertEqual(mod1, mod2) self.assertEqual(suid1, suid2)
def test_event_accessor(self): utc = pytz.utc self.portal.invokeFactory( 'plone.app.event.dx.event', 'event1', start=datetime(2011, 11, 11, 11, 0, tzinfo=utc), end=datetime(2011, 11, 11, 12, 0, tzinfo=utc), timezone='UTC', whole_day=False ) e1 = self.portal['event1'] # setting attributes via the accessor acc = IEventAccessor(e1) acc.end = datetime(2011, 11, 13, 10, 0) acc.timezone = TZNAME tz = pytz.timezone(TZNAME) # accessor should return end datetime in the event's timezone self.assertTrue(acc.end == datetime(2011, 11, 13, 11, 0, tzinfo=tz)) # the behavior's end datetime is stored in utc on the content object self.assertTrue(e1.end == datetime(2011, 11, 13, 10, 0, tzinfo=utc)) # accessing the end property via the behavior adapter, returns the # value converted to the event's timezone self.assertTrue( IEventBasic(e1).end == datetime(2011, 11, 13, 11, 0, tzinfo=tz) ) # timezone should be the same on the event object and accessor self.assertTrue(e1.timezone == acc.timezone)
def test_event_accessor__start_end(self): e1 = createContentInContainer( self.portal, 'plone.app.event.dx.event', title=u'event1' ) dt = datetime(2161, 1, 1) # United Federation of Planets DT = DateTime('2161/01/01 00:00:00 UTC') acc = IEventAccessor(e1) # Setting a timezone-naive datetime should convert it to UTC acc.start = dt self.assertEqual(acc.start, utils.utc(dt)) self.assertEqual(e1.start, utils.utc(dt)) # Setting a DateTime should convert it to datetime acc.start = DT self.assertEqual(acc.start, utils.utc(dt)) self.assertEqual(e1.start, utils.utc(dt)) # Same goes for acc.end # Setting a timezone-naive datetime should convert it to UTC acc.end = dt self.assertEqual(acc.end, utils.utc(dt)) self.assertEqual(e1.end, utils.utc(dt)) # Setting a DateTime should convert it to datetime acc.end = DT self.assertEqual(acc.end, utils.utc(dt)) self.assertEqual(e1.end, utils.utc(dt))
def test_import_from_ics(self): # Ical import unit test. self.portal.invokeFactory('Folder', 'impfolder1') impfolder = self.portal.impfolder1 directory = os.path.dirname(__file__) icsfile = open(os.path.join(directory, 'icaltest.ics'), 'rb').read() res = ical_import(impfolder, icsfile, self.event_type) self.assertEqual(res['count'], 5) self.assertEqual(len(impfolder.contentIds()), 5) at = pytz.timezone('Europe/Vienna') utc = pytz.utc # Use pydt to normalize for DST times. # TODO: test for attendees. see note in # plone.app.event.ical.importer.ical_import e1 = IEventAccessor(impfolder.e1) self.assertEqual(e1.start, at.localize(datetime(2013, 7, 19, 12, 0))) self.assertEqual(e1.end, at.localize(datetime(2013, 7, 20, 13, 0))) self.assertEqual(e1.description, 'A basic event with many properties.') self.assertEqual(e1.whole_day, False) self.assertEqual(e1.open_end, False) self.assertEqual( e1.sync_uid, u'48f1a7ad64e847568d860cd092344970', ) e2 = IEventAccessor(impfolder.e2) self.assertEqual(e2.start, utc.localize(datetime(1996, 4, 1, 1, 0))) self.assertEqual(e2.end, utc.localize(datetime(1996, 4, 1, 2, 0))) self.assertEqual( e2.recurrence, u'RRULE:FREQ=DAILY;COUNT=100\nEXDATE:19960402T010000Z,' u'19960403T010000Z,19960404T010000Z') e3 = IEventAccessor(impfolder.e3) self.assertEqual(e3.start, at.localize(datetime(2012, 3, 27, 10, 0))) self.assertEqual(e3.end, at.localize(datetime(2012, 3, 27, 18, 0))) self.assertEqual( e3.recurrence, u'RRULE:FREQ=WEEKLY;UNTIL=20120703T080000Z;BYDAY=TU\n' u'EXDATE:20120529T100000,20120403T100000,20120410T100000,' u'20120501T100000,20120417T100000') e4 = IEventAccessor(impfolder.e4) self.assertEqual(e4.start, utc.localize(datetime(2013, 4, 4, 0, 0))) self.assertEqual(e4.end, utc.localize(datetime(2013, 4, 4, 23, 59, 59))) self.assertEqual(e4.whole_day, True) self.assertEqual(e4.open_end, False) e5 = IEventAccessor(impfolder.e5) self.assertEqual(e5.start, utc.localize(datetime(2013, 4, 2, 12, 0))) self.assertEqual(e5.end, utc.localize(datetime(2013, 4, 2, 23, 59, 59))) self.assertEqual(e5.whole_day, False) self.assertEqual(e5.open_end, True)
def updateObject(self, context, v): # Get the accessor object acc = IEventAccessor(context) # Establish the input arguments kwargs = self.getRequestDataAsArguments(v) # Pass the arguments into the object acc.edit(**kwargs) # Update any arguments that are not part of the default event schema acc.update(**kwargs) # Update complex fields (text, event agenda) self.updateComplexFields(acc.context, v) # Reindex the object acc.context.reindexObject() # Ref: http://docs.plone.org/external/plone.app.event/docs/development.html#accessing-event-objects-via-an-unified-accessor-object # Throw ObjectModifiedEvent after setting properties to call an event subscriber which does some timezone related post calculations notify(ObjectModifiedEvent(acc.context)) # Return object that was updated return acc.context
def test_event_accessor(self): utc = pytz.utc self.portal.invokeFactory('Event', 'event1', description='a description', start=datetime(2011,11,11,11,0, tzinfo=utc), end=datetime(2011,11,11,12,0, tzinfo=utc), timezone='UTC', wholeDay=False) e1 = self.portal['event1'] # setting attributes via the accessor acc = IEventAccessor(e1) acc.end = datetime(2011,11,13,10,0, tzinfo=utc) acc.timezone = 'Europe/Vienna' vienna = pytz.timezone('Europe/Vienna') # test description self.assertTrue(acc.description == 'a description') acc.description = 'another desc' self.assertTrue(acc.description == 'another desc') # accessor should return end datetime in the event's timezone self.assertTrue(acc.end == datetime(2011,11,13,11,0, tzinfo=vienna)) # start/end dates are stored in UTC zone on the context, but converted # to event's timezone via the attribute getter. self.assertTrue(e1.end() == DateTime('2011/11/13 11:00:00 Europe/Vienna')) # timezone should be the same on the event object and accessor self.assertTrue(e1.getTimezone() == acc.timezone) self.portal.manage_delObjects(['event1'])
def create( cls, container, content_id, title, description=None, start=None, end=None, timezone=None, whole_day=None, open_end=None, **kwargs ): container.invokeFactory( cls.event_type, id=content_id, title=title, description=description, startDate=start, endDate=end, wholeDay=whole_day, open_end=open_end, timezone=timezone, ) content = container[content_id] acc = IEventAccessor(content) acc.edit(**kwargs) return acc
def test_data_postprocessing(self): # Addressing bug #62 self.portal.invokeFactory( 'plone.app.event.dx.event', 'event1', start=datetime(2012, 10, 19, 0, 30), end=datetime(2012, 10, 19, 1, 30), timezone="Europe/Vienna", whole_day=False ) e1 = self.portal['event1'] e1.reindexObject() # Prepare reference objects tzname_1 = "Europe/Vienna" tz_1 = pytz.timezone(tzname_1) dt_1 = tz_1.localize(datetime(2012, 10, 19, 0, 30)) dt_1_1 = tz_1.localize(datetime(2012, 10, 19, 0, 0)) dt_1_2 = tz_1.localize(datetime(2012, 10, 19, 23, 59, 59)) tzname_2 = "Hongkong" tz_2 = pytz.timezone(tzname_2) dt_2 = tz_2.localize(datetime(2012, 10, 19, 0, 30)) dt_2_1 = tz_2.localize(datetime(2012, 10, 19, 0, 0)) dt_2_2 = tz_2.localize(datetime(2012, 10, 19, 23, 59, 59)) # See, if start isn't moved by timezone offset. Addressing issue #62 self.assertTrue(IEventBasic(e1).start == dt_1) notify(ObjectModifiedEvent(e1)) self.assertTrue(IEventBasic(e1).start == dt_1) # After timezone changes, only the timezone should be applied, but the # date and time values not converted. IEventAccessor(e1).timezone = tzname_2 notify(ObjectModifiedEvent(e1)) self.assertTrue(IEventBasic(e1).start == dt_2) # Test open_end events # For open_end events, setting the end date has no effect IEventAccessor(e1).edit( timezone=tzname_1, open_end=True, end=datetime(2012, 11, 11, 10, 10, 0), ) notify(ObjectModifiedEvent(e1)) self.assertTrue(IEventBasic(e1).start == dt_1) self.assertTrue(IEventBasic(e1).end == dt_1_2) # Likewise with whole_day events. If values were converted, the days # would drift apart. IEventAccessor(e1).whole_day = True notify(ObjectModifiedEvent(e1)) self.assertTrue(IEventBasic(e1).start == dt_1_1) self.assertTrue(IEventBasic(e1).end == dt_1_2) IEventAccessor(e1).timezone = tzname_2 notify(ObjectModifiedEvent(e1)) self.assertTrue(IEventBasic(e1).start == dt_2_1) self.assertTrue(IEventBasic(e1).end == dt_2_2)
def set_allDay(self, v): v = bool(v) if HAS_PAE: if IEvent.providedBy(self.context): acc = IEventAccessor(self.context) acc.whole_day = v return self._all_day = v
def test_occurrence_accessor(self): start = self.now end = self.future occ = Occurrence('ignored', start, end) occ = occ.__of__(self.now_event) acc_occ = IEventAccessor(occ) acc_ctx = IEventAccessor(self.now_event) self.assertEqual(acc_occ.start, acc_ctx.start) self.assertEqual(acc_occ.url, 'http://nohost/plone/now/ignored')
def onEventsFolderCreate(context, event, sample=True): # restrict what this folder can contain behavior = ISelectableConstrainTypes(context) behavior.setConstrainTypesMode(constrains.ENABLED) behavior.setImmediatelyAddableTypes(['Event']) behavior.setLocallyAllowedTypes(['Event', 'Collection']) # Create sample event and set publishing date to 01-01-YYYY if sample: # Calculate dates now = DateTime() start_date = DateTime() + 30 end_date = start_date + 1.0 / 24 item = createContentInContainer(context, "Event", id="sample", title="Sample Event", description="This is a sample Event", checkConstraints=False) item.text = RichTextValue(raw='<p>You may delete this item</p>', mimeType=u'text/html', outputMimeType='text/x-html-safe') item.setEffectiveDate(now) acc = IEventAccessor(item) acc.start = localize(start_date) acc.end = localize(end_date) # create 'upcoming' collection if 'upcoming' not in context.objectIds(): item = createContentInContainer( context, "Collection", id="upcoming", title='Upcoming Events', ) item.setQuery([{ u'i': u'path', u'o': u'plone.app.querystring.operation.string.absolutePath', u'v': u'%s::1' % context.UID() }, { u'i': u'portal_type', u'o': u'plone.app.querystring.operation.selection.any', u'v': [u'Event'] }]) item.setSort_on('start') # Set default page to the latest news collection context.setDefaultPage('upcoming')
def test_data_postprocessing(self): # TODO: since we use an IEventAccessor here, this is a possible # canditate for merging with # the test_dxevent.TestDXIntegration.test_data_postprocessing test. # Addressing bug #62 self.portal.invokeFactory('Event', 'ate1', startDate=DateTime(2012,10,19,0,30), endDate=DateTime(2012,10,19,1,30), timezone="Europe/Vienna", wholeDay=False) e1 = self.portal['ate1'] e1.reindexObject() acc = IEventAccessor(e1) # Prepare reference objects tzname_1 = "Europe/Vienna" tz_1 = pytz.timezone(tzname_1) dt_1 = tz_1.localize(datetime(2012,10,19,0,30)) dt_1_1 = tz_1.localize(datetime(2012,10,19,0,0)) dt_1_2 = tz_1.localize(datetime(2012,10,19,23,59,59)) tzname_2 = "Hongkong" tz_2 = pytz.timezone(tzname_2) dt_2 = tz_2.localize(datetime(2012,10,19,0,30)) dt_2_1 = tz_2.localize(datetime(2012,10,19,0,0)) dt_2_2 = tz_2.localize(datetime(2012,10,19,23,59,59)) # See, if start isn't moved by timezone offset. self.assertTrue(acc.start == dt_1) notify(ObjectModifiedEvent(e1)) self.assertTrue(acc.start == dt_1) # After timezone changes, only the timezone should be applied, but the # date and time values not converted. acc.timezone = tzname_2 notify(ObjectModifiedEvent(e1)) self.assertTrue(acc.start == dt_2) # Likewise with wholeDay events. If values were converted, the days # would drift apart. acc.whole_day = True acc.timezone = tzname_1 notify(ObjectModifiedEvent(e1)) self.assertTrue(acc.start == dt_1_1) self.assertTrue(acc.end == dt_1_2) acc.timezone = tzname_2 notify(ObjectModifiedEvent(e1)) self.assertTrue(acc.start == dt_2_1) self.assertTrue(acc.end == dt_2_2) self.portal.manage_delObjects(['ate1'])
def setUp(self): self.request = self.layer['request'] portal = self.layer['portal'] setRoles(portal, TEST_USER_ID, ['Manager']) portal.invokeFactory('Folder', id='events', title=u"Events", Description=u"The portal's Events") portal.events.invokeFactory('Event', id='ploneconf2007', title='Plone Conf 2007', startDate='2007/10/10', endDate='2007/10/12', location='Naples', eventUrl='http://plone.org/events/conferences/2007-naples', attendees=['anne','bob','cesar']) portal.events.invokeFactory('Event', id='ploneconf2008', title='Plone Conf 2008', startDate='2008/10/08', endDate='2008/10/10', location='DC', recurrence=u'RRULE:FREQ=DAILY;COUNT=5\r\nEXDATE:20081011T000000,20081012T000000\r\nRDATE:20081007T000000', eventUrl='http://plone.org/events/conferences/2008-washington-dc') portal.events.invokeFactory('plone.app.event.dx.event', id='ploneconf2012', title='Plone Conf 2012', recurrence=u'RRULE:FREQ=DAILY;COUNT=5\r\nEXDATE:20121013T000000,20121014T000000\r\nRDATE:20121009T000000', start=datetime(2012,10,10,8,0), end=datetime(2012,10,10,18,0), timezone='Europe/Amsterdam') pc12 = IEventAccessor(portal.events.ploneconf2012) pc12.location='Arnhem' pc12.contact_name='Four Digits' pc12.contact_email='*****@*****.**' notify(ObjectModifiedEvent(pc12)) portal.events.invokeFactory('plone.app.event.dx.event', id='artsprint2013', title='Artsprint 2013', start=datetime(2013,2,18), end=datetime(2012,2,22), whole_day=True, timezone='Europe/Vienna') portal.invokeFactory("Collection", "collection", title="New Collection", sort_on='start') portal['collection'].setQuery([{ 'i': 'Type', 'o': 'plone.app.querystring.operation.string.is', 'v': 'Event', }, ]) self.portal = portal
def setup_event(site): """Set the default start and end event properties.""" folder = site['institucional']['eventos'] event = folder['1o-ano-do-site'] acc = IEventAccessor(event) future = datetime.now() + relativedelta(years=1) year = future.year month = future.month day = future.day acc.start = datetime(year, month, day, 0, 0, 0) acc.end = datetime(year, month, day, 23, 59, 59) notify(ObjectModifiedEvent(event)) event.reindexObject() logger.debug(u'Evento padrao configurado')
def validate_dates(self, performance, performance_data): startDateTime = performance_data.get('startDateTime', '') endDateTime = performance_data.get('endDateTime', '') if startDateTime and not endDateTime: performance_date_fields = IEventAccessor(performance) performance_date_fields.end = performance_date_fields.start return True if not startDateTime and not endDateTime: logger("[Error] There are no dates for the performance. ", "Performance dates cannot be found.") return False return True
def get_location(accessor): """Return the location. This method can be overwritten by external packages, for example to provide a reference to a Location object as done by collective.venue. :param accessor: Event, Occurrence or IEventAccessor object. :type accessor: IEvent, IOccurrence or IEventAccessor :returns: A location string. Possibly a HTML structure to link to another object, representing the location. :rtype: string """ if not IEventAccessor.providedBy(accessor): accessor = IEventAccessor(accessor) return accessor.location
def test_pact_1_0_dxevent_is_migrated(self): def getNewEventDetail(obj): acc = IEventAccessor(obj) return [ obj.id, [acc.start.year, acc.start.month, acc.start.day], [acc.end.year, acc.end.month, acc.end.day], acc.location, acc.attendees ] # Create some 1.0 Event objects create1_0EventType(self.portal) self.portal.invokeFactory('Folder', 'event-folder') self.createOldEvent( self.portal, 'eventa', start_date=datetime(2012, 1, 1, 15, 20), end_date=datetime(2015, 9, 2, 16, 20), ) self.createOldEvent( self.portal['event-folder'], 'eventb', start_date=datetime(2013, 3, 3, 15, 20), end_date=datetime(2019, 5, 6, 16, 20), ) # IEventAccessor? What's that? with self.assertRaisesRegexp(TypeError, 'IEventAccessor'): IEventAccessor(self.portal['eventa']) # Run upgrade step self.doUpgradeStep('1001', '1100')
def valuesTable(self): acc = IEventAccessor(self.context) if acc.start: horaInici = acc.start.strftime('%d/%m/%Y %H:%M') year = acc.start.strftime('%Y') + '/' else: horaInici = '' year = '' if acc.end: horaFi = acc.end.strftime('%d/%m/%Y %H:%M') else: horaFi = '' if self.context.llocConvocatoria is None: llocConvocatoria = '' else: llocConvocatoria = self.context.llocConvocatoria session = self.context.numSessio organ = self.context.aq_parent.acronim sessionNumber = str(organ) + '/' + str(year) + str(session) value = api.content.get_state(obj=self.context) lang = self.context.language status = translate(msgid=value, domain='genweb', target_language=lang) values = dict( horaInici=horaInici, horaFi=horaFi, llocConvocatoria=llocConvocatoria, organTitle=self.OrganTitle(), sessionNumber=sessionNumber, status=status, ) return values
def test_event_summary_occurrences_next_upcoming(self): event = IEventAccessor(self.portal['daily']) date = patched_now() with mock.patch('plone.app.event.dx.behaviors.localized_now', return_value=date): view = zope.component.getMultiAdapter( (self.portal['daily'], self.request), name='event_summary') # altogether 4 occurrences, start occurrence is included self.assertEqual(4, len(view.next_occurrences)) self.assertEqual(event.start, date) self.assertEqual(event.end, date + datetime.timedelta(hours=1)) IAnnotations(self.request).clear() # clear cache next_date = patched_now() + datetime.timedelta(hours=24) with mock.patch('plone.app.event.dx.behaviors.localized_now', return_value=next_date): view = zope.component.getMultiAdapter( (self.portal['daily'], self.request), name='event_summary') # 3 occurrences remaining self.assertEqual(3, len(view.next_occurrences)) self.assertEqual(event.start, next_date) next_end_date = next_date + datetime.timedelta(hours=1) self.assertEqual(event.end, next_end_date) IAnnotations(self.request).clear() # clear cache next_date = patched_now() + datetime.timedelta(days=10) with mock.patch('plone.app.event.dx.behaviors.localized_now', return_value=next_date): view = zope.component.getMultiAdapter( (self.portal['daily'], self.request), name='event_summary') # no occurrences remaining, show all original 4 self.assertEqual(4, len(view.next_occurrences)) self.assertEqual(event.start, date) self.assertEqual(event.end, date + datetime.timedelta(hours=1))
def get_location(context): """Return the location. This method can be used to be overwritten by external packages, for example to provide a reference to a Location object as done by collective.venue. """ data = IEventAccessor(context) return data.location
def test_event_accessor_whole_day__open_end(self): at = pytz.timezone("Europe/Vienna") start = at.localize(datetime(2012, 10, 19, 0, 30)) end = at.localize(datetime(2012, 10, 19, 1, 30)) start_start = at.localize(datetime(2012, 10, 19, 0, 0, 0)) end_end = at.localize(datetime(2012, 10, 19, 23, 59, 59)) e1 = createContentInContainer(self.portal, 'plone.app.event.dx.event', title='event1', start=start, end=end) acc = IEventAccessor(e1) # check set self.assertEqual(e1.start, start) self.assertEqual(e1.end, end) # Setting open end e1.open_end = True self.assertEqual(e1.start, start) self.assertEqual(e1.end, end) self.assertEqual(acc.start, start) self.assertEqual(acc.end, end_end) # Setting whole day e1.whole_day = True self.assertEqual(e1.start, start) self.assertEqual(e1.end, end) self.assertEqual(acc.start, start_start) self.assertEqual(acc.end, end_end)
def create(cls, container, content_id, title, description=None, start=None, end=None, timezone=None, whole_day=None, open_end=None, **kwargs): container.invokeFactory(cls.event_type, id=content_id, title=title, description=description, startDate=start, endDate=end, wholeDay=whole_day, open_end=open_end, timezone=timezone) content = container[content_id] acc = IEventAccessor(content) acc.edit(**kwargs) return acc
def getNewEventDetail(obj): acc = IEventAccessor(obj) return [ obj.id, [acc.start.year, acc.start.month, acc.start.day], [acc.end.year, acc.end.month, acc.end.day], acc.location, acc.attendees ]
def construct_calendar(events, start=None, end=None): """Return a dictionary with dates in a given timeframe as keys and the actual occurrences for that date for building calendars. Long lasting events will occur on every day until their end. :param events: List of IEvent and/or IOccurrence objects, to construct a calendar data structure from. :type events: list :param start: An optional start range date. :type start: Python datetime or date :param end: An optional start range date. :type end: Python datetime or date :returns: Dictionary with isoformat date strings as keys and event occurrences as values. :rtype: dict """ if start: if is_datetime(start): start = start.date() assert is_date(start) if end: if is_datetime(end): end = end.date() assert is_date(end) cal = {} def _add_to_cal(cal_data, event, date): date_str = date.isoformat() if date_str not in cal_data: cal_data[date_str] = [event] else: cal_data[date_str].append(event) return cal_data for event in events: acc = IEventAccessor(event) start_date = acc.start.date() end_date = acc.end.date() # day span between start and end + 1 for the initial date range_days = (end_date - start_date).days + 1 for add_day in range(range_days): next_start_date = start_date + timedelta(add_day) # initial = 0 # avoid long loops if start and end_date < start: break # if the date is completly outside the range if start and next_start_date < start: continue # if start_date is outside but end reaches into range if end and next_start_date > end: break # if date is outside range _add_to_cal(cal, event, next_start_date) return cal
def _get_context(self, name): # TODO: save parent context on self, so it must not be called every # time oa = self._own_attr if name in oa: return self.context else: return IEventAccessor(aq_parent(self.context))
def __init__(self, context, request): self.context = context self.request = request self.data = IEventAccessor(context) self.max_occurrences = 6 self.excludes = [ 'title', ]
def test_import_from_ics__sync_drop_older(self): """SYNC_KEEP_NEWER and importing the same file again should update only newer and on equal modified date but drop the change when it is older. """ self.portal.invokeFactory('Folder', 'impfolder4') impfolder = self.portal.impfolder4 directory = os.path.dirname(__file__) with open(os.path.join(directory, 'icaltest.ics'), 'rb') as icsfile: icsdata1 = icsfile.read() with open(os.path.join(directory, 'icaltest2.ics'), 'rb') as icsfile: icsdata2 = icsfile.read() res = ical_import(impfolder, icsdata1, self.event_type) self.assertEqual(res['count'], 5) e1a = IEventAccessor(impfolder.e1) mod1 = e1a.last_modified suid1 = e1a.sync_uid title1 = e1a.title desc1 = e1a.description start1 = e1a.start end1 = e1a.end res = ical_import(impfolder, icsdata2, self.event_type, sync_strategy=base.SYNC_KEEP_NEWER) self.assertEqual(res['count'], 4) e1a = IEventAccessor(impfolder.e1) mod2 = e1a.last_modified suid2 = e1a.sync_uid title2 = e1a.title desc2 = e1a.description start2 = e1a.start end2 = e1a.end self.assertEqual(len(impfolder.contentIds()), 5) self.assertTrue(mod1 < mod2) self.assertEqual(suid1, suid2) self.assertNotEqual(title1, title2) self.assertNotEqual(desc1, desc2) self.assertTrue(start1 < start2) self.assertTrue(end1 < end2)
def migrate_schema_fields(self): oldacc = IEventAccessor(self.old) newacc = IEventAccessor(self.new) newacc.start = oldacc.start newacc.end = oldacc.end newacc.timezone = oldacc.timezone newacc.location = oldacc.location newacc.attendees = oldacc.attendees newacc.event_url = oldacc.event_url newacc.contact_name = oldacc.contact_name newacc.contact_email = oldacc.contact_email newacc.contact_phone = oldacc.contact_phone # Copy the entire richtext object, not just it's representation IEventSummary(self.new).text = IEventSummary(self.old).text # Trigger ObjectModified, so timezones can be fixed up. notify(ObjectModifiedEvent(self.new))
def _allDay(self): if HAS_PAE: if IEvent.providedBy(self.context): acc = IEventAccessor(self.context) return acc.whole_day or False if self._all_day is not None: return bool(self._all_day) return False
def construct_icalendar(context, events): """Returns an icalendar.Calendar object. :param context: A content object, which is used for calendar details like Title and Description. Usually a container, collection or the event itself. :param events: The list of event objects, which are included in this calendar. """ cal = icalendar.Calendar() cal.add('prodid', PRODID) cal.add('version', VERSION) cal_tz = default_timezone(context) if cal_tz: cal.add('x-wr-timezone', cal_tz) tzmap = {} if not hasattr(events, '__getslice__'): # LazyMap doesn't have __iter__ events = [events] for event in events: if ICatalogBrain.providedBy(event) or\ IContentListingObject.providedBy(event): event = event.getObject() acc = IEventAccessor(event) tz = acc.timezone # TODO: the standard wants each recurrence to have a valid timezone # definition. sounds decent, but not realizable. if not acc.whole_day: # whole day events are exported as dates without # timezone information tzmap = add_to_zones_map(tzmap, tz, acc.start) tzmap = add_to_zones_map(tzmap, tz, acc.end) cal.add_component(IICalendarEventComponent(event).to_ical()) for (tzid, transitions) in tzmap.items(): cal_tz = icalendar.Timezone() cal_tz.add('tzid', tzid) cal_tz.add('x-lic-location', tzid) for (transition, tzinfo) in transitions.items(): if tzinfo['dst']: cal_tz_sub = icalendar.TimezoneDaylight() else: cal_tz_sub = icalendar.TimezoneStandard() cal_tz_sub.add('tzname', tzinfo['name']) cal_tz_sub.add('dtstart', transition) cal_tz_sub.add('tzoffsetfrom', tzinfo['tzoffsetfrom']) cal_tz_sub.add('tzoffsetto', tzinfo['tzoffsetto']) # TODO: add rrule # tzi.add('rrule', # {'freq': 'yearly', 'bymonth': 10, 'byday': '-1su'}) cal_tz.add_component(cal_tz_sub) cal.add_component(cal_tz) return cal
def test_get_occurrences(self): result = get_occurrences(self.portal, get_portal_events(self.portal)) self.assertTrue(len(result) == 9) result = get_occurrences(self.portal, get_portal_events(self.portal), limit=5) self.assertTrue(len(result) == 5) self.assertTrue(IEventAccessor.providedBy(result[0]))
def ticket_title_generator(obj): """Generate a title for the ticket, also using event information. """ event = obj ret = { 'title': obj.title, 'eventtitle': '', 'eventstart': '', 'eventend': '' } if ITicketOccurrence.providedBy(event): event = aq_parent(aq_parent(event)) # Traverse to the Occurrence object if IATEvent.providedBy(event): # get the request out of thin air to be able to publishTraverse to # the transient Occurrence object. traverser = OccTravAT(event, getRequest()) elif IDXEvent.providedBy(event): # TODO traverser = OccTravDX(event, getRequest()) else: raise NotImplementedError( u"There is no event occurrence traverser implementation for " u"this kind of object.") try: event = traverser.publishTraverse(getRequest(), obj.id) except KeyError: # Maybe the ticket occurrence isn't valid anymore because the # event occurence doesn't exist anymore. # Just ignore that case. return ret elif ITicket.providedBy(event): event = aq_parent(event) if IEvent.providedBy(event) or IOccurrence.providedBy(event): acc = IEventAccessor(event) lstart = ulocalized_time(DT(acc.start), long_format=True, context=event) lend = ulocalized_time(DT(acc.start), long_format=True, context=event) # XXX: no unicode, store as utf-8 encoded string instead ret = dict( title=u'%s - %s (%s - %s)' % ( safe_unicode(acc.title), safe_unicode(obj.title), lstart, lend, ), eventtitle=acc.title, eventstart=acc.start, eventend=acc.end, ) return ret
def publishTraverse(self, request, name): context = self.context dateobj = guess_date_from(name, context) if dateobj: occurrence = IRecurrenceSupport(context).occurrences( range_start=dateobj)[0] occ_acc = IEventAccessor(occurrence) if is_same_day(dateobj, occ_acc.start): return occurrence return self.fallbackTraverse(request, name)
def _obj_or_acc(obj, ret_mode): """Return the content object or an IEventAccessor wrapper, depending on ret_mode. ret_mode 2 returns objects, ret_mode 3 returns IEventAccessor object wrapper. ret_mode 1 is not supported. """ assert (ret_mode is not RET_MODE_BRAINS) if ret_mode == RET_MODE_OBJECTS: return obj elif ret_mode == RET_MODE_ACCESSORS: return IEventAccessor(obj)
def test_event_summary_occurrences_whole_day(self): self.portal['daily'].whole_day = True date = patched_now() with mock.patch('plone.app.event.dx.behaviors.localized_now', return_value=date): view = zope.component.getMultiAdapter( (self.portal['daily'], self.request), name='event_summary') # altogether 4 occurrences, start occurrence is included self.assertEqual(4, len(view.next_occurrences)) for occurrence in view.next_occurrences: event_occ = IEventAccessor(occurrence) self.assertEqual(event_occ.start.hour, 0) self.assertEqual(event_occ.end.hour, 23) self.assertEqual(event_occ.start.day, event_occ.end.day) IAnnotations(self.request).clear() # clear cache next_date = patched_now() + datetime.timedelta(hours=24) with mock.patch('plone.app.event.dx.behaviors.localized_now', return_value=next_date): view = zope.component.getMultiAdapter( (self.portal['daily'], self.request), name='event_summary') # 3 occurrences remaining self.assertEqual(3, len(view.next_occurrences)) for occurrence in view.next_occurrences: event_occ = IEventAccessor(occurrence) self.assertEqual(event_occ.start.hour, 0) self.assertEqual(event_occ.end.hour, 23) self.assertEqual(event_occ.start.day, event_occ.end.day) IAnnotations(self.request).clear() # clear cache next_date = patched_now() + datetime.timedelta(days=10) with mock.patch('plone.app.event.dx.behaviors.localized_now', return_value=next_date): view = zope.component.getMultiAdapter( (self.portal['daily'], self.request), name='event_summary') # no occurrences remaining, show all original 4 self.assertEqual(4, len(view.next_occurrences)) for occurrence in view.next_occurrences: event_occ = IEventAccessor(occurrence) self.assertEqual(event_occ.start.hour, 0) self.assertEqual(event_occ.end.hour, 23) self.assertEqual(event_occ.start.day, event_occ.end.day)
def test_get_occurrences(self): res = get_events(self.portal, ret_mode=3, expand=True) self.assertTrue(len(res) == 9) res = get_events(self.portal, start=self.now, ret_mode=3, expand=True) self.assertTrue(len(res) == 9) res = get_events(self.portal, ret_mode=3, expand=True, limit=5) self.assertTrue(len(res) == 5) self.assertTrue(IEventAccessor.providedBy(res[0]))
def test_event_accessor(self): tz = pytz.timezone("Europe/Vienna") e1 = createContentInContainer( self.portal, 'plone.app.event.dx.event', title=u'event1', start=tz.localize(datetime(2011, 11, 11, 11, 0)), end=tz.localize(datetime(2011, 11, 11, 12, 0)), ) # setting attributes via the accessor acc = IEventAccessor(e1) new_end = tz.localize(datetime(2011, 11, 13, 10, 0)) acc.end = new_end # context's end should be set to new_end self.assertEqual(e1.end, new_end) # accessor's and context datetime should be the same self.assertEqual(acc.end, e1.end)
def test_get_occurrences(self): res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, expand=True) self.assertEqual(len(res), 9) res = get_events(self.portal, start=self.now, ret_mode=RET_MODE_ACCESSORS, expand=True) self.assertEqual(len(res), 8) res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, expand=True, limit=5) self.assertEqual(len(res), 5) self.assertTrue(IEventAccessor.providedBy(res[0]))
def test_get_occurrences(self): brains = get_portal_events(self.portal) result = get_occurrences_from_brains(self.portal, brains) self.assertTrue(len(result) == 9) result = get_occurrences_from_brains(self.portal, brains, range_start=self.now) self.assertTrue(len(result) == 9) result = get_occurrences_from_brains(self.portal, brains, limit=5) self.assertTrue(len(result) == 5) self.assertTrue(IEventAccessor.providedBy(result[0]))
def migrate_schema_fields(self): from plone.app.event.dx.behaviors import IEventSummary old_start = self.old.getField('startDate').get(self.old) old_end = self.old.getField('endDate').get(self.old) old_location = self.old.getField('location').get(self.old) old_attendees = self.old.getField('attendees').get(self.old) old_eventurl = self.old.getField('eventUrl').get(self.old) old_contactname = self.old.getField('contactName').get(self.old) old_contactemail = self.old.getField('contactEmail').get(self.old) old_contactphone = self.old.getField('contactPhone').get(self.old) old_text_field = self.old.getField('text') raw_text = safe_unicode(old_text_field.getRaw(self.old)) mime_type = old_text_field.getContentType(self.old) if raw_text.strip() == '': raw_text = '' old_richtext = RichTextValue(raw=raw_text, mimeType=mime_type, outputMimeType='text/x-html-safe') if self.old.getField('timezone'): old_timezone = self.old.getField('timezone').get(self.old) else: old_timezone = default_timezone(fallback='UTC') acc = IEventAccessor(self.new) acc.start = old_start.asdatetime() # IEventBasic acc.end = old_end.asdatetime() # IEventBasic acc.timezone = old_timezone # IEventBasic acc.location = old_location # IEventLocation acc.attendees = old_attendees # IEventAttendees acc.event_url = old_eventurl # IEventContact acc.contact_name = old_contactname # IEventContact acc.contact_email = old_contactemail # IEventContact acc.contact_phone = old_contactphone # IEventContact # Copy the entire richtext object, not just it's representation IEventSummary(self.new).text = old_richtext # Trigger ObjectModified, so timezones can be fixed up. notify(ObjectModifiedEvent(self.new))
def test_pae_dxevent_is_migrated(self): from datetime import datetime from plone.app.contenttypes.migration.migration import migrate_events from plone.app.textfield.value import RichTextValue from plone.app.event.dx.behaviors import IEventSummary # Enable plone.app.event.dx from plone.app.testing import applyProfile applyProfile(self.portal, 'plone.app.event.dx:default') old_event = self.portal[self.portal.invokeFactory( 'plone.app.event.dx.event', 'dx-event', start=datetime(2011, 11, 11, 11, 0), end=datetime(2011, 11, 11, 12, 0), timezone="Asia/Tbilisi", whole_day=False, )] old_event_acc = IEventAccessor(old_event) old_event_acc.contact_name = 'George' old_event_acc.contact_email = '*****@*****.**' old_event_acc.contact_phone = '+99512345' old_event_acc.event_url = 'http://geor.ge/event' old_event_acc.text = RichTextValue( raw='Woo, yeah', mimeType='text/plain', outputMimeType='text/x-html-safe' ) # migrate applyProfile(self.portal, 'plone.app.contenttypes:default') migrate_events(self.portal) # Compare new and old events new_event = self.portal['dx-event'] new_event_acc = IEventAccessor(new_event) self.assertEqual(False, old_event.exclude_from_nav) self.assertEqual('Event', new_event.portal_type) self.assertEqual(2011, new_event_acc.start.year) self.assertEqual(11, new_event_acc.start.month) self.assertEqual(11, new_event_acc.start.day) self.assertEqual(11, new_event_acc.start.hour) self.assertEqual('Asia/Tbilisi', str(new_event_acc.start.tzinfo)) self.assertEqual(2011, new_event_acc.end.year) self.assertEqual(11, new_event_acc.end.month) self.assertEqual(11, new_event_acc.end.day) self.assertEqual(12, new_event_acc.end.hour) self.assertEqual('Asia/Tbilisi', str(new_event_acc.end.tzinfo)) self.assertEqual(u'Asia/Tbilisi', new_event_acc.timezone) self.assertEqual(u'George', new_event_acc.contact_name) self.assertEqual(u'*****@*****.**', new_event_acc.contact_email) self.assertEqual(u'+99512345', new_event_acc.contact_phone) self.assertEqual(u'http://geor.ge/event', new_event_acc.event_url) self.assertEqual(u'<p>Woo, yeah</p>', new_event_acc.text) self.assertEqual('Woo, yeah', IEventSummary(new_event).text.raw) self.assertEqual(False, new_event.exclude_from_nav)
def importContent(self): # Create new content importer object v = AtlasProductImporter(uid=self.uid, domain=self.domain) # Additional fields kwargs = {} # Add a Webinar Group webinar_group = self.addWebinarGroup(self.import_path, v, **kwargs) # Add the Webinar webinar = self.addWebinar(webinar_group, v, **kwargs) # Set the webinar start/end dates to publish date of imported object webinar_date_start = v.data.start webinar_date_start = DateTime(webinar_date_start).asdatetime() webinar_date_end = v.data.end webinar_date_end = DateTime(webinar_date_end).asdatetime() acc = IEventAccessor(webinar) acc.edit(start=webinar_date_start, end=webinar_date_end) acc.update(start=webinar_date_start, end=webinar_date_end) # Initialize webinar_url variable webinar_url = v.data.event_url # If we have a 'webinar_url', set that on the Webinar Product if webinar_url: acc.edit(webinar_url=webinar_url) acc.update(webinar_url=webinar_url) # Finalize items self.finalize(webinar) self.finalize(webinar_group) # Return JSON output return self.getJSON(webinar_group)
def _last_migrate_time_zone(self): acc = IEventAccessor(self.new) acc.timezone = 'Europe/Berlin' notify(ObjectModifiedEvent(self.new))
def ical_import(container, ics_resource, event_type, sync_strategy=base.SYNC_KEEP_NEWER): cal = icalendar.Calendar.from_ical(ics_resource) events = cal.walk('VEVENT') cat = getToolByName(container, 'portal_catalog') container_path = '/'.join(container.getPhysicalPath()) def _get_by_sync_uid(uid): return cat( sync_uid=uid, path={'query': container_path, 'depth': 1} ) def _get_prop(prop, item, default=None): ret = default if prop in item: ret = safe_unicode(item.decoded(prop)) return ret def _from_list(ical, prop): """For EXDATE and RDATE recurrence component properties, the dates can be defined within one EXDATE/RDATE line or for each date an individual line. In the latter case, icalendar creates a list. This method handles this case. TODO: component property parameters like TZID are not used here. """ val = prop in ical and ical[prop] or [] if not isinstance(val, list): val = [val] #ret = '' #for item in val: # ret = ret and '%s\n' % ret or ret # insert linebreak # ret = '%s%s:%s' % (ret, prop, item.to_ical()) #return ret # Zip multiple lines into one, since jquery.recurrenceinput.js does # not support multiple lines here # https://github.com/collective/jquery.recurrenceinput.js/issues/15 ret = '' for item in val: ret = ret and '%s,' % ret or ret # insert linebreak ret = '%s%s' % (ret, item.to_ical()) return ret and '%s:%s' % (prop, ret) or None count = 0 for item in events: start = _get_prop('DTSTART', item) end = _get_prop('DTEND', item) if not end: duration = _get_prop('DURATION', item) if duration: end = start + duration # else: whole day or open end timezone = getattr(getattr(start, 'tzinfo', None), 'zone', None) or\ base.default_timezone(container) whole_day = False open_end = False if is_date(start) and (is_date(end) or end is None): # All day / whole day events # End must be same type as start (RFC5545, 3.8.2.2) whole_day = True if end is None: end = start if start < end: # RFC5545 doesn't define clearly, if all day events should have # a end date one day after the start day at 0:00. # Internally, we handle all day events with start=0:00, # end=:23:59:59, so we substract one day here. end = end - datetime.timedelta(days=1) start = base.dt_start_of_day(date_to_datetime(start)) end = base.dt_end_of_day(date_to_datetime(end)) elif is_datetime(start) and end is None: # Open end event, see RFC 5545, 3.6.1 open_end = True end = base.dt_end_of_day(date_to_datetime(start)) assert(is_datetime(start)) assert(is_datetime(end)) title = _get_prop('SUMMARY', item) description = _get_prop('DESCRIPTION', item) location = _get_prop('LOCATION', item) url = _get_prop('URL', item) rrule = _get_prop('RRULE', item) rrule = rrule and 'RRULE:%s' % rrule.to_ical() or '' rdates = _from_list(item, 'RDATE') exdates = _from_list(item, 'EXDATE') rrule = '\n'.join([it for it in [rrule, rdates, exdates] if it]) # TODO: attendee-lists are not decoded properly and contain only # vCalAddress values attendees = item.get('ATTENDEE', ()) contact = _get_prop('CONTACT', item) categories = item.get('CATEGORIES', ()) if hasattr(categories, '__iter__'): categories = [safe_unicode(it) for it in categories] ext_modified = utc(_get_prop('LAST-MODIFIED', item)) # TODO: better use plone.api for content creation, from which some of # the code here is copied content = None new_content_id = None existing_event = None sync_uid = _get_prop('UID', item) if sync_strategy != base.SYNC_NONE and sync_uid: existing_event = _get_by_sync_uid(sync_uid) if existing_event: if sync_strategy == base.SYNC_KEEP_MINE: # On conflict, keep mine continue exist_event = existing_event[0].getObject() acc = IEventAccessor(exist_event) if sync_strategy == base.SYNC_KEEP_NEWER and\ (not ext_modified or acc.last_modified >= ext_modified): # Update only, if newer, if ext_modified exists continue # Else: update content = exist_event else: # TODO: if AT had the same attrs like IDXEventBase, we could set # everything within this invokeFactory call. new_content_id = str(random.randint(0, 99999999)) container.invokeFactory(event_type, id=new_content_id, title=title, description=description) content = container[new_content_id] assert(content) # At this point, a content must be available. event = IEventAccessor(content) event.title = title event.description = description event.start = start event.end = end event.timezone = timezone event.whole_day = whole_day event.open_end = open_end event.location = location event.event_url = url event.recurrence = rrule event.attendees = attendees event.contact_name = contact event.subjects = categories if sync_strategy != base.SYNC_NONE: # Don't import the sync_uid, if no sync strategy is chosen. Let the # sync_uid be autogenerated then. event.sync_uid = sync_uid notify(ObjectModifiedEvent(content)) # Archetypes specific code if getattr(content, 'processForm', False): # Will finish Archetypes content item creation process, # rename-after-creation and such content.processForm() # Use commits instead of savepoints to avoid "FileStorageError: # description too long" on large imports. transaction.get().commit() # Commit before rename if new_content_id and new_content_id in container: # Rename with new id from title, if processForm didn't do it. chooser = INameChooser(container) new_id = chooser.chooseName(title, content) content.aq_parent.manage_renameObject(new_content_id, new_id) # Do this at the end, otherwise it's overwritten if ext_modified: event.last_modified = ext_modified count += 1 return {'count': count}
def load_loc(self): context = self.context loc = IEventAccessor(context).location if loc: self.request['loc'] = loc.strip() self.has_loc = True
def test_event_accessor(self): utc = pytz.utc vienna = pytz.timezone('Europe/Vienna') self.portal.invokeFactory('Event', 'event1', description='a description', startDate=datetime(2011, 11, 11, 11, 0, tzinfo=utc), endDate=datetime(2011, 11, 11, 12, 0, tzinfo=utc), timezone='UTC', wholeDay=False) e1 = self.portal['event1'] acc = IEventAccessor(e1) # TEST DATES self.assertEqual(acc.start, datetime(2011, 11, 11, 11, 0, tzinfo=utc)) self.assertEqual(acc.end, datetime(2011, 11, 11, 12, 0, tzinfo=utc)) acc.start = datetime(2011, 11, 13, 9, 0) # tzinfo does not matter, acc.end = datetime(2011, 11, 13, 10, 0) # it's set by subscription # adapter # If using EventAccessor's edit method, calling notify isn't needed acc.edit(timezone=u'Europe/Vienna') # accessor should return start/end datetimes in the event's timezone self.assertEqual( acc.start, datetime(2011, 11, 13, 9, 0, tzinfo=vienna)) self.assertEqual( acc.end, datetime(2011, 11, 13, 10, 0, tzinfo=vienna)) # start/end dates are stored in UTC zone on the context, but converted # to event's timezone via the attribute getter. self.assertEqual( e1.end(), DateTime('2011/11/13 10:00:00 Europe/Vienna') ) # timezone should be the same on the event object and accessor self.assertEqual(e1.getTimezone(), acc.timezone) # Open End Test acc.edit(open_end=True) self.assertEqual( acc.start, datetime(2011, 11, 13, 9, 0, tzinfo=vienna)) self.assertEqual( acc.end, datetime(2011, 11, 13, 23, 59, 59, tzinfo=vienna)) # Whole Day Test acc.edit(whole_day=True, open_end=False) self.assertEqual( acc.start, datetime(2011, 11, 13, 0, 0, tzinfo=vienna)) self.assertEqual( acc.end, datetime(2011, 11, 13, 23, 59, 59, tzinfo=vienna)) # TEST DESCRIPTION self.assertTrue(acc.description == 'a description') acc.description = 'another desc' self.assertTrue(acc.description == 'another desc') # TEST OTHER PROPERTIES acc.title = u"An Event" acc.recurrence = u'RRULE:FREQ=DAILY;COUNT=5' acc.location = u"Home" acc.attendees = [u'me', u'you'] acc.contact_name = u"Max Mustermann" acc.contact_email = u"*****@*****.**" acc.contact_phone = u"+1234567890" acc.event_url = u"http://plone.org/" acc.subjects = [u"tag1", u"tag2"] acc.text = u"body text with <b>html</b> formating." # If not using EventAccessor's edit method, call notify manually notify(ObjectModifiedEvent(acc.context)) self.assertEqual(acc.recurrence, u'RRULE:FREQ=DAILY;COUNT=5') self.assertEqual(acc.location, u'Home') self.assertEqual(acc.attendees, (u'me', u'you')) self.assertEqual(acc.contact_name, u"Max Mustermann") self.assertEqual(acc.contact_email, u'*****@*****.**') self.assertEqual(acc.contact_phone, u"+1234567890") self.assertEqual(acc.event_url, u"http://plone.org/") self.assertEqual(acc.subjects, (u"tag1", u"tag2")) self.assertEqual(acc.text, u"body text with <b>html</b> formating.") # CLEANUP self.portal.manage_delObjects(['event1'])
def dates_for_display(occurrence): """ Return a dictionary containing pre-calculated information for building <start>-<end> date strings. Keys are: 'start_date' - date string of the start date 'start_time' - time string of the start date 'end_date' - date string of the end date 'end_time' - time string of the end date 'start_iso' - start date in iso format 'end_iso' - end date in iso format 'same_day' - event ends on the same day 'same_time' - event ends at same time 'whole_day' - whole day events 'open_end' - events without end time :param occurrence: Event or occurrence object. :type occurrence: IEvent, IOccurrence or IEventAccessor based object. :returns: Dictionary with date strings. :rtype: dict The behavior os ulocalized_time() with time_only is odd. Setting time_only=False should return the date part only and *not* the time NOTE: these tests are not run, but serve as documentation. TODO: remove. >>> from DateTime import DateTime >>> start = DateTime(2010,3,16,14,40) >>> from zope.component.hooks import getSite >>> site = getSite() >>> ulocalized_time(start, False, time_only=True, context=site) u'14:40' >>> ulocalized_time(start, False, time_only=False, context=site) u'14:40' >>> ulocalized_time(start, False, time_only=None, context=site) u'16.03.2010' """ if IEventAccessor.providedBy(occurrence): acc = occurrence occurrence = occurrence.context else: acc = IEventAccessor(occurrence) # this needs to separate date and time as ulocalized_time does DT_start = DT(acc.start) DT_end = DT(acc.end) start_date = ulocalized_time( DT_start, long_format=False, time_only=None, context=occurrence ) start_time = ulocalized_time( DT_start, long_format=False, time_only=True, context=occurrence ) end_date = ulocalized_time( DT_end, long_format=False, time_only=None, context=occurrence ) end_time = ulocalized_time( DT_end, long_format=False, time_only=True, context=occurrence ) same_day = is_same_day(acc.start, acc.end) same_time = is_same_time(acc.start, acc.end) # set time fields to None for whole day events if acc.whole_day: start_time = end_time = None if acc.open_end: end_time = None start_iso = acc.whole_day and acc.start.date().isoformat()\ or acc.start.isoformat() end_iso = acc.whole_day and acc.end.date().isoformat()\ or acc.end.isoformat() return dict( # Start start_date=start_date, start_time=start_time, start_iso=start_iso, # End end_date=end_date, end_time=end_time, end_iso=end_iso, # Meta same_day=same_day, same_time=same_time, whole_day=acc.whole_day, open_end=acc.open_end, )
def test_get_events(self): # whole range res = get_events(self.portal) self.assertEqual(len(res), 4) res = get_events(self.portal, start=self.past, end=self.future) self.assertEqual(len(res), 4) res = get_events(self.portal, end=self.future) self.assertEqual(len(res), 4) res = get_events(self.portal, start=self.past) self.assertEqual(len(res), 4) # Limit res = get_events(self.portal, limit=2) self.assertEqual(len(res), 2) # Return objects res = get_events(self.portal, ret_mode=RET_MODE_OBJECTS) self.assertTrue(IEvent.providedBy(res[0])) # Return IEventAccessor res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS) self.assertTrue(IEventAccessor.providedBy(res[0])) # Test sorting self.assertTrue(res[0].start < res[-1].start) # Test reversed sorting res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, sort_reverse=True) self.assertTrue(res[0].start > res[-1].start) # Test sort_on res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, sort="start") self.assertEqual( [it.title for it in res][2:], [u'Now Event', u'Future Event'] ) res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, sort="end") self.assertEqual( [it.title for it in res], [u'Past Event', u'Now Event', u'Future Event', u'Long Event'] ) # Test expansion res = get_events(self.portal, ret_mode=RET_MODE_OBJECTS, expand=True) self.assertEqual(len(res), 8) res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, expand=True) self.assertEqual(len(res), 8) # Test sorting self.assertTrue(res[0].start < res[-1].start) res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, expand=True, sort_reverse=True) # Test sorting self.assertTrue(res[0].start > res[-1].start) # only on now-date res = get_events(self.portal, start=self.now, end=self.now) self.assertEqual(len(res), 2) # only on now-date as date # NOTE: converting self.now to python datetime to allow testing also # with dates as Zope DateTime objects. res = get_events(self.portal, start=pydt(self.now).date(), end=pydt(self.now).date()) self.assertEqual(len(res), 2) # only on past date res = get_events(self.portal, start=self.past, end=self.past) self.assertEqual(len(res), 2) # one recurrence occurrence in far future res = get_events(self.portal, start=self.far, end=self.far) self.assertEqual(len(res), 1) # from now on res = get_events(self.portal, start=self.now) self.assertEqual(len(res), 3) # until now res = get_events(self.portal, end=self.now) self.assertEqual(len(res), 3) # in subfolder path = '/'.join(self.portal.sub.getPhysicalPath()) res = get_events(self.portal, path=path) self.assertEqual(len(res), 1)
def ical_import(container, ics_resource, event_type): cal = icalendar.Calendar.from_ical(ics_resource) events = cal.walk('VEVENT') def _get_prop(prop, item): ret = None if prop in item: ret = safe_unicode(item.decoded(prop)) return ret def _from_list(ical, prop): """For EXDATE and RDATE recurrence component properties, the dates can be defined within one EXDATE/RDATE line or for each date an individual line. In the latter case, icalendar creates a list. This method handles this case. TODO: component property parameters like TZID are not used here. """ val = prop in ical and ical[prop] or [] if not isinstance(val, list): val = [val] #ret = '' #for item in val: # ret = ret and '%s\n' % ret or ret # insert linebreak # ret = '%s%s:%s' % (ret, prop, item.to_ical()) #return ret # Zip multiple lines into one, since jquery.recurrenceinput.js does # not support multiple lines here # https://github.com/collective/jquery.recurrenceinput.js/issues/15 ret = '' for item in val: ret = ret and '%s,' % ret or ret # insert linebreak ret = '%s%s' % (ret, item.to_ical()) return ret and '%s:%s' % (prop, ret) or None count = 0 for item in events: start = _get_prop('DTSTART', item) end = _get_prop('DTEND', item) if not end: duration = _get_prop('DURATION', item) if duration: end = start + duration # else: whole day or open end timezone = getattr(getattr(start, 'tzinfo', None), 'zone', None) or\ base.default_timezone(container) whole_day = False open_end = False if is_date(start) and (is_date(end) or end is None): # All day / whole day events # End must be same type as start (RFC5545, 3.8.2.2) whole_day = True if end is None: end = start if start < end: # RFC5545 doesn't define clearly, if all day events should have # a end date one day after the start day at 0:00. # Internally, we handle all day events with start=0:00, # end=:23:59:59, so we substract one day here. end = end - datetime.timedelta(days=1) start = base.dt_start_of_day(date_to_datetime(start)) end = base.dt_end_of_day(date_to_datetime(end)) elif is_datetime(start) and end is None: # Open end event, see RFC 5545, 3.6.1 open_end = True end = base.dt_end_of_day(date_to_datetime(start)) assert(is_datetime(start)) assert(is_datetime(end)) title = _get_prop('SUMMARY', item) description = _get_prop('DESCRIPTION', item) location = _get_prop('LOCATION', item) url = _get_prop('URL', item) rrule = _get_prop('RRULE', item) rrule = rrule and 'RRULE:%s' % rrule.to_ical() or '' rdates = _from_list(item, 'RDATE') exdates = _from_list(item, 'EXDATE') rrule = '\n'.join([it for it in [rrule, rdates, exdates] if it]) # TODO: attendee-lists are not decoded properly and contain only # vCalAddress values attendees = _get_prop('ATTENDEE', item) contact = _get_prop('CONTACT', item) categories = _get_prop('CATEGORIES', item) if hasattr(categories, '__iter__'): categories = [safe_unicode(it) for it in categories] ## for sync #created = _get_prop('CREATED', item) #modified = _get_prop('LAST-MODIFIED', item) # TODO: better use plone.api, from which some of the code here is # copied content_id = str(random.randint(0, 99999999)) # TODO: if AT had the same attrs like IDXEventBase, we could set # everything within this invokeFactory call. container.invokeFactory(event_type, id=content_id, title=title, description=description) content = container[content_id] event = IEventAccessor(content) event.start = start event.end = end event.timezone = timezone event.whole_day = whole_day event.open_end = open_end event.location = location event.event_url = url event.recurrence = rrule event.attendees = attendees event.contact_name = contact event.subjects = categories notify(ObjectModifiedEvent(content)) # Archetypes specific code if getattr(content, 'processForm', False): # Will finish Archetypes content item creation process, # rename-after-creation and such content.processForm() if content_id in container: # Rename with new id from title, if processForm didn't do it. chooser = INameChooser(container) new_id = chooser.chooseName(title, content) transaction.savepoint(optimistic=True) # Commit before renaming content.aq_parent.manage_renameObject(content_id, new_id) else: transaction.savepoint(optimistic=True) count += 1 return {'count': count}
def ical_import(container, ics_resource, event_type): cal = icalendar.Calendar.from_ical(ics_resource) events = cal.walk('VEVENT') def _get_prop(prop, item): ret = None if prop in item: ret = safe_unicode(item.decoded(prop)) return ret count = 0 for item in events: start = _get_prop('DTSTART', item) end = _get_prop('DTEND', item) if not end: duration = _get_prop('DURATION', item) if duration: end = start + duration # else: whole day or open end timezone = getattr(getattr(start, 'tzinfo', None), 'zone', None) or\ base.default_timezone(container) whole_day = False open_end = False if is_date(start) and (is_date(end) or end is None): # All day / whole day events # End must be same type as start (RFC5545, 3.8.2.2) whole_day = True if end is None: end = start if start < end: # RFC5545 doesn't define clearly, if all day events should have # a end date one day after the start day at 0:00. # Internally, we handle all day events with start=0:00, # end=:23:59:59, so we substract one day here. end = end - datetime.timedelta(days=1) start = base.dt_start_of_day(date_to_datetime(start)) end = base.dt_end_of_day(date_to_datetime(end)) elif is_datetime(start) and end is None: # Open end event, see RFC 5545, 3.6.1 open_end = True end = base.dt_end_of_day(date_to_datetime(start)) assert(is_datetime(start)) assert(is_datetime(end)) title = _get_prop('SUMMARY', item) description = _get_prop('DESCRIPTION', item) location = _get_prop('LOCATION', item) url = _get_prop('URL', item) rrule = _get_prop('RRULE', item) rrule = rrule and 'RRULE:%s' % rrule.to_ical() or '' rdate = _get_prop('RDATE', item) rrule = rdate and '%s\nRDATE:%s' % (rrule, rdate.to_ical()) or '' exdate = _get_prop('EXDATE', item) rrule = exdate and '%s\nEXDATE:%s' % (rrule, exdate.to_ical()) or '' attendees = _get_prop('ATTENDEE', item) contact = _get_prop('CONTACT', item) categories = _get_prop('CATEGORIES', item) if hasattr(categories, '__iter__'): categories = [safe_unicode(it) for it in categories] # for sync created = _get_prop('CREATED', item) modified = _get_prop('LAST-MODIFIED', item) # TODO: better use plone.api, from which some of the code here is # copied content_id = str(random.randint(0, 99999999)) # TODO: if AT had the same attrs like IDXEventBase, we could set # everything within this invokeFactory call. container.invokeFactory(event_type, id=content_id, title=title, description=description) content = container[content_id] event = IEventAccessor(content) event.start = start event.end = end event.timezone = timezone event.whole_day = whole_day event.open_end = open_end event.location = location event.event_url = url event.recurrence = rrule event.attendees = attendees event.contact_name = contact event.subjects = categories notify(ObjectModifiedEvent(content)) # Archetypes specific code if getattr(content, 'processForm', False): # Will finish Archetypes content item creation process, # rename-after-creation and such content.processForm() if content_id in container: # Rename with new id from title, if processForm didn't do it. chooser = INameChooser(container) new_id = chooser.chooseName(title, content) transaction.savepoint(optimistic=True) # Commit before renaming content.aq_parent.manage_renameObject(content_id, new_id) else: transaction.savepoint(optimistic=True) count += 1 return {'count': count}