class StaticSite(StoredFileMixin, db.Model): """Static site for an fossir event.""" __tablename__ = 'static_sites' __table_args__ = {'schema': 'events'} # StoredFileMixin settings add_file_date_column = False file_required = False #: Entry ID id = db.Column(db.Integer, primary_key=True) #: ID of the event event_id = db.Column(db.Integer, db.ForeignKey('events.events.id'), index=True, nullable=False) #: The state of the static site (a :class:`StaticSiteState` member) state = db.Column(PyIntEnum(StaticSiteState), default=StaticSiteState.pending, nullable=False) #: The date and time the static site was requested requested_dt = db.Column(UTCDateTime, default=now_utc, nullable=False) #: ID of the user who created the static site creator_id = db.Column(db.Integer, db.ForeignKey('users.users.id'), index=True, nullable=False) #: The user who created the static site creator = db.relationship('User', lazy=False, backref=db.backref('static_sites', lazy='dynamic')) #: The Event this static site is associated with event = db.relationship('Event', lazy=True, backref=db.backref('static_sites', lazy='dynamic')) @property def locator(self): return {'confId': self.event_id, 'id': self.id} def _build_storage_path(self): path_segments = ['event', strict_unicode(self.event.id), 'static'] self.assign_id() filename = '{}-{}'.format(self.id, self.filename) path = posixpath.join(*(path_segments + [filename])) return config.STATIC_SITE_STORAGE, path @return_ascii def __repr__(self): return format_repr(self, 'id', 'event_id', 'state')
class PaperCompetence(db.Model): __tablename__ = 'competences' __table_args__ = (db.UniqueConstraint('user_id', 'event_id'), {'schema': 'event_paper_reviewing'}) id = db.Column( db.Integer, primary_key=True ) user_id = db.Column( db.Integer, db.ForeignKey('users.users.id'), index=True, nullable=False ) event_id = db.Column( db.Integer, db.ForeignKey('events.events.id'), index=True, nullable=False ) competences = db.Column( ARRAY(db.String), nullable=False, default=[] ) event = db.relationship( 'Event', lazy=True, backref=db.backref( 'paper_competences', cascade='all, delete-orphan', lazy=True ) ) user = db.relationship( 'User', lazy=True, backref=db.backref( 'paper_competences', lazy='dynamic' ) ) @return_ascii def __repr__(self): return format_repr(self, 'id', 'user_id', 'event_id', _text=', '.join(self.competences))
class EquipmentType(db.Model): __tablename__ = 'equipment_types' __table_args__ = (db.UniqueConstraint('name', 'location_id'), { 'schema': 'roombooking' }) id = db.Column(db.Integer, primary_key=True) parent_id = db.Column(db.Integer, db.ForeignKey('roombooking.equipment_types.id')) name = db.Column(db.String, nullable=False, index=True) location_id = db.Column(db.Integer, db.ForeignKey('roombooking.locations.id'), nullable=False) children = db.relationship('EquipmentType', backref=db.backref('parent', remote_side=[id])) # relationship backrefs: # - location (Location.equipment_types) # - parent (EquipmentType.children) # - reservations (Reservation.used_equipment) # - rooms (Room.available_equipment) @return_ascii def __repr__(self): return u'<EquipmentType({0}, {1}, {2})>'.format( self.id, self.name, self.location_id)
class CategorySetting(JSONSettingsBase, db.Model): @strict_classproperty @staticmethod def __auto_table_args(): return (db.Index(None, 'category_id', 'module', 'name'), db.Index(None, 'category_id', 'module'), db.UniqueConstraint('category_id', 'module', 'name'), { 'schema': 'categories' }) @declared_attr def __table_args__(cls): return auto_table_args(cls) category_id = db.Column(db.Integer, db.ForeignKey('categories.categories.id'), index=True, nullable=False) category = db.relationship('Category', lazy=True, backref=db.backref('settings', lazy='dynamic')) @return_ascii def __repr__(self): return '<CategorySetting({}, {}, {}, {!r})>'.format( self.category_id, self.module, self.name, self.value)
class SuggestedCategory(db.Model): __tablename__ = 'suggested_categories' __table_args__ = {'schema': 'users'} user_id = db.Column( db.Integer, db.ForeignKey('users.users.id'), primary_key=True, index=True, autoincrement=False ) category_id = db.Column( db.Integer, db.ForeignKey('categories.categories.id'), primary_key=True, index=True, autoincrement=False ) is_ignored = db.Column( db.Boolean, nullable=False, default=False ) score = db.Column( db.Float, nullable=False, default=0 ) category = db.relationship( 'Category', lazy=False, backref=db.backref( 'suggestions', lazy=True, cascade='all, delete-orphan' ) ) # relationship backrefs: # - user (User.suggested_categories) @return_ascii def __repr__(self): return format_repr(self, 'user_id', 'category_id', 'score', is_ignored=False) @classmethod def merge_users(cls, target, source): """Merge the suggestions for two users. :param target: The target user of the merge. :param source: The user that is being merged into `target`. """ target_suggestions = {x.category: x for x in target.suggested_categories} for suggestion in source.suggested_categories: new_suggestion = target_suggestions.get(suggestion.category) or cls(user=target, category=suggestion.category) new_suggestion.score = max(new_suggestion.score, suggestion.score) new_suggestion.is_ignored = new_suggestion.is_ignored or suggestion.is_ignored db.session.flush()
class RoomAttribute(db.Model): __tablename__ = 'room_attributes' __table_args__ = (db.UniqueConstraint('name', 'location_id'), { 'schema': 'roombooking' }) id = db.Column(db.Integer, primary_key=True) parent_id = db.Column(db.Integer, db.ForeignKey('roombooking.room_attributes.id')) name = db.Column(db.String, nullable=False, index=True) title = db.Column(db.String, nullable=False) location_id = db.Column(db.Integer, db.ForeignKey('roombooking.locations.id'), nullable=False) type = db.Column(db.String, nullable=False) is_required = db.Column(db.Boolean, nullable=False) is_hidden = db.Column(db.Boolean, nullable=False) children = db.relationship('RoomAttribute', backref=db.backref('parent', remote_side=[id])) # relationship backrefs: # - location (Location.attributes) # - parent (RoomAttribute.children) # - room_associations (RoomAttributeAssociation.attribute) @return_ascii def __repr__(self): return u'<RoomAttribute({}, {}, {})>'.format(self.id, self.name, self.location.name)
class LegacyCategoryMapping(db.Model): """Legacy category ID mapping Legacy categories have non-numeric IDs which are not supported by any new code. This mapping maps them to proper integer IDs to avoid breaking things. """ __tablename__ = 'legacy_id_map' __table_args__ = {'schema': 'categories'} legacy_category_id = db.Column(db.String, primary_key=True, index=True) category_id = db.Column(db.Integer, db.ForeignKey('categories.categories.id'), index=True, primary_key=True, autoincrement=False) category = db.relationship('Category', lazy=True, backref=db.backref('legacy_mapping', uselist=False, lazy=True)) @return_ascii def __repr__(self): return '<LegacyCategoryMapping({}, {})>'.format( self.legacy_category_id, self.category_id)
class SurveySection(SurveyItem): __mapper_args__ = {'polymorphic_identity': SurveyItemType.section} #: The child items of this section children = db.relationship('SurveyItem', order_by='SurveyItem.position', cascade='all, delete-orphan', backref=db.backref('parent', remote_side=[SurveyItem.id])) @property def locator(self): return dict(self.survey.locator, section_id=self.id) @return_ascii def __repr__(self): return '<SurveySection({}, {}, {})>'.format(self.id, self.survey_id, self.title) def to_dict(self): data = super(SurveySection, self).to_dict() content = [child.to_dict() for child in self.children] data.update({ 'content': content, 'display_as_section': self.display_as_section }) if not self.display_as_section: del data['title'] del data['description'] return data
class UserSetting(JSONSettingsBase, db.Model): """User-specific settings""" __table_args__ = (db.Index(None, 'user_id', 'module', 'name'), db.Index(None, 'user_id', 'module'), db.UniqueConstraint('user_id', 'module', 'name'), db.CheckConstraint('module = lower(module)', 'lowercase_module'), db.CheckConstraint('name = lower(name)', 'lowercase_name'), { 'schema': 'users' }) user_id = db.Column(db.Integer, db.ForeignKey('users.users.id'), nullable=False, index=True) user = db.relationship('User', lazy=True, backref=db.backref('_all_settings', lazy='dynamic', cascade='all, delete-orphan')) @return_ascii def __repr__(self): return '<UserSetting({}, {}, {}, {!r})>'.format( self.user_id, self.module, self.name, self.value)
def file(cls): return db.relationship( cls.stored_file_class, primaryjoin=lambda: cls.file_id == cls.stored_file_class.id, foreign_keys=lambda: cls.file_id, lazy=False, post_update=True )
def event(cls): return db.relationship( 'Event', lazy=True, backref=db.backref( cls.events_backref_name, lazy='dynamic' ) )
def modified_by(cls): return db.relationship( 'User', lazy=True, foreign_keys=cls.modified_by_id, backref=db.backref( cls.user_modified_backref_name, primaryjoin='({0}.modified_by_id == User.id) & ~{0}.is_deleted' .format(cls.__name__), lazy='dynamic'))
def subcontribution(cls): if LinkType.subcontribution in cls.allowed_link_types: return db.relationship( 'SubContribution', lazy=True, backref=db.backref( cls.link_backref_name, cascade='all, delete-orphan', uselist=(cls.unique_links != True), # noqa lazy=cls.link_backref_lazy))
def contribution_field(cls): return db.relationship( 'ContributionField', lazy=False, backref=db.backref( cls.contribution_field_backref_name, cascade='all, delete-orphan', lazy=True ) )
def category(cls): if LinkType.category in cls.allowed_link_types: return db.relationship( 'Category', lazy=True, backref=db.backref( cls.link_backref_name, cascade='all, delete-orphan', uselist=(cls.unique_links != True), # noqa lazy=cls.link_backref_lazy))
class LegacySubContributionMapping(db.Model): """Legacy subcontribution id mapping Legacy subcontributions had ids unique only within their event and contribution. This table maps those ids to the new globally unique subcontribution id. """ __tablename__ = 'legacy_subcontribution_id_map' __table_args__ = {'schema': 'events'} event_id = db.Column(db.Integer, db.ForeignKey('events.events.id'), primary_key=True, autoincrement=False) legacy_contribution_id = db.Column(db.String, primary_key=True) legacy_subcontribution_id = db.Column(db.String, primary_key=True) subcontribution_id = db.Column( db.Integer, db.ForeignKey('events.subcontributions.id', name='fk_legacy_subcontribution_id_map_subcontribution'), nullable=False, index=True) event = db.relationship('Event', lazy=True, backref=db.backref( 'legacy_subcontribution_mappings', cascade='all, delete-orphan', lazy='dynamic')) subcontribution = db.relationship('SubContribution', lazy=False, backref=db.backref( 'legacy_mapping', cascade='all, delete-orphan', uselist=False, lazy=True)) @return_ascii def __repr__(self): return format_repr(self, 'event_id', 'legacy_contribution_id', 'legacy_subcontribution_id', 'subcontribution_id')
def linked_event(cls): if LinkType.event in cls.allowed_link_types: return db.relationship( 'Event', foreign_keys=cls.linked_event_id, lazy=True, backref=db.backref( cls.link_backref_name, cascade='all, delete-orphan', uselist=(cls.unique_links != True), # noqa lazy=cls.link_backref_lazy))
class DesignerImageFile(StoredFileMixin, db.Model): __tablename__ = 'designer_image_files' __table_args__ = {'schema': 'fossir'} # Image files are not version-controlled version_of = None #: The ID of the file id = db.Column( db.Integer, primary_key=True ) #: The designer template the image belongs to template_id = db.Column( db.Integer, db.ForeignKey('fossir.designer_templates.id'), nullable=False, index=True ) template = db.relationship( 'DesignerTemplate', lazy=False, foreign_keys=template_id, backref=db.backref( 'images', cascade='all, delete-orphan', lazy=True ) ) @property def download_url(self): return url_for('designer.download_image', self) @property def locator(self): return dict(self.template.locator, image_id=self.id, filename=self.filename) def _build_storage_path(self): path_segments = ['designer_templates', strict_unicode(self.template.id), 'images'] self.assign_id() filename = '{}-{}'.format(self.id, self.filename) path = posixpath.join(*(path_segments + [filename])) return config.ATTACHMENT_STORAGE, path @return_ascii def __repr__(self): return '<DesignerImageFile({}, {}, {}, {})>'.format( self.id, self.template_id, self.filename, self.content_type )
class AbstractEmailTemplate(db.Model): """Represents an email template for abstracts notifications.""" __tablename__ = 'email_templates' __table_args__ = {'schema': 'event_abstracts'} id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String, nullable=False) event_id = db.Column(db.Integer, db.ForeignKey('events.events.id'), index=True, nullable=False) #: The relative position of the template in the list of templates position = db.Column(db.Integer, nullable=False, default=_get_next_position) #: The address to use as Reply-To in the email reply_to_address = db.Column(db.String, nullable=False) #: The subject of the email subject = db.Column(db.String, nullable=False) #: The body of the template body = db.Column(db.Text, nullable=False, default='') #: List of extra email addresses to be added as CC in the email extra_cc_emails = db.Column( ARRAY(db.String), nullable=False, default=[], ) #: Whether to include the submitter's email address as To for emails include_submitter = db.Column(db.Boolean, nullable=False, default=False) #: Whether to include authors' email addresses as To for emails include_authors = db.Column(db.Boolean, nullable=False, default=False) #: Whether to include co-authors' email addresses as CC for emails include_coauthors = db.Column(db.Boolean, nullable=False, default=False) #: Whether to stop checking the rest of the conditions when a match is found stop_on_match = db.Column(db.Boolean, nullable=False, default=True) #: Conditions need to be met to send the email rules = db.Column(JSON, nullable=False) event = db.relationship('Event', lazy=True, backref=db.backref('abstract_email_templates', lazy=True)) # relationship backrefs: # - logs (AbstractEmailLogEntry.email_template) @locator_property def locator(self): return dict(self.event.locator, email_tpl_id=self.id) @return_ascii def __repr__(self): return format_repr(self, 'id', 'event_id', _text=self.title)
def event(cls): return db.relationship( 'Event', lazy=True, backref=db.backref( cls.event_backref_name, primaryjoin='({0}.event_id == Event.id) & ~{0}.is_deleted'. format(cls.__name__), order_by=cls.position, cascade='all, delete-orphan', lazy=True))
def paper_revision(cls): return db.relationship( 'PaperRevision', lazy=True, backref=db.backref( 'comments', primaryjoin='(PaperReviewComment.revision_id == PaperRevision.id) & ~PaperReviewComment.is_deleted', order_by=cls.created_dt, cascade='all, delete-orphan', lazy=True, ) )
def abstract(cls): return db.relationship( 'Abstract', lazy=True, backref=db.backref( 'comments', primaryjoin='(AbstractComment.abstract_id == Abstract.id) & ~AbstractComment.is_deleted', order_by=cls.created_dt, cascade='all, delete-orphan', lazy=True, ) )
def _paper_last_revision(cls): # Incompatible with joinedload subquery = (db.select([ db.func.max(PaperRevision.submitted_dt) ]).where(PaperRevision._contribution_id == cls.id).correlate_except( PaperRevision).as_scalar()) return db.relationship('PaperRevision', uselist=False, lazy=True, viewonly=True, primaryjoin=db.and_( PaperRevision._contribution_id == cls.id, PaperRevision.submitted_dt == subquery))
def all_files(cls): return db.relationship( cls.stored_file_class, primaryjoin=lambda: cls.id == getattr(cls.stored_file_class, cls.stored_file_fkey), foreign_keys=lambda: getattr(cls.stored_file_class, cls.stored_file_fkey), lazy=True, cascade='all, delete, delete-orphan', order_by=lambda: cls.stored_file_class.created_dt.desc(), backref=db.backref( getattr(cls.stored_file_class, 'version_of'), lazy=False ) )
class LegacySessionBlockMapping(db.Model): """Legacy session block id mapping Legacy sessions blocks had ids unique only within their session. """ __tablename__ = 'legacy_session_block_id_map' __table_args__ = {'schema': 'events'} event_id = db.Column(db.Integer, db.ForeignKey('events.events.id'), primary_key=True, autoincrement=False) legacy_session_id = db.Column(db.String, primary_key=True) legacy_session_block_id = db.Column(db.String, primary_key=True) session_block_id = db.Column(db.Integer, db.ForeignKey('events.session_blocks.id'), nullable=False, index=True) event = db.relationship('Event', lazy=True, backref=db.backref('legacy_session_block_mappings', cascade='all, delete-orphan', lazy='dynamic')) session_block = db.relationship('SessionBlock', lazy=False, backref=db.backref( 'legacy_mapping', cascade='all, delete-orphan', uselist=False, lazy=True)) @return_ascii def __repr__(self): return format_repr(self, 'event_id', 'legacy_session_id', 'legacy_session_block_id', 'session_block_id')
class LocalGroup(db.Model): __tablename__ = 'groups' @declared_attr def __table_args__(cls): return (db.Index('ix_uq_groups_name_lower', db.func.lower(cls.name), unique=True), { 'schema': 'users' }) #: the unique id of the group id = db.Column(db.Integer, primary_key=True) #: the name of the group name = db.Column(db.String, nullable=False, index=True) #: the local groups this user belongs to members = db.relationship( 'User', secondary='users.group_members', lazy=True, collection_class=set, backref=db.backref('local_groups', lazy=True, collection_class=set), ) # relationship backrefs: # - in_attachment_acls (AttachmentPrincipal.local_group) # - in_attachment_folder_acls (AttachmentFolderPrincipal.local_group) # - in_blocking_acls (BlockingPrincipal.local_group) # - in_category_acls (CategoryPrincipal.local_group) # - in_contribution_acls (ContributionPrincipal.local_group) # - in_event_acls (EventPrincipal.local_group) # - in_event_settings_acls (EventSettingPrincipal.local_group) # - in_session_acls (SessionPrincipal.local_group) # - in_settings_acls (SettingPrincipal.local_group) @return_ascii def __repr__(self): return '<LocalGroup({}, {})>'.format(self.id, self.name) @property def proxy(self): """Returns a GroupProxy wrapping this group""" from fossir.modules.groups import GroupProxy return GroupProxy(self.id, _group=self)
class LegacyImageMapping(db.Model): """Legacy image id mapping Legacy images had event-unique numeric ids. Using this mapping we can resolve old ones to their new id. """ __tablename__ = 'legacy_image_id_map' __table_args__ = {'schema': 'events'} event_id = db.Column( db.Integer, db.ForeignKey('events.events.id'), primary_key=True, index=True, autoincrement=False ) legacy_image_id = db.Column( db.Integer, primary_key=True, index=True, autoincrement=False ) image_id = db.Column( db.Integer, db.ForeignKey('events.image_files.id'), nullable=False, index=True ) image = db.relationship( 'ImageFile', lazy=False, backref=db.backref( 'legacy_mapping', cascade='all, delete-orphan', uselist=False, lazy=True ) ) @return_ascii def __repr__(self): return format_repr(self, 'legacy_image_id', 'image_id')
class AbstractFile(StoredFileMixin, db.Model): __tablename__ = 'files' __table_args__ = {'schema': 'event_abstracts'} # StoredFileMixin settings add_file_date_column = False id = db.Column(db.Integer, primary_key=True) abstract_id = db.Column(db.Integer, db.ForeignKey('event_abstracts.abstracts.id'), nullable=False, index=True) abstract = db.relationship('Abstract', lazy=True, backref=db.backref( 'files', lazy=True, cascade='all, delete-orphan')) @property def locator(self): return dict(self.abstract.locator, file_id=self.id, filename=self.filename) def _build_storage_path(self): self.abstract.assign_id() path_segments = [ 'event', strict_unicode(self.abstract.event.id), 'abstracts', strict_unicode(self.abstract.id) ] self.assign_id() filename = '{}-{}'.format(self.id, self.filename) path = posixpath.join(*(path_segments + [filename])) return config.ATTACHMENT_STORAGE, path @return_ascii def __repr__(self): return format_repr(self, 'id', 'abstract_id', content_type=None, _text=text_to_repr(self.filename))
class PaperTemplate(StoredFileMixin, db.Model): __tablename__ = 'templates' __table_args__ = {'schema': 'event_paper_reviewing'} # StoredFileMixin settings add_file_date_column = False id = db.Column(db.Integer, primary_key=True) event_id = db.Column(db.Integer, db.ForeignKey('events.events.id'), index=True, nullable=False) name = db.Column(db.String, nullable=False) description = db.Column(db.Text, nullable=False, default='') event = db.relationship('Event', lazy=True, backref=db.backref('paper_templates', cascade='all, delete-orphan', lazy=True)) @return_ascii def __repr__(self): return format_repr(self, 'id', 'event_id', 'filename', content_type=None) @locator_property def locator(self): return dict(self.event.locator, template_id=self.id, filename=self.filename) def _build_storage_path(self): self.assign_id() path_segments = [ 'event', strict_unicode(self.event.id), 'paper_templates' ] path = posixpath.join(*(path_segments + ['{}_{}'.format(self.id, self.filename)])) return config.ATTACHMENT_STORAGE, path
class ImageFile(StoredFileMixin, db.Model): __tablename__ = 'image_files' __table_args__ = {'schema': 'events'} # Image files are not version-controlled version_of = None #: The ID of the file id = db.Column(db.Integer, primary_key=True) #: The event the image belongs to event_id = db.Column(db.Integer, db.ForeignKey('events.events.id'), nullable=False, index=True) event = db.relationship('Event', lazy=False, backref=db.backref('layout_images', lazy='dynamic')) # relationship backrefs: # - legacy_mapping (LegacyImageMapping.image) @property def locator(self): return dict(self.event.locator, image_id=self.id, filename=self.filename) def _build_storage_path(self): path_segments = ['event', strict_unicode(self.event.id), 'images'] self.assign_id() filename = '{}-{}'.format(self.id, self.filename) path = posixpath.join(*(path_segments + [filename])) return config.ATTACHMENT_STORAGE, path @return_ascii def __repr__(self): return '<ImageFile({}, {}, {}, {})>'.format(self.id, self.event_id, self.filename, self.content_type)