Esempio n. 1
0
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
Esempio n. 2
0
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)
Esempio n. 3
0
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