class DBShortMessage(item.Item): """ I represent a contact on the DB """ # (id integer, category integer, number text, date text, smstext text) typeName = 'DBShortMessage' schemaVersion = 1 date = attributes.timestamp(allowNone=False) number = attributes.text(allowNone=False) text = attributes.text(allowNone=False) where = attributes.integer(allowNone=False) def __repr__(self): args = (self.number, self.text, self.date) return '<DBShortMessage number="%s" text="%s" date="%s">' % args def __eq__(self, m): if isinstance(m, DBShortMessage): return (self.number == m.number and self.text == m.text and self.date == m.date) return False def __ne__(self, m): return not (self.number == m.number and self.text == m.text and self.date == m.date) def get_index(self): return self.storeID def get_number(self): return self.number def get_text(self): return self.text
class SubScheduler(Item, SchedulerMixin): """ Track and execute persistent timed events for a substore. """ schemaVersion = 1 typeName = 'axiom_subscheduler' implements(IScheduler) powerupInterfaces = (IScheduler, ) eventsRun = integer(default=0) lastEventAt = timestamp() nextEventAt = timestamp() # Also testing hooks now = inmemory() def __repr__(self): return '<SubScheduler for %r>' % (self.store, ) def activate(self): self.now = Time def _transientSchedule(self, when, now): if self.store.parent is not None: loginAccount = self.store.parent.getItemByID(self.store.idInParent) hook = self.store.parent.findOrCreate( _SubSchedulerParentHook, lambda hook: installOn(hook, hook.store), loginAccount=loginAccount) hook._schedule(when) def migrateDown(self): """ Remove the components in the site store for this SubScheduler. """ loginAccount = self.store.parent.getItemByID(self.store.idInParent) ssph = self.store.parent.findUnique( _SubSchedulerParentHook, _SubSchedulerParentHook.loginAccount == loginAccount, default=None) if ssph is not None: te = self.store.parent.findUnique(TimedEvent, TimedEvent.runnable == ssph, default=None) if te is not None: te.deleteFromStore() ssph.deleteFromStore() def migrateUp(self): """ Recreate the hooks in the site store to trigger this SubScheduler. """ te = self.store.findFirst(TimedEvent, sort=TimedEvent.time.descending) if te is not None: self._transientSchedule(te.time, Time)
class _PowerupConnector(Item): """ I am a connector between the store and a powerup. """ typeName = 'axiom_powerup_connector' powerup = reference() item = reference() interface = text() priority = integer()
class Catalog(Item): typeName = 'tag_catalog' schemaVersion = 2 tagCount = integer(default=0) def tag(self, obj, tagName, tagger=None): """ """ # check to see if that tag exists. Put the object attribute first, # since each object should only have a handful of tags and the object # reference is indexed. As long as this is the case, it doesn't matter # whether the name or catalog attributes are indexed because selecting # from a small set of results is fast even without an index. if self.store.findFirst( Tag, AND(Tag.object == obj, Tag.name == tagName, Tag.catalog == self)): return # if the tag doesn't exist, maybe we need to create a new tagname object self.store.findOrCreate(_TagName, name=tagName, catalog=self) # Increment only if we are creating a new tag self.tagCount += 1 Tag(store=self.store, object=obj, name=tagName, catalog=self, created=Time(), tagger=tagger) def tagNames(self): """ Return an iterator of unicode strings - the unique tag names which have been applied objects in this catalog. """ return self.store.query(_TagName, _TagName.catalog == self).getColumn("name") def tagsOf(self, obj): """ Return an iterator of unicode strings - the tag names which apply to the given object. """ return self.store.query(Tag, AND(Tag.catalog == self, Tag.object == obj)).getColumn("name") def objectsIn(self, tagName): return self.store.query(Tag, AND(Tag.catalog == self, Tag.name == tagName)).getColumn("object")
class UsageItem(item.Item): umts = attributes.boolean(allowNone=False) start_time = attributes.timestamp(allowNone=False) end_time = attributes.timestamp(allowNone=False) bits_recv = attributes.integer(allowNone=False) bits_sent = attributes.integer(allowNone=False) def __repr__(self): _type = (self.umts == True) and 'UMTS' or 'GPRS' args = (_type, self.end_time - self.start_time, self.bits_recv + self.bits_sent) return "<%s UsageItem time: %s total bits: %d>" % args def __eq__(self, other): if not isinstance(other, UsageItem): raise ValueError("Cannot reliably compare myself with %s" % other) return self.storeID == other.storeID def __ne__(self, other): return not self.__eq__(other)
class DBShortMessage(item.Item): """ I represent a contact on the DB """ # (id integer, category integer, number text, date text, smstext text) typeName = 'DBShortMessage' schemaVersion = 1 date = attributes.timestamp(allowNone=False) number = attributes.text(allowNone=False) text = attributes.text(allowNone=False) where = attributes.integer(allowNone=False) def __repr__(self): args = (self.number, self.text, self.date) return '<DBShortMessage number="%s" text="%s" date="%s">' % args def __eq__(self, m): if isinstance(m, DBShortMessage): return (self.number == m.number and self.text == m.text and self.date == m.date) return False def __ne__(self, m): return not (self.number == m.number and self.text == m.text and self.date == m.date) def get_index(self): return self.storeID def get_number(self): return self.number def get_text(self): return self.text def to_csv(self): """Returns a list with the date, number and text formatted for csv""" tzinfo = osobj.get_tzinfo() date = '"' + time.strftime( "%c", self.date.asDatetime(tzinfo=tzinfo).timetuple()) + '"' number = '"' + self.number + '"' text = '"' + self.text + '"' return [date, number, text]
class Scheduler(Item, Service, SchedulerMixin): """ Track and execute persistent timed events for a I{site} store. """ typeName = 'axiom_scheduler' schemaVersion = 1 implements(IService, IScheduler) powerupInterfaces = (IService, IScheduler) parent = inmemory() name = inmemory() timer = inmemory() # Also testing hooks callLater = inmemory() now = inmemory() eventsRun = integer() lastEventAt = timestamp() nextEventAt = timestamp() class running(descriptor.attribute): def get(self): return (self.parent is self.store._axiom_service and self.store._axiom_service is not None and self.store._axiom_service.running) def set(self, value): # Eh whatever pass def __init__(self, **kw): super(Scheduler, self).__init__(**kw) self.eventsRun = 0 self.lastEventAt = None self.nextEventAt = None def __repr__(self): return '<Scheduler>' def installed(self): self.setServiceParent(IService(self.store)) def activate(self): self.timer = None self.callLater = reactor.callLater self.now = Time def startService(self): """ Start calling persistent timed events whose time has come. """ super(Scheduler, self).startService() self._transientSchedule(self.now(), self.now()) def stopService(self): """ Stop calling persistent timed events. """ super(Scheduler, self).stopService() if self.timer is not None: self.timer.cancel() self.timer = None def tick(self): self.timer = None return super(Scheduler, self).tick() def _transientSchedule(self, when, now): if not self.running: return if self.timer is not None: if self.timer.getTime() < when.asPOSIXTimestamp(): return self.timer.cancel() delay = when.asPOSIXTimestamp() - now.asPOSIXTimestamp() # reactor.callLater allows only positive delay values. The scheduler # may want to have scheduled things in the past and that's OK, since we # are dealing with Time() instances it's impossible to predict what # they are relative to the current time from user code anyway. delay = max(_EPSILON, delay) self.timer = self.callLater(delay, self.tick) self.nextEventAt = when
class LoginSystem(Item, LoginBase, SubStoreLoginMixin): schemaVersion = 1 typeName = 'login_system' loginCount = integer(default=0) failedLogins = integer(default=0)
class LoginAccount(Item): """ I am an entry in a LoginBase. @ivar avatars: An Item which is adaptable to various cred client interfaces. Plural because it represents a collection of potentially disparate implementors, such as an IResource for web access and an IContact for SIP access. @ivar disabled: This account has been disabled. It is still database-resident but the user should not be allowed to log in. """ typeName = 'login' schemaVersion = 2 password = text() avatars = reference() # reference to a thing which can be adapted to # implementations for application-level # protocols. In general this is a reference to # a SubStore because this is optimized for # applications where per-user data is a # substantial portion of the cost. disabled = integer() def __conform__(self, interface): """ For convenience, forward adaptation to my 'avatars' attribute. """ ifa = interface(self.avatars, None) return ifa def migrateDown(self): """ Assuming that self.avatars is a SubStore which should contain *only* the LoginAccount for the user I represent, remove all LoginAccounts and LoginMethods from that store and copy all methods from the site store down into it. """ ss = self.avatars.open() def _(): oldAccounts = ss.query(LoginAccount) oldMethods = ss.query(LoginMethod) for x in list(oldAccounts) + list(oldMethods): x.deleteFromStore() self.cloneInto(ss, ss) sched = IScheduler(ss, None) if sched is not None: sched.migrateDown() ss.transact(_) def migrateUp(self): """ Copy this LoginAccount and all associated LoginMethods from my store (which is assumed to be a SubStore, most likely a user store) into the site store which contains it. """ siteStore = self.store.parent def _(): # No convenience method for the following because needing to do it is # *rare*. It *should* be ugly; 99% of the time if you need to do this # you're making a mistake. -glyph siteStoreSubRef = siteStore.getItemByID(self.store.idInParent) self.cloneInto(siteStore, siteStoreSubRef) sched = IScheduler(self.store, None) if sched is not None: sched.migrateUp() siteStore.transact(_) def cloneInto(self, newStore, avatars): """ Create a copy of this LoginAccount and all associated LoginMethods in a different Store. Return the copied LoginAccount. """ la = LoginAccount(store=newStore, password=self.password, avatars=avatars, disabled=self.disabled) for siteMethod in self.store.query(LoginMethod, LoginMethod.account == self): lm = LoginMethod(store=newStore, localpart=siteMethod.localpart, domain=siteMethod.domain, internal=siteMethod.internal, protocol=siteMethod.protocol, verified=siteMethod.verified, account=la) return la def deleteLoginMethods(self): self.store.query(LoginMethod, LoginMethod.account == self).deleteFromStore() def addLoginMethod(self, localpart, domain, protocol=ANY_PROTOCOL, verified=False, internal=False): """ Add a login method to this account, propogating up or down as necessary to site store or user store to maintain consistency. """ # Out takes you west or something if self.store.parent is None: # West takes you in otherStore = self.avatars.open() peer = otherStore.findUnique(LoginAccount) else: # In takes you east otherStore = self.store.parent subStoreItem = self.store.parent.getItemByID(self.store.idInParent) peer = otherStore.findUnique(LoginAccount, LoginAccount.avatars == subStoreItem) # Up and down take you home for store, account in [(otherStore, peer), (self.store, self)]: store.findOrCreate(LoginMethod, account=account, localpart=localpart, domain=domain, protocol=protocol, verified=verified, internal=internal)