def test_default_timezone(self): self.assertTrue(os_default_timezone() == default_timezone() == 'UTC') reg = getUtility(IRegistry) settings = reg.forInterface(IEventSettings, prefix="plone.app.event") settings.portal_timezone = "Europe/Vienna" self.assertTrue(default_timezone() == 'Europe/Vienna')
def test_default_timezone(self): assert os_default_timezone() == default_timezone() == "UTC" reg = getUtility(IRegistry) settings = reg.forInterface(IEventSettings, prefix="plone.app.event") settings.portal_timezone = "Europe/Vienna" assert default_timezone() == "Europe/Vienna"
def __call__(self, value, filestore, item, disable_constraints=False, logger=None): if isinstance(value, datetime): value = value.date() if isinstance(value, basestring): # Fix some rare use case if 'Universal' in value: value = value.replace('Universal', 'UTC') try: value = datetime.strptime(value, '%Y-%m-%d %H:%M') except: value = datetime.now() # Fix timezone tz_default = default_timezone(as_tzinfo=True) if value.tzinfo is None: value = tz_default.localize(value) try: self.field.validate(value) except Exception, e: if not disable_constraints: raise e else: if logger: logger( "%s is invalid in %s: %s" % ( self.field.__name__, item['_path'], e) )
def construct_calendar(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) # TODO: use plone's new uuid adapter instead of uid # Non-Standard calendar extensions. also see: # http://en.wikipedia.org/wiki/ICalendar#cite_note-11 cal_title = context.Title() if cal_title: cal.add('x-wr-calname', cal_title) cal_desc = context.Description() if cal_desc: cal.add('x-wr-caldesc', cal_desc) if getattr(context, 'UID', False): # portal object does not have UID cal_uid = context.UID() cal.add('x-wr-relcalid', cal_uid) cal_tz = default_timezone(context) if cal_tz: cal.add('x-wr-timezone', cal_tz) for event in events: cal.add_component(IICalendarComponent(event)) return cal
def extract(self, default=NO_VALUE): value = self.request.get(self.name, default) if value == default: return default if isinstance(value, str): # Date only date = datetime(value, '%Y-%m-%d') if value[0] == u'': return None else: if value[1] == u'': value[1] = u'0:00' time_str = "{0} {1}".format(*value) date = datetime.strptime(time_str, '%Y-%m-%d %H:%M') timezone_name = (self.request.get('%s-timezone' % self.name, '') or self.request.get('timezone', '') or default_timezone(self.context)) if isinstance(timezone_name, unicode): timezone_name.encode('utf8') # pytz "does not work" with datetime tzinfo. Use localize instead # Symptoms are times in "LMT" format which are off a few minutes. # http://stackoverflow.com/questions/24856643/unexpected-results-converting-timezones-in-python # noqa tz = timezone(timezone_name) date = tz.localize(date) return date
def default_now(): """ Returns the current time using the default_timezone from plone.app.event. Said timezone is either set globally or per user. """ utcnow = to_utc(datetime.utcnow()) return pytz.timezone(default_timezone()).normalize(utcnow)
def construct_calendar(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_title = context.Title() if cal_title: cal.add('x-wr-calname', cal_title) cal_desc = context.Description() if cal_desc: cal.add('x-wr-caldesc', cal_desc) uuid = IUUID(context, None) if uuid: # portal object does not have UID cal.add('x-wr-relcalid', uuid) cal_tz = default_timezone(context) if cal_tz: cal.add('x-wr-timezone', cal_tz) for event in events: cal.add_component(IICalendarEventComponent(event).to_ical()) return cal
def __call__( self, value, filestore, item, disable_constraints=False, logger=None ): if value == "None": return None if isinstance(value, six.string_types): # Fix some rare use case if "Universal" in value: value = value.replace("Universal", "UTC") try: value = dateutil.parser.isoparse(value) # Fix timezone tz_default = default_timezone(as_tzinfo=True) if value.tzinfo is None: value = tz_default.localize(value) value = value.astimezone(tz_default) except ValueError: value = DateTime(value) try: self.field.validate(value) except Exception as e: if not disable_constraints: raise e else: if logger: logger( "%s is invalid in %s: %s" % (self.field.__name__, item["_path"], e) ) return value
def __call__(self, value, filestore, item, disable_constraints=False, logger=None): if isinstance(value, datetime): value = value.date() if isinstance(value, basestring): # Fix some rare use case if 'Universal' in value: value = value.replace('Universal', 'UTC') try: value = datetime.strptime(value, '%Y-%m-%d %H:%M') except: value = datetime.now() # Fix timezone tz_default = default_timezone(as_tzinfo=True) if value.tzinfo is None: value = tz_default.localize(value) try: self.field.validate(value) except Exception, e: if not disable_constraints: raise e else: if logger: logger("%s is invalid in %s: %s" % (self.field.__name__, item['_path'], e))
def getICal(self): # collect iCal entries for found events events = self.getEvents() if not events: return None cal = icalendar.Calendar() cal.add('prodid', PRODID) cal.add('version', VERSION) # TODO: is there a UUID to use instead of UID? # Non-Standard calendar extensions. also see: # http://en.wikipedia.org/wiki/ICalendar#cite_note-11 cal_context = self.context cal_title = cal_context.Title() if cal_title: cal.add('x-wr-calname', cal_title) cal_desc = cal_context.Description() if cal_desc: cal.add('x-wr-caldesc', cal_desc) if getattr(cal_context, 'UID', False): # portal object does not have UID cal_uid = cal_context.UID() cal.add('x-wr-relcalid', cal_uid) cal_tz = default_timezone(cal_context) if cal_tz: cal.add('x-wr-timezone', cal_tz) for event in events: cal.add_component(event.to_icalendar()) return cal
def test_importer_values(self): try: importer = ExternalEventImporter(self.directory) string_values = [ 'title', 'short_description', 'long_description', 'locality', 'street', 'housenumber', 'zipcode', 'town', 'location_url', 'event_url', 'organizer', 'contact_name', 'contact_email', 'contact_phone', 'prices', 'registration', 'source_id', 'fetch_id' ] now = default_now().replace(microsecond=0) then = now + timedelta(days=10) event = {s: s for s in string_values} event['last_update'] = now event['start'] = then event['end'] = then + timedelta(hours=1) event['timezone'] = default_timezone() event['whole_day'] = False event['recurrence'] = 'RRULE:FREQ=DAILY;COUNT=2' event['cat1'] = set(['c1', 'c2']) event['cat2'] = set(['c3', 'c4']) event['longitude'] = 7.8673189 event['latitude'] = 46.6859853 # :TODO: test image and attachement download # event['image'] = # event['attachment_1'] = # event['attachment_2'] = imports, deleted = importer.fetch_one('source', lambda: [event]) imported = self.catalog.query() self.assertEquals(imports, 1) self.assertEquals(len(imported), 1) imported = imported[0].getObject() for s in string_values: self.assertTrue(s in vars(imported)) self.assertTrue(vars(imported)[s] == s) self.assertEquals(imported.start, now + timedelta(days=10)) self.assertEquals(imported.end, now + timedelta(days=10, hours=1)) self.assertEquals(imported.recurrence, 'RRULE:FREQ=DAILY;COUNT=2') self.assertFalse(imported.whole_day) self.assertTrue('c1' in imported.cat1) self.assertTrue('c2' in imported.cat1) self.assertTrue('c3' in imported.cat2) self.assertTrue('c4' in imported.cat2) self.assertEquals( IGeoreferenced(imported).coordinates, [7.8673189, 46.6859853]) finally: # Clean up (transaction has been commited) self.cleanup_after_fetch_one()
def migrate_to_1300(context): """ Add tzinfo to scadenza_bando """ tz = pytz.timezone(default_timezone()) bandi = api.content.find(portal_type="Bando") tot_results = len(bandi) logger.info("### Fixing {tot} Bandi ###".format(tot=tot_results)) for counter, brain in enumerate(bandi): bando = brain.getObject() if not getattr(bando, "scadenza_bando", None): continue try: bando.scadenza_bando = pytz.utc.localize( bando.scadenza_bando).astimezone(tz) except ValueError: # convert to right timezone if bando.scadenza_bando.tzinfo.zone == tz.zone: # same tz, skip continue bando.scadenza_bando = pytz.utc.localize( bando.scadenza_bando.replace(tzinfo=None)).astimezone(tz) bando.reindexObject(idxs=["scadenza_bando"]) logger.info("[{counter}/{tot}] - {bando}".format( counter=counter + 1, tot=tot_results, bando=brain.getPath()))
def patched_now(context=None): """Patch localized_now to allow stable results in tests. """ if not context: context = None tzinfo = default_timezone(context=context, as_tzinfo=True) now = datetime(2013, 5, 5, 10, 0, 0, tzinfo=tzinfo).replace(microsecond=0) return now
def get_selected_tz(self): ''' Let's try to get this from the start attribute (if found). Otherwise default to the default timezone. ''' try: return str(self.context.start.tzinfo) except: return default_timezone()
def make_dates(self): def_tz = default_timezone() now = self.now = DateTime(2012, 9,10,10,10, 0, def_tz) past = self.past = DateTime(2012, 9, 1,10,10, 0, def_tz) future = self.future = DateTime(2012, 9,20,10,10, 0, def_tz) far = self.far = DateTime(2012, 9,22,10,10, 0, def_tz) duration = self.duration = 0.1 return (now, past, future, far, duration)
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 make_dates(self): def_tz = default_timezone() now = self.now = DateTime(2013, 5, 5, 10, 0, 0, def_tz) self.tomorrow = DateTime(2013, 5, 6, 10, 0, 0, def_tz) past = self.past = DateTime(2013, 4, 25, 10, 0, 0, def_tz) future = self.future = DateTime(2013, 5, 15, 10, 0, 0, def_tz) far = self.far = DateTime(2013, 6, 4, 10, 0, 0, def_tz) duration = self.duration = 0.1 return (now, past, future, far, duration)
def create_fetch_entry(self, **kw): def_start = datetime.today().replace(second=0) + timedelta(days=10) def_end = def_start + timedelta(hours=1) defaults = { # from IDirectoryItem # description not used 'title': '', # from IEventsDirectoryItem # submitter, submitter_email not used 'short_description': '', 'long_description': '', 'image': '', 'attachment_1': '', 'attachment_2': '', 'locality': '', 'street': '', 'housenumber': '', 'zipcode': '', 'town': '', 'location_url': '', 'event_url': '', 'organizer': '', 'contact_name': '', 'contact_email': '', 'contact_phone': '', 'prices': '', 'registration': '', # from IExternalEvent # source not used (filled in by ExternalEventImporter) 'source_id': 'id-1', # From IEventBasic 'start': def_start, 'end': def_end, 'whole_day': False, 'timezone': default_timezone(), # From IEventRecurrence 'recurrence': '', # additional attributes used to control import 'fetch_id': 'fetch-1', 'last_update': default_now().replace(microsecond=0), 'latitude': '', 'longitude': '', 'cat1': set(), 'cat2': set(), } for attr in defaults: if not attr in kw: kw[attr] = defaults[attr] return kw
def setUp(self): self.portal = self.layer['portal'] self.app = self.layer['app'] self.request = self.layer['request'] set_browserlayer(self.request) reg = zope.component.getUtility(IRegistry) settings = reg.forInterface(IEventSettings, prefix="plone.app.event") default_tz = default_timezone() settings.portal_timezone = default_tz now, past, future, far, duration = self.make_dates() setRoles(self.portal, TEST_USER_ID, ['Manager']) factory = self.event_factory() self.past_event = factory( container=self.portal, content_id='past', title=u'Past Event', start=past, end=past + duration, location=u"Vienna", timezone=default_tz, recurrence='RRULE:FREQ=DAILY;COUNT=3', ).context self.now_event = factory( container=self.portal, content_id='now', title=u'Now Event', start=now, end=now + duration, location=u"Vienna", timezone=default_tz, recurrence='RRULE:FREQ=DAILY;COUNT=3;INTERVAL=2', ).context self.future_event = factory( container=self.portal, content_id='future', title=u'Future Event', start=future, end=future + duration, location=u'Graz', timezone=default_tz).context self.portal.invokeFactory('Folder', 'sub', title=u'sub') self.long_event = factory( container=self.portal.sub, content_id='long', title=u'Long Event', start=past, end=far, location=u'Schaftal', timezone=default_tz).context
def setUp(self): self.portal = self.layer['portal'] self.app = self.layer['app'] self.request = self.layer['request'] set_browserlayer(self.request) reg = zope.component.getUtility(IRegistry) settings = reg.forInterface(IEventSettings, prefix="plone.app.event") default_tz = default_timezone() settings.portal_timezone = default_tz now, past, future, far, duration = self.make_dates() setRoles(self.portal, TEST_USER_ID, ['Manager']) factory = self.event_factory() self.past_event = factory( container=self.portal, content_id='past', title=u'Past Event', start=past, end=past + duration, location=u"Vienna", timezone=default_tz, recurrence='RRULE:FREQ=DAILY;COUNT=3', ).context self.now_event = factory( container=self.portal, content_id='now', title=u'Now Event', start=now, end=now + duration, location=u"Vienna", timezone=default_tz, recurrence='RRULE:FREQ=DAILY;COUNT=3;INTERVAL=2', ).context self.future_event = factory(container=self.portal, content_id='future', title=u'Future Event', start=future, end=future + duration, location=u'Graz', timezone=default_tz).context self.portal.invokeFactory('Folder', 'sub', title=u'sub') self.long_event = factory(container=self.portal.sub, content_id='long', title=u'Long Event', start=past, end=far, location=u'Schaftal', timezone=default_tz).context
def create_fetch_entry(self, **kw): def_start = datetime.today().replace(second=0) + timedelta(days=10) def_end = def_start + timedelta(hours=1) defaults = { # from IDirectoryItem # description not used "title": "", # from IEventsDirectoryItem # submitter, submitter_email not used "short_description": "", "long_description": "", "image": "", "attachment_1": "", "attachment_2": "", "locality": "", "street": "", "housenumber": "", "zipcode": "", "town": "", "location_url": "", "event_url": "", "organizer": "", "contact_name": "", "contact_email": "", "contact_phone": "", "prices": "", "registration": "", # from IExternalEvent # source not used (filled in by ExternalEventImporter) "source_id": "id-1", # From IEventBasic "start": def_start, "end": def_end, "whole_day": False, "timezone": default_timezone(), # From IEventRecurrence "recurrence": "", # additional attributes used to control import "fetch_id": "fetch-1", "last_update": default_now().replace(microsecond=0), "latitude": "", "longitude": "", "cat1": set(), "cat2": set(), } for attr in defaults: if attr not in kw: kw[attr] = defaults[attr] return kw
def create(self, data): data['timezone'] = default_timezone() self.prepare_coordinates(data) self.prepare_submission(data) content = createContent('seantis.dir.events.item', **data) if IAcquirer.providedBy(content): content = content.__of__(aq_inner(self.context)) # must be done before adding to the container self.apply_submission(content) return aq_base(content)
def calendar_timezone(self): """http://tools.ietf.org/html/rfc4791#section-5.2.2 """ try: from plone.app.event.base import default_timezone except ImportError: timezone = 'UTC' else: timezone = default_timezone() calendar = icalendar.Calendar() tzc = icalendar.Timezone() tzc.add('tzid', timezone) calendar.add_component(tzc) return calendar.to_ical()
def extract(self, default=NO_VALUE): value = self.request.get(self.name, default) if value == default: return default if isinstance(value, str): # Date only date = datetime(value, '%Y-%m-%d') if value[0] == u'': return None else: if value[1] == u'': value[1] = u'0:00' time_str = "{0} {1}".format(*value) date = datetime.strptime(time_str, '%Y-%m-%d %H:%M') date = date.replace( tzinfo=default_timezone(self.context, as_tzinfo=True)) return date
def extract(self, default=NO_VALUE): value = self.request.get(self.name, default) if (value == default or not value or not isinstance(value, basestring)): return None date = datetime.strptime(value, '%Y-%m-%d') timezone_name = default_timezone(self.context) if isinstance(timezone_name, unicode): timezone_name.encode('utf8') # pytz "does not work" with datetime tzinfo. Use localize instead # Symptoms are times in "LMT" format which are off a few minutes. # http://stackoverflow.com/questions/24856643/unexpected-results-converting-timezones-in-python # noqa tz = timezone(timezone_name) date = tz.localize(date) return date
def __call__(self, value): # Datetime fields may contain timezone naive or timezone aware # objects. Unfortunately the zope.schema.Datetime field does not # contain any information if the field value should be timezone naive # or timezone aware. While some fields (start, end) store timezone # aware objects others (effective, expires) store timezone naive # objects. # We try to guess the correct deserialization from the current field # value. if value is None: self.field.validate(value) return # get tz from already stored value dm = queryMultiAdapter((self.context, self.field), IDataManager) current = dm.get() if current is not None: tzinfo = current.tzinfo else: # this is the patch tzinfo = pytz.timezone(default_timezone()) # Parse ISO 8601 string with dateutil try: dt = dateutil.parser.parse(value) except ValueError: raise ValueError(u"Invalid date: {}".format(value)) # Convert to TZ aware in UTC if dt.tzinfo is not None: dt = dt.astimezone(utc) else: dt = utc.localize(dt) # Convert to local TZ aware or naive UTC if tzinfo is not None: tz = timezone(tzinfo.zone) value = tz.normalize(dt.astimezone(tz)) else: value = utc.normalize(dt.astimezone(utc)).replace(tzinfo=None) self.field.validate(value) return value
def data_postprocessing(start, end, whole_day, open_end): """Adjust start and end according to whole_day and open_end setting. """ def _fix_dt(dt, tz): """Fix datetime: Apply missing timezones, remove microseconds. """ if dt.tzinfo is None: dt = tz.localize(dt) return dt.replace(microsecond=0) tz_default = default_timezone() tz_start = getattr(start, 'tzinfo', tz_default) tz_end = getattr(end, 'tzinfo', tz_default) start = _fix_dt(start, tz_start) end = _fix_dt(end, tz_end) # Adapt for whole day if whole_day: start = dt_start_of_day(start) if open_end: end = start # Open end events end on same day if open_end or whole_day: end = dt_end_of_day(end) # TODO: """ if not obj.sync_uid: # sync_uid has to be set for icalendar data exchange. uid = IUUID(obj) # We don't want to fail when getRequest() returns None, e.g when # creating an event during test layer setup time. request = getRequest() or {} domain = request.get('HTTP_HOST') obj.sync_uid = '%s%s' % ( uid, domain and '@%s' % domain or '' ) """ return start, end, whole_day, open_end
def extract(self, default=NO_VALUE): value = self.request.get(self.name, default) if value == default: return default if isinstance(value, str): # Date only date = datetime(value, '%Y-%m-%d') if value[0] == u'': return None else: if value[1] == u'': value[1] = u'0:00' time_str = "{0} {1}".format(*value) date = datetime.strptime(time_str, '%Y-%m-%d %H:%M') timezone_name = (self.request.get('%s-timezone' % self.name, '') or self.request.get('timezone', '') or default_timezone(self.context)) if isinstance(timezone_name, unicode): timezone_name.encode('utf8') date = date.replace(tzinfo=timezone(timezone_name)) return date
def get_dates_status_error(self): open_date = getattr(self.context, "open_date", None) close_date = getattr(self.context, "close_date", None) now = datetime.now(pytz.timezone(default_timezone())) res = {} if not open_date and not close_date: return res if not open_date: if now > close_date: res["status_message"] = self.close_past_msg(close_date) return res elif not close_date: if now < open_date: res["status_message"] = self.open_future_msg(open_date) return res if not open_date < now < close_date: # too early or too late res["active"] = False if open_date > now: res["status_message"] = self.open_future_msg(open_date) elif close_date < now: res["status_message"] = self.close_past_msg(close_date) return res
def extract(self, default=NO_VALUE): value = self.request.get(self.name, default) if value == default: return default if isinstance(value, str): # Date only date = datetime(value, '%Y-%m-%d') if value[0] == u'': return None else: if value[1] == u'': value[1] = u'0:00' time_str = "{0} {1}".format(*value) date = datetime.strptime(time_str, '%Y-%m-%d %H:%M') timezone_name = ( self.request.get('%s-timezone' % self.name, '') or self.request.get('timezone', '') or default_timezone(self.context) ) if isinstance(timezone_name, unicode): timezone_name.encode('utf8') date = date.replace(tzinfo=timezone(timezone_name)) return date
def fix_start_end(self): ''' If the start date is lower than the end one, modify the request setting end = start + 1 hour ''' localized_start = DateTime( '%s %s' % (' '.join(self.request.get('start')), self.request.get('timezone', default_timezone()))) localized_end = localized_start + 1. / 24 # If you know a smarter way to hijack the request, # please modify the following lines:) self.request.end = [ localized_end.strftime('%Y-%m-%d'), localized_end.strftime('%H:%M'), ] self.request.form['end'] = self.request.end self.request.other['end'] = self.request.end ts = api.portal.get_tool('translation_service') msg = _('dates_hijacked', default=(u'Start time should be lower than end time. ' u'The system set the end time to: ${date}'), mapping={u'date': ts.toLocalizedTime(localized_end)}) api.portal.show_message(msg, request=self.request, type="warning")
def fix_start_end(self): ''' If the start date is lower than the end one, modify the request setting end = start + 1 hour ''' localized_start = DateTime( '%s %s' % ( ' '.join(self.request.get('start')), self.request.get('timezone', default_timezone()) ) ) localized_end = localized_start + 1. / 24 # If you know a smarter way to hijack the request, # please modify the following lines:) self.request.end = [ localized_end.strftime('%Y-%m-%d'), localized_end.strftime('%H:%M'), ] self.request.form['end'] = self.request.end self.request.other['end'] = self.request.end ts = api.portal.get_tool('translation_service') msg = _( 'dates_hijacked', default=( u'Start time should be lower than end time. ' u'The system set the end time to: ${date}' ), mapping={ u'date': ts.toLocalizedTime(localized_end) } ) api.portal.show_message( msg, request=self.request, type="warning" )
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 = ical[prop] if prop in ical else [] if not isinstance(val, list): val = [val] # 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 = '%s,' % ret if ret else ret # insert linebreak ret = '%s%s' % (ret, item.to_ical()) return '%s:%s' % (prop, ret) if ret else 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 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)) # Set timezone, if not already set tz = base.default_timezone(container, as_tzinfo=True) if not getattr(start, 'tzinfo', False): start = tz.localize(start) if not getattr(end, 'tzinfo', False): end = tz.localize(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:%s' % rrule.to_ical() if rrule else '' 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 getattr(categories, '__iter__', False): categories = tuple([safe_unicode(it) for it in categories]) ext_modified = utc(_get_prop('LAST-MODIFIED', item)) content = None new_content_id = None existing_event = None sync_uid = _get_prop('UID', item) if sync_uid and sync_strategy is not base.SYNC_NONE: 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 modified date was passed in and it is not # older than the current modified date. The client is not # expected to update the "last-modified" property, it is the # job of the server (calendar store) to keep it up to date. # This makes sure the client did the change on an up-to-date # version of the object. See # http://tools.ietf.org/search/rfc5545#section-3.8.7.3 continue # Else: update content = exist_event else: 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.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_uid and sync_strategy is not base.SYNC_NONE: # Set the external sync_uid for sync strategies other than # SYNC_NONE. event.sync_uid = sync_uid notify(ObjectModifiedEvent(content)) # 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 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 default_tz(data): return default_timezone()
def last_modified(self, value): tz = default_timezone(self.context, as_tzinfo=True) mod = DT(pydt(value, missing_zone=tz)) setattr(self.context, 'modification_date', mod)
def add_timezone(dt, force=False): if HAS_PAE: TZ = default_timezone(as_tzinfo=True) if force or (not HAS_PLONE_5 and not dt.tzinfo): return TZ.localize(dt) return dt
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 test__default_timezone(self): """Test, if default_timezone returns something other than None if called with and without a context. """ self.assertTrue(default_timezone() is not None) self.assertTrue(default_timezone(context=self.portal) is not None)
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 setUp(self): self.portal = self.layer["portal"] default_tz = default_timezone() reg = zope.component.getUtility(IRegistry) settings = reg.forInterface(IEventSettings, prefix="plone.app.event") settings.portal_timezone = default_tz # Zope DateTime now = DateTime(2012, 9, 10, 10, 10, 0, default_tz) past = DateTime(2012, 9, 1, 10, 10, 0, default_tz) future = DateTime(2012, 9, 20, 10, 10, 0, default_tz) far = DateTime(2012, 9, 22, 10, 10, 0, default_tz) setRoles(self.portal, TEST_USER_ID, ["Manager"]) self.portal.invokeFactory( "Event", "past", title=u"Past event", startDate=past, endDate=past + 0.1, # Zope DT location=u"Vienna", timezone=default_tz, whole_day=False, ) self.portal.invokeFactory( "Event", "now", title=u"Now event", startDate=now, endDate=now + 0.1, location=u"Vienna", recurrence="RRULE:FREQ=DAILY;COUNT=4;INTERVAL=4", timezone=default_tz, whole_day=False, ) self.portal.invokeFactory( "Event", "future", title=u"Future event", startDate=future, endDate=future + 0.1, location=u"Graz", timezone=default_tz, whole_day=False, ) self.portal.invokeFactory( "Event", "long", title=u"Long event", startDate=past, endDate=future, location=u"Schaftal", timezone=default_tz, whole_day=False, ) self.now = now self.past = past self.future = future self.far = far self.now_event = self.portal["now"] self.past_event = self.portal["past"] self.future_event = self.portal["future"] self.long_event = self.portal["long"]
def setUp(self): self.portal = self.layer["portal"] default_tz = default_timezone() # default_tz = 'Europe/Vienna' reg = zope.component.getUtility(IRegistry) settings = reg.forInterface(IEventSettings, prefix="plone.app.event") settings.portal_timezone = default_tz now = localized_now() past = now - datetime.timedelta(days=2) future = now + datetime.timedelta(days=2) far = now + datetime.timedelta(days=8) setRoles(self.portal, TEST_USER_ID, ["Manager"]) self.portal.invokeFactory( "Event", "past", title=u"Past event", startDate=past, endDate=past + datetime.timedelta(hours=1), location=u"Vienna", timezone=default_tz, whole_day=False, ) self.portal.invokeFactory( "Event", "now", title=u"Now event", startDate=now, endDate=now + datetime.timedelta(hours=1), location=u"Vienna", recurrence="RRULE:FREQ=DAILY;COUNT=4;INTERVAL=4", timezone=default_tz, whole_day=False, ) self.portal.invokeFactory( "Event", "future", title=u"Future event", startDate=future, endDate=future + datetime.timedelta(hours=1), location=u"Graz", timezone=default_tz, whole_day=False, ) self.portal.invokeFactory( "Event", "long", title=u"Long event", startDate=past, endDate=future, location=u"Schaftal", timezone=default_tz, whole_day=False, ) self.now = now self.past = past self.future = future self.far = far self.now_event = self.portal["now"] self.past_event = self.portal["past"] self.future_event = self.portal["future"] self.long_event = self.portal["long"]
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 test_default_timezone(self): self.assertTrue(os_default_timezone() == default_timezone() == 'UTC') registry = getUtility(IRegistry) registry['plone.portal_timezone'] = "Europe/Vienna" self.assertTrue(default_timezone() == 'Europe/Vienna')
def import_csv(self, action): # Extract form field values and errors from HTTP request data, errors = self.extractData() if errors: self.status = self.formErrorsMessage return # Delete all existing events self.context.manage_delObjects(self.context.objectIds()) workflow_tool = getToolByName(self.context, 'portal_workflow') fieldmap = get_map(self.context) io = StringIO(data['csv_file'].data) reader = unicode_csv_reader(io, delimiter=';', quotechar='"') # Skip header line reader.next() counter = 0 for row in reader: attributes = dict() for attr, ix in fieldmap.fieldmap.items(): if not attr in fieldmap.readonly: value = row[ix] unwrapped = fieldmap.get_unwrapper(ix=ix)(value) attributes[attr] = unwrapped # Unmapped fields are filled with defaults add_defaults(attributes, fieldmap) # Adjust coordinates coordinates = attributes['coordinates_json'] if coordinates: coordinates = coordinates.replace("'", '"') cords = json.loads(coordinates) latitude = cords[1][0] longitude = cords[1][1] cords[1][0] = longitude cords[1][1] = latitude attributes['coordinates_json'] = json.dumps(cords) # "What" category cats1 = [] if attributes['cat1']: for cat in attributes['cat1']: if cat: cats1.append(categories1[cat]) attributes['cat1'] = cats1 # "Where" category if attributes['town']: attributes['town'] = categories2.get( attributes['town'], attributes['town'] ) attributes['cat2'] = [attributes['town']] # Manipulate some attributes attributes['timezone'] = default_timezone() attributes['long_description'] = self.readable_html( attributes['long_description'] ) # Fetch image form URL image_url = row[-3] if image_url: response = urllib2.urlopen(image_url) image = response.read() attributes['image'] = NamedImage(image) # Fetach PDF from URL pdf_url = row[-2] if pdf_url: response = urllib2.urlopen(pdf_url) pdf_file = response.read() attributes['attachment_1'] = NamedFile(pdf_file) # Create event event = createContentInContainer( self.context, fieldmap.typename, **attributes ) # Log the events which span multiple days, they need to manually # adjusted by the client if attributes['start'].date() != attributes['end'].date(): log.info( '"%s" spans multiple days' % event.absolute_url() ) # Publish event workflow_tool.doActionFor(event, 'submit') workflow_tool.doActionFor(event, 'publish') counter += 1 self.status = u'Imported %s events' % counter