class WaveModel(object): """ Wave model class. Top level model which sends all events. @class {public} pygowave.model.WaveModel """ # --- Event documentation --- """ Fired if a Wavelet has been added. @event onWaveletAdded @param {String} waveletId ID of the Wavelet that has been added @param {Boolean} isRoot True if this is the (new) root Wavelet """ """ Fired before a wavelet is removed. @event onWaveletAboutToBeRemoved @param {String} waveletId ID of the Wavelet that will be removed """ # --------------------------- def __init__(self, waveId, viewerId): """ Called on instantiation. @constructor {public} initialize @param {String} waveId ID of the Wave @param {String} viewerId ID of the viewer """ self._rootWavelet = None self._waveId = waveId self._viewerId = viewerId self._wavelets = Hash() def id(self): """ Returns the ID of this Wave. @function {public String} id """ return self._waveId def viewerId(self): """ Returns the ID of the viewer. @function {public String} viewerId """ return self._viewerId def loadFromSnapshot(self, obj, participants): """ Load the wave's contents from a JSON-serialized snapshot and a map of participant objects. @function {public} loadFromSnapshot @param {Object} obj The JSON-serialized snapshot to load @param {Hash} participants A map of participant objects """ rootWavelet = obj["wavelet"] wvl_options = { "creator": participants[rootWavelet["creator"]], "is_root": True, "created": rootWavelet["creationTime"], "last_modified": rootWavelet["lastModifiedTime"], "title": rootWavelet["title"], "version": rootWavelet["version"] } rootWaveletObj = self.createWavelet(rootWavelet["waveletId"], wvl_options) for part_id in rootWavelet["participants"]: rootWaveletObj.addParticipant(participants[part_id]) rootWaveletObj.loadBlipsFromSnapshot(obj["blips"], rootWavelet["rootBlipId"], participants); def createWavelet(self, id, options): """ Create a Wavelet and add it to this Wave. For options see the {@link pygowave.model.Wavelet.initialize Wavelet constructor}.<br/> Note: Fires {@link pygowave.model.WaveModel.onWaveletAdded onWaveletAdded} @function {public Wavelet} createWavelet @param {String} id Wavelet ID @param {Object} options Information about the Wavelet. """ w = Wavelet(self, id, options) self._wavelets.set(id, w) self.fireEvent('waveletAdded', [id, w.isRoot()]) return w def wavelet(self, waveletId): """ Return a Wavelet of this Wave by its ID. @function {public Wavelet} wavelet @param {String} waveletId ID of the Wavelet """ return self._wavelets.get(waveletId) def allWavelets(self): """ Return a list of all Wavelets on this Wave. @function {public Wavelet[]} allWavelets """ return self._wavelets.getValues() def rootWavelet(self): """ Returns the root Wavelet of this Wave. @function {public Wavelet} rootWavelet """ return self._rootWavelet def _setRootWavelet(self, wavelet): """ Internal method to set the root Wavelet. Not intended to be called outside of this implementation. @function {private} _setRootWavelet @param {Wavelet} wavelet Wavelet to be set as root Wavelet """ self._rootWavelet = wavelet def removeWavelet(self, waveletId): """ Removes and deletes a wavelet by its id. Fires {@link pygowave.model.WaveModel.onWaveletAboutToBeRemoved} beforehand. @function {public} removeWavelet @param {String} waveletId ID of the Wavelet to remove """ if not self._wavelets.has(waveletId): return self.fireEvent('waveletAboutToBeRemoved', waveletId) wavelet = self._wavelets.get(waveletId) self._wavelets.erase(waveletId) if wavelet == self._rootWavelet: self._rootWavelet = None
class Wavelet(object): """ Models a Wavelet on a Wave.<br/> This is a private class and cannot be instanitated directly. Please use {@link pygowave.model.WaveModel.createWavelet WaveModel.createWavelet}. @class {private} pygowave.model.Wavelet """ options = { "creator": None, "is_root": False, "created": None, "last_modified": None, "title": "", "version": 0, "status": "clean" } # --- Event documentation --- """ Fired if a participant joins or leaves. @event onParticipantsChanged """ """ Fired if a Blip was inserted. @event onBlipInserted @param {int} index Index of the inserted Blip @param {String} blip_id ID of the inserted Blip """ """ Fired when the Wavelet's status changed. @event onStatusChange @param {String} status The new status; can be 'clean', 'dirty' or 'invalid' """ """ Fired when the Wavelet's title changed. @event onTitleChanged @param {String} title The Wavelet's new title """ """ Fired when the Wavelet's last modification date/time changed. @event onLastModifiedChanged @param {Date} datetime New date of last modification """ # --------------------------- def __init__(self, wave, id, options): """ Called on instantiation. Documented for internal purposes. @constructor {private} initialize @param {WaveModel} wave Parent WaveModel object @param {String} id Wavelet ID @param {Object} options Information about the Wavelet. Possible values: @... {Participant} creator Creator of this Wavelet @... {Boolean} is_root True if this is the root Wavelet; if this value is set and the parent WaveModel does not have a root Wavelet yet, this Wavelet is set as the Wave's root Wavelet @... {Date} created Date of creation @... {Date} last_modified Date of last modification @... {String} title Title of the Wavelet @... {int} version Version of the Wavelet @... {String} status Status of the Wavelet; can be 'clean', 'dirty' or 'invalid' """ self.setOptions(options) self._wave = wave if self.options["is_root"]: if self._wave.rootWavelet() == None: self._wave._setRootWavelet(self) else: self.options["is_root"] = False self._id = id self._participants = Hash() self._blips = [] self._rootBlip = None def title(self): """ Returns the title of this Wavelet @function {public String} title """ return self.options["title"] def setTitle(self, title): """ Sets the title of this Wavelet @function {public} setTitle @param {String} title The new title """ if self.options["title"] != title: self.options["title"] = title self.fireEvent('titleChanged', title) def isRoot(self): """ Returns true, if this Wavelet is the Wave's root Wavelet. @function {public Boolean} isRoot """ return self.options["is_root"] def id(self): """ Returns the ID of this Wavelet. @function {public String} id """ return self._id def waveId(self): """ Returns the ID of this Wavelet's Wave. @function {public String} waveId """ return self._wave.id() def waveModel(self): """ Returns the parent WaveModel object. @function {public WaveModel} waveModel """ return self._wave def participantCount(self): """ Returns the number of participants on this Wavelet. @function {public int} participantCount """ return self._participants.getLength() def addParticipant(self, participant): """ Add a participant to this Wavelet.<br/> Note: Fires {@link pygowave.model.Wavelet.onParticipantsChanged onParticipantsChanged} @function {public} addParticipant @param {Participant} participant Participant to be added """ if not self._participants.has(participant.id()): self._participants.set(participant.id(), participant) self.fireEvent('participantsChanged', self._id) def removeParticipant(self, participantId): """ Removes a participant from this Wavelet.<br/> Note: Fires {@link pygowave.model.Wavelet.onParticipantsChanged onParticipantsChanged} @function {public} removeParticipant @param {String} participantId ID of the participant to remove """ if self._participants.has(participantId): del self._participants[participantId] self.fireEvent('participantsChanged', self._id) def participant(self, id): """ Returns the Participant object with the given id, if the participant resides on this Wavelet. Returns null otherwise. @function {Participant} participant @param {String} id ID of the Participant """ if self._participants.has(id): return self._participants.get(id) else: return None def allParticipants(self): """ Returns a list of all participants on this Wavelet. @function {public Participant[]} allParticipants """ return self._participants.getValues() def allParticipantIDs(self): """ Returns a list of all IDs of the participants on this Wavelet. @function {public String[]} allParticipantIDs """ return self._participants.getKeys() def allParticipantsForGadget(self): """ Convenience function to serialize all participants into the Wave Gadget API format. @function {public Object} allParticipantsForGadget """ ret = {} for id, participant in self._participants.iteritems(): ret[id] = participant.toGadgetFormat() return ret def appendBlip(self, id, options, content = "", elements = []): """ Convenience function for inserting a new Blip at the end. For options see the {@link pygowave.model.Blip.initialize Blip constructor}.<br/> Note: Fires {@link pygowave.model.Wavelet.onBlipInserted onBlipInserted} @function {public Blip} appendBlip @param {String} id ID of the new Blip @param {Object} options Information about the Blip @param {optional String} content Content of the Blip @param {optional Element[]} elements Element objects which initially reside in this Blip """ return self.insertBlip(len(self._blips), id, options, content, elements) def insertBlip(self, index, id, options, content = "", elements = []): """ Insert a new Blip at the specified index. For options see the {@link pygowave.model.Blip.initialize Blip constructor}.<br/> Note: Fires {@link pygowave.model.Wavelet.onBlipInserted onBlipInserted} @function {public Blip} insertBlip @param {int} index Index where to insert the Blip @param {String} id ID of the new Blip @param {Object} options Information about the Blip @param {optional String} content Content of the Blip @param {optional Element[]} elements Element objects which initially reside in this Blip """ blip = Blip(self, id, options, content, elements) self._blips.insert(index, blip) self.fireEvent('blipInserted', [index, id]) return blip def blipByIndex(self, index): """ Returns the Blip object at the given index. @function {Blip} blipByIndex @param {int} index Index of the Blip """ return self._blips[index] def blipById(self, id): """ Returns the Blip object with the given id, if the Blip resides on this Wavelet. Returns null otherwise. @function {Blip} blipById @param {String} id ID of the Blip """ for blip in self._blips: if blip.id() == id: return blip return None def allBlips(self): """ Returns a list of all Blips on this Wavelet, starting with the root Blip. @function {Blip[]} allBlips """ return self._blips def allBlipIDs(self): """ Returns a list of all IDs of the Blips on this Wavelet, starting with the root Blip. @function {public String[]} allBlipIDs """ ids = [] for blip in self._blips: ids.append(blip.id()) return ids def _setRootBlip(self, blip): """ Internal method to set the root Blip. Not intended to be called outside of this implementation. @function {private} _setRootBlip @param {Blip} blip Blip to be set as root Blip """ self._rootBlip = blip def _setStatus(self, status): """ Internal method to set the status. Fires {@link pygowave.model.Wavelet.onStatusChange onStatusChange} if the status changed. @function {private} _setStatus @param {String} status The status to set """ if self.options["status"] != status: self.options["status"] = status self.fireEvent("statusChange", status) def status(self): """ Returns the Wavelet's current status. Can be "clean", "dirty" or "invalid". @function {public String} status """ return self.options["status"] def created(self): """ Returns the creation date/time of this Wavelet. @function {public Date} created """ return self.options["created"] def lastModified(self): """ Returns the date/time of the last modification of this Wavelet. @function {public Date} lastModified """ return self.options["last_modified"] def setLastModified(self, value): """ Sets the date/time of the last modification of this Wavelet. @function {public} setLastModified @param {Date} value The new date/time of the last modification """ if value != self.options["last_modified"]: self.options["last_modified"] = value self.fireEvent('lastModifiedChanged', value) def checkSync(self, blipsums): """ Calculate and compare checksums of all Blips to the given map. Fires {@link pygowave.model.Wavelet.onStatusChange onStatusChange} if the status changes. @function {public} checkSync @param {Object} blipsums Checksums to compare to """ valid = True for blipId, checksum in blipsums.iteritems(): blip = self.blipById(blipId); if blip != None: if not blip.checkSync(checksum): valid = False if valid: self._setStatus("clean") else: self._setStatus("invalid") def applyOperations(self, ops, participants): """ Apply the operations on the wavelet. @function {public} applyOperations @param {pygowave.operations.Operation[]} ops List of operations to apply """ for op in ops: if op.blipId != "": blip = self.blipById(op.blipId) if op.type == pygowave.operations.DOCUMENT_DELETE: blip.deleteText(op.index, op.property) elif op.type == pygowave.operations.DOCUMENT_INSERT: blip.insertText(op.index, op.property) elif op.type == pygowave.operations.DOCUMENT_ELEMENT_DELETE: blip.deleteElement(op.index) elif op.type == pygowave.operations.DOCUMENT_ELEMENT_INSERT: blip.insertElement(op.index, op.property["type"], op.property["properties"]) elif op.type == pygowave.operations.DOCUMENT_ELEMENT_DELTA: blip.applyElementDelta(op.index, op.property) elif op.type == pygowave.operations.DOCUMENT_ELEMENT_SETPREF: blip.setElementUserpref(op.index, op.property["key"], op.property["value"]) else: if op.type == pygowave.operations.WAVELET_ADD_PARTICIPANT: self.addParticipant(participants[op.property]) elif op.type == pygowave.operations.WAVELET_REMOVE_PARTICIPANT: self.removeParticipant(op.property) def loadBlipsFromSnapshot(self, blips, rootBlipId, participants): """ Load the blips from a snapshot. @function {public} loadBlipsFromSnapshot @param {Object} blips The JSON-serialized snapshot to load @param {String} rootBlipId ID to identify the root Blip @param {Hash} participants A map of participant objects """ for blip_id, blip in blips.iteritems(): #TODO: handle Blip contributors blip_options = { "creator": participants[blip["creator"]], "is_root": blip_id == rootBlipId, "last_modified": blip["lastModifiedTime"], "version": blip["version"], "submitted": blip["submitted"] } blip_elements = [] for serialelement in blip["elements"]: if serialelement["type"] == ELEMENT_TYPE["GADGET"]: blip_elements.append(GadgetElement(None, serialelement["id"], serialelement["index"], serialelement["properties"])) else: blip_elements.append(Element(None, serialelement["id"], serialelement["index"], serialelement["type"], serialelement["properties"])) blipObj = self.appendBlip(blip_id, blip_options, blip["content"], blip_elements)
class WaveModel(object): """ Wave model class. Top level model which sends all events. @class {public} pygowave.model.WaveModel """ # --- Event documentation --- """ Fired if a Wavelet has been added. @event onWaveletAdded @param {String} waveletId ID of the Wavelet that has been added @param {Boolean} isRoot True if this is the (new) root Wavelet """ # --------------------------- def __init__(self, waveId, viewerId): """ Called on instantiation. @constructor {public} initialize @param {String} waveId ID of the Wave @param {String} viewerId ID of the viewer """ self._rootWavelet = None self._waveId = waveId self._viewerId = viewerId self._wavelets = Hash() def id(self): """ Returns the ID of this Wave. @function {public String} id """ return self._waveId def viewerId(self): """ Returns the ID of the viewer. @function {public String} viewerId """ return self._viewerId def loadFromSnapshot(self, obj, participants): """ Load the wave's contents from a JSON-serialized snapshot and a map of participant objects. @function {public} loadFromSnapshot @param {Object} obj The JSON-serialized snapshot to load @param {Hash} participants A map of participant objects """ rootWavelet = obj["wavelet"] wvl_options = { "creator": participants[rootWavelet["creator"]], "is_root": True, "created": rootWavelet["creationTime"], "last_modified": rootWavelet["lastModifiedTime"], "title": rootWavelet["title"], "version": rootWavelet["version"] } rootWaveletObj = self.createWavelet(rootWavelet["waveletId"], wvl_options) for part_id in rootWavelet["participants"]: rootWaveletObj.addParticipant(participants[part_id]) for blip_id, blip in obj["blips"].iteritems(): #TODO: handle Blip contributors blip_options = { "creator": participants[blip["creator"]], "is_root": blip["blipId"] == rootWavelet["rootBlipId"], "last_modified": blip["lastModifiedTime"], "version": blip["version"], "submitted": blip["submitted"] } blip_elements = [] for serialelement in blip["elements"]: if serialelement["type"] == ELEMENT_TYPE["GADGET"]: blip_elements.append(GadgetElement(None, serialelement["id"], serialelement["index"], serialelement["properties"])) else: blip_elements.append(Element(None, serialelement["id"], serialelement["index"], serialelement["type"], serialelement["properties"])) blipObj = rootWaveletObj.appendBlip(blip_id, blip_options, blip["content"], blip_elements) def createWavelet(self, id, options): """ Create a Wavelet and add it to this Wave. For options see the {@link pygowave.model.Wavelet.initialize Wavelet constructor}.<br/> Note: Fires {@link pygowave.model.WaveModel.onWaveletAdded onWaveletAdded} @function {public Wavelet} createWavelet @param {String} id Wavelet ID @param {Object} options Information about the Wavelet. """ w = Wavelet(self, id, options) self._wavelets.set(id, w) self.fireEvent('waveletAdded', [id, w.isRoot()]) return w def wavelet(self, waveletId): """ Return a Wavelet of this Wave by its ID. @function {public Wavelet} wavelet @param {String} waveletId ID of the Wavelet """ return self._wavelets.get(waveletId) def rootWavelet(self): """ Returns the root Wavelet of this Wave. @function {public Wavelet} rootWavelet """ return self._rootWavelet def _setRootWavelet(self, wavelet): """ Internal method to set the root Wavelet. Not intended to be called outside of this implementation. @function {private} _setRootWavelet @param {Wavelet} wavelet Wavelet to be set as root Wavelet """ self._rootWavelet = wavelet