class DeferralList(QonPersistent): """Provides simple timed deferral mechanism. Maintains a list of keyed times. Client calls defer() to add a key and a delay. Subsequent calls to defer() with the same key will return True if delay has elapsed, or false if not. """ persistenceVersion = 1 def __init__(self): self.defers = ConflictAvoidingOOBTree() def upgradeToVersion1(self): # upgrade from PersistentMapping bt = ConflictAvoidingOOBTree() for k,v in self.defers.iteritems(): bt[k] = v del self.defers self.defers = bt self.version_upgrade_done() def defer(self, key, delay): """Schedule a deferred event labeled by key. If key already exists, returns True if enough time has elapsed since key was originally added. Otherwise, add key and return False. """ now = datetime.utcnow() if self.defers.has_key(key): if self.defers[key] <= now: del self.defers[key] return True else: return False else: self.defers[key] = now + delay return False def cancel(self, key): """Unschedule a deferred task.""" if self.defers.has_key(key): del self.defers[key]
class WatchList(QonPersistent): """Keep track of a collection of Watchable items by their oids. Stores last-seen time for each object watched. """ persistenceVersion = 3 _max_footprints = 10 def __init__(self): self.__items = ConflictAvoidingOOBTree() self.__footprints = ConflictAvoidingPersistentList() def upgradeToVersion3(self): """Make footprints persistent. No need to del _v_footprints since it's volatile anyway.""" self.__footprints = ConflictAvoidingPersistentList() self.version_upgrade_done() def upgradeToVersion2(self): newbt = ConflictAvoidingOOBTree(self.__items) del self.__items self.__items = newbt self.version_upgrade_done() def upgradeToVersion1(self): if hasattr(self, '_WatchList__footprints'): del self.__footprints def watch_item(self, item): """Add item to watch list. Records current time as last-seen.""" typecheck(item, Watchable) typecheck(item, Persistent) if not item.watchable(): return if not self.__items.has_key(item._p_oid): self.__items[item._p_oid] = datetime.utcnow() def stop_watching_item(self, item): """Remove item from watch list.""" typecheck(item, Watchable) typecheck(item, Persistent) if not item.watchable(): return if self.__items.has_key(item._p_oid): del self.__items[item._p_oid] def watchable_seen(self, obj): """Called when a watchable item is seen, even if it's not in my watchable list.""" typecheck(obj, Watchable) typecheck(obj, Persistent) if not obj.watchable(): return if self.__items.has_key(obj._p_oid): self.__items[obj._p_oid] = datetime.utcnow() self.add_footprint(obj) def changed_items(self, since): """Return list of items changed since datetime since.""" watched = self.watched_items() changed = [item for item in watched if item.changed_since(since)] return changed def changed_unseen_items(self): """Return list of items changed and unseen since datetime since.""" changed = self.watched_items() return [item for item in changed \ if self.last_seen(item) < item.watchable_last_change()] def watched_items(self): """Return list of all items being watched.""" return self._get_items(self.__items.keys()) def watched_items_oids(self): """Return list of all item OIDs being watched.""" return self.__items.keys() def _get_items(self, oid_list): """Return a list of valid watchable items from oid_list""" items = [] for k in oid_list: try: obj = get_oid(k) obj_name = obj.watchable_name() except: self._lost_oid(k) else: items.append(obj) return items def _lost_oid(self, oid): """We can't find this oid, remove from list.""" if self.__items.has_key(oid): del self.__items[oid] while oid in self.__footprints: self.__footprints.remove(oid) def footprints(self): """Return list of footprinted items, most recent first.""" items = self._get_items(self.__footprints) items.reverse() return items def add_footprint(self, obj): """Add obj to fooprint list.""" self.__footprints.append(obj._p_oid) if len(self.__footprints) > self._max_footprints: self.__footprints = ConflictAvoidingPersistentList(self.__footprints[-self._max_footprints:]) remove_left_duplicates(self.__footprints) def last_seen(self, item): """Return datetime this item was last seen.""" typecheck(item, Watchable) typecheck(item, Persistent) return self.__items.get(item._p_oid, never) def is_watching(self, item): """Returns true if item is in watch list.""" typecheck(item, Watchable) typecheck(item, Persistent) return item._p_oid in self.__items