def onExportIcalendarEvent(self, event): # triggered from "File | Import/Export" Menu wildcard = "iCalendar files|*.ics|All files (*.*)|*.*" dlg = wx.FileDialog(wx.GetApp().mainFrame, "Choose filename to export to", "", "export.ics", wildcard, wx.SAVE | wx.OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: (dir, filename) = os.path.split(dlg.GetPath()) dlg.Destroy() else: dlg.Destroy() self.setStatusMessage("Export aborted") return self.setStatusMessage ("Exporting to %s" % filename) try: share = Sharing.OneTimeFileSystemShare(dir, filename, ICalendar.ICalendarFormat, view=self.itsView) collection = ItemCollection(view=self.itsView) for event in Calendar.CalendarEvent.iterItems(self.itsView): collection.add(event) share.contents = collection share.put() self.setStatusMessage("Export completed") except: trace = "".join(traceback.format_exception (*sys.exc_info())) logger.info("Failed exportFile:\n%s" % trace) self.setStatusMessage("Export failed")
def onExportIcalendarEvent(self, event): # triggered from "File | Import/Export" Menu wildcard = "iCalendar files|*.ics|All files (*.*)|*.*" dlg = wx.FileDialog( wx.GetApp().mainFrame, "Choose filename to export to", "", "export.ics", wildcard, wx.SAVE | wx.OVERWRITE_PROMPT, ) if dlg.ShowModal() == wx.ID_OK: (dir, filename) = os.path.split(dlg.GetPath()) dlg.Destroy() else: dlg.Destroy() self.setStatusMessage("Export aborted") return self.setStatusMessage("Exporting to %s" % filename) try: share = Sharing.OneTimeFileSystemShare(dir, filename, ICalendar.ICalendarFormat, view=self.itsView) collection = ItemCollection(view=self.itsView) for event in Calendar.CalendarEvent.iterItems(self.itsView): collection.add(event) share.contents = collection share.put() self.setStatusMessage("Export completed") except: trace = "".join(traceback.format_exception(*sys.exc_info())) logger.info("Failed exportFile:\n%s" % trace) self.setStatusMessage("Export failed")
def writeICalendarUnicodeBug3338(self): event = Calendar.CalendarEvent(view = self.repo.view) event.displayName = u"unicode \u0633\u0644\u0627\u0645" event.startTime = datetime.datetime(2010, 1, 1, 10) event.endTime = datetime.datetime(2010, 1, 1, 11) coll = ItemCollection(name="testcollection", parent=self.sandbox) coll.add(event) filename = "unicode_export.ics" conduit = Sharing.FileSystemConduit(name="conduit", sharePath=".", shareName=filename, view=self.repo.view) format = ICalendar.ICalendarFormat(name="format", view=self.repo.view) self.share = Sharing.Share(name="share",contents=coll, conduit=conduit, format=format, view=self.repo.view) if self.share.exists(): self.share.destroy() self.share.create() self.share.put() cal=vobject.readComponents(file(filename, 'rb')).next() self.assertEqual(cal.vevent[0].summary[0].value, event.displayName) self.share.destroy()
def importProcess(self, text, extension=None, item=None): # the item parameter is so that a share item can be passed in for us # to populate. # An ICalendar file doesn't have any 'share' info, just the collection # of events, etc. Therefore, we want to actually populate the share's # 'contents': view = self.itsView filters = self.share.filterAttributes newItemParent = self.findPath("//userdata") eventKind = self.itsView.findPath(self._calendarEventPath) taskKind = self.itsView.findPath(self._taskPath) textKind = self.itsView.findPath(self._lobPath) if self.fileStyle() == self.STYLE_SINGLE: if item is None: item = ItemCollection(view=view) elif isinstance(item, Sharing.Share): if item.contents is None: item.contents = ItemCollection(view=view) item = item.contents if not isinstance(item, ItemCollection): print "Only a share or an item collection can be passed in" #@@@MOR Raise something input = StringIO.StringIO(text) calendar = vobject.readComponents(input, validate=True).next() if self.fileStyle() == self.STYLE_SINGLE: try: calName = calendar.contents[u'x-wr-calname'][0].value except: calName = "Imported Calendar" item.displayName = calName countNew = 0 countUpdated = 0 eventlist = getattr(calendar, 'vevent', []) todolist = getattr(calendar, 'vtodo', []) # this is just a quick hack to get VTODO working, FIXME write # more readable table driven code to process VEVENTs and VTODOs for event in itertools.chain(eventlist, todolist): vtype = event.name if vtype == u'VEVENT': logger.debug("got VEVENT") pickKind = eventKind elif vtype == u'VTODO': logger.debug("got VTODO") pickKind = taskKind try: displayName = event.summary[0].value except AttributeError: displayName = "" try: description = event.description[0].value except AttributeError: description = None try: location = event.location[0].value except AttributeError: location = None try: status = event.status[0].value.lower() if status in ('confirmed', 'tentative'): pass elif status == 'cancelled': #Chandler doesn't have CANCELLED status = 'fyi' else: status = 'confirmed' except AttributeError: status = 'confirmed' try: # FIXME assumes DURATION, not DATE-TIME reminderDelta = event.valarm[0].trigger[0].value except AttributeError: reminderDelta = None # RFC2445 allows VEVENTs without DTSTART, but it's hard to guess # what that would mean, so we won't catch an exception if there's no # dtstart. dtstart = event.dtstart[0].value try: duration = event.duration[0].value except AttributeError: # note that duration = dtend - dtstart isn't strictly correct # throughout a recurrence set, 1 hour differences might happen # around DST, but we'll ignore that corner case for now try: duration = event.dtend[0].value - dtstart # FIXME no end time or duration, Calendar UI doesn't seem to # like events with no duration, so for now we'll set a dummy # duration of 1 hour except AttributeError: # FIXME Nesting try/excepts is ugly. Also, we're assuming # DATE-TIMEs, not DATEs. try: duration = event.due[0].value - dtstart except AttributeError: if vtype == u'VEVENT': duration = datetime.timedelta(hours=1) elif vtype == u'VTODO': duration = None isDate = type(dtstart) == date if isDate: dtstart = datetime.datetime.combine(dtstart, time(0)) if duration: # convert to Chandler's notion of all day duration duration -= datetime.timedelta(days=1) # ignore timezones and recurrence till tzinfo -> PyICU is written # give the repository a naive datetime, no timezone dtstart = Recurrence.stripTZ(dtstart) # See if we have a corresponding item already recurrenceID = None uidMatchItem = self.findUID(event.uid[0].value) if uidMatchItem is not None: logger.debug("matched UID") try: recurrenceID = event.contents['recurrence-id'][0].value if type(recurrenceID) == date: recurrenceID = datetime.datetime.combine( recurrenceID, time(0)) else: recurrenceID = Recurrence.stripTZ(recurrenceID) except: pass if recurrenceID: eventItem = uidMatchItem.getRecurrenceID(recurrenceID) if eventItem == None: raise Exception, "RECURRENCE-ID didn't match rule. " + \ "RECURRENCE-ID = %s" % recurrenceID else: eventItem = uidMatchItem countUpdated += 1 else: eventItem = pickKind.newItem(None, newItemParent) countNew += 1 eventItem.icalUID = event.uid[0].value # vobject isn't meshing well with dateutil when dtstart isDate; # dtstart is converted to a datetime for dateutil, but rdate # isn't. To make dateutil happy, convert rdates which are dates to # datetimes until vobject is fixed. for i, rdate in enumerate(event.rdate): if type(rdate) == date: event.rdate[i] = datetime.datetime.combine(rdate, time(0)) else: event.rdate[i] = Recurrence.stripTZ(event.rdate[i]) # get rid of RDATES that match dtstart, created by vobject to # deal with unusual RRULEs correctly if event.rdate[i] == dtstart: del event.rdate[i] logger.debug("eventItem is %s" % str(eventItem)) #Default to NOT any time eventItem.anyTime = False eventItem.displayName = displayName if isDate: eventItem.allDay = True eventItem.startTime = dtstart if vtype == u'VEVENT': eventItem.endTime = dtstart + duration elif vtype == u'VTODO': if duration is not None: eventItem.dueDate = dtstart + duration if not filters or "transparency" not in filters: eventItem.transparency = status # I think Item.description describes a Kind, not userdata, so # I'm using DESCRIPTION <-> body if description is not None: eventItem.body = textKind.makeValue(description) if location: eventItem.location = Calendar.Location.getLocation( view, location) if not filters or "reminderTime" not in filters: if reminderDelta is not None: eventItem.reminderTime = dtstart + reminderDelta if len(event.rdate) > 0 or len(event.rrule) > 0: eventItem.setRuleFromDateUtil(event.rruleset) elif recurrenceID is None: # delete any existing rule eventItem.removeRecurrence() logger.debug("Imported %s %s" % (eventItem.displayName, eventItem.startTime)) if self.fileStyle() == self.STYLE_SINGLE: item.add(eventItem) else: return eventItem logger.info("...iCalendar import of %d new items, %d updated" % \ (countNew, countUpdated)) return item
def importProcess(self, text, extension=None, item=None): # the item parameter is so that a share item can be passed in for us # to populate. # An ICalendar file doesn't have any 'share' info, just the collection # of events, etc. Therefore, we want to actually populate the share's # 'contents': view = self.itsView filters = self.share.filterAttributes newItemParent = self.findPath("//userdata") eventKind = self.itsView.findPath(self._calendarEventPath) taskKind = self.itsView.findPath(self._taskPath) textKind = self.itsView.findPath(self._lobPath) if self.fileStyle() == self.STYLE_SINGLE: if item is None: item = ItemCollection(view=view) elif isinstance(item, Sharing.Share): if item.contents is None: item.contents = ItemCollection(view=view) item = item.contents if not isinstance(item, ItemCollection): print "Only a share or an item collection can be passed in" #@@@MOR Raise something input = StringIO.StringIO(text) calendar = vobject.readComponents(input, validate=True).next() if self.fileStyle() == self.STYLE_SINGLE: try: calName = calendar.contents[u'x-wr-calname'][0].value except: calName = "Imported Calendar" item.displayName = calName countNew = 0 countUpdated = 0 eventlist = getattr(calendar, 'vevent', []) todolist = getattr(calendar, 'vtodo', []) # this is just a quick hack to get VTODO working, FIXME write # more readable table driven code to process VEVENTs and VTODOs for event in itertools.chain(eventlist, todolist): vtype = event.name if vtype == u'VEVENT': logger.debug("got VEVENT") pickKind = eventKind elif vtype == u'VTODO': logger.debug("got VTODO") pickKind = taskKind try: displayName = event.summary[0].value except AttributeError: displayName = "" try: description = event.description[0].value except AttributeError: description = None try: location = event.location[0].value except AttributeError: location = None try: status = event.status[0].value.lower() if status in ('confirmed', 'tentative'): pass elif status == 'cancelled': #Chandler doesn't have CANCELLED status = 'fyi' else: status = 'confirmed' except AttributeError: status = 'confirmed' try: # FIXME assumes DURATION, not DATE-TIME reminderDelta = event.valarm[0].trigger[0].value except AttributeError: reminderDelta = None # RFC2445 allows VEVENTs without DTSTART, but it's hard to guess # what that would mean, so we won't catch an exception if there's no # dtstart. dtstart = event.dtstart[0].value try: duration = event.duration[0].value except AttributeError: # note that duration = dtend - dtstart isn't strictly correct # throughout a recurrence set, 1 hour differences might happen # around DST, but we'll ignore that corner case for now try: duration = event.dtend[0].value - dtstart # FIXME no end time or duration, Calendar UI doesn't seem to # like events with no duration, so for now we'll set a dummy # duration of 1 hour except AttributeError: # FIXME Nesting try/excepts is ugly. Also, we're assuming # DATE-TIMEs, not DATEs. try: duration = event.due[0].value - dtstart except AttributeError: if vtype == u'VEVENT': duration = datetime.timedelta(hours=1) elif vtype == u'VTODO': duration = None isDate = type(dtstart) == date if isDate: dtstart = datetime.datetime.combine(dtstart, time(0)) if duration: # convert to Chandler's notion of all day duration duration -= datetime.timedelta(days=1) # ignore timezones and recurrence till tzinfo -> PyICU is written # give the repository a naive datetime, no timezone dtstart = Recurrence.stripTZ(dtstart) # See if we have a corresponding item already recurrenceID = None uidMatchItem = self.findUID(event.uid[0].value) if uidMatchItem is not None: logger.debug("matched UID") try: recurrenceID = event.contents['recurrence-id'][0].value if type(recurrenceID) == date: recurrenceID = datetime.datetime.combine(recurrenceID, time(0)) else: recurrenceID = Recurrence.stripTZ(recurrenceID) except: pass if recurrenceID: eventItem = uidMatchItem.getRecurrenceID(recurrenceID) if eventItem == None: raise Exception, "RECURRENCE-ID didn't match rule. " + \ "RECURRENCE-ID = %s" % recurrenceID else: eventItem = uidMatchItem countUpdated += 1 else: eventItem = pickKind.newItem(None, newItemParent) countNew += 1 eventItem.icalUID = event.uid[0].value # vobject isn't meshing well with dateutil when dtstart isDate; # dtstart is converted to a datetime for dateutil, but rdate # isn't. To make dateutil happy, convert rdates which are dates to # datetimes until vobject is fixed. for i, rdate in enumerate(event.rdate): if type(rdate) == date: event.rdate[i] = datetime.datetime.combine(rdate, time(0)) else: event.rdate[i] = Recurrence.stripTZ(event.rdate[i]) # get rid of RDATES that match dtstart, created by vobject to # deal with unusual RRULEs correctly if event.rdate[i] == dtstart: del event.rdate[i] logger.debug("eventItem is %s" % str(eventItem)) #Default to NOT any time eventItem.anyTime = False eventItem.displayName = displayName if isDate: eventItem.allDay = True eventItem.startTime = dtstart if vtype == u'VEVENT': eventItem.endTime = dtstart + duration elif vtype == u'VTODO': if duration is not None: eventItem.dueDate = dtstart + duration if not filters or "transparency" not in filters: eventItem.transparency = status # I think Item.description describes a Kind, not userdata, so # I'm using DESCRIPTION <-> body if description is not None: eventItem.body = textKind.makeValue(description) if location: eventItem.location = Calendar.Location.getLocation(view, location) if not filters or "reminderTime" not in filters: if reminderDelta is not None: eventItem.reminderTime = dtstart + reminderDelta if len(event.rdate) > 0 or len(event.rrule) > 0: eventItem.setRuleFromDateUtil(event.rruleset) elif recurrenceID is None: # delete any existing rule eventItem.removeRecurrence() logger.debug("Imported %s %s" % (eventItem.displayName, eventItem.startTime)) if self.fileStyle() == self.STYLE_SINGLE: item.add(eventItem) else: return eventItem logger.info("...iCalendar import of %d new items, %d updated" % \ (countNew, countUpdated)) return item