class BaseStormOpenIDAssociation: """Database representation of a stored OpenID association.""" __storm_primary__ = ('server_url', 'handle') server_url = Unicode() handle = Unicode() secret = RawStr() issued = Int() lifetime = Int() assoc_type = Unicode() def __init__(self, server_url, association): super(BaseStormOpenIDAssociation, self).__init__() self.server_url = server_url.decode('UTF-8') self.handle = association.handle.decode('ASCII') self.update(association) def update(self, association): assert self.handle == association.handle.decode('ASCII'), ( "Association handle does not match (expected %r, got %r" % (self.handle, association.handle)) self.secret = association.secret self.issued = association.issued self.lifetime = association.lifetime self.assoc_type = association.assoc_type.decode('ASCII') def as_association(self): """Return an equivalent openid-python `Association` object.""" return Association( self.handle.encode('ASCII'), self.secret, self.issued, self.lifetime, self.assoc_type.encode('ASCII'))
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 Product(BaseModel): __storm_table__ = "sfec_product" name = Unicode() stock = Int(default=0) description = Unicode() price = Decimal() is_available = Bool(default=False) categories = ReferenceSet('Product.id', 'CategoryProduct.product_id', 'CategoryProduct.category_id', 'Category.id') # # Implicit Properties # @property def category_list(self): return [c.name for c in self.categories] def dict(self): pdict = super(Product, self).dict() pdict['price'] = float(pdict['price']) # price is decimal return pdict
class MusicTrack(BaseModel): '''Music file references and metadata''' __storm_table__ = 'musictrack' id = Int(primary=True) filename = Unicode() title = Unicode() tracknumber = Int() rating = Int() length = Int() bitrate = Int() comment = Unicode() lyrics = Unicode() album_id = Int() album = Reference(album_id, 'MusicAlbum.id') def to_dict(self, recurse=True): '''See BaseModel.to_dict.''' ret = { 'id': self.id, 'filename': self.filename, 'title': self.title, 'tracknumber': self.tracknumber, 'rating': self.rating, 'length': self.length, 'bitrate': self.bitrate, 'comment': self.comment, 'lyrics': self.lyrics } if recurse: ret['album_id'] = self.album_id ret['album'] = self.album.to_dict(recurse=False) return ret
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 MusicAlbum(BaseModel): '''A music file container Music can be categorized many ways. Albums are found in the music file's ID3 tags ''' __storm_table__ = 'musicalbum' id = Int(primary=True) artist = Unicode() title = Unicode() year = Int() genre = Unicode() tracks = ReferenceSet('MusicAlbum.id', 'MusicTrack.album_id') def to_dict(self, recurse=True): '''See BaseModel.to_dict.''' ret = { 'id': self.id, 'artist': self.artist, 'title': self.title, 'year': self.year, 'genre': self.genre, } if recurse: ret['tracks'] = [ track.to_dict(recurse=False) for track in self.tracks ] return ret
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 BaseStormOpenIDNonce: """Database representation of a stored OpenID nonce.""" __storm_primary__ = ('server_url', 'timestamp', 'salt') server_url = Unicode() timestamp = Int() salt = Unicode() def __init__(self, server_url, timestamp, salt): super(BaseStormOpenIDNonce, self).__init__() self.server_url = server_url self.timestamp = timestamp self.salt = salt
class VideoMeta(BaseModel): '''Extra meta data for different types of videos''' __storm_table__ = 'videometa' id = Int(primary=True) season = Int() episode = Int() runtime = Int() genres = Unicode() year = Int() plot = Unicode() file_id = Int() file = Reference(file_id, 'VideoFile.id') series_id = Int() series = Reference(series_id, VideoSeries.id)
class Category(Storm): __storm_table__ = 'category' id = Int(primary=True, default=AutoReload) short_name = Unicode() name = Unicode() total_laps = Int() race_id = Int() race = Reference(race_id, 'Race.id') def update(self): self._complete_laps = None self._total_racers = None @property def total_racers(self): if hasattr(self, '_total_racers') and self._total_racers is not None: return self._total_racers store = Store.of(self) query = And(Racer.id == RacerLap.racer_id, Racer.category_id == self.id) data = store.using(RacerLap, Racer).find((Count(1)), query) data = list(data.group_by(Racer.category_id, Racer.id)) complete_racers = len([i for i in data if i == self.total_laps]) total_racers = Store.of(self).find(Racer, Racer.category == self).count() self._total_racers = '%s / %s' % (complete_racers, total_racers) return self._total_racers @property def is_last_lap(self): return self.completed_laps == (self.total_laps - 1) @property def completed_laps(self): if hasattr(self, '_complete_laps') and self._complete_laps is not None: return self._complete_laps store = Store.of(self) query = And(Racer.id == RacerLap.racer_id, Racer.category_id == self.id) data = store.using(RacerLap, Racer).find((Count(1)), query) data = data.group_by(Racer.category_id, Racer.id) self._complete_laps = max(list(data) or [0]) return self._complete_laps
class Foo(object): """ Test table. """ __storm_table__ = "foo" id = Int(primary=True) title = Unicode()
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 SignedCodeOfConduct(SQLBase): """Code of Conduct.""" _table = 'SignedCodeOfConduct' owner = ForeignKey(foreignKey="Person", dbName="owner", notNull=True) signedcode = StringCol(dbName='signedcode', notNull=False, default=None) signing_key_fingerprint = Unicode() datecreated = UtcDateTimeCol(dbName='datecreated', notNull=True, default=UTC_NOW) recipient = ForeignKey(foreignKey="Person", dbName="recipient", notNull=False, default=None) admincomment = StringCol(dbName='admincomment', notNull=False, default=None) active = BoolCol(dbName='active', notNull=True, default=False) @cachedproperty def signingkey(self): if self.signing_key_fingerprint is not None: return getUtility(IGPGKeySet).getByFingerprint( self.signing_key_fingerprint) @property def displayname(self): """Build a Fancy Title for CoC.""" displayname = self.datecreated.strftime('%Y-%m-%d') if self.signingkey: displayname += ( ': digitally signed by %s (%s)' % (self.owner.displayname, self.signingkey.displayname)) else: displayname += (': paper submission accepted by %s' % self.recipient.displayname) return displayname def sendAdvertisementEmail(self, subject, content): """See ISignedCodeOfConduct.""" assert self.owner.preferredemail template = get_email_template('signedcoc-acknowledge.txt', app='registry') fromaddress = format_address("Launchpad Code Of Conduct System", config.canonical.noreply_from_address) replacements = {'user': self.owner.displayname, 'content': content} message = template % replacements simple_sendmail(fromaddress, str(self.owner.preferredemail.email), subject, message)
class VideoFile(BaseModel): '''Video file reference and metadata''' __storm_table__ = 'videofile' id = Int(primary=True) filename = Unicode() thumb = Unicode() type = Unicode() #Default 'CLIP' def to_dict(self, recurse=True): '''See BaseModel.to_dict.''' ret = { 'id': self.id, 'filename': self.filename, 'thumb': self.thumb, 'type': self.type } return ret
class Bar(object): """ Test table referencing to C{Foo} """ __storm_table__ = "bar" id = Int(primary=True) title = Unicode() foo_id = Int() foo = DeferredReference(foo_id, Foo.id)
class VideoSeries(BaseModel): '''A video container Videos can be grouped by the series they were created for, with their accompanying season and episode numbers. This does not apply to anything but television ''' __storm_table__ = 'videoseries' id = Int(primary=True) title = Unicode() actor_1 = Unicode() actor_2 = Unicode() actor_3 = Unicode() actor_4 = Unicode() actor_5 = Unicode() writer_1 = Unicode() writer_2 = Unicode() director_1 = Unicode() director_2 = Unicode()
class MusicPlaylist(BaseModel): '''A music file container Users can create and add music tracks to a playlist ''' __storm_table__ = 'musicplaylist' id = Int(primary=True) title = Unicode() tracks = ReferenceSet('MusicPlaylist.id', 'MusicPlaylistTrack.playlist_id', 'MusicPlaylistTrack.track_id', 'MusicTrack.id')
class Category(BaseModel): __storm_table__ = "sfec_category" name = Unicode() products = ReferenceSet('Category.id', 'CategoryProduct.category_id', 'CategoryProduct.product_id', 'Product.id') def __init__(self, name): self.name = name
class Order(BaseModel): __storm_table__ = "sfec_order" status = Unicode() products = ReferenceSet('Order.id', 'OrderProduct.order_id') user_id = Int() user = Reference(user_id, User.id) def __init__(self): self.status = u"Buying"
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 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 BugSubscriptionFilterTag(StormBase): """Tags to filter.""" __storm_table__ = "BugSubscriptionFilterTag" id = Int(primary=True) filter_id = Int("filter", allow_none=False) filter = Reference(filter_id, "BugSubscriptionFilter.id") include = Bool(allow_none=False) tag = Unicode(allow_none=False) @property def qualified_tag(self): """The tag qualified with a hyphen if it is to be omitted.""" if self.include: return self.tag else: return u"-" + self.tag
class BugSummary(Storm): """BugSummary Storm database class.""" implements(IBugSummary) __storm_table__ = 'combinedbugsummary' id = Int(primary=True) count = Int() product_id = Int(name='product') product = Reference(product_id, Product.id) productseries_id = Int(name='productseries') productseries = Reference(productseries_id, ProductSeries.id) distribution_id = Int(name='distribution') distribution = Reference(distribution_id, Distribution.id) distroseries_id = Int(name='distroseries') distroseries = Reference(distroseries_id, DistroSeries.id) sourcepackagename_id = Int(name='sourcepackagename') sourcepackagename = Reference(sourcepackagename_id, SourcePackageName.id) milestone_id = Int(name='milestone') milestone = Reference(milestone_id, Milestone.id) status = EnumCol(dbName='status', schema=(BugTaskStatus, BugTaskStatusSearch)) importance = EnumCol(dbName='importance', schema=BugTaskImportance) tag = Unicode() viewed_by_id = Int(name='viewed_by') viewed_by = Reference(viewed_by_id, Person.id) access_policy_id = Int(name='access_policy') access_policy = Reference(access_policy_id, AccessPolicy.id) has_patch = Bool()
class Racer(Storm): __storm_table__ = 'racer' id = Int(primary=True) number = Int() name = Unicode() category_id = Int() category = Reference(category_id, 'Category.id') race_id = Int() race = Reference(race_id, 'Race.id') def update(self): self._complete_laps = None self._last_lap = -1 @property def last_lap(self): if hasattr(self, '_last_lap') and self._last_lap != -1: return self._last_lap store = Store.of(self) query = (RacerLap.racer_id == self.id) self._last_lap = store.find(RacerLap, query).order_by( RacerLap.event_time).last() return self._last_lap @property def is_finished(self): last_lap = self.last_lap if last_lap is None: return False return last_lap.remaining_laps == 0 @property def completed_laps(self): if hasattr(self, '_complete_laps') and self._complete_laps is not None: return self._complete_laps self._complete_laps = self.get_laps().count() return self._complete_laps @property def total_time(self): last = self.last_lap if not last: return None delta = last.event_time - self.race.start_time return str(datetime.timedelta(seconds=delta.seconds)) @cached_property def category_str(self): return self.category.name def add_lap(self): lap = RacerLap() lap.racer = self lap.race = self.race lap.event_time = datetime.datetime.now() Store.of(self).add(lap) return lap def get_laps(self, before=None): store = Store.of(self) query = (RacerLap.racer == self) if before is not None: query = And(query, RacerLap.event_time < before) return store.find(RacerLap, query).order_by(RacerLap.event_time)
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 BugSubscriptionFilter(StormBase): """A filter to specialize a *structural* subscription.""" implements(IBugSubscriptionFilter) __storm_table__ = "BugSubscriptionFilter" id = Int(primary=True) structural_subscription_id = Int("structuralsubscription", allow_none=False) structural_subscription = Reference(structural_subscription_id, "StructuralSubscription.id") bug_notification_level = DBEnum(enum=BugNotificationLevel, default=BugNotificationLevel.COMMENTS, allow_none=False) find_all_tags = Bool(allow_none=False, default=False) include_any_tags = Bool(allow_none=False, default=False) exclude_any_tags = Bool(allow_none=False, default=False) other_parameters = Unicode() description = Unicode('description') def _get_collection(self, cls, attribute): kind = getattr(cls, attribute) return frozenset( IStore(cls).find(cls, cls.filter == self).values(kind)) def _set_collection(self, cls, enum, attribute, current_set, desired_set): desired_set = frozenset(desired_set) if desired_set == frozenset(enum.items): # Setting all is the same as setting none, and setting none is # cheaper for reading and storage. desired_set = frozenset() # Add missing. store = IStore(cls) for kind in desired_set.difference(current_set): bsf = cls() bsf.filter = self setattr(bsf, attribute, kind) store.add(bsf) # Remove unused. kind = getattr(cls, attribute) store.find(cls, cls.filter == self, kind.is_in(current_set.difference(desired_set))).remove() def _get_statuses(self): return self._get_collection(BugSubscriptionFilterStatus, 'status') def _set_statuses(self, statuses): self._set_collection(BugSubscriptionFilterStatus, BugTaskStatus, 'status', self.statuses, statuses) statuses = property(_get_statuses, _set_statuses, doc=("A frozenset of statuses filtered on.")) def _get_importances(self): return self._get_collection(BugSubscriptionFilterImportance, 'importance') def _set_importances(self, importances): self._set_collection(BugSubscriptionFilterImportance, BugTaskImportance, 'importance', self.importances, importances) importances = property(_get_importances, _set_importances, doc=("A frozenset of importances filtered on.")) def _get_tags(self): """Return a frozenset of tags to filter on.""" wildcards = [] if self.include_any_tags: wildcards.append(u"*") if self.exclude_any_tags: wildcards.append(u"-*") tags = (tag_filter.qualified_tag for tag_filter in IStore(BugSubscriptionFilterTag).find( BugSubscriptionFilterTag, BugSubscriptionFilterTag.filter == self)) return frozenset(chain(wildcards, tags)) def _set_tags(self, tags): """Update the tags to filter on. The tags can be qualified with a leading hyphen, and can be bundled in any iterable. If they are passed within a `searchbuilder.any` or `searchbuilder.all` object, the `find_all_tags` attribute will be updated to match. Wildcard tags - `*` and `-*` - can be given too, and will update `include_any_tags` and `exclude_any_tags`. """ # Deal with searchbuilder terms. if isinstance(tags, searchbuilder.all): self.find_all_tags = True tags = frozenset(tags.query_values) elif isinstance(tags, searchbuilder.any): self.find_all_tags = False tags = frozenset(tags.query_values) else: # Leave find_all_tags unchanged. tags = frozenset(tags) wildcards = frozenset((u"*", u"-*")).intersection(tags) # Set wildcards. self.include_any_tags = "*" in wildcards self.exclude_any_tags = "-*" in wildcards # Deal with other tags. tags = tags - wildcards store = IStore(BugSubscriptionFilterTag) current_tag_filters = dict( (tag_filter.qualified_tag, tag_filter) for tag_filter in store.find( BugSubscriptionFilterTag, BugSubscriptionFilterTag.filter == self)) # Remove unused tags. for tag in set(current_tag_filters).difference(tags): tag_filter = current_tag_filters.pop(tag) store.remove(tag_filter) # Add additional tags. for tag in tags.difference(current_tag_filters): tag_filter = BugSubscriptionFilterTag() tag_filter.filter = self tag_filter.include = not tag.startswith("-") tag_filter.tag = tag.lstrip("-") store.add(tag_filter) tags = property(_get_tags, _set_tags, doc=("A frozenset of tags filtered on.")) def _get_information_types(self): return self._get_collection(BugSubscriptionFilterInformationType, 'information_type') def _set_information_types(self, information_types): self._set_collection(BugSubscriptionFilterInformationType, InformationType, 'information_type', self.information_types, information_types) information_types = property( _get_information_types, _set_information_types, doc=("A frozenset of information_types filtered on.")) def delete(self): """See `IBugSubscriptionFilter`.""" BugSubscriptionFilter.deleteMultiple([self.id]) Store.of(self).remove(self) @classmethod def deleteMultiple(cls, ids): from lp.bugs.model.structuralsubscription import StructuralSubscription store = IStore(BugSubscriptionFilter) structsub_ids = list( store.find(BugSubscriptionFilter.structural_subscription_id, BugSubscriptionFilter.id.is_in(ids))) kinds = [ BugSubscriptionFilterImportance, BugSubscriptionFilterStatus, BugSubscriptionFilterTag, BugSubscriptionFilterInformationType ] for kind in kinds: store.find(kind, kind.filter_id.is_in(ids)).remove() store.find(BugSubscriptionFilter, BugSubscriptionFilter.id.is_in(ids)).remove() # Now delete any structural subscriptions that have no filters. # Take out a SHARE lock on the filters that we use as evidence # for keeping structsubs, to ensure that they haven't been # deleted under us. filter_expr = Select( 1, tables=[BugSubscriptionFilter], where=(BugSubscriptionFilter.structural_subscription_id == StructuralSubscription.id)) locked_filter_expr = SQL( convert_storm_clause_to_string(filter_expr) + ' FOR SHARE') store.find(StructuralSubscription, StructuralSubscription.id.is_in(structsub_ids), Not(Exists(locked_filter_expr))).remove() def isMuteAllowed(self, person): """See `IBugSubscriptionFilter`.""" subscriber = self.structural_subscription.subscriber # The person can mute the Subscription if the subscription is via a # team of which they are a member and the team doesn't have a contact # address (because if the team does, then the mute would be # ineffectual). return (subscriber.is_team and person.inTeam(subscriber) and subscriber.preferredemail is None) def muted(self, person): store = Store.of(self) existing_mutes = store.find( BugSubscriptionFilterMute, BugSubscriptionFilterMute.filter_id == self.id, BugSubscriptionFilterMute.person_id == person.id) if not existing_mutes.is_empty(): return existing_mutes.one().date_created def mute(self, person): """See `IBugSubscriptionFilter`.""" subscriber = self.structural_subscription.subscriber if subscriber.is_team and subscriber.preferredemail: raise MuteNotAllowed( "This subscription cannot be muted because team %s has a " "contact address." % subscriber.name) if not self.isMuteAllowed(person): raise MuteNotAllowed("This subscription cannot be muted for %s" % person.name) store = Store.of(self) existing_mutes = store.find( BugSubscriptionFilterMute, BugSubscriptionFilterMute.filter_id == self.id, BugSubscriptionFilterMute.person_id == person.id) if existing_mutes.is_empty(): mute = BugSubscriptionFilterMute() mute.person = person mute.filter = self.id store.add(mute) def unmute(self, person): """See `IBugSubscriptionFilter`.""" store = Store.of(self) existing_mutes = store.find( BugSubscriptionFilterMute, BugSubscriptionFilterMute.filter_id == self.id, BugSubscriptionFilterMute.person_id == person.id) existing_mutes.remove()