def __auto_table_args(cls): uniques = () if cls.unique_columns: uniques = [db.Index('ix_uq_{}_user'.format(cls.__tablename__), 'user_id', *cls.unique_columns, unique=True, postgresql_where=db.text('type = {}'.format(PrincipalType.user))), db.Index('ix_uq_{}_local_group'.format(cls.__tablename__), 'local_group_id', *cls.unique_columns, unique=True, postgresql_where=db.text('type = {}'.format(PrincipalType.local_group))), db.Index('ix_uq_{}_mp_group'.format(cls.__tablename__), 'mp_group_provider', 'mp_group_name', *cls.unique_columns, unique=True, postgresql_where=db.text('type = {}'.format(PrincipalType.multipass_group)))] if cls.allow_emails: uniques.append(db.Index('ix_uq_{}_email'.format(cls.__tablename__), 'email', *cls.unique_columns, unique=True, postgresql_where=db.text('type = {}'.format(PrincipalType.email)))) indexes = [db.Index(None, 'mp_group_provider', 'mp_group_name')] checks = [_make_check(PrincipalType.user, cls.allow_emails, cls.allow_networks, cls.allow_event_roles, 'user_id'), _make_check(PrincipalType.local_group, cls.allow_emails, cls.allow_networks, cls.allow_event_roles, 'local_group_id'), _make_check(PrincipalType.multipass_group, cls.allow_emails, cls.allow_networks, cls.allow_event_roles, 'mp_group_provider', 'mp_group_name')] if cls.allow_emails: checks.append(_make_check(PrincipalType.email, cls.allow_emails, cls.allow_networks, cls.allow_event_roles, 'email')) checks.append(db.CheckConstraint('email IS NULL OR email = lower(email)', 'lowercase_email')) if cls.allow_networks: checks.append(_make_check(PrincipalType.network, cls.allow_emails, cls.allow_networks, cls.allow_event_roles, 'ip_network_group_id')) if cls.allow_event_roles: checks.append(_make_check(PrincipalType.event_role, cls.allow_emails, cls.allow_networks, cls.allow_event_roles, 'event_role_id')) return tuple(uniques + indexes + checks)
def __auto_table_args(cls): return ( db.Index('ix_events_start_dt_desc', cls.start_dt.desc()), db.Index('ix_events_end_dt_desc', cls.end_dt.desc()), db.Index('ix_events_not_deleted_category', cls.is_deleted, cls.category_id), db.Index('ix_events_not_deleted_category_dates', cls.is_deleted, cls.category_id, cls.start_dt, cls.end_dt), db.Index('ix_uq_events_url_shortcut', db.func.lower(cls.url_shortcut), unique=True, postgresql_where=db.text('NOT is_deleted')), db.CheckConstraint("category_id IS NOT NULL OR is_deleted", 'category_data_set'), db.CheckConstraint( "(logo IS NULL) = (logo_metadata::text = 'null')", 'valid_logo'), db.CheckConstraint( "(stylesheet IS NULL) = (stylesheet_metadata::text = 'null')", 'valid_stylesheet'), db.CheckConstraint("end_dt >= start_dt", 'valid_dates'), db.CheckConstraint("url_shortcut != ''", 'url_shortcut_not_empty'), db.CheckConstraint("cloned_from_id != id", 'not_cloned_from_self'), db.CheckConstraint('visibility IS NULL OR visibility >= 0', 'valid_visibility'), { 'schema': 'events' })
def check_plugin_schema(self, name): """Checks if a plugin schema exists in the database. :param name: Name of the plugin """ sql = 'SELECT COUNT(*) FROM "information_schema"."schemata" WHERE "schema_name" = :name' count = db.engine.execute(db.text(sql), name='plugin_{}'.format(name)).scalar() if not count: print cformat('%{red!}Plugin schema does not exist%{reset}') print cformat('Run %{yellow!}indico plugindb upgrade --plugin {}%{reset} to create it').format(name) return False return True
def check_plugin_schema(self, name): """Checks if a plugin schema exists in the database. :param name: Name of the plugin """ sql = 'SELECT COUNT(*) FROM "information_schema"."schemata" WHERE "schema_name" = :name' count = db.engine.execute(db.text(sql), name='plugin_{}'.format(name)).scalar() if not count: print cformat('%{red!}Plugin schema does not exist%{reset}') print cformat( 'Run %{yellow!}indico plugindb upgrade --plugin {}%{reset} to create it' ).format(name) return False return True
def __auto_table_args(cls): return (db.Index('ix_events_start_dt_desc', cls.start_dt.desc()), db.Index('ix_events_end_dt_desc', cls.end_dt.desc()), db.Index('ix_events_not_deleted_category', cls.is_deleted, cls.category_id), db.Index('ix_events_not_deleted_category_dates', cls.is_deleted, cls.category_id, cls.start_dt, cls.end_dt), db.Index('ix_uq_events_url_shortcut', db.func.lower(cls.url_shortcut), unique=True, postgresql_where=db.text('NOT is_deleted')), db.CheckConstraint("category_id IS NOT NULL OR is_deleted", 'category_data_set'), db.CheckConstraint("(logo IS NULL) = (logo_metadata::text = 'null')", 'valid_logo'), db.CheckConstraint("(stylesheet IS NULL) = (stylesheet_metadata::text = 'null')", 'valid_stylesheet'), db.CheckConstraint("end_dt >= start_dt", 'valid_dates'), db.CheckConstraint("url_shortcut != ''", 'url_shortcut_not_empty'), db.CheckConstraint("cloned_from_id != id", 'not_cloned_from_self'), db.CheckConstraint('visibility IS NULL OR visibility >= 0', 'valid_visibility'), {'schema': 'events'})
class EventPerson(PersonMixin, db.Model): """A person inside an event, e.g. a speaker/author etc.""" __tablename__ = 'persons' __table_args__ = (db.UniqueConstraint('event_id', 'user_id'), db.CheckConstraint('email = lower(email)', 'lowercase_email'), db.Index(None, 'event_id', 'email', unique=True, postgresql_where=db.text("email != ''")), { 'schema': 'events' }) id = db.Column(db.Integer, primary_key=True) event_id = db.Column(db.Integer, db.ForeignKey('events.events.id'), nullable=False, index=True) user_id = db.Column(db.Integer, db.ForeignKey('users.users.id'), nullable=True, index=True) first_name = db.Column(db.String, nullable=False, default='') last_name = db.Column(db.String, nullable=False) email = db.Column(db.String, nullable=False, index=True, default='') # the title of the user - you usually want the `title` property! _title = db.Column('title', PyIntEnum(UserTitle), nullable=False, default=UserTitle.none) affiliation = db.Column(db.String, nullable=False, default='') address = db.Column(db.Text, nullable=False, default='') phone = db.Column(db.String, nullable=False, default='') invited_dt = db.Column(UTCDateTime, nullable=True) is_untrusted = db.Column(db.Boolean, nullable=False, default=False) event = db.relationship('Event', lazy=True, backref=db.backref('persons', cascade='all, delete-orphan', cascade_backrefs=False, lazy='dynamic')) user = db.relationship('User', lazy=True, backref=db.backref('event_persons', cascade_backrefs=False, lazy='dynamic')) # relationship backrefs: # - abstract_links (AbstractPersonLink.person) # - contribution_links (ContributionPersonLink.person) # - event_links (EventPersonLink.person) # - session_block_links (SessionBlockPersonLink.person) # - subcontribution_links (SubContributionPersonLink.person) @locator_property def locator(self): return dict(self.event.locator, person_id=self.id) @return_ascii def __repr__(self): return format_repr(self, 'id', is_untrusted=False, _text=self.full_name) @property def principal(self): if self.user is not None: return self.user elif self.email: return EmailPrincipal(self.email) return None @classmethod def create_from_user(cls, user, event=None, is_untrusted=False): return EventPerson(user=user, event=event, first_name=user.first_name, last_name=user.last_name, email=user.email, affiliation=user.affiliation, address=user.address, phone=user.phone, is_untrusted=is_untrusted) @classmethod def for_user(cls, user, event=None, is_untrusted=False): """Return EventPerson for a matching User in Event creating if needed""" person = event.persons.filter_by(user=user).first() if event else None return person or cls.create_from_user( user, event, is_untrusted=is_untrusted) @classmethod def merge_users(cls, target, source): """Merge the EventPersons of two users. :param target: The target user of the merge :param source: The user that is being merged into `target` """ existing_persons = {ep.event_id: ep for ep in target.event_persons} for event_person in source.event_persons: existing = existing_persons.get(event_person.event_id) if existing is None: event_person.user = target else: existing.merge_person_info(event_person) db.session.delete(event_person) db.session.flush() @classmethod def link_user_by_email(cls, user): """ Links all email-based persons matching the user's email addresses with the user. :param user: A User object. """ from indico.modules.events.models.events import Event query = (cls.query.join(EventPerson.event).filter( ~Event.is_deleted, cls.email.in_(user.all_emails), cls.user_id.is_(None))) for event_person in query: existing = (cls.query.filter_by( user_id=user.id, event_id=event_person.event_id).one_or_none()) if existing is None: event_person.user = user else: existing.merge_person_info(event_person) db.session.delete(event_person) db.session.flush() @no_autoflush def merge_person_info(self, other): from indico.modules.events.contributions.models.persons import AuthorType for column_name in { '_title', 'affiliation', 'address', 'phone', 'first_name', 'last_name' }: value = getattr(self, column_name) or getattr(other, column_name) setattr(self, column_name, value) for event_link in other.event_links: existing_event_link = next( (link for link in self.event_links if link.event_id == event_link.event_id), None) if existing_event_link is None: event_link.person = self else: other.event_links.remove(event_link) for abstract_link in other.abstract_links: existing_abstract_link = next( (link for link in self.abstract_links if link.abstract_id == abstract_link.abstract_id), None) if existing_abstract_link is None: abstract_link.person = self else: existing_abstract_link.is_speaker |= abstract_link.is_speaker existing_abstract_link.author_type = AuthorType.get_highest( existing_abstract_link.author_type, abstract_link.author_type) other.abstract_links.remove(abstract_link) for contribution_link in other.contribution_links: existing_contribution_link = next( (link for link in self.contribution_links if link.contribution_id == contribution_link.contribution_id), None) if existing_contribution_link is None: contribution_link.person = self else: existing_contribution_link.is_speaker |= contribution_link.is_speaker existing_contribution_link.author_type = AuthorType.get_highest( existing_contribution_link.author_type, contribution_link.author_type) other.contribution_links.remove(contribution_link) for subcontribution_link in other.subcontribution_links: existing_subcontribution_link = next( (link for link in self.subcontribution_links if link.subcontribution_id == subcontribution_link.subcontribution_id), None) if existing_subcontribution_link is None: subcontribution_link.person = self else: other.subcontribution_links.remove(subcontribution_link) for session_block_link in other.session_block_links: existing_session_block_link = next( (link for link in self.session_block_links if link.session_block_id == session_block_link.session_block_id), None) if existing_session_block_link is None: session_block_link.person = self else: other.session_block_links.remove(session_block_link) db.session.flush() def has_role(self, role, obj): """Whether the person has a role in the ACL list of a given object""" principals = [ x for x in obj.acl_entries if x.has_management_permission(role, explicit=True) ] return any( x for x in principals if ((self.user_id is not None and self.user_id == x.user_id) or ( self.email is not None and self.email == x.email)))