class AddressJoiner3(SQLObject): zip = StringCol(length=5) personJoiner3 = ForeignKey('PersonJoiner3') def _get_personJoiner3(self): value = self._SO_get_personJoiner3() _personJoiner3_getters.append((self, value)) return value def _set_personJoiner3(self, value): self._SO_set_personJoiner3(value) _personJoiner3_setters.append((self, value))
class _CodeImportEventData(SQLBase): """Additional data associated to a CodeImportEvent. This class is for internal use only. This data should be created by CodeImportEventSet event creation methods, and should be accessed by CodeImport methods. """ _table = 'CodeImportEventData' event = ForeignKey(dbName='event', foreignKey='CodeImportEvent') data_type = EnumCol(enum=CodeImportEventDataType, notNull=True) data_value = StringCol()
class LogStat(ICTVObject): logger_name = StringCol(notNone=True, alternateID=True, length=50) last_debug = DateTimeCol(default=None) last_info = DateTimeCol(default=None) last_warning = DateTimeCol(default=None) last_error = DateTimeCol(default=None) n_entries = IntCol(notNone=True, default=0) @property def last_activity(self): infos = [ i for i in [ self.last_debug, self.last_info, self.last_warning, self.last_error ] if i is not None ] return max(infos) if infos else None @classmethod def dump_log_stats(cls, log_stats): for name, stats in log_stats.items(): try: log_stat = LogStat.byLogger_name(name) except SQLObjectNotFound: log_stat = LogStat(logger_name=name) for attr_name, stat_name in [("last_debug", "DEBUG"), ("last_info", "INFO"), ("last_warning", "WARNING"), ("last_error", "ERROR"), ("n_entries", "n_entries")]: try: setattr(log_stat, attr_name, stats[stat_name]) except KeyError: setattr(log_stat, attr_name, None if attr_name != "n_entries" else 0) @classmethod def load_log_stats(cls): result = {} for log_stat in LogStat.select(): result[log_stat.logger_name] = {} for attr_name, stat_name in [("last_debug", "DEBUG"), ("last_info", "INFO"), ("last_warning", "WARNING"), ("last_error", "ERROR"), ("last_activity", "last_activity"), ("n_entries", "n_entries")]: if getattr(log_stat, attr_name) is not None: result[log_stat.logger_name][stat_name] = getattr( log_stat, attr_name) return result
class BugAttachment(SQLBase): """A bug attachment.""" _table = 'BugAttachment' bug = ForeignKey( foreignKey='Bug', dbName='bug', notNull=True) type = EnumCol( schema=BugAttachmentType, notNull=True, default=IBugAttachment['type'].default) title = StringCol(notNull=True) libraryfile = ForeignKey( foreignKey='LibraryFileAlias', dbName='libraryfile', notNull=True) data = ForeignKey( foreignKey='LibraryFileAlias', dbName='libraryfile', notNull=True) _message = ForeignKey( foreignKey='Message', dbName='message', notNull=True) @cachedproperty def message(self): """This is a cachedproperty to allow message to be an IIndexedMessage. This is needed for the bug/attachments API call which needs to index an IIndexedMessage rather than a simple DB model IMessage. See Bug.attachments where the injection occurs. """ return self._message @property def is_patch(self): """See IBugAttachment.""" return self.type == BugAttachmentType.PATCH def removeFromBug(self, user): """See IBugAttachment.""" notify(ObjectDeletedEvent(self, user)) self.destroySelf() def destroySelf(self): """See IBugAttachment.""" # Delete the reference to the LibraryFileContent record right now, # in order to avoid problems with not deleted files as described # in bug 387188. self.libraryfile.content = None super(BugAttachment, self).destroySelf() def getFileByName(self, filename): """See IBugAttachment.""" if filename == self.libraryfile.filename: return self.libraryfile raise NotFoundError(filename)
class PersonNotification(SQLBase): """See `IPersonNotification`.""" person = ForeignKey(dbName='person', notNull=True, foreignKey='Person') date_created = UtcDateTimeCol(notNull=True, default=UTC_NOW) date_emailed = UtcDateTimeCol(notNull=False) body = StringCol(notNull=True) subject = StringCol(notNull=True) @cachedproperty def to_addresses(self): """See `IPersonNotification`.""" if self.person.is_team: return self.person.getTeamAdminsEmailAddresses() elif self.person.preferredemail is None: return [] else: return [ format_address(self.person.displayname, self.person.preferredemail.email) ] @property def can_send(self): """See `IPersonNotification`.""" return len(self.to_addresses) > 0 def send(self, logger=None): """See `IPersonNotification`.""" if not self.can_send: raise AssertionError( "Can't send a notification to a person without an email.") to_addresses = self.to_addresses if logger: logger.info("Sending notification to %r." % to_addresses) from_addr = config.canonical.bounce_address simple_sendmail(from_addr, to_addresses, self.subject, self.body) self.date_emailed = datetime.now(pytz.timezone('UTC'))
class RevisionAuthor(SQLBase): _table = 'RevisionAuthor' name = StringCol(notNull=True, alternateID=True) @property def name_without_email(self): """Return the name of the revision author without the email address. If there is no name information (i.e. when the revision author only supplied their email address), return None. """ if '@' not in self.name: return self.name return email.utils.parseaddr(self.name)[0] email = StringCol(notNull=False, default=None) person = ForeignKey(dbName='person', foreignKey='Person', notNull=False, storm_validator=validate_public_person, default=None) def linkToLaunchpadPerson(self): """See `IRevisionAuthor`.""" if self.person is not None or self.email is None: return False lp_email = getUtility(IEmailAddressSet).getByEmail(self.email) # If not found, we didn't link this person. if lp_email is None: return False # Only accept an email address that is validated. if lp_email.status != EmailAddressStatus.NEW: self.personID = lp_email.personID return True else: return False
class BinaryPackageName(SQLBase): implements(IBinaryPackageName) _table = 'BinaryPackageName' name = StringCol(dbName='name', notNull=True, unique=True, alternateID=True) def __unicode__(self): return self.name def __repr__(self): return "<BinaryPackageName at %X name=%r>" % (id(self), self.name)
class Processor(SQLBase): _table = 'Processor' name = StringCol(dbName='name', notNull=True) title = StringCol(dbName='title', notNull=True) description = StringCol(dbName='description', notNull=True) restricted = Bool(allow_none=False, default=False) # When setting this to true you may want to add missing # ArchiveArches. build_by_default = Bool(allow_none=False, default=False) # This controls build creation, so you may want to create or cancel # some builds after changing it on an existing processor. supports_virtualized = Bool(allow_none=False, default=False) # Queued and failed builds' BuildQueue.virtualized and # BinaryPackageBuild.virtualized may need tweaking if this is # changed on an existing processor. supports_nonvirtualized = Bool(allow_none=False, default=True) def __repr__(self): return "<Processor %r>" % self.title
class MirrorCDImageDistroSeries(SQLBase): """See IMirrorCDImageDistroSeries""" implements(IMirrorCDImageDistroSeries) _table = 'MirrorCDImageDistroSeries' _defaultOrder = 'id' distribution_mirror = ForeignKey(dbName='distribution_mirror', foreignKey='DistributionMirror', notNull=True) distroseries = ForeignKey(dbName='distroseries', foreignKey='DistroSeries', notNull=True) flavour = StringCol(notNull=True)
class Country(SQLBase): """A country.""" implements(ICountry) _table = 'Country' # default to listing newest first _defaultOrder = 'name' # db field names name = StringCol(dbName='name', unique=True, notNull=True) iso3166code2 = StringCol(dbName='iso3166code2', unique=True, notNull=True) iso3166code3 = StringCol(dbName='iso3166code3', unique=True, notNull=True) title = StringCol(dbName='title', notNull=False, default=DEFAULT) description = StringCol(dbName='description') continent = ForeignKey(dbName='continent', foreignKey='Continent', default=None) languages = SQLRelatedJoin('Language', joinColumn='country', otherColumn='language', intermediateTable='SpokenIn')
class Key(SQLObject): name = UnicodeCol(unique=True) bits = IntCol() pubkey = StringCol(unique=True) revoked = BoolCol(default=False) exported = BoolCol(default=False) certs = MultipleJoin("Cert", joinColumn="key_id") ca = ForeignKey("CA", default=None) is_ca = BoolCol(default=False) def delete_key(self): for cert in self.certs: cert.delete_cert() self.delete(self.id)
class TemporaryBlobStorage(SQLBase): """A temporary BLOB stored in Launchpad.""" implements(ITemporaryBlobStorage) _table = 'TemporaryBlobStorage' uuid = StringCol(notNull=True, alternateID=True) file_alias = ForeignKey(dbName='file_alias', foreignKey='LibraryFileAlias', notNull=True, alternateID=True) date_created = UtcDateTimeCol(notNull=True, default=DEFAULT) @property def blob(self): self.file_alias.open() try: return self.file_alias.read() finally: self.file_alias.close() @property def _apport_job(self): # Imported here to avoid circular imports from lp.bugs.interfaces.apportjob import IProcessApportBlobJobSource try: job_for_blob = getUtility( IProcessApportBlobJobSource).getByBlobUUID(self.uuid) except SQLObjectNotFound: return None return job_for_blob def hasBeenProcessed(self): """See `ITemporaryBlobStorage`.""" job_for_blob = self._apport_job if not job_for_blob: return False return (job_for_blob.job.status == JobStatus.COMPLETED) def getProcessedData(self): """See `ITemporaryBlobStorage`.""" job_for_blob = self._apport_job if not job_for_blob: return None if 'processed_data' not in job_for_blob.metadata: return {} return job_for_blob.metadata['processed_data']
class Log(SQLObject): """ The log object Stores a list of word id's """ entry = StringCol() lex = ForeignKey('Lex') entry_date = DateTimeCol(default=sqlbuilder.func.NOW()) class sqlmeta: defaultOrder = 'entry_date' def wordList(self): """Return a list of word_ids for this log""" return self.entry.split('|')
class CodeImportJob(SQLBase): """See `ICodeImportJob`.""" implements(ICodeImportJob) date_created = UtcDateTimeCol(notNull=True, default=UTC_NOW) code_import = ForeignKey( dbName='code_import', foreignKey='CodeImport', notNull=True) machine = ForeignKey( dbName='machine', foreignKey='CodeImportMachine', notNull=False, default=None) date_due = UtcDateTimeCol(notNull=True) state = EnumCol( enum=CodeImportJobState, notNull=True, default=CodeImportJobState.PENDING) requesting_user = ForeignKey( dbName='requesting_user', foreignKey='Person', storm_validator=validate_public_person, notNull=False, default=None) ordering = IntCol(notNull=False, default=None) heartbeat = UtcDateTimeCol(notNull=False, default=None) logtail = StringCol(notNull=False, default=None) date_started = UtcDateTimeCol(notNull=False, default=None) def isOverdue(self): """See `ICodeImportJob`.""" # SQLObject offers no easy way to compare a timestamp to UTC_NOW, so # we must use trickery here. # First we flush any pending update to self to ensure that the # following database query will give the correct result even if # date_due was modified in this transaction. self.syncUpdate() # Then, we try to find a CodeImportJob object with the id of self, and # a date_due of now or past. If we find one, this means self is # overdue. import_job = CodeImportJob.selectOne( "id = %s AND date_due <= %s" % sqlvalues(self.id, UTC_NOW)) return import_job is not None
class Asset(ICTVObject): """ Represents the metadata of a file stored by the StorageManager. """ plugin_channel = ForeignKey('PluginChannel', notNone=True, cascade=True) user = ForeignKey('User') # The user who uploaded the file, if known filename = StringCol( default=None ) # The original filename of the asset, beginning with a period mime_type = StringCol( default=None) # The MIME type associated with the file extension = StringCol(default=None) file_size = BigIntCol(default=None) # File size in kilobytes created = DateTimeCol(default=DateTimeCol.now) last_reference = DateTimeCol(default=DateTimeCol.now) in_flight = BoolCol( default=False) # Is this asset being cached at the moment is_cached = BoolCol( default=False) # Is this asset a cached asset from CacheManager def _get_path(self, force=False): """ Returns the path to the asset on the filesystem or None if the asset file is being cached. """ self.last_reference = datetime.now() if not force and self.in_flight: return None elif force: self.in_flight = False # Prevent failures in the caching process to block asset in flight mode return os.path.join( 'static', 'storage', str(self.plugin_channel.id), str(self.id) + (self.extension if self.extension is not None else '')) def write_to_asset_file(self, content): """ Writes the content to the asset file. """ asset_path = os.path.join(get_root_path(), self.path) os.makedirs(os.path.dirname(asset_path), exist_ok=True) with open(asset_path, 'wb') as f: f.write(content)
class Vote(SQLBase): """See IVote.""" _table = 'Vote' _defaultOrder = ['preference', 'id'] person = ForeignKey(dbName='person', foreignKey='Person', storm_validator=validate_public_person) poll = ForeignKey(dbName='poll', foreignKey='Poll', notNull=True) option = ForeignKey(dbName='option', foreignKey='PollOption') preference = IntCol(dbName='preference') token = StringCol(dbName='token', notNull=True, unique=True)
class TG_Visit(SQLObject): class sqlmeta: table = "tg_visit" visit_key = StringCol(length=40, alternateID=True, alternateMethodName="by_visit_key") created = DateTimeCol(default=datetime.now) expiry = DateTimeCol() @classmethod def lookup_visit(cls, visit_key): try: return cls.by_visit_key(visit_key) except SQLObjectNotFound: return None
class POTranslation(SQLBase): implements(IPOTranslation) _table = 'POTranslation' # alternateID=False because we have to select by hash in order to do # index lookups. translation = StringCol(dbName='translation', notNull=True, unique=True, alternateID=False) def byTranslation(cls, key): """Return a POTranslation object for the given translation.""" # We can't search directly on msgid, because this database column # contains values too large to index. Instead we search on its # hash, which *is* indexed r = cls.selectOne('sha1(translation) = sha1(%s)' % quote(key)) if r is not None: return r else: # To be 100% compatible with the alternateID behaviour, we should # raise SQLObjectNotFound instead of KeyError raise SQLObjectNotFound(key) byTranslation = classmethod(byTranslation) def getOrCreateTranslation(cls, key): """Return a POTranslation object for the given translation, or create it if it doesn't exist. """ if isinstance(key, str): # If this is not a unicode object, it had better be ASCII or # UTF-8. # XXX: JeroenVermeulen 2008-06-06 bug=237868: non-ascii str # strings should be contained in the parser or the browser # code. key = key.decode('UTF-8') try: return cls.byTranslation(key) except SQLObjectNotFound: return cls(translation=key) getOrCreateTranslation = classmethod(getOrCreateTranslation)
class PersonLocation(SQLBase): """A person's location.""" _defaultOrder = ['id'] date_created = UtcDateTimeCol(notNull=True, default=UTC_NOW) person = ForeignKey( dbName='person', foreignKey='Person', storm_validator=validate_public_person, notNull=True, unique=True) latitude = FloatCol(notNull=False) longitude = FloatCol(notNull=False) time_zone = StringCol(notNull=True) last_modified_by = ForeignKey( dbName='last_modified_by', foreignKey='Person', storm_validator=validate_public_person, notNull=True) date_last_modified = UtcDateTimeCol(notNull=True, default=UTC_NOW) visible = BoolCol(notNull=True, default=True)
class OAuthNonce(OAuthBase, StormBase): """See `IOAuthNonce`.""" implements(IOAuthNonce) __storm_table__ = 'OAuthNonce' __storm_primary__ = 'access_token_id', 'request_timestamp', 'nonce' access_token_id = Int(name='access_token') access_token = Reference(access_token_id, OAuthAccessToken.id) request_timestamp = UtcDateTimeCol(default=UTC_NOW, notNull=True) nonce = StringCol(notNull=True) def __init__(self, access_token, request_timestamp, nonce): super(OAuthNonce, self).__init__() self.access_token = access_token self.request_timestamp = request_timestamp self.nonce = nonce
class EmailAddress(SQLBase, HasOwnerMixin): implements(IEmailAddress) _table = 'EmailAddress' _defaultOrder = ['email'] email = StringCol(dbName='email', notNull=True, unique=True, alternateID=True) status = EnumCol(dbName='status', schema=EmailAddressStatus, notNull=True) person = ForeignKey(dbName='person', foreignKey='Person', notNull=False) def __repr__(self): return '<EmailAddress at 0x%x <%s> [%s]>' % (id(self), self.email, self.status) def destroySelf(self): """See `IEmailAddress`.""" # Import this here to avoid circular references. from lp.registry.interfaces.mailinglist import MailingListStatus from lp.registry.model.mailinglist import (MailingListSubscription) if self.status == EmailAddressStatus.PREFERRED: raise UndeletableEmailAddress( "This is a person's preferred email, so it can't be deleted.") mailing_list = self.person and self.person.mailing_list if (mailing_list is not None and mailing_list.status != MailingListStatus.PURGED and mailing_list.address == self.email): raise UndeletableEmailAddress( "This is the email address of a team's mailing list, so it " "can't be deleted.") # XXX 2009-05-04 jamesh bug=371567: This function should not # be responsible for removing subscriptions, since the SSO # server can't write to that table. for subscription in MailingListSubscription.selectBy( email_address=self): subscription.destroySelf() super(EmailAddress, self).destroySelf() @property def rdf_sha1(self): """See `IEmailAddress`.""" return hashlib.sha1('mailto:' + self.email).hexdigest().upper()
class SprintSpecification(SQLBase): """A link between a sprint and a specification.""" _table = 'SprintSpecification' sprint = ForeignKey(dbName='sprint', foreignKey='Sprint', notNull=True) specification = ForeignKey(dbName='specification', foreignKey='Specification', notNull=True) status = EnumCol(schema=SprintSpecificationStatus, notNull=True, default=SprintSpecificationStatus.PROPOSED) whiteboard = StringCol(notNull=False, default=None) registrant = ForeignKey(dbName='registrant', foreignKey='Person', storm_validator=validate_public_person, notNull=True) date_created = UtcDateTimeCol(notNull=True, default=DEFAULT) decider = ForeignKey(dbName='decider', foreignKey='Person', storm_validator=validate_public_person, notNull=False, default=None) date_decided = UtcDateTimeCol(notNull=False, default=None) @property def is_confirmed(self): """See ISprintSpecification.""" return self.status == SprintSpecificationStatus.ACCEPTED @property def is_decided(self): """See ISprintSpecification.""" return self.status != SprintSpecificationStatus.PROPOSED def acceptBy(self, decider): """See ISprintSpecification.""" self.status = SprintSpecificationStatus.ACCEPTED self.decider = decider self.date_decided = UTC_NOW def declineBy(self, decider): """See ISprintSpecification.""" self.status = SprintSpecificationStatus.DECLINED self.decider = decider self.date_decided = UTC_NOW
class BranchJob(SQLBase): """Base class for jobs related to branches.""" implements(IBranchJob) _table = 'BranchJob' job = ForeignKey(foreignKey='Job', notNull=True) branch = ForeignKey(foreignKey='Branch') job_type = EnumCol(enum=BranchJobType, notNull=True) _json_data = StringCol(dbName='json_data') @property def metadata(self): return simplejson.loads(self._json_data) def __init__(self, branch, job_type, metadata, **job_args): """Constructor. Extra keyword parameters are used to construct the underlying Job object. :param branch: The database branch this job relates to. :param job_type: The BranchJobType of this job. :param metadata: The type-specific variables, as a JSON-compatible dict. """ json_data = simplejson.dumps(metadata) SQLBase.__init__(self, job=Job(**job_args), branch=branch, job_type=job_type, _json_data=json_data) def destroySelf(self): """See `IBranchJob`.""" SQLBase.destroySelf(self) self.job.destroySelf() def makeDerived(self): return BranchJobDerived.makeSubclass(self)
class Translator(SQLBase): """A Translator in a TranslationGroup.""" # default to listing newest first _defaultOrder = '-id' # db field names translationgroup = ForeignKey(dbName='translationgroup', foreignKey='TranslationGroup', notNull=True) language = ForeignKey(dbName='language', foreignKey='Language', notNull=True) translator = ForeignKey(dbName='translator', foreignKey='Person', storm_validator=validate_public_person, notNull=True) datecreated = UtcDateTimeCol(notNull=True, default=DEFAULT) style_guide_url = StringCol(notNull=False, default=None)
class Visit(SQLObject): """ A visit to your site """ class sqlmeta: table = 'visit' visit_key = StringCol(length=40, alternateID=True, alternateMethodName='by_visit_key') created = DateTimeCol(default=datetime.now) expiry = DateTimeCol() def lookup_visit(cls, visit_key): try: return cls.by_visit_key(visit_key) except SQLObjectNotFound: return None lookup_visit = classmethod(lookup_visit)
class SeriesMixin(HasDriversMixin): """See `ISeriesMixin`.""" summary = StringCol(notNull=True) @property def active(self): return self.status in ACTIVE_STATUSES @property def bug_supervisor(self): """See `ISeriesMixin`.""" return self.parent.bug_supervisor @property def drivers(self): """See `IHasDrivers`.""" drivers = set() drivers.add(self.driver) drivers = drivers.union(self.parent.drivers) drivers.discard(None) return sorted(drivers, key=attrgetter('displayname'))
class POMsgID(SQLBase): implements(IPOMsgID) _table = 'POMsgID' # alternateID is technically true, but we don't use it because this # column is too large to be indexed. msgid = StringCol(dbName='msgid', notNull=True, unique=True, alternateID=False) def byMsgid(cls, key): """Return a POMsgID object for the given msgid.""" # We can't search directly on msgid, because this database column # contains values too large to index. Instead we search on its # hash, which *is* indexed r = POMsgID.selectOne('sha1(msgid) = sha1(%s)' % quote(key)) if r is None: # To be 100% compatible with the alternateID behaviour, we should # raise SQLObjectNotFound instead of KeyError raise SQLObjectNotFound(key) return r byMsgid = classmethod(byMsgid)
class CodeImportResult(SQLBase): """See `ICodeImportResult`.""" date_created = UtcDateTimeCol(notNull=True, default=UTC_NOW) code_import = ForeignKey(dbName='code_import', foreignKey='CodeImport', notNull=True) machine = ForeignKey(dbName='machine', foreignKey='CodeImportMachine', notNull=True) requesting_user = ForeignKey(dbName='requesting_user', foreignKey='Person', storm_validator=validate_public_person, default=None) log_excerpt = StringCol(default=None) log_file = ForeignKey(dbName='log_file', foreignKey='LibraryFileAlias', default=None) status = EnumCol(enum=CodeImportResultStatus, notNull=True) date_job_started = UtcDateTimeCol(notNull=True) @property def date_job_finished(self): """See `ICodeImportResult`.""" return self.date_created @property def job_duration(self): return self.date_job_finished - self.date_job_started
class PluginParamAccessRights(ICTVObject): plugin = ForeignKey('Plugin', cascade=True) name = StringCol(notNone=True) channel_contributor_read = BoolCol(default=False) channel_contributor_write = BoolCol(default=False) channel_administrator_read = BoolCol(default=True) channel_administrator_write = BoolCol(default=False) administrator_read = BoolCol(default=True) administrator_write = BoolCol(default=True) def get_access_rights_for(self, permission_level): """ Returns a tuple of booleans (read_access, write_access) indicating which type of rights this permission level gives on this. """ if permission_level is UserPermissions.super_administrator: return True, True if permission_level is UserPermissions.administrator: return self.administrator_read, self.administrator_write if permission_level is UserPermissions.channel_administrator: return self.channel_administrator_read, self.channel_administrator_write if permission_level is UserPermissions.channel_contributor: return self.channel_contributor_read, self.channel_contributor_write return False, False
class SourcePackageName(SQLBase): implements(ISourcePackageName) _table = 'SourcePackageName' name = StringCol(dbName='name', notNull=True, unique=True, alternateID=True) potemplates = SQLMultipleJoin( 'POTemplate', joinColumn='sourcepackagename') packagings = SQLMultipleJoin( 'Packaging', joinColumn='sourcepackagename', orderBy='Packaging.id') def __unicode__(self): return self.name def __repr__(self): return "<%s '%s'>" % (self.__class__.__name__, self.name) def ensure(klass, name): try: return klass.byName(name) except SQLObjectNotFound: return klass(name=name) ensure = classmethod(ensure)