class Sponsor(db.Model): """Sponsor.""" __tablename__ = 'sponsors' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) description = db.Column(db.Text) url = db.Column(db.String(255)) logo = db.Column(db.String(255)) contact_name = db.Column(db.String(255)) contact_email = db.Column(db.String(255)) accepted = db.Column(db.Boolean) payment_received = db.Column(db.Boolean) level_id = db.Column( db.Integer, db.ForeignKey('sponsor_levels.id'), nullable=False, ) level = db.relationship( 'Level', backref=db.backref('sponsors', lazy='dynamic'), ) applicant_id = db.Column( db.Integer, db.ForeignKey('users.id'), nullable=False, ) applicant = db.relationship('User') def __str__(self): """Return a printable representation.""" return self.name @cached_property def slug(self): """Return the slug for the sponsor.""" return slugify(self.name)
class Announcement(db.Model): """News announcement.""" __tablename__ = 'announcements' query_class = EventQuery id = db.Column(db.Integer, primary_key=True) slug = db.Column(db.String(255), nullable=False) title = db.Column(db.String(255), nullable=False) content = db.Column(db.Text, nullable=False) active = db.Column(db.Boolean, nullable=False) published = db.Column(ArrowType) event_id = db.Column(db.Integer, db.ForeignKey('events.id'), nullable=False) event = db.relationship('Event', backref=db.backref('announcements', lazy='dynamic')) def __str__(self): """Return a printable representation.""" return self.title @observes('title') def _create_slug(self, title): """Create a slug from the title of the announcement.""" self.slug = slugify(self.title)
class AboutPage(db.Model): """About page.""" __tablename__ = 'about_pages' query_class = EventQuery id = db.Column(db.Integer, primary_key=True) # TODO: validate that the navbar_section / slug combination do not conflict # with an existing generated blueprint view route # The navbar_path dictates the location of this menu item in the # navbar hierarchy. navbar_path = db.Column(postgresql.ARRAY(db.String), nullable=False) # A slug may be empty. If it is, the item will be placed at the # root of the navbar hierarchy. slug = db.Column(db.String(255), default='', nullable=False) title = db.Column(db.String(255), nullable=False) content = db.Column(db.Text, nullable=False) active = db.Column(db.Boolean, nullable=False) event_id = db.Column( db.Integer, db.ForeignKey('events.id'), nullable=False, ) event = db.relationship( 'Event', backref=db.backref('about_pages', lazy='dynamic'), ) __table_args__ = ( db.UniqueConstraint( 'navbar_path', 'slug', 'event_id', name='ix_about_pages_navbar_path_slug_event_id', ), ) def __str__(self): """Return a printable representation.""" return self.title @observes('title') def _create_slug(self, title): """Create the slug for the page.""" if not self.slug: self.slug = slugify(self.title) @property def rst_document(self): """Return the full reST document, including the title. The page's title was be used as the document heading, causing any headings defined in the page's content to be used as subheadings. To cut down on potential collisions, ``#`` symbols will be placed on the lines before and after the title. """ lines = ('{divider}', '{page.title}', '{divider}', '{page.content}') return '\n'.join(lines).format( divider='#' * len(self.title), page=self)
class Category(db.Model): """Talk category.""" __tablename__ = 'categories' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) slug = db.Column(db.String(75), unique=True, nullable=False) def __str__(self): """Return a printable representation.""" return self.name
class Level(db.Model): """Sponsorship level.""" __tablename__ = 'sponsor_levels' query_class = EventQuery id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) description = db.Column(db.Text) order = db.Column(db.Integer, default=0) cost = db.Column(db.String, default=0) # This isn't always money. limit = db.Column(db.Integer, default=0) event_id = db.Column( db.Integer, db.ForeignKey('events.id'), nullable=False) event = db.relationship( 'Event', backref=db.backref('sponsor_levels', lazy='dynamic')) def __str__(self): """Return a printable representation.""" return self.name @cached_property def accepted_sponsors(self): """Return the accepted sponsors for the level.""" return self.sponsors.filter(Sponsor.accepted == True) # NOQA @cached_property def is_sold_out(self): """Return whether the level is sold out.""" return 0 < self.limit <= self.accepted_sponsors.count()
class Role(db.Model, RoleMixin): """User role.""" __tablename__ = 'roles' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), unique=True) description = db.Column(db.String(255)) def __str__(self): """Return a printable representation.""" return self.name def __eq__(self, other): return self.name == other or self.name == getattr(other, 'name', None) def __hash__(self): return id(self) def __ne__(self, other): return self.name != other and self.name != getattr(other, 'name', None)
class Room(db.Model): """Room of talks.""" __tablename__ = 'rooms' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) order = db.Column(db.Integer, nullable=False) def __str__(self): """Return a printable representation.""" return self.name
class Duration(db.Model): """Talk duration.""" __tablename__ = 'durations' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) duration = db.Column(db.Integer, nullable=False) inactive = db.Column(db.Boolean, default=False) __mapper_args__ = { 'order_by': (inactive, duration), } def __str__(self): """Return a printable representation.""" return self.name
class CallToAction(db.Model): """Call to action.""" __tablename__ = 'calls_to_action' query_class = EventQuery id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(255), nullable=False) url = db.Column(URLType) active = db.Column(db.Boolean, nullable=False) begins = db.Column(ArrowType) ends = db.Column(ArrowType) event_id = db.Column( db.Integer, db.ForeignKey('events.id'), nullable=False) event = db.relationship( 'Event', backref=db.backref('calls_to_action', lazy='dynamic')) def __str__(self): """Return a printable representation.""" return self.title
class Event(db.Model): """Event.""" __tablename__ = 'events' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), unique=True, nullable=False) slug = db.Column(db.String(75), unique=True, nullable=True) # Event dates begins = db.Column(db.Date) ends = db.Column(db.Date) # Fields to control when the event is active active = db.Column(db.Boolean, nullable=False) activity_begins = db.Column(ArrowType) activity_ends = db.Column(ArrowType) # Proposal window proposals_begin = db.Column(ArrowType) proposals_end = db.Column(ArrowType) # When to publish the talks talk_list_begins = db.Column(ArrowType) # When to publish the schedule talk_schedule_begins = db.Column(ArrowType) # Registration information registration_closed = db.Column( db.Boolean, server_default='false', nullable=False, ) registration_url = db.Column(db.String(255)) registration_begins = db.Column(ArrowType) registration_ends = db.Column(ArrowType) def __str__(self): """Return a printable representation.""" return self.name @cached_property def accepted_talks(self): """Return the accepted :class:`~pygotham.models.Talk` list.""" return self.talks.filter(Talk.status == 'accepted').order_by(Talk.name) @observes('name') def _create_slug(self, title): """Create a slug from the name of the event.""" if not self.slug: self.slug = slugify(self.name) @property def dates(self): """Return the date(s) for the event.""" dates = ['{:%B %d}'.format(self.begins)] if self.begins != self.ends: if self.begins.month == self.ends.month: format = '{:%d}' else: format = '{:%B %d}' dates.append(format.format(self.ends)) return ' - '.join(dates) @property def is_call_for_proposals_active(self): """Return whether the call for proposals for an event is active. The CFP is active when the current :class:`~datetime.datetime` is greater than or equal to :attribute:`~pygotham.events.models.Event.proposals_begin` and less than :attribute:`~pygotham.events.models.Event.proposals_end`. """ now = arrow.utcnow().to(current_app.config['TIME_ZONE']).naive if not self.proposals_begin or now < self.proposals_begin.naive: return False if self.proposals_end and self.proposals_end.naive < now: return False return True @property def is_call_for_proposals_expired(self): """Return whether the call for proposals has expired.""" now = arrow.utcnow().to(current_app.config['TIME_ZONE']).naive return self.proposals_end and self.proposals_end.naive < now @property def is_registration_active(self): """Return whether registration for an event is active. There are several pieces to the logic of whether or not an event's registration is active: - :attribute:`~pygotham.events.models.Event.registration_closed` must be ``False``. - :attribute:`~pygotham.events.models.Event.registration_url` must be set. - :attribute:`~pygotham.events.models.Event.registration_begins` must be earlier than the current date and time. - :attribute:`~pygotham.events.models.Event.registration_ends` must be ``None`` or later than the current date and time. """ if self.registration_closed: return False if not self.registration_url: return False now = arrow.utcnow().to(current_app.config['TIME_ZONE']).naive begins = self.registration_begins if not begins or now < begins.naive: return False ends = self.registration_ends if ends and ends.naive < now: return False return True @property def schedule_is_published(self): """Return whether the schedule for an event is published.""" now = arrow.utcnow().to(current_app.config['TIME_ZONE']).naive talk_schedule_begins = self.talk_schedule_begins if not talk_schedule_begins or talk_schedule_begins.naive > now: return False return True @property def talks_are_published(self): """Return whether the talk list for an event is published.""" now = arrow.utcnow().to(current_app.config['TIME_ZONE']).naive if not self.talk_list_begins or self.talk_list_begins.naive > now: return False return True
class User(db.Model, UserMixin): """User.""" __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), info={'label': 'Name'}) email = db.Column(db.String(255), unique=True, nullable=False) password = db.Column(db.String(130)) active = db.Column(db.Boolean) confirmed_at = db.Column(db.DateTime) last_login_at = db.Column(db.DateTime) current_login_at = db.Column(db.DateTime) last_login_ip = db.Column(db.String(100)) current_login_ip = db.Column(db.String(100)) login_count = db.Column(db.Integer) registered_at = db.Column(db.DateTime) bio = db.Column(db.Text) twitter_handle = db.Column(db.String(15)) roles = db.relationship( 'Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'), ) def __str__(self): """Return a printable representation.""" return self.name or self.email def __eq__(self, other): return self.id == getattr(other, 'id', None) def __hash__(self): return id(self) def __ne__(self, other): return self.id != getattr(other, 'id', None) @cached_property def accepted_talks(self): """Return the user's accepted talks.""" return Talk.query.current.filter(Talk.status == 'accepted', Talk.user == self).order_by(Talk.name) @cached_property def has_accepted_talks(self): """Return whether the user has accepted talks.""" return self.accepted_talks.count() > 0 @cached_property def is_volunteer(self): """Return whether the user has signed up to volunteer.""" volunteers = Volunteer.query.filter( Volunteer.event_id == g.current_event.id, Volunteer.user_id == self.id, ) return db.session.query(volunteers.exists()).scalar()
class Talk(db.Model): """Talk.""" __tablename__ = 'talks' query_class = EventQuery id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) description = db.Column(db.Text, nullable=False) status = db.Column( db.Enum('draft', 'submitted', 'accepted', 'rejected', name='status'), default='draft', nullable=False, ) level = db.Column( db.Enum('novice', 'intermediate', 'advanced', name='level'), nullable=False, ) type = db.Column( db.Enum('talk', 'tutorial', name='type'), nullable=False, ) duration_id = db.Column(db.ForeignKey('durations.id'), nullable=False) duration = db.relationship('Duration') recording_release = db.Column(db.Boolean, nullable=True) abstract = db.Column(db.Text) additional_requirements = db.Column(db.Text) objectives = db.Column(db.Text) outline = db.Column(db.Text) target_audience = db.Column(db.Text) event_id = db.Column( db.Integer, db.ForeignKey('events.id'), nullable=False, ) event = db.relationship( 'Event', backref=db.backref('talks', lazy='dynamic'), ) category_id = db.Column(db.Integer, db.ForeignKey('categories.id'), nullable=True) category = db.relationship( 'Category', backref=db.backref('talks', lazy='dynamic'), ) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) user = db.relationship('User', backref=db.backref('talks', lazy='dynamic')) video_url = db.Column(db.String(255)) def __str__(self): """Return a printable representation.""" return self.name @property def is_accepted(self): """Return whether the instance is accepted.""" return self.status == 'accepted' @property def slug(self): """Return a slug for the instance.""" return slugify(self.name, max_length=25)