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 FindTrashCollection(view): """ this is a really ugly hack - basically we have to iterate through all ItemCollections until we find the right one when the special collections stop being copied into the soup, this will just look like: return schema.ns("osaf.app", view).trash """ for collection in ItemCollection.iterItems(view): if IsTrashCollection(collection): return collection assert False, "No Trash collection!"
def sharedWebDAVCollections(self): # return the list of all the shared collections # @@@DLD - use new query, once it can handle method calls, or when our item.isShared # attribute is correctly set. UseNewQuery = False if UseNewQuery: qString = u"for i in '//parcels/osaf/pim/ItemCollection' where len (i.sharedURL) > 0" collQuery = Query.Query(self.itsView.repository, qString) collQuery.recursive = False collections = list(collQuery) else: collections = [coll for coll in ItemCollection.iterItems(self.itsView) if Sharing.isShared(coll)] return collections
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 sharedWebDAVCollections (self): # return the list of all the shared collections # @@@DLD - use new query, once it can handle method calls, or when our item.isShared # attribute is correctly set. UseNewQuery = False if UseNewQuery: qString = u"for i in '//parcels/osaf/pim/ItemCollection' where len (i.sharedURL) > 0" collQuery = Query.Query (self.itsView.repository, qString) collQuery.recursive = False collections = list(collQuery) else: collections = [ coll for coll in ItemCollection.iterItems(self.itsView) if Sharing.isShared(coll) ] return collections
def _createCollection(self, repo): sandbox = repo.findPath("//sandbox") coll = ItemCollection(name="testcollection", parent=sandbox)
def _mapItemToCacheKeyItem(self, item): key = item rerender = False sidebar = Block.Block.findBlockByName ("Sidebar") """ collectionList should be in the order that the source items are overlayed in the Calendar view """ collectionList = [theItem for theItem in sidebar.contents if (theItem in sidebar.checkedItems) and (theItem is not item)] if isinstance (item, ItemCollection): collectionList.insert (0, item) if len (collectionList) > 0: """ tupleList is sorted so we always end up with on collection for any order of collections in the source """ tupleList = [theItem.itsUUID for theItem in collectionList] tupleList.sort() filterKind = sidebar.filterKind if not filterKind is None: tupleList.append (filterKind.itsUUID) if len (tupleList) > 1: tupleKey = tuple (tupleList) try: key = self.itemTupleKeyToCacheKey [tupleKey] except KeyError: """ We need to make a new filtered item collection that depends upon the unfiltered collection. Unfortunately, making a new ItemCollection with a rule whose results include all items in the original ItemCollection has a problem: when the results in the unfiltered ItemCollection change we don't get notified. Alternatively we make a copy of the ItemCollection (and it's rule) which has another problem: When the rule in the original ItemCollection change we don't update our copied rule. """ key = ItemCollection(view=self.itsView) key.source = collectionList displayName = u" and ".join ([theItem.displayName for theItem in collectionList]) if filterKind is not None: key.addFilterKind (filterKind) displayName += u" filtered by " + filterKind.displayName key.displayName = displayName self.itemTupleKeyToCacheKey [tupleKey] = key else: """ Check to see if we need to reorder the source list. The list is kept sorted by the order of the collections as they overlay one another. We don't bother to sort when we're looking up a collection that isn't displayed in the summary view, both because it's not necessary and because it causes the source attribute to change which causes a notification to update the sidebar, which causes the order to change, causing a notification, ... repeating forever. """ if sidebar.selectedItemToView is item: for new, old in map (None, key.source, collectionList): if new is not old: key.source = collectionList rerender = True break return key, rerender
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