class Race(Storm): __storm_table__ = 'race' id = Int(primary=True, default=AutoReload) name = Unicode() start_time = DateTime() end_time = DateTime() def start(self): now = datetime.datetime.now().replace(microsecond=0) self.start_time = now def get_categories(self): return Store.of(self).find(Category, race=self) @cached_property def start_hour(self): return self.start_time.time() @property def time_elapsed(self): if not self.start_time: return None delta = datetime.datetime.now() - self.start_time return datetime.timedelta(seconds=delta.seconds)
class User(BaseModel): __storm_table__ = "sfec_user" name = Unicode() # Basic login data email = Unicode() password = Unicode() birth_date = DateTime() register_date = DateTime() def __init__(self): self.register_date = datetime.now() # # Properties # @property def is_admin(self): store = get_default_store() return store.find(Admin, id=self.id).one() is not None # # Static API # @staticmethod def hash(password): """The hash function to be used by the table""" return unicode(sha512(password).hexdigest()) @classmethod def authenticate(cls, store, email, password): """Returns the user that matches the email password combination""" pw_hash = cls.hash(password) queries = [cls.email == email, cls.password == pw_hash] user = store.find(cls, And(*queries)).one() if user: user.last_login = datetime.now() user.last_ip = unicode(request.remote_addr) store.commit() return user return False # # Public API # def dict(self): super_dict = super(User, self).dict() super_dict['is_admin'] = self.is_admin return super_dict def set_password(self, password): self.password = self.hash(password)
class BugSubscriptionFilterMute(StormBase): """Bug subscription filters a person has decided to block emails from.""" implements(IBugSubscriptionFilterMute) __storm_table__ = "BugSubscriptionFilterMute" def __init__(self, person=None, filter=None): if person is not None: self.person = person if filter is not None: self.filter = filter.id person_id = Int("person", allow_none=False, validator=validate_person) person = Reference(person_id, "Person.id") filter_id = Int("filter", allow_none=False) filter = Reference(filter_id, "BugSubscriptionFilter.id") __storm_primary__ = 'person_id', 'filter_id' date_created = DateTime("date_created", allow_none=False, default=UTC_NOW, tzinfo=pytz.UTC)
class PhotoImage(BaseModel): '''Image file references and metadata''' __storm_table__ = 'photoimage' id = Int(primary=True) filename = Unicode() thumbnail = Unicode() title = Unicode() description = Unicode() creation_date = DateTime() album_id = Int() album = Reference(album_id, 'PhotoAlbum.id') def to_dict(self, recurse=True): '''See BaseModel.to_dict''' ret = { 'id': self.id, 'filename': self.filename, 'thumbnail': self.thumbnail, 'title': self.title, 'description': self.description, 'creation_date': self.creation_date, } if recurse: ret['album_id'] = self.album_id ret['album'] = self.album.to_dict(recurse=False) return ret
class XRef(StormBase): """Cross-reference between two objects. For references to local objects (there is currently no other kind), another reference in the opposite direction exists. The to_id_int and from_id_int columns exist for efficient SQL joins. They are set automatically when the ID looks like an integer. NOTE: This should rarely be used directly. Prefer IXRefSet unless porting an old query. """ __storm_table__ = 'XRef' __storm_primary__ = "from_type", "from_id", "to_type", "to_id" to_type = Unicode(allow_none=False) to_id = Unicode(allow_none=False) to_id_int = Int() # For efficient joins. from_type = Unicode(allow_none=False) from_id = Unicode(allow_none=False) from_id_int = Int() # For efficient joins. creator_id = Int(name="creator") creator = Reference(creator_id, "Person.id") date_created = DateTime(name='date_created', tzinfo=pytz.UTC) metadata = JSON()
class AccessArtifactGrant(StormBase): implements(IAccessArtifactGrant) __storm_table__ = 'AccessArtifactGrant' __storm_primary__ = 'abstract_artifact_id', 'grantee_id' abstract_artifact_id = Int(name='artifact') abstract_artifact = Reference(abstract_artifact_id, 'AccessArtifact.id') grantee_id = Int(name='grantee') grantee = Reference(grantee_id, 'Person.id') grantor_id = Int(name='grantor') grantor = Reference(grantor_id, 'Person.id') date_created = DateTime(tzinfo=pytz.UTC) @property def concrete_artifact(self): if self.abstract_artifact is not None: return self.abstract_artifact.concrete_artifact @classmethod def grant(cls, grants): """See `IAccessArtifactGrantSource`.""" return create((cls.abstract_artifact, cls.grantee, cls.grantor), grants, get_objects=True) @classmethod def find(cls, grants): """See `IAccessArtifactGrantSource`.""" return IStore(cls).find( cls, Or(*(And(cls.abstract_artifact == artifact, cls.grantee == grantee) for (artifact, grantee) in grants))) @classmethod def findByArtifact(cls, artifacts, grantees=None): """See `IAccessArtifactGrantSource`.""" artifact_ids = [artifact.id for artifact in artifacts] constraints = [cls.abstract_artifact_id.is_in(artifact_ids)] if grantees: grantee_ids = [grantee.id for grantee in grantees] constraints.append(cls.grantee_id.is_in(grantee_ids)) return IStore(cls).find(cls, *constraints) @classmethod def revokeByArtifact(cls, artifacts, grantees=None): """See `IAccessArtifactGrantSource`.""" cls.findByArtifact(artifacts, grantees).remove()
class RacerLap(Storm): __storm_table__ = 'racer_lap' id = Int(primary=True, default=AutoReload) event_time = DateTime() race_id = Int() race = Reference(race_id, 'Race.id') racer_id = Int() racer = Reference(racer_id, 'Racer.id') @property def previous_lap(self): laps = self.racer.get_laps(before=self.event_time) return laps.last() @cached_property def number(self): return self.racer.number @cached_property def name(self): return self.racer.name @cached_property def category(self): return self.racer.category.short_name @cached_property def remaining_laps(self): return self.racer.category.total_laps - self.lap_number @cached_property def lap_time(self): previous = self.previous_lap if previous: delta = self.event_time - previous.event_time else: # This is the first lap. Calculate it using the race start. delta = self.event_time - self.race.start_time # Create a new timedelta without the microseconds return datetime.timedelta(seconds=delta.seconds) @cached_property def lap_number(self): laps = self.racer.get_laps(before=self.event_time) return laps.count() + 1
class AccessPolicyGrant(StormBase): implements(IAccessPolicyGrant) __storm_table__ = 'AccessPolicyGrant' __storm_primary__ = 'policy_id', 'grantee_id' policy_id = Int(name='policy') policy = Reference(policy_id, 'AccessPolicy.id') grantee_id = Int(name='grantee') grantee = Reference(grantee_id, 'Person.id') grantor_id = Int(name='grantor') grantor = Reference(grantor_id, 'Person.id') date_created = DateTime(tzinfo=pytz.UTC) @classmethod def grant(cls, grants): """See `IAccessPolicyGrantSource`.""" return create((cls.policy, cls.grantee, cls.grantor), grants, get_objects=True) @classmethod def find(cls, grants): """See `IAccessPolicyGrantSource`.""" return IStore(cls).find( cls, Or(*(And(cls.policy == policy, cls.grantee == grantee) for (policy, grantee) in grants))) @classmethod def findByPolicy(cls, policies): """See `IAccessPolicyGrantSource`.""" ids = [policy.id for policy in policies] return IStore(cls).find(cls, cls.policy_id.is_in(ids)) @classmethod def revoke(cls, grants): """See `IAccessPolicyGrantSource`.""" cls.find(grants).remove() @classmethod def revokeByPolicy(cls, policies): """See `IAccessPolicyGrantSource`.""" cls.findByPolicy(policies).remove()
class MilestoneTag(object): """A tag belonging to a milestone.""" __storm_table__ = 'milestonetag' id = Int(primary=True) milestone_id = Int(name='milestone', allow_none=False) milestone = Reference(milestone_id, 'milestone.id') tag = Unicode(allow_none=False) created_by_id = Int(name='created_by', allow_none=False) created_by = Reference(created_by_id, 'person.id') date_created = DateTime(allow_none=False) def __init__(self, milestone, tag, created_by, date_created=None): self.milestone_id = milestone.id self.tag = tag self.created_by_id = created_by.id if date_created is not None: self.date_created = date_created
class LatestPersonSourcePackageReleaseCache(Storm): """See `LatestPersonSourcePackageReleaseCache`.""" __storm_table__ = 'LatestPersonSourcePackageReleaseCache' cache_id = Int(name='id', primary=True) publication_id = Int(name='publication') publication = Reference(publication_id, 'SourcePackagePublishingHistory.id') dateuploaded = DateTime(name='date_uploaded') creator_id = Int(name='creator') maintainer_id = Int(name='maintainer') upload_archive_id = Int(name='upload_archive') upload_archive = Reference(upload_archive_id, 'Archive.id') archive_purpose = EnumCol(schema=ArchivePurpose) upload_distroseries_id = Int(name='upload_distroseries') upload_distroseries = Reference(upload_distroseries_id, 'DistroSeries.id') sourcepackagename_id = Int(name='sourcepackagename') sourcepackagename = Reference(sourcepackagename_id, 'SourcePackageName.id') sourcepackagerelease_id = Int(name='sourcepackagerelease') sourcepackagerelease = Reference(sourcepackagerelease_id, 'SourcePackageRelease.id')
class PhotoAlbum(BaseModel): '''A photo group''' __storm_table__ = 'photoalbum' id = Int(primary=True) title = Unicode() description = Unicode() creation_date = DateTime() images = ReferenceSet('PhotoAlbum.id', 'PhotoImage.album_id') def to_dict(self, recurse=True): '''See BaseModel.to_dict.''' ret = { 'id': self.id, 'title': self.title, 'description': self.description, 'creation_date': self.creation_date, } if recurse: ret['images'] = [ image.to_dict(recurse=False) for image in self.images ] return ret
class UserData(object): """ User database table abstraction. """ __storm_table__ = 'goliat_user' id = Int(primary=True) username = Unicode(allow_none=False) password = Unicode(allow_none=False) groups = Unicode() created_on = DateTime() last_login = DateTime() is_active = Bool(value_factory=True) superuser = Bool(value_factory=False) def __init__(self, username='', password=''): """ Initialized the object. """ super(UserData, self).__init__() self.username = unicode(username) self.password = unicode(password) def set_username(self, username): """ Set the username. """ self.username = unicode(username) def set_password(self, password): """ Set the password. """ self.password = unicode(password) def set_groups(self, groups=[]): """ Set the groups """ self.groups = unicode(','.join(groups)) def set_creation_date(self): """ Set the creation datetime. """ self.created_on = datetime.datetime.now() def set_last_login(self): """ Set the last login datetime. """ self.last_login = datetime.datetime.now() def activate(self): """ Activate the user. """ self.is_active = True def deactivate(self): """ Deactivate the user. """ self.is_active = False def set_superuser(self, value): """ Set the superuser flag. """ self.superuser = value
class BuildQueue(SQLBase): _table = "BuildQueue" _defaultOrder = "id" def __init__(self, build_farm_job, estimated_duration=DEFAULT, virtualized=DEFAULT, processor=DEFAULT, lastscore=None): super(BuildQueue, self).__init__(_build_farm_job=build_farm_job, virtualized=virtualized, processor=processor, estimated_duration=estimated_duration, lastscore=lastscore) if lastscore is None and self.specific_build is not None: self.score() _build_farm_job_id = Int(name='build_farm_job') _build_farm_job = Reference(_build_farm_job_id, 'BuildFarmJob.id') status = EnumCol(enum=BuildQueueStatus, default=BuildQueueStatus.WAITING) date_started = DateTime(tzinfo=pytz.UTC) builder = ForeignKey(dbName='builder', foreignKey='Builder', default=None) logtail = StringCol(dbName='logtail', default=None) lastscore = IntCol(dbName='lastscore', default=0) manual = BoolCol(dbName='manual', default=False) estimated_duration = IntervalCol() processor = ForeignKey(dbName='processor', foreignKey='Processor') virtualized = BoolCol(dbName='virtualized') @cachedproperty def specific_build(self): """See `IBuildQueue`.""" bfj = self._build_farm_job specific_source = specific_build_farm_job_sources()[bfj.job_type] return specific_source.getByBuildFarmJob(bfj) @property def build_cookie(self): """See `IBuildQueue`.""" return self.specific_build.build_cookie def _clear_specific_build_cache(self): del get_property_cache(self).specific_build @staticmethod def preloadSpecificBuild(queues): from lp.buildmaster.model.buildfarmjob import BuildFarmJob queues = [removeSecurityProxy(bq) for bq in queues] load_related(BuildFarmJob, queues, ['_build_farm_job_id']) bfj_to_bq = dict((bq._build_farm_job, bq) for bq in queues) key = attrgetter('_build_farm_job.job_type') for job_type, group in groupby(sorted(queues, key=key), key=key): source = getUtility(ISpecificBuildFarmJobSource, job_type.name) builds = source.getByBuildFarmJobs( [bq._build_farm_job for bq in group]) for build in builds: bq = bfj_to_bq[removeSecurityProxy(build).build_farm_job] get_property_cache(bq).specific_build = build @property def current_build_duration(self): """See `IBuildQueue`.""" date_started = self.date_started if date_started is None: return None else: return self._now() - date_started def destroySelf(self): """Remove this record.""" builder = self.builder specific_build = self.specific_build Store.of(self).remove(self) Store.of(self).flush() if builder is not None: del get_property_cache(builder).currentjob del get_property_cache(specific_build).buildqueue_record self._clear_specific_build_cache() def manualScore(self, value): """See `IBuildQueue`.""" self.lastscore = value self.manual = True def score(self): """See `IBuildQueue`.""" if self.manual: return # Allow the `IBuildFarmJob` instance with the data/logic specific to # the job at hand to calculate the score as appropriate. self.lastscore = self.specific_build.calculateScore() def markAsBuilding(self, builder): """See `IBuildQueue`.""" self.builder = builder self.status = BuildQueueStatus.RUNNING self.date_started = UTC_NOW self.specific_build.updateStatus(BuildStatus.BUILDING) if builder is not None: del get_property_cache(builder).currentjob def collectStatus(self, slave_status): """See `IBuildQueue`.""" builder_status = slave_status["builder_status"] if builder_status == "BuilderStatus.ABORTING": self.logtail = "Waiting for slave process to be terminated" elif slave_status.get("logtail") is not None: # slave_status["logtail"] is normally an xmlrpclib.Binary # instance, and the contents might include invalid UTF-8 due to # being a fixed number of bytes from the tail of the log. Turn # it into Unicode as best we can. self.logtail = str(slave_status.get("logtail")).decode( "UTF-8", errors="replace") def suspend(self): """See `IBuildQueue`.""" if self.status != BuildQueueStatus.WAITING: raise AssertionError("Only waiting jobs can be suspended.") self.status = BuildQueueStatus.SUSPENDED def resume(self): """See `IBuildQueue`.""" if self.status != BuildQueueStatus.SUSPENDED: raise AssertionError("Only suspended jobs can be resumed.") self.status = BuildQueueStatus.WAITING def reset(self): """See `IBuildQueue`.""" builder = self.builder self.builder = None self.status = BuildQueueStatus.WAITING self.date_started = None self.logtail = None self.specific_build.updateStatus(BuildStatus.NEEDSBUILD) if builder is not None: del get_property_cache(builder).currentjob def cancel(self): """See `IBuildQueue`.""" if self.status == BuildQueueStatus.WAITING: # If the job's not yet on a slave then we can just # short-circuit to completed cancellation. self.markAsCancelled() elif self.status == BuildQueueStatus.RUNNING: # Otherwise set the statuses to CANCELLING so buildd-manager # can kill the slave, grab the log, and call # markAsCancelled() when it's done. self.status = BuildQueueStatus.CANCELLING self.specific_build.updateStatus(BuildStatus.CANCELLING) else: raise AssertionError("Tried to cancel %r from %s" % (self, self.status.name)) def markAsCancelled(self): """See `IBuildQueue`.""" self.specific_build.updateStatus(BuildStatus.CANCELLED) self.destroySelf() def getEstimatedJobStartTime(self, now=None): """See `IBuildQueue`.""" from lp.buildmaster.queuedepth import estimate_job_start_time return estimate_job_start_time(self, now or self._now()) @staticmethod def _now(): """Return current time (UTC). Overridable for test purposes.""" return datetime.now(pytz.utc)