class Note(items.ContentItem): ## ## Attribute declarations ## # ensure that the displayName carries over schema.kindInfo(displayName="Note") # temporarily make this a real attribute instead of a redirection, # because we want don't want to redirect this anywhere who = schema.One(schema.String, initialValue="") # redirections about = schema.One(redirectTo="displayName") date = schema.One(redirectTo="createdOn") def InitOutgoingAttributes(self): """ Init any attributes on ourself that are appropriate for a new outgoing item. """ try: super(Note, self).InitOutgoingAttributes() except AttributeError: pass self.processingStatus = 'processing' def getAnyAbout(self): """ Get any non-empty definition for the "about" attribute. """ return self.displayName def getAnyDate(self): """ Get any non-empty definition for the "date" attribute. """ return self.createdOn def getAnyWho(self): """ Get any non-empty definition for the "who" attribute. """ raise AttributeError def getAnyWhoFrom(self): """ Get any non-empty definition for the "whoFrom" attribute. """ return self.creator def ExportItemData(self, clipboardHandler): # Create data for this kind of item in the clipboard handler # The data is used for Drag and Drop or Cut and Paste super(Note, self).ExportItemData(clipboardHandler) # Let the clipboard handler know we've got a Note to export clipboardHandler.ExportItemFormat(self, 'Note')
class Calendar(ContentItem): """ @note: Calendar should have an attribute that points to all the Calendar Events. @note: Calendar should maybe have a 'Timezone' attribute. """ schema.kindInfo(displayName="Calendar", displayAttribute="displayName")
class Project(ContentItem): schema.kindInfo( displayName="Project", examples=[ 'my "Housewarming Party" project', "my department's \"Move to new building\" project", "my company's \"Open Seattle Store\" project", ], description= "Users can create projects to help organize their work. Users can " "take content items (like tasks and mail messages) and assign " "them to different projects.") parentProject = schema.One( 'Project', displayName='Parent Project', doc= 'Projects can be organized into hierarchies. Each project can have one parent.', inverse='subProjects', ) subProjects = schema.Sequence( 'Project', displayName='Sub Projects', doc= 'Projects can be organized into hierarchies. Each project can have many sub-projects.', inverse='parentProject', )
class AmazonCollection(ItemCollection): schema.kindInfo(displayName="Amazon Collection Kind") keywords = schema.One(schema.String, displayName='Keywords') myKindID = None myKindPath = "//parcels/osaf/examples/amazon/schema/AmazonCollection" def __init__(self, keywords=None, email=None, name=None, parent=None, kind=None, view=None): super(AmazonCollection, self).__init__(name, parent, kind, view) if keywords: bags = amazon.searchByKeyword(keywords) self.displayName = 'Amzn: ' + keywords elif email: results = amazon.searchWishListByEmail(email) customerName = results[0] bags = results[1] self.displayName = 'Amzn: ' + customerName else: bags = {} for aBag in bags: self.add(AmazonItem(aBag, view=view))
class StampBranchSubtree(BranchSubtree): """ A mapping between an Item and the list of top-level blocks ('rootBlocks') that should appear when an Item inheriting from that Kind is displayed. Each rootBlock entry should have its 'position' attribute specified, to enable it to be sorted with other root blocks.) """ schema.kindInfo(annotates=schema.Item)
class Location(ContentItem): """ @note: Location may not be calendar specific. """ schema.kindInfo(displayName="Location", displayAttribute="displayName") eventsAtLocation = schema.Sequence(CalendarEventMixin, displayName="Calendar Events", inverse=CalendarEventMixin.location) def __str__(self): """ User readable string version of this Location """ if self.isStale(): return super(Location, self).__str__() # Stale items can't access their attributes return self.getItemDisplayName() def getLocation(cls, view, locationName): """ Factory Method for getting a Location. Lookup or create a Location based on the supplied name string. If a matching Location object is found in the repository, it is returned. If there is no match, then a new item is created and returned. @param locationName: name of the Location @type locationName: C{String} @return: C{Location} created or found """ # make sure the locationName looks reasonable assert locationName, "Invalid locationName passed to getLocation factory" # get all Location objects whose displayName match the param its = Location.iterItems(view, exact=True) locQuery = [i for i in its if i.displayName == locationName] ## locQuery = view.findPath('//Queries/calendarLocationQuery') ## if locQuery is None: ## queryString = u'for i in "//parcels/osaf/pim/calendar/Location" \ ## where i.displayName == $0' ## p = view.findPath('//Queries') ## k = view.findPath('//Schema/Core/Query') ## locQuery = Query.Query ('calendarLocationQuery', p, k, queryString) ## locQuery.args["$0"] = ( locationName, ) # return the first match found, if any for firstSpot in locQuery: return firstSpot # make a new Location newLocation = Location(view=view) newLocation.displayName = locationName return newLocation getLocation = classmethod(getLocation)
class EventTask( tasks.TaskMixin, tasks.TaskEventExtraMixin, calendar.Calendar.CalendarEvent ): schema.kindInfo( displayName = "Event Task", description = "A Task stamped as an Event, or vica versa", )
class TimeTransparencyEnum(schema.Enumeration): """Time Transparency Enum The iCalendar values for Time Transparency are slightly different. We should consider making our values match the iCalendar ones, or be a superset of them. Mitch suggested that a 'cancelled' value would be a useful extension. It'd be nice to not maintain the transparency choices separately from the enum values""" schema.kindInfo(displayName="Time Transparency") values = "confirmed", "tentative", "fyi"
class Group(ContentItem): schema.kindInfo( description= "See http://wiki.osafoundation.org/twiki/bin/view/Jungle/CollectionProject\n\n" "Issues:\n" ' We still need to work out some issues about how ' '"playlists"/"item collections" are modeled.\n' ' We need to find a name for these things.\n')
class ContentKind(Kind): """This kind is a metakind for creating other kinds. Kinds which are an instance of ContentKind will have an attribute 'detailView' of type Block. We could also make this attribute a bidiref.""" __metaclass__ = schema.ItemClass schema.kindInfo(displayName="Metakind 'Content Kind'") detailView = schema.One() # Block
class MailedEventTask( tasks.TaskMixin, tasks.TaskEventExtraMixin, calendar.Calendar.CalendarEventMixin, mail.MailMessage ): schema.kindInfo( displayName = "Mailed Event Task", description = "A Task stamped as an Event stamped as Mail, in any sequence", )
class Script(pim.ContentItem): """ Persistent Script Item, to be executed. """ schema.kindInfo(displayName="Script", displayAttribute="displayName") def __init__(self, name=_('untitled'), parent=None, kind=None): super(Script, self).__init__(name, parent, kind, displayName=name) def execute(self): executable = ExecutableScript(self.bodyString, self.itsView) executable.execute()
class CalendarEvent(CalendarEventMixin, Note): """ @note: CalendarEvent should maybe have a 'Timezone' attribute. @note: Do we want to have 'Duration' as a derived attribute on Calendar Event? @note: Do we want to have a Boolean 'AllDay' attribute, to indicate that an event is an all day event? Or should we instead have the 'startTime' and 'endTime' attributes be 'RelativeDateTime' instead of 'DateTime', so that they can store all day values like '14 June 2004' as well as specific time values like '4:05pm 14 June 2004'? """ schema.kindInfo(displayName="Calendar Event") def __init__(self, name=None, parent=None, kind=None, view=None, **kw): kw.setdefault('participants', []) super(CalendarEvent, self).__init__(name, parent, kind, view, **kw)
class Group(ContentItem): schema.kindInfo( displayName='"Playlist"/"Item Collection"', description= "See http://wiki.osafoundation.org/twiki/bin/view/Jungle/CollectionProject", issues=[ 'We still need to work out some issues about how ' '"playlists"/"item collections" are modeled.', 'We need to find a name for these things.', ])
class Task(TaskMixin, notes.Note): schema.kindInfo( displayName = "Task", issues = [ "Do we want to support the idea of tasks having sub-tasks? If so, " "then we need to add attributes for 'superTask' and 'subTasks'.", "Task should maybe have a 'Timezone' attribute.", ] )
class String(schema.Item): schema.kindInfo(displayName="Container for string attribute tests") uString = schema.One(schema.UString) bString = schema.One(schema.BString) localizableString = schema.One(schema.LocalizableString) text = schema.One(schema.Text)
class UserCollection(schema.Annotation): schema.kindInfo(annotates=ContentCollection) renameable = schema.One(schema.Boolean, defaultValue=True) color = schema.One(ColorType) iconName = schema.One(schema.Text, defaultValue="") colorizeIcon = schema.One(schema.Boolean, defaultValue=True) dontDisplayAsCalendar = schema.One(schema.Boolean, defaultValue=False) outOfTheBoxCollection = schema.One(schema.Boolean, defaultValue=False) canAdd = schema.One(schema.Boolean, defaultValue=True) allowOverlay = schema.One(schema.Boolean, defaultValue=True) searchMatches = schema.One(schema.Integer, defaultValue=0) checked = schema.One(schema.Boolean, defaultValue=False) """ preferredClass is used as a hint to the user-interface to choose the right view for the display, e.g. CalendarView for collections that have a preferredClass of EventStamp. """ preferredClass = schema.One(schema.Class) schema.addClouds(copying=schema.Cloud(byRef=[preferredClass]), ) def ensureColor(self): """ Make sure the collection has a color. Pick up the next color in a predefined list if none was set. """ if not hasattr(self, 'color'): self.color = schema.ns( 'osaf.usercollections', self.itsItem.itsView).collectionColors.nextColor() return self def setColor(self, colorname): """ Set the collection color by name. Raises an error if colorname doesn't exist. """ hue = None for colname, coltitle, colhue in collectionHues: if colname == colorname: hue = colhue break if hue is None: raise ValueError("Unknown color name") rgb = colorsys.hsv_to_rgb(hue / 360.0, 0.5, 1.0) self.color = ColorType(int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255), 255) def setValues(self, **kwds): for attr, value in kwds.iteritems(): setattr(self, attr, value)
class TaskEventExtraMixin(items.ContentItem): """ Task Event Extra Mixin is the bag of attributes that appears when you have an Item that is both a Task and a CalendarEvent. We only instantiate these Items when we "unstamp" an Item, to save the attributes for later "restamping". """ schema.kindInfo( displayName = "Task Event Extra Mixin Kind", description = "The attributes specific to an item that is both a task and an " "event. This is additional 'due by' information. " ) dueByDate = schema.One( schema.DateTime, displayName = 'Due by Date', doc = 'The date when a Task Event is due.', ) dueByRecurrence = schema.Sequence( 'osaf.pim.calendar.Calendar.RecurrencePattern', displayName = 'Due by Recurrence', doc = 'Recurrence information for a Task Event.', ) dueByTickler = schema.One( schema.DateTime, displayName = 'Due by Tickler', doc = 'The reminder information for a Task Event.', ) def InitOutgoingAttributes (self): """ Init any attributes on ourself that are appropriate for a new outgoing item. """ try: super(TaskEventExtraMixin, self).InitOutgoingAttributes () except AttributeError: pass TaskEventExtraMixin._initMixin (self) # call our init, not the method of a subclass def _initMixin (self): """ Init only the attributes specific to this mixin. Called when stamping adds these attributes, and from __init__ above. """ # default the dueByDate to the task's dueDate try: self.dueByDate = self.dueDate except AttributeError: pass
class NotificationItem(schema.Item): """ A notification item, mainly intended as a dummy for query subscriptions. """ schema.kindInfo( displayName="Notification Item", description="A notification item, mainly intended as a dummy for " "query subscriptions.") def __init__(self, name=None, parent=None, kind=None): super(NotificationItem, self).__init__(name, parent, kind) def handle(self, action): pass
class TaskStamp(Stamp): """ TaskStamp is the bag of Task-specific attributes. """ schema.kindInfo(annotates = notes.Note) __use_collection__ = True requestor = schema.One( Contact, description = "Issues:\n" ' Type could be Contact, EmailAddress or String\n' ' Think about using the icalendar terminology\n', inverse = Contact.requestedTasks, ) requestee = schema.Sequence( Contact, description = "Issues:\n" ' Type could be Contact, EmailAddress or String\n' ' Think about using the icalendar terminology\n', inverse = Contact.taskRequests, ) # Redirections @apply def summary(): def fget(self): return self.itsItem.displayName def fset(self, value): self.itsItem.displayName = value return schema.Calculated(schema.Text, (items.ContentItem.displayName,), fget, fset) schema.addClouds( copying = schema.Cloud( requestor, requestee ) ) def InitOutgoingAttributes (self): self.itsItem.InitOutgoingAttributes()
class MyKind1(ContentItem): """An example content kind""" attr1 = schema.One(schema.String, displayName="Attribute 1") schema.kindInfo( displayName = "Example Kind" ) # redirection attributes who = schema.Role(redirectTo="attr1") attr2 = schema.One(schema.String, displayName="Attribute 2") # Typical clouds include a "copying" cloud, and a "sharing" cloud schema.addClouds( sharing = schema.Cloud(attr1, attr2) )
class AmazonItem(ContentItem): schema.kindInfo(displayName="Amazon Item") amazonCollection = schema.One( AmazonCollection, displayName='Amazon Collection', ) ProductName = schema.One(schema.String, displayName='Product Name') Author = schema.One(schema.String, displayName='Author(s)') ReleaseDate = schema.One(schema.String, displayName='Release Date') imageURL = schema.One(schema.String, displayName='image path') about = schema.One(redirectTo='ProductName') who = schema.One(redirectTo='Author') date = schema.One(redirectTo='ReleaseDate') myKindID = None myKindPath = "//parcels/osaf/examples/amazon/schema/AmazonItem" def __init__(self, bag, name=None, parent=None, kind=None, view=None): super(AmazonItem, self).__init__(name, parent, kind, view) if bag: self.ProductName = bag.ProductName self.imageURL = bag.ImageUrlLarge self.ReleaseDate = getattr(bag, 'ReleaseDate', '') if hasattr(bag, 'Authors'): if type(bag.Authors.Author) == type([]): self.Author = ', '.join(bag.Authors.Author) else: self.Author = bag.Authors.Author elif hasattr(bag, 'Directors'): if type(bag.Directors.Director) == type([]): self.Author = ', '.join(bag.Directors.Director) else: self.Author = bag.Directors.Director elif hasattr(bag, 'Artists'): if type(bag.Artists.Artist) == type([]): self.Author = ', '.join(bag.Artists.Artist) else: self.Author = bag.Artists.Artist else: self.Author = '' self.displayName = self.ProductName
class KindCollection(AbstractCollection): """ """ schema.kindInfo( displayName="KindCollection" ) kind = schema.One(schema.TypeReference('//Schema/Core/Kind'), initialValue=None) def contentsUpdated(self, item): # print "KindCollection.contentsUpdated: ",item self._collectionChanged('changed' , 'rep', item) pass def onValueChanged(self, name): if name == "kind": try: self.rep = KindSet(self.kind) except AttributeError: pass
class IntersectionCollection(AbstractCollection): """ """ schema.kindInfo( displayName="IntersectionCollection" ) left = schema.One(AbstractCollection, initialValue=None) right = schema.One(AbstractCollection, initialValue=None) schema.addClouds( copying = schema.Cloud(byCloud=[left, right]), ) def onValueChanged(self, name): if name == "left" or name == "right": try: if self.left != None and self.right != None: self.rep = Intersection((self.left, "rep"),(self.right, "rep")) except AttributeError: pass
class FilteredCollection(AbstractCollection): """ """ schema.kindInfo( displayName="FilteredCollection" ) source = schema.One(AbstractCollection, initialValue=None) filterExpression = schema.One(schema.String, initialValue="") schema.addClouds( copying = schema.Cloud(byCloud=[source]), ) def onValueChanged(self, name): if name == "source" or name == "filterExpression": try: if self.source != None and self.filterExpression != "": s = "lambda item: %s" % self.filterExpression self.rep = FilteredSet((self.source, "rep"), eval(s)) except AttributeError, ae: print ae pass
class ListCollection(AbstractCollection): """ """ schema.kindInfo( displayName="ListCollection" ) refCollection = schema.Sequence(otherName=Item.collections,initialValue=[]) def __init__(self, *args, **kw): super(ListCollection, self).__init__(*args, **kw) self.rep = Set((self,'refCollection')) self.refCollection = [] def add(self, item): self.refCollection.append(item) def remove(self, item): self.refCollection.remove(item) def contentsUpdated(self, item): self._collectionChanged('changed' , 'rep', item) pass
class WeekdayEnum(schema.Enumeration): """The names of weekdays.""" schema.kindInfo(displayName="Weekdays") values="monday","tuesday","wednesday","thursday","friday", \ "saturday","sunday"
class FrequencyEnum(schema.Enumeration): """The base frequency for a recurring event.""" schema.kindInfo(displayName="Frequency") values = "yearly", "monthly", "weekly", "daily", "hourly", "minutely", "secondly"
class Photo(PhotoMixin, Note): schema.kindInfo(displayName="Photo")
class PhotoMixin(items.ContentItem): schema.kindInfo(displayName="Photo Mixin Kind", displayAttribute="displayName") dateTaken = schema.One(schema.DateTime, displayName="taken") file = schema.One(schema.String) exif = schema.Mapping(schema.String, initialValue={}) photoBody = schema.One(schema.Lob) about = schema.One(redirectTo='displayName') date = schema.One(redirectTo='dateTaken') who = schema.One(redirectTo='creator') schema.addClouds(sharing=schema.Cloud(dateTaken, photoBody)) def importFromFile(self, path): data = file(path, "rb").read() (mimeType, encoding) = mimetypes.guess_type(path) self.photoBody = utils.dataToBinary(self, 'photoBody', data, mimeType=mimeType) def importFromURL(self, url): if isinstance(url, URL): url = str(url) data = urllib.urlopen(url).read() (mimeType, encoding) = mimetypes.guess_type(url) self.photoBody = utils.dataToBinary(self, 'photoBody', data, mimeType=mimeType) def exportToFile(self, path): data = utils.binaryToData(self.photoBody) out = file(path, "wb") out.write(data) out.close() def processEXIF(self): input = self.photoBody.getInputStream() data = input.read() input.close() stream = cStringIO.StringIO(data) try: exif = EXIF.process_file(stream) # Warning, serious nesting ahead self.dateTaken = datetime.datetime.fromtimestamp( time.mktime( time.strptime(str(exif['Image DateTime']), "%Y:%m:%d %H:%M:%S"))) self.exif = {} for (key, value) in exif.iteritems(): if isinstance(value, EXIF.IFD_Tag): self.exif[key] = value.printable else: self.exif[key] = value except Exception, e: logger.debug("Couldn't process EXIF of Photo %s (%s)" % \ (self.itsPath, e))