class UserFavouriteSession(db.Model, Timestamp): __tablename__ = 'user_favourite_sessions' __table_args__ = (db.UniqueConstraint('session_id', 'user_id', name='uq_session_user'), ) id = db.Column(db.Integer, primary_key=True) session_id = db.Column(db.Integer, db.ForeignKey('sessions.id', ondelete='CASCADE')) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE')) session = db.relationship('Session', backref='favourites') user = db.relationship('User', backref='favourite_sessions') @property def safe_user(self): from app.api.helpers.permission_manager import require_current_user from app.models.user import User if not self.user_id: return None can_access = require_current_user() and ( current_user.is_staff or current_user.id == self.user_id) if not self.user.is_profile_public and not can_access: name = self.user.anonymous_name return User( id=self.user.id, email='*****@*****.**', public_name=name, ) return self.user
class Permission(db.Model): """Role-Service Permissions""" __tablename__ = 'permissions' __table_args__ = (db.UniqueConstraint('role_id', 'service_id', name='role_service_uc'), ) id = db.Column(db.Integer, primary_key=True) role_id = db.Column(db.Integer, db.ForeignKey('roles.id', ondelete='CASCADE')) role = db.relationship('Role') service_id = db.Column(db.Integer, db.ForeignKey('services.id', ondelete='CASCADE')) service = db.relationship('Service') can_create = db.Column(db.Boolean, nullable=False, default=True) can_read = db.Column(db.Boolean, nullable=False, default=True) can_update = db.Column(db.Boolean, nullable=False, default=True) can_delete = db.Column(db.Boolean, nullable=False, default=True) def __repr__(self): return f'<Perm {self.role!r} for {self.service!r}>'
class Feedback(SoftDeletionModel): """Feedback model class""" __tablename__ = 'feedback' __table_args__ = (db.UniqueConstraint('session_id', 'user_id', name='session_user_uc'), ) id = db.Column(db.Integer, primary_key=True) rating = db.Column(db.Float, nullable=False) comment = db.Column(db.String, nullable=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE')) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) session_id = db.Column(db.Integer, db.ForeignKey('sessions.id', ondelete='CASCADE')) def __init__(self, **kwargs): super().__init__(**kwargs) # TODO(Areeb): Test rating rounding on __init__ rating = float(kwargs.get('rating')) self.rating = round(rating * 2, 0) / 2 # Rounds to nearest 0.5 def __repr__(self): return '<Feedback %r>' % self.rating
class UsersEventsRoles(db.Model): __tablename__ = 'users_events_roles' __table_args__ = ( db.UniqueConstraint( 'user_id', 'event_id', 'role_id', name='uq_uer_user_event_role' ), ) id = db.Column(db.Integer, primary_key=True) event_id = db.Column( db.Integer, db.ForeignKey('events.id', ondelete='CASCADE'), nullable=False ) user_id = db.Column( db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False ) user = db.relationship("User") role_id = db.Column( db.Integer, db.ForeignKey('roles.id', ondelete='CASCADE'), nullable=False ) role = db.relationship("Role") def __repr__(self): return f'<UER {self.user!r}:{self.event_id!r}:{self.role!r}>'
class TicketTag(SoftDeletionModel): """ Tags to group tickets """ __tablename__ = 'ticket_tag' __table_args__ = (db.UniqueConstraint('name', 'event_id', name='unique_ticket_tag'), ) id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) event = db.relationship('Event', backref='ticket_tags') def __init__(self, name=None, event_id=None, deleted_at=None): self.name = name self.event_id = event_id self.deleted_at = deleted_at def __repr__(self): return '<TicketTag %r>' % self.name def __str__(self): return self.__repr__()
class BookedTicket(db.Model): __tablename__ = 'booked_ticket' __table_args__ = (db.UniqueConstraint('user_id', 'ticket_id', name='user_ticket_uc'), ) id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE')) user = db.relationship('User', backref='booked_tickets') ticket_id = db.Column(db.Integer, db.ForeignKey('ticket.id', ondelete='CASCADE')) ticket = db.relationship('Ticket', backref='booked_tickets') quantity = db.Column(db.Integer) def __init__(self, user, ticket, quantity): self.user = user self.ticket = ticket self.quantity = quantity def __repr__(self): return '<BookedTicket %r by %r' % ( self.ticket, self.user, ) def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): return self.ticket
class UsersGroupsRoles(db.Model): __tablename__ = 'users_groups_roles' __table_args__ = ( db.UniqueConstraint( 'user_id', 'group_id', 'role_id', name='uq_ugr_user_group_role' ), db.UniqueConstraint( 'email', 'group_id', 'role_id', name='uq_ugr_email_group_role' ), ) id = db.Column(db.Integer, primary_key=True) token = db.Column(db.String, default=generate_hash) email = db.Column(CIText, nullable=False) accepted = db.Column( db.Boolean, default=False, nullable=False, server_default='False' ) group_id = db.Column( db.Integer, db.ForeignKey('groups.id', ondelete='CASCADE'), nullable=False ) user_id = db.Column( db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=True ) user = db.relationship("User") role_id = db.Column( db.Integer, db.ForeignKey('roles.id', ondelete='CASCADE'), nullable=False ) role = db.relationship("Role") def __repr__(self): return f'<UGR {self.email!r}:{self.user!r}:{self.group!r}:{self.role!r}>' def send_invite(self): """ Send mail to invitee """ group = self.group role = self.role frontend_url = get_settings()['frontend_url'] link = f"{frontend_url}/group-invites?token={self.token}" if group.user != current_user: raise ForbiddenError({'pointer': 'group'}, 'Owner access is required.') send_email_group_role_invite(self.email, role.name, group.name, link)
class Permission(db.Model): """Role-Service Permissions """ __tablename__ = 'permissions' __table_args__ = (db.UniqueConstraint('role_id', 'service_id', name='role_service_uc'), ) id = db.Column(db.Integer, primary_key=True) role_id = db.Column(db.Integer, db.ForeignKey('roles.id', ondelete='CASCADE')) role = db.relationship('Role') service_id = db.Column(db.Integer, db.ForeignKey('services.id', ondelete='CASCADE')) service = db.relationship('Service') can_create = db.Column(db.Boolean, nullable=False) can_read = db.Column(db.Boolean, nullable=False) can_update = db.Column(db.Boolean, nullable=False) can_delete = db.Column(db.Boolean, nullable=False) def __init__(self, role, service, can_create=True, can_read=True, can_update=True, can_delete=True): self.role = role self.service = service self.can_create = can_create self.can_read = can_read self.can_update = can_update self.can_delete = can_delete def __repr__(self): return '<Perm %r for %r>' % ( self.role, self.service, ) def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): return 'Perm %r for %r' % ( self.role, self.service, )
class UserFollowGroup(db.Model, Timestamp): __tablename__ = 'user_follow_groups' __table_args__ = (db.UniqueConstraint('group_id', 'user_id', name='uq_group_user'), ) id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False) user = db.relationship('User', backref='followed_groups') group_id = db.Column(db.Integer, db.ForeignKey('groups.id', ondelete='CASCADE'), nullable=False) group = db.relationship("Group", backref='followers')
class Organisation(ModelMixin): """ Organisation table definition """ _tablename_ = 'organisations' __table_args__ = (db.UniqueConstraint('name', name='organisation_unique_name'),) # fields of the Organisation table id = db.Column(UUID(as_uuid=True), primary_key=True, server_default=sa_text("uuid_generate_v4()")) name = db.Column(db.String(256), nullable=False) date_created = db.Column(db.DateTime, default=db.func.current_timestamp()) admins = relationship('User', secondary='organisation_admins', backref='organisation_admin') members = relationship('User', secondary='organisation_members', backref='organisations') def __init__(self, name): """ initialize with name, member and namespace """ self.name = name
class PlatformConfigModel(db.Model): __tablename__ = 'platform_config' __table_args__ = (db.UniqueConstraint('platform_id', 'id_config', name='unq_config_platform'), { 'schema': cfg_db_schema }) id = db.Column(db.Integer, primary_key=True) platform_id = db.Column(db.Integer, db.ForeignKey(cfg_db_schema + '.platform.id'), nullable=False) id_config = db.Column(db.Integer, nullable=False) name = db.Column(db.String(128), unique=True, nullable=False) value = db.Column(db.String(256), nullable=False) def __repr__(self): return "<PlatformConfig %r val: %r>" % (self.name, self.value)
class Feedback(SoftDeletionModel): """Feedback model class""" __tablename__ = 'feedback' __table_args__ = (db.UniqueConstraint('session_id', 'user_id', name='session_user_uc'), ) id = db.Column(db.Integer, primary_key=True) rating = db.Column(db.Float, nullable=False) comment = db.Column(db.String, nullable=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE')) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) session_id = db.Column(db.Integer, db.ForeignKey('sessions.id', ondelete='CASCADE')) def __init__( self, rating=None, comment=None, event_id=None, user_id=None, session_id=None, deleted_at=None, ): rating = float(rating) self.rating = round(rating * 2, 0) / 2 # Rounds to nearest 0.5 self.comment = comment self.event_id = event_id self.user_id = user_id self.session_id = session_id self.deleted_at = deleted_at def __repr__(self): return '<Feedback %r>' % self.rating def __str__(self): return self.__repr__() @property def serialize(self): """Return object data in easily serializable format""" return {'id': self.id, 'rating': self.rating, 'comment': self.comment}
class UserTokenBlackListTime(db.Model): """user token blacklist time model class""" __tablename__ = 'user_token_blacklist_time' __table_args__ = (db.UniqueConstraint('user_id', name='user_blacklist_time_uc'),) id = db.Column(db.Integer, primary_key=True) created_at = db.Column(db.DateTime(timezone=True), default=sql_func.now(), nullable=False) blacklisted_at = db.Column(db.DateTime(timezone=True), default=sql_func.now(), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False) user = db.relationship("User", backref="token_blacklist_times", foreign_keys=[user_id]) def __init__(self, user_id=None, created_at=None, blacklisted_at=None): self.user_id = user_id self.created_at = created_at self.blacklisted_at = blacklisted_at def __str__(self): return '<TokenBlackListTime User %s blacklisted at %s>' % (self.user, self.blacklisted_at)
class UserTokenBlackListTime(db.Model): """user token blacklist time model class""" __tablename__ = 'user_token_blacklist_time' __table_args__ = (db.UniqueConstraint('user_id', name='user_blacklist_time_uc'),) id: int = db.Column(db.Integer, primary_key=True) created_at: datetime = db.Column( db.DateTime(timezone=True), default=sql_func.now(), nullable=False ) blacklisted_at: datetime = db.Column( db.DateTime(timezone=True), default=sql_func.now(), nullable=False ) user_id: int = db.Column( db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False ) user = db.relationship( "User", backref="token_blacklist_times", foreign_keys=[user_id] )
class Namespace(ModelMixin): """ Namespace table definition """ _tablename_ = 'namespace' __table_args__ = (db.UniqueConstraint('name', name='namespace_unique_name'), ) # fields of the Namespace table id = db.Column(UUID(as_uuid=True), primary_key=True, server_default=sa_text("uuid_generate_v4()")) name = db.Column(db.String(256), nullable=False) organisation_id = db.Column( "organisation_id", UUID(as_uuid=True), db.ForeignKey(Organisation.id, ondelete='CASCADE')) date_created = db.Column(db.DateTime, default=db.func.current_timestamp()) def __init__(self, name, organisation_id): """ initialize with name and org_id """ self.name = name self.organisation_id = organisation_id
class Speaker(SoftDeletionModel, Timestamp): """Speaker model class""" __tablename__ = 'speaker' __table_args__ = ( db.UniqueConstraint( 'event_id', 'email', 'deleted_at', name='uq_speaker_event_email' ), db.Index('speaker_event_idx', 'event_id'), ) id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) photo_url = db.Column(db.String) thumbnail_image_url = db.Column(db.String) small_image_url = db.Column(db.String) icon_image_url = db.Column(db.String) short_biography = db.Column(db.Text) long_biography = db.Column(db.Text) speaking_experience = db.Column(db.Text) email = db.Column(CIText) mobile = db.Column(db.String) website = db.Column(db.String) twitter = db.Column(db.String) facebook = db.Column(db.String) github = db.Column(db.String) mastodon = db.Column(db.String) linkedin = db.Column(db.String) instagram = db.Column(db.String) organisation = db.Column(db.String) is_featured = db.Column(db.Boolean, default=False) is_email_overridden = db.Column( db.Boolean, default=False, nullable=False, server_default='False' ) position = db.Column(db.String) country = db.Column(db.String) city = db.Column(db.String) address = db.Column(db.String) gender = db.Column(db.String) order = db.Column(db.Integer, default=0, nullable=False) heard_from = db.Column(db.String) sponsorship_required = db.Column(db.Text) complex_field_values = db.Column(db.JSON) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL')) @staticmethod def get_service_name(): return 'speaker' def __repr__(self): return '<Speaker %r>' % self.name def __setattr__(self, name, value): if ( name == 'short_biography' or name == 'long_biography' or name == 'speaking_experience' or name == 'sponsorship_required' ): super().__setattr__(name, clean_html(clean_up_string(value))) else: super().__setattr__(name, value)
class Event(db.Model): """Event object table""" __tablename__ = 'events' __versioned__ = {'exclude': ['schedule_published_on', 'created_at']} id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String) name = db.Column(db.String, nullable=False) event_url = db.Column(db.String) email = db.Column(db.String) logo = db.Column(db.String) start_time = db.Column(db.DateTime, nullable=False) end_time = db.Column(db.DateTime, nullable=False) timezone = db.Column(db.String, nullable=False, default="UTC") latitude = db.Column(db.Float) longitude = db.Column(db.Float) location_name = db.Column(db.String) searchable_location_name = db.Column(db.String) description = db.Column(db.Text) background_url = db.Column(db.String) thumbnail = db.Column(db.String) large = db.Column(db.String) icon = db.Column(db.String) organizer_name = db.Column(db.String) show_map = db.Column(db.Integer) organizer_description = db.Column(db.String) has_session_speakers = db.Column(db.Boolean, default=False) track = db.relationship('Track', backref="event") microlocation = db.relationship('Microlocation', backref="event") session = db.relationship('Session', backref="event") speaker = db.relationship('Speaker', backref="event") sponsor = db.relationship('Sponsor', backref="event") users = db.relationship("EventsUsers", backref="event") roles = db.relationship("UsersEventsRoles", backref="event") role_invites = db.relationship('RoleInvite', back_populates='event') privacy = db.Column(db.String, default="public") state = db.Column(db.String, default="Draft") type = db.Column(db.String) topic = db.Column(db.String) sub_topic = db.Column(db.String) ticket_url = db.Column(db.String) db.UniqueConstraint('track.name') code_of_conduct = db.Column(db.String) schedule_published_on = db.Column(db.DateTime) ticket_include = db.Column(db.Boolean, default=False) deleted_at = db.Column(db.DateTime) payment_country = db.Column(db.String) payment_currency = db.Column(db.String) paypal_email = db.Column(db.String) tax_allow = db.Column(db.Boolean, default=False) pay_by_paypal = db.Column(db.Boolean, default=False) pay_by_stripe = db.Column(db.Boolean, default=False) pay_by_cheque = db.Column(db.Boolean, default=False) pay_by_bank = db.Column(db.Boolean, default=False) pay_onsite = db.Column(db.Boolean, default=False) cheque_details = db.Column(db.String) bank_details = db.Column(db.String) onsite_details = db.Column(db.String) created_at = db.Column(db.DateTime) discount_code_id = db.Column(db.Integer, db.ForeignKey('discount_codes.id', ondelete='SET NULL'), nullable=True, default=None) discount_code = db.relationship('DiscountCode', backref='events', foreign_keys=[discount_code_id]) def __init__(self, name=None, logo=None, start_time=None, end_time=None, timezone='UTC', latitude=None, longitude=None, location_name=None, email=None, description=None, event_url=None, background_url=None, thumbnail=None, large=None, icon=None, organizer_name=None, organizer_description=None, state=None, type=None, privacy=None, topic=None, sub_topic=None, ticket_url=None, copyright=None, code_of_conduct=None, schedule_published_on=None, has_session_speakers=False, show_map=1, searchable_location_name=None, ticket_include=None, deleted_at=None, payment_country=None, payment_currency=None, paypal_email=None, call_for_papers=None, pay_by_paypal=None, pay_by_stripe=None, pay_by_cheque=None, identifier=None, pay_by_bank=None, pay_onsite=None, cheque_details=None, bank_details=None, discount_code_id=None, onsite_details=None): self.name = name self.logo = logo self.email = email self.start_time = start_time self.end_time = end_time self.timezone = timezone self.latitude = latitude self.longitude = longitude self.location_name = location_name self.description = clean_up_string(description) self.event_url = event_url self.background_url = background_url self.thumbnail = thumbnail self.large = large self.icon = icon self.organizer_name = organizer_name self.organizer_description = clean_up_string(organizer_description) self.state = state self.show_map = show_map self.privacy = privacy self.type = type self.topic = topic self.copyright = copyright self.sub_topic = sub_topic self.ticket_url = ticket_url self.code_of_conduct = code_of_conduct self.schedule_published_on = schedule_published_on self.has_session_speakers = has_session_speakers self.searchable_location_name = searchable_location_name self.ticket_include = ticket_include self.deleted_at = deleted_at self.payment_country = payment_country self.payment_currency = payment_currency self.paypal_email = paypal_email self.call_for_papers = call_for_papers self.pay_by_paypal = pay_by_paypal self.pay_by_stripe = pay_by_stripe self.pay_by_cheque = pay_by_cheque self.pay_by_bank = pay_by_bank self.pay_onsite = pay_onsite self.identifier = get_new_event_identifier() self.cheque_details = cheque_details self.bank_details = bank_details self.onsite_details = onsite_details self.discount_code_id = discount_code_id self.created_at = datetime.utcnow() def __repr__(self): return '<Event %r>' % self.name def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): return self.name def __setattr__(self, name, value): if name == 'organizer_description' or name == 'description' or name == 'code_of_conduct': super(Event, self).__setattr__(name, clean_html(clean_up_string(value))) else: super(Event, self).__setattr__(name, value) def notification_settings(self, user_id): try: return EmailNotification.query.filter_by( user_id=(login.current_user.id if not user_id else int(user_id) )).filter_by(event_id=self.id).first() except: return None def has_staff_access(self, user_id): """does user have role other than attendee""" access = False for _ in self.roles: if _.user_id == (login.current_user.id if not user_id else int(user_id)): if _.role.name != ATTENDEE: access = True return access def get_staff_roles(self): """returns only roles which are staff i.e. not attendee""" return [role for role in self.roles if role.role.name != ATTENDEE] @property def serialize(self): """Return object data in easily serializable format""" return { 'id': self.id, 'name': self.name, 'logo': self.logo, 'begin': DateFormatter().format_date(self.start_time), 'end': DateFormatter().format_date(self.end_time), 'timezone': self.timezone, 'latitude': self.latitude, 'longitude': self.longitude, 'location_name': self.location_name, 'email': self.email, 'description': self.description, 'event_url': self.event_url, 'background_url': self.background_url, 'thumbnail': self.thumbnail, 'large': self.large, 'icon': self.icon, 'organizer_name': self.organizer_name, 'organizer_description': self.organizer_description, 'has_session_speakers': self.has_session_speakers, 'privacy': self.privacy, 'ticket_url': self.ticket_url, 'code_of_conduct': self.code_of_conduct, 'schedule_published_on': self.schedule_published_on }
class Ticket(db.Model): __tablename__ = 'ticket' __table_args__ = (db.UniqueConstraint('name', 'event_id', name='name_event_uc'), ) id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String) description = db.Column(db.String) description_toggle = db.Column(db.Boolean) type = db.Column(db.String) quantity = db.Column(db.Integer) price = db.Column(db.Float) absorb_fees = db.Column(db.Boolean) sales_start = db.Column(db.DateTime) sales_end = db.Column(db.DateTime) hide = db.Column(db.Boolean) min_order = db.Column(db.Integer) max_order = db.Column(db.Integer) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) event = db.relationship('Event', backref='tickets') tags = db.relationship('TicketTag', secondary=ticket_tags_table, backref='tickets') order_ticket = db.relationship('OrderTicket', backref="ticket", passive_deletes=True) def __init__(self, name=None, event=None, type=None, sales_start=None, sales_end=None, hide=False, description=None, description_toggle=True, quantity=100, price=0, min_order=1, max_order=10, absorb_fees=False, tags=None): if tags is None: tags = [] self.name = name self.quantity = quantity self.type = type self.event = event self.description = description self.description_toggle = description_toggle self.price = price self.sales_start = sales_start self.sales_end = sales_end self.hide = hide self.min_order = min_order self.max_order = max_order self.tags = tags self.absorb_fees = absorb_fees def has_order_tickets(self): """Returns True if ticket has already placed orders. Else False. """ from app.helpers.helpers import get_count orders = Order.id.in_( OrderTicket.query.with_entities( OrderTicket.order_id).filter_by(ticket_id=self.id).all()) count = get_count( Order.query.filter(orders).filter(Order.status != 'deleted')) # Count is zero if no completed orders are present return bool(count > 0) def has_completed_order_tickets(self): """Returns True if ticket has already placed orders. Else False. """ order_tickets = OrderTicket.query.filter_by(ticket_id=self.id) count = 0 for order_ticket in order_tickets: order = Order.query.filter_by(id=order_ticket.order_id).first() if order.status == "completed" or order.status == "placed": count += 1 return bool(count > 0) def tags_csv(self): """Return list of Tags in CSV. """ tag_names = [tag.name for tag in self.tags] return ','.join(tag_names) def __repr__(self): return '<Ticket %r>' % self.name def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): return self.name @property def serialize(self): """Return object data in easily serializeable format""" data = { 'id': self.id, 'name': self.name, 'quantity': self.quantity, 'type': self.type, 'description_visibility': self.description_toggle, 'description': self.description, 'price': self.price, 'sales_start_date': self.sales_start.strftime('%m/%d/%Y') if self.sales_start else '', 'sales_start_time': self.sales_start.strftime('%H:%M') if self.sales_start else '', 'sales_end_date': self.sales_end.strftime('%m/%d/%Y') if self.sales_end else '', 'sales_end_time': self.sales_end.strftime('%H:%M') if self.sales_end else '', 'ticket_visibility': self.hide, 'min_order': self.min_order, 'max_order': self.max_order, 'tags_string': '', 'has_orders': self.has_order_tickets(), 'has_completed_orders': self.has_completed_order_tickets(), 'absorb_fees': self.absorb_fees } tags = [] for tag in self.tags: tags.append(tag.name) data['tags'] = ",".join(tags) return data
class Ticket(SoftDeletionModel): __tablename__ = 'tickets' __table_args__ = (db.UniqueConstraint('name', 'event_id', 'deleted_at', name='name_event_deleted_at_uc'), ) id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) description = db.Column(db.String) is_description_visible = db.Column(db.Boolean) type = db.Column(db.String, nullable=False) quantity = db.Column(db.Integer) position = db.Column(db.Integer) price = db.Column(db.Float) min_price = db.Column(db.Float, default=0, nullable=False) max_price = db.Column(db.Float) is_fee_absorbed = db.Column(db.Boolean) sales_starts_at = db.Column(db.DateTime(timezone=True), nullable=False) sales_ends_at = db.Column(db.DateTime(timezone=True), nullable=False) is_hidden = db.Column(db.Boolean) min_order = db.Column(db.Integer) max_order = db.Column(db.Integer) is_checkin_restricted = db.Column(db.Boolean) auto_checkin_enabled = db.Column(db.Boolean) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) event = db.relationship('Event', backref='tickets_') tags = db.relationship('TicketTag', secondary=ticket_tags_table, backref='tickets') order_ticket = db.relationship('OrderTicket', backref="ticket", passive_deletes=True) access_codes = db.relationship('AccessCode', secondary=access_codes_tickets, backref='tickets') discount_codes = db.relationship('DiscountCode', secondary=discount_codes_tickets, backref="tickets") def __init__(self, name=None, event_id=None, type=None, sales_starts_at=None, sales_ends_at=None, is_hidden=False, description=None, is_description_visible=True, is_checkin_restricted=True, auto_checkin_enabled=False, quantity=100, position=1, price=0, min_order=1, max_order=10, min_price=0, max_price=0, is_fee_absorbed=False, tags=[], access_codes=[], discount_codes=[]): self.name = name self.quantity = quantity self.position = position self.type = type self.event_id = event_id self.description = description self.is_description_visible = is_description_visible self.is_checkin_restricted = is_checkin_restricted self.auto_checkin_enabled = auto_checkin_enabled self.price = price self.min_price = min_price self.max_price = max_price self.sales_starts_at = sales_starts_at self.sales_ends_at = sales_ends_at self.is_hidden = is_hidden self.min_order = min_order self.max_order = max_order self.tags = tags self.is_fee_absorbed = is_fee_absorbed self.access_codes = access_codes self.discount_codes = discount_codes def has_order_tickets(self): """Returns True if ticket has already placed orders. Else False. """ from app.api.helpers.db import get_count orders = Order.id.in_( OrderTicket.query.with_entities( OrderTicket.order_id).filter_by(ticket_id=self.id).all()) count = get_count( Order.query.filter(orders).filter(Order.status != 'deleted')) # Count is zero if no completed orders are present return bool(count > 0) def has_completed_order_tickets(self): """Returns True if ticket has already placed orders. Else False. """ order_tickets = OrderTicket.query.filter_by(ticket_id=self.id) count = 0 for order_ticket in order_tickets: order = Order.query.filter_by(id=order_ticket.order_id).first() if order.status == "completed" or order.status == "placed": count += 1 return bool(count > 0) def tags_csv(self): """Return list of Tags in CSV. """ tag_names = [tag.name for tag in self.tags] return ','.join(tag_names) def __repr__(self): return '<Ticket %r>' % self.name def __str__(self): return self.__repr__() @property def serialize(self): """Return object data in easily serializable format""" data = { 'id': self.id, 'name': self.name, 'quantity': self.quantity, 'position': self.position, 'type': self.type, 'description_visibility': self.is_description_visible, 'description': self.description, 'price': self.price, 'sales_start_date': self.sales_starts_at.strftime('%m/%d/%Y') if self.sales_starts_at else '', 'sales_starts_at': self.sales_starts_at.strftime('%H:%M') if self.sales_starts_at else '', 'sales_end_date': self.sales_ends_at.strftime('%m/%d/%Y') if self.sales_ends_at else '', 'sales_ends_at': self.sales_ends_at.strftime('%H:%M') if self.sales_ends_at else '', 'ticket_visibility': self.hide, 'min_order': self.min_order, 'max_order': self.max_order, 'tags_string': '', 'has_orders': self.has_order_tickets(), 'has_completed_orders': self.has_completed_order_tickets(), 'is_fee_absorbed': self.is_fee_absorbed } tags = [] for tag in self.tags: tags.append(tag.name) data['tags'] = ",".join(tags) return data
class Event(SoftDeletionModel): """Event object table""" __tablename__ = 'events' __versioned__ = {'exclude': ['schedule_published_on', 'created_at']} id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String, default=get_new_event_identifier) name = db.Column(db.String, nullable=False) external_event_url = db.Column(db.String) logo_url = db.Column(db.String) starts_at = db.Column(db.DateTime(timezone=True), nullable=False) ends_at = db.Column(db.DateTime(timezone=True), nullable=False) timezone = db.Column(db.String, nullable=False, default="UTC") online = db.Column(db.Boolean, nullable=False, default=False, server_default='False') latitude = db.Column(db.Float) longitude = db.Column(db.Float) location_name = db.Column(db.String) searchable_location_name = db.Column(db.String) is_featured = db.Column(db.Boolean, default=False, nullable=False) is_promoted = db.Column(db.Boolean, default=False, nullable=False) description = db.Column(db.Text) original_image_url = db.Column(db.String) thumbnail_image_url = db.Column(db.String) large_image_url = db.Column(db.String) show_remaining_tickets = db.Column(db.Boolean, default=False, nullable=False) icon_image_url = db.Column(db.String) owner_name = db.Column(db.String) is_map_shown = db.Column(db.Boolean) has_owner_info = db.Column(db.Boolean) owner_description = db.Column(db.String) is_sessions_speakers_enabled = db.Column(db.Boolean, default=False) track = db.relationship('Track', backref="event") microlocation = db.relationship('Microlocation', backref="event") session = db.relationship('Session', backref="event") speaker = db.relationship('Speaker', backref="event") sponsor = db.relationship('Sponsor', backref="event") tickets = db.relationship('Ticket', backref="event_") tags = db.relationship('TicketTag', backref='events') roles = db.relationship("UsersEventsRoles", backref="event") role_invites = db.relationship('RoleInvite', back_populates='event') custom_form = db.relationship('CustomForms', backref="event") faqs = db.relationship('Faq', backref="event") feedbacks = db.relationship('Feedback', backref="event") attendees = db.relationship('TicketHolder', backref="event") privacy = db.Column(db.String, default="public") state = db.Column(db.String, default="Draft") event_type_id = db.Column( db.Integer, db.ForeignKey('event_types.id', ondelete='CASCADE')) event_topic_id = db.Column( db.Integer, db.ForeignKey('event_topics.id', ondelete='CASCADE')) event_sub_topic_id = db.Column( db.Integer, db.ForeignKey('event_sub_topics.id', ondelete='CASCADE')) ticket_url = db.Column(db.String) db.UniqueConstraint('track.name') code_of_conduct = db.Column(db.String) schedule_published_on = db.Column(db.DateTime(timezone=True)) is_ticketing_enabled = db.Column(db.Boolean, default=False) is_donation_enabled = db.Column(db.Boolean, default=False) is_ticket_form_enabled = db.Column(db.Boolean, default=True, nullable=False) payment_country = db.Column(db.String) payment_currency = db.Column(db.String) paypal_email = db.Column(db.String) is_tax_enabled = db.Column(db.Boolean, default=False) is_billing_info_mandatory = db.Column(db.Boolean, default=False, nullable=False) can_pay_by_paypal = db.Column(db.Boolean, default=False, nullable=False) can_pay_by_stripe = db.Column(db.Boolean, default=False, nullable=False) can_pay_by_cheque = db.Column(db.Boolean, default=False, nullable=False) can_pay_by_bank = db.Column(db.Boolean, default=False, nullable=False) can_pay_onsite = db.Column(db.Boolean, default=False, nullable=False) can_pay_by_omise = db.Column(db.Boolean, default=False, nullable=False) can_pay_by_alipay = db.Column(db.Boolean, default=False, nullable=False) can_pay_by_paytm = db.Column(db.Boolean, default=False, nullable=False) cheque_details = db.Column(db.String) bank_details = db.Column(db.String) onsite_details = db.Column(db.String) created_at = db.Column(db.DateTime(timezone=True), default=func.now()) pentabarf_url = db.Column(db.String) ical_url = db.Column(db.String) xcal_url = db.Column(db.String) is_sponsors_enabled = db.Column(db.Boolean, default=False) refund_policy = db.Column(db.String) is_stripe_linked = db.Column(db.Boolean, default=False) live_stream_url = db.Column(db.String) webinar_url = db.Column(db.String) discount_code_id = db.Column( db.Integer, db.ForeignKey('discount_codes.id', ondelete='CASCADE')) discount_code = db.relationship('DiscountCode', backref='events', foreign_keys=[discount_code_id]) event_type = db.relationship('EventType', backref='event', foreign_keys=[event_type_id]) event_topic = db.relationship('EventTopic', backref='event', foreign_keys=[event_topic_id]) event_sub_topic = db.relationship('EventSubTopic', backref='event', foreign_keys=[event_sub_topic_id]) owner = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "owner"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='owner_events', uselist=False, ) organizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "organizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='organizer_events', ) coorganizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "coorganizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='coorganizer_events', ) track_organizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id,' ' Role.name == "track_organizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='track_organizer_events', ) registrars = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "registrar"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='registrar_events', ) moderators = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "moderator"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='moderator_events', ) # staff users = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name != "attendee"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='events', ) def __init__(self, **kwargs): super().__init__(**kwargs) original_image_url = kwargs.get('original_image_url') self.original_image_url = (self.set_default_event_image( kwargs.get('event_topic_id')) if original_image_url is None else original_image_url) # TODO(Areeb): Test for cleaning up of these on __init__ self.description = clean_up_string(kwargs.get('description')) self.owner_description = clean_up_string( kwargs.get('owner_description')) self.code_of_conduct = clean_up_string(kwargs.get('code_of_conduct')) def __repr__(self): return '<Event %r>' % self.name def __setattr__(self, name, value): allow_link = name == 'description' if (name == 'owner_description' or name == 'description' or name == 'code_of_conduct'): super().__setattr__( name, clean_html(clean_up_string(value), allow_link=allow_link)) else: super().__setattr__(name, value) @classmethod def set_default_event_image(cls, event_topic_id): if event_topic_id is None: return None event_topic = EventTopic.query.filter_by(id=event_topic_id).first() return event_topic.system_image_url @property def fee(self): """ Returns the fee as a percentage from 0 to 100 for this event """ return get_fee(self.payment_country, self.payment_currency) @property def maximum_fee(self): """ Returns the maximum fee for this event """ return get_maximum_fee(self.payment_country, self.payment_currency) def notification_settings(self, user_id): try: return (EmailNotification.query.filter_by( user_id=(login.current_user.id if not user_id else int(user_id) )).filter_by(event_id=self.id).first()) except: return None def get_average_rating(self): avg = (db.session.query(func.avg( Feedback.rating)).filter_by(event_id=self.id).scalar()) if avg is not None: avg = round(avg, 2) return avg def is_payment_enabled(self): return (self.can_pay_by_paypal or self.can_pay_by_stripe or self.can_pay_by_omise or self.can_pay_by_alipay or self.can_pay_by_cheque or self.can_pay_by_bank or self.can_pay_onsite or self.can_pay_by_paytm) @property def average_rating(self): return self.get_average_rating() def get_organizer(self): """returns organizer of an event""" for role in self.roles: if role.role.name == ORGANIZER: return role.user return None def get_owner(self): """returns owner of an event""" for role in self.roles: if role.role.name == OWNER: return role.user return None def has_staff_access(self, user_id): """does user have role other than attendee""" for _ in self.roles: if _.user_id == (login.current_user.id if not user_id else int(user_id)): if _.role.name != ATTENDEE: return True return False def get_staff_roles(self): """returns only roles which are staff i.e. not attendee""" return [role for role in self.roles if role.role.name != ATTENDEE] def as_dict(self): return {c.name: getattr(self, c.name) for c in self.__table__.columns} @property def tickets_sold_object(self): obj = (db.session.query(Order.event_id).filter_by( event_id=self.id, status='completed').join(TicketHolder)) return obj def calc_tickets_sold_count(self): """Calculate total number of tickets sold for the event""" return self.tickets_sold_object.count() def calc_tickets_sold_prev_month(self): """Calculate tickets sold in the previous month""" previous_month = datetime.now().month - 1 return self.tickets_sold_object.filter_by( completed_at=previous_month).count() def calc_total_tickets_count(self): """Calculate total available tickets for all types of tickets""" total_available = (db.session.query(func.sum( Ticket.quantity)).filter_by(event_id=self.id).scalar()) if total_available is None: total_available = 0 return total_available def get_orders_query(self, start=None, end=None): query = Order.query.filter_by(event_id=self.id, status='completed') if start: query = query.filter(Order.completed_at > start) if end: query = query.filter(Order.completed_at < end) return query def calc_revenue(self, start=None, end=None): """Returns total revenues of all completed orders for the given event""" return (self.get_orders_query(start=start, end=end).with_entities( func.sum(Order.amount)).scalar() or 0) @property def tickets_available(self): return self.calc_total_tickets_count() @property def tickets_sold(self): return self.calc_tickets_sold_count() @property def revenue(self): return self.calc_revenue() @property def has_sessions(self): return Session.query.filter_by(event_id=self.id).count() > 0 @property def has_speakers(self): return Speaker.query.filter_by(event_id=self.id).count() > 0 @property def order_statistics(self): return Namespace(id=self.id) @property def general_statistics(self): return Namespace(id=self.id)
class Ticket(SoftDeletionModel): __tablename__ = 'tickets' __table_args__ = (db.UniqueConstraint('name', 'event_id', 'deleted_at', name='name_event_deleted_at_uc'), ) id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) description = db.Column(db.String) is_description_visible = db.Column(db.Boolean) type = db.Column(db.String, nullable=False) quantity = db.Column(db.Integer, default=100) position = db.Column(db.Integer, default=1) price = db.Column(db.Float, default=0) min_price = db.Column(db.Float, default=0, nullable=False) max_price = db.Column(db.Float, default=0) is_fee_absorbed = db.Column(db.Boolean, default=False) sales_starts_at = db.Column(db.DateTime(timezone=True), nullable=False) sales_ends_at = db.Column(db.DateTime(timezone=True), nullable=False) is_hidden = db.Column(db.Boolean, default=False) min_order = db.Column(db.Integer, default=1) max_order = db.Column(db.Integer, default=10) is_checkin_restricted = db.Column(db.Boolean) auto_checkin_enabled = db.Column(db.Boolean, default=False) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) event = db.relationship('Event', backref='tickets_') tags = db.relationship('TicketTag', secondary=ticket_tags_table, backref='tickets') order_ticket = db.relationship('OrderTicket', backref="ticket", passive_deletes=True) access_codes = db.relationship('AccessCode', secondary=access_codes_tickets, backref='tickets') discount_codes = db.relationship('DiscountCode', secondary=discount_codes_tickets, backref="tickets") def has_order_tickets(self): """Returns True if ticket has already placed orders. Else False. """ from app.api.helpers.db import get_count orders = Order.id.in_( OrderTicket.query.with_entities( OrderTicket.order_id).filter_by(ticket_id=self.id).all()) count = get_count( Order.query.filter(orders).filter(Order.status != 'deleted')) # Count is zero if no completed orders are present return bool(count > 0) def has_completed_order_tickets(self): """Returns True if ticket has already placed orders. Else False. """ order_tickets = OrderTicket.query.filter_by(ticket_id=self.id) count = 0 for order_ticket in order_tickets: order = Order.query.filter_by(id=order_ticket.order_id).first() if order.status == "completed" or order.status == "placed": count += 1 return bool(count > 0) def tags_csv(self): """Return list of Tags in CSV.""" tag_names = [tag.name for tag in self.tags] return ','.join(tag_names) @property def has_current_orders(self): return db.session.query( Order.query.join(TicketHolder).filter( TicketHolder.ticket_id == self.id, or_(Order.status == 'completed', Order.status == 'placed', Order.status == 'pending', Order.status == 'initializing')).exists()).scalar() @property def reserved_count(self): from app.api.attendees import get_sold_and_reserved_tickets_count return get_sold_and_reserved_tickets_count(self.id) @property def is_available(self): return self.reserved_count < self.quantity def raise_if_unavailable(self): if not self.is_available: raise ConflictError({'id': self.id}, f"Ticket {self.id} already sold out") def __repr__(self): return '<Ticket %r>' % self.name
class Event(SoftDeletionModel): """Event object table""" __tablename__ = 'events' __versioned__ = {'exclude': ['schedule_published_on', 'created_at']} id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String) name = db.Column(db.String, nullable=False) external_event_url = db.Column(db.String) logo_url = db.Column(db.String) starts_at = db.Column(db.DateTime(timezone=True), nullable=False) ends_at = db.Column(db.DateTime(timezone=True), nullable=False) timezone = db.Column(db.String, nullable=False, default="UTC") is_event_online = db.Column(db.Boolean, default=False) latitude = db.Column(db.Float) longitude = db.Column(db.Float) location_name = db.Column(db.String) searchable_location_name = db.Column(db.String) is_featured = db.Column(db.Boolean, default=False, nullable=False) description = db.Column(db.Text) original_image_url = db.Column(db.String) thumbnail_image_url = db.Column(db.String) large_image_url = db.Column(db.String) show_remaining_tickets = db.Column(db.Boolean, default=False, nullable=False) icon_image_url = db.Column(db.String) owner_name = db.Column(db.String) is_map_shown = db.Column(db.Boolean) has_owner_info = db.Column(db.Boolean) owner_description = db.Column(db.String) is_sessions_speakers_enabled = db.Column(db.Boolean, default=False) track = db.relationship('Track', backref="event") microlocation = db.relationship('Microlocation', backref="event") session = db.relationship('Session', backref="event") speaker = db.relationship('Speaker', backref="event") sponsor = db.relationship('Sponsor', backref="event") tickets = db.relationship('Ticket', backref="event_") tags = db.relationship('TicketTag', backref='events') roles = db.relationship("UsersEventsRoles", backref="event") role_invites = db.relationship('RoleInvite', back_populates='event') custom_form = db.relationship('CustomForms', backref="event") faqs = db.relationship('Faq', backref="event") feedbacks = db.relationship('Feedback', backref="event") attendees = db.relationship('TicketHolder', backref="event") privacy = db.Column(db.String, default="public") state = db.Column(db.String, default="Draft") event_type_id = db.Column( db.Integer, db.ForeignKey('event_types.id', ondelete='CASCADE')) event_topic_id = db.Column( db.Integer, db.ForeignKey('event_topics.id', ondelete='CASCADE')) event_sub_topic_id = db.Column( db.Integer, db.ForeignKey('event_sub_topics.id', ondelete='CASCADE')) events_orga_id = db.Column( db.Integer, db.ForeignKey('events_orga.id', ondelete='CASCADE')) ticket_url = db.Column(db.String) db.UniqueConstraint('track.name') code_of_conduct = db.Column(db.String) schedule_published_on = db.Column(db.DateTime(timezone=True)) is_ticketing_enabled = db.Column(db.Boolean, default=False) is_donation_enabled = db.Column(db.Boolean, default=False) is_ticket_form_enabled = db.Column(db.Boolean, default=True, nullable=False) payment_country = db.Column(db.String) payment_currency = db.Column(db.String) paypal_email = db.Column(db.String) is_tax_enabled = db.Column(db.Boolean, default=False) is_billing_info_mandatory = db.Column(db.Boolean, default=False) can_pay_by_paypal = db.Column(db.Boolean, default=False) can_pay_by_stripe = db.Column(db.Boolean, default=False) can_pay_by_cheque = db.Column(db.Boolean, default=False) can_pay_by_bank = db.Column(db.Boolean, default=False) can_pay_onsite = db.Column(db.Boolean, default=False) can_pay_by_omise = db.Column(db.Boolean, default=False) can_pay_by_alipay = db.Column(db.Boolean, default=False) cheque_details = db.Column(db.String) bank_details = db.Column(db.String) onsite_details = db.Column(db.String) created_at = db.Column(db.DateTime(timezone=True)) pentabarf_url = db.Column(db.String) ical_url = db.Column(db.String) xcal_url = db.Column(db.String) is_sponsors_enabled = db.Column(db.Boolean, default=False) refund_policy = db.Column( db.String, default='All sales are final. No refunds shall be issued in any case.') is_stripe_linked = db.Column(db.Boolean, default=False) discount_code_id = db.Column( db.Integer, db.ForeignKey('discount_codes.id', ondelete='CASCADE')) discount_code = db.relationship('DiscountCode', backref='events', foreign_keys=[discount_code_id]) event_type = db.relationship('EventType', backref='event', foreign_keys=[event_type_id]) event_topic = db.relationship('EventTopic', backref='event', foreign_keys=[event_topic_id]) event_sub_topic = db.relationship('EventSubTopic', backref='event', foreign_keys=[event_sub_topic_id]) events_orga = db.relationship('EventOrgaModel', backref='event', foreign_keys=[events_orga_id]) owner = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "owner"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='owner_events', uselist=False) organizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "organizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='organizer_events') coorganizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "coorganizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='coorganizer_events') track_organizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id,' ' Role.name == "track_organizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='track_organizer_events') registrars = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "registrar"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='registrar_events') moderators = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "moderator"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='moderator_events') # staff users = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name != "attendee"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='events') def __init__( self, name=None, logo_url=None, starts_at=None, ends_at=None, timezone='UTC', is_event_online=False, latitude=None, longitude=None, location_name=None, description=None, external_event_url=None, original_image_url=None, thumbnail_image_url=None, large_image_url=None, icon_image_url=None, owner_name=None, owner_description=None, state=None, event_type_id=None, privacy=None, event_topic_id=None, event_sub_topic_id=None, events_orga_id=None, ticket_url=None, copyright=None, code_of_conduct=None, schedule_published_on=None, is_sessions_speakers_enabled=False, show_remaining_tickets=False, is_ticket_form_enabled=True, is_donation_enabled=False, is_map_shown=False, has_owner_info=False, searchable_location_name=None, is_ticketing_enabled=None, deleted_at=None, payment_country=None, payment_currency=None, paypal_email=None, speakers_call=None, can_pay_by_paypal=None, can_pay_by_stripe=None, can_pay_by_cheque=None, can_pay_by_omise=None, can_pay_by_alipay=None, identifier=None, can_pay_by_bank=None, is_featured=False, can_pay_onsite=None, cheque_details=None, bank_details=None, pentabarf_url=None, ical_url=None, xcal_url=None, discount_code_id=None, onsite_details=None, is_tax_enabled=None, is_billing_info_mandatory=False, is_sponsors_enabled=None, stripe_authorization=None, tax=None, refund_policy='All sales are final. No refunds shall be issued in any case.', is_stripe_linked=False): self.name = name self.logo_url = logo_url self.starts_at = starts_at self.ends_at = ends_at self.timezone = timezone self.is_event_online = is_event_online self.latitude = latitude self.longitude = longitude self.location_name = location_name self.description = clean_up_string(description) self.external_event_url = external_event_url self.original_image_url = original_image_url self.original_image_url = self.set_default_event_image(event_topic_id) if original_image_url is None \ else original_image_url self.thumbnail_image_url = thumbnail_image_url self.large_image_url = large_image_url self.icon_image_url = icon_image_url self.owner_name = owner_name self.owner_description = clean_up_string(owner_description) self.state = state self.is_map_shown = is_map_shown self.has_owner_info = has_owner_info self.privacy = privacy self.event_type_id = event_type_id self.event_topic_id = event_topic_id self.show_remaining_tickets = show_remaining_tickets self.copyright = copyright self.event_sub_topic_id = event_sub_topic_id self.events_orga_id = events_orga_id self.ticket_url = ticket_url self.code_of_conduct = code_of_conduct self.schedule_published_on = schedule_published_on self.is_sessions_speakers_enabled = is_sessions_speakers_enabled self.searchable_location_name = searchable_location_name self.is_ticketing_enabled = is_ticketing_enabled self.deleted_at = deleted_at self.payment_country = payment_country self.payment_currency = payment_currency self.paypal_email = paypal_email self.speakers_call = speakers_call self.can_pay_by_paypal = can_pay_by_paypal self.can_pay_by_stripe = can_pay_by_stripe self.can_pay_by_cheque = can_pay_by_cheque self.can_pay_by_bank = can_pay_by_bank self.can_pay_onsite = can_pay_onsite self.can_pay_by_omise = can_pay_by_omise self.can_pay_by_alipay = can_pay_by_alipay self.is_donation_enabled = is_donation_enabled self.is_featured = is_featured self.is_ticket_form_enabled = is_ticket_form_enabled self.identifier = get_new_event_identifier() self.cheque_details = cheque_details self.bank_details = bank_details self.pentabarf_url = pentabarf_url self.ical_url = ical_url self.xcal_url = xcal_url self.onsite_details = onsite_details self.discount_code_id = discount_code_id self.created_at = datetime.now(pytz.utc) self.is_tax_enabled = is_tax_enabled self.is_billing_info_mandatory = is_billing_info_mandatory self.is_sponsors_enabled = is_sponsors_enabled self.stripe_authorization = stripe_authorization self.tax = tax self.refund_policy = refund_policy self.is_stripe_linked = is_stripe_linked def __repr__(self): return '<Event %r>' % self.name def __str__(self): return self.__repr__() def __setattr__(self, name, value): if name == 'owner_description' or name == 'description' or name == 'code_of_conduct': super(Event, self).__setattr__(name, clean_html(clean_up_string(value))) else: super(Event, self).__setattr__(name, value) @classmethod def set_default_event_image(self, event_topic_id): if event_topic_id is None: return None else: event_topic = EventTopic.query.filter_by(id=event_topic_id).first() return event_topic.system_image_url @property def fee(self): """ Returns the fee as a percentage from 0 to 100 for this event """ return get_fee(self.payment_country, self.payment_currency) @property def maximum_fee(self): """ Returns the maximum fee for this event """ return get_maximum_fee(self.payment_country, self.payment_currency) def notification_settings(self, user_id): try: return EmailNotification.query.filter_by( user_id=(login.current_user.id if not user_id else int(user_id))). \ filter_by(event_id=self.id).first() except: return None def get_average_rating(self): avg = db.session.query(func.avg( Feedback.rating)).filter_by(event_id=self.id).scalar() if avg is not None: avg = round(avg, 2) return avg def is_payment_enabled(self): return self.can_pay_by_paypal or self.can_pay_by_stripe or self.can_pay_by_omise or self.can_pay_by_alipay \ or self.can_pay_by_cheque or self.can_pay_by_bank or self.can_pay_onsite @property def average_rating(self): return self.get_average_rating() def get_organizer(self): """returns organizer of an event""" for role in self.roles: if role.role.name == ORGANIZER: return role.user return None def get_owner(self): """returns owner of an event""" for role in self.roles: if role.role.name == OWNER: return role.user return None def has_staff_access(self, user_id): """does user have role other than attendee""" for _ in self.roles: if _.user_id == (login.current_user.id if not user_id else int(user_id)): if _.role.name != ATTENDEE: return True return False def get_staff_roles(self): """returns only roles which are staff i.e. not attendee""" return [role for role in self.roles if role.role.name != ATTENDEE] def as_dict(self): return {c.name: getattr(self, c.name) for c in self.__table__.columns} def calc_tickets_sold_count(self): """Calculate total number of tickets sold for the event""" return db.session.query(Order.event_id).filter_by(event_id=self.id, status='completed').join(TicketHolder)\ .count() def calc_total_tickets_count(self): """Calculate total available tickets for all types of tickets""" total_available = db.session.query(func.sum( Ticket.quantity)).filter_by(event_id=self.id).scalar() if total_available is None: total_available = 0 return total_available def calc_revenue(self): """Returns total revenues of all completed orders for the given event""" revenue = db.session.query(func.sum(Order.amount)).filter_by( event_id=self.id, status='completed').scalar() if revenue is None: revenue = 0 return revenue def calc_monthly_revenue(self): """Returns revenue of current month. Invoice sent every 1st of the month for the previous month""" previous_month = datetime.datetime.now().month - 1 monthly_revenue = db.session.query(func.sum(Order.amount)).filter( Order.event_id == self.id, Order.completed_at.month == previous_month, Order.status == 'completed').scalar() if monthly_revenue is None: monthly_revenue = 0 return monthly_revenue @property def tickets_available(self): return self.calc_total_tickets_count() @property def tickets_sold(self): return self.calc_tickets_sold_count() @property def revenue(self): return self.calc_revenue() @property def has_sessions(self): return Session.query.filter_by(event_id=self.id).count() > 0 @property def has_speakers(self): return Speaker.query.filter_by(event_id=self.id).count() > 0
class Event(SoftDeletionModel): """Event object table""" class State: PUBLISHED = 'published' DRAFT = 'draft' class Privacy: PUBLIC = 'public' PRIVATE = 'private' __tablename__ = 'events' __versioned__ = {'exclude': ['schedule_published_on', 'created_at']} id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String, default=get_new_event_identifier, nullable=False, unique=True) name = db.Column(db.String, nullable=False) external_event_url = db.Column(db.String) logo_url = db.Column(db.String) starts_at = db.Column(db.DateTime(timezone=True), nullable=False) ends_at = db.Column(db.DateTime(timezone=True), nullable=False) timezone = db.Column(db.String, nullable=False, default="UTC") online = db.Column(db.Boolean, nullable=False, default=False, server_default='False') latitude = db.Column(db.Float) longitude = db.Column(db.Float) location_name = db.Column(db.String) searchable_location_name = db.Column(db.String) is_featured = db.Column(db.Boolean, default=False, nullable=False) is_promoted = db.Column(db.Boolean, default=False, nullable=False) is_demoted = db.Column(db.Boolean, default=False, nullable=False) is_chat_enabled = db.Column(db.Boolean, default=False, nullable=False) is_videoroom_enabled = db.Column(db.Boolean, default=False, nullable=False) is_document_enabled = db.Column(db.Boolean, default=False, nullable=False) document_links = db.Column(JSONB) chat_room_id = db.Column(db.String) description = db.Column(db.Text) after_order_message = db.Column(db.Text) original_image_url = db.Column(db.String) thumbnail_image_url = db.Column(db.String) large_image_url = db.Column(db.String) show_remaining_tickets = db.Column(db.Boolean, default=False, nullable=False) icon_image_url = db.Column(db.String) owner_name = db.Column(db.String) is_map_shown = db.Column(db.Boolean) is_oneclick_signup_enabled = db.Column(db.Boolean) has_owner_info = db.Column(db.Boolean) owner_description = db.Column(db.String) is_sessions_speakers_enabled = db.Column(db.Boolean, default=False) is_cfs_enabled = db.Column(db.Boolean, default=False, nullable=False, server_default='False') track = db.relationship('Track', backref="event") microlocation = db.relationship('Microlocation', backref="event") session = db.relationship('Session', backref="event") speaker = db.relationship('Speaker', backref="event") sponsor = db.relationship('Sponsor', backref="event") exhibitors = db.relationship('Exhibitor', backref="event") tickets = db.relationship('Ticket', backref="event_") tags = db.relationship('TicketTag', backref='events') roles = db.relationship("UsersEventsRoles", backref="event") role_invites = db.relationship('RoleInvite', back_populates='event') custom_form = db.relationship('CustomForms', backref="event") faqs = db.relationship('Faq', backref="event") feedbacks = db.relationship('Feedback', backref="event") attendees = db.relationship('TicketHolder', backref="event") privacy = db.Column(db.String, default="public") state = db.Column(db.String, default="draft") event_type_id = db.Column( db.Integer, db.ForeignKey('event_types.id', ondelete='CASCADE')) event_topic_id = db.Column( db.Integer, db.ForeignKey('event_topics.id', ondelete='CASCADE')) event_sub_topic_id = db.Column( db.Integer, db.ForeignKey('event_sub_topics.id', ondelete='CASCADE')) group_id = db.Column(db.Integer, db.ForeignKey('groups.id', ondelete='SET NULL')) is_announced = db.Column(db.Boolean, default=False, nullable=False, server_default='False') ticket_url = db.Column(db.String) db.UniqueConstraint('track.name') code_of_conduct = db.Column(db.String) schedule_published_on = db.Column(db.DateTime(timezone=True)) is_ticketing_enabled = db.Column(db.Boolean, default=False) is_donation_enabled = db.Column(db.Boolean, default=False) is_ticket_form_enabled = db.Column(db.Boolean, default=True, nullable=False) payment_country = db.Column(db.String) payment_currency = db.Column(db.String) paypal_email = db.Column(db.String) is_tax_enabled = db.Column(db.Boolean, default=False) is_billing_info_mandatory = db.Column(db.Boolean, default=False, nullable=False) can_pay_by_paypal = db.Column(db.Boolean, default=False, nullable=False, server_default='False') can_pay_by_stripe = db.Column(db.Boolean, default=False, nullable=False, server_default='False') can_pay_by_cheque = db.Column(db.Boolean, default=False, nullable=False, server_default='False') can_pay_by_bank = db.Column(db.Boolean, default=False, nullable=False, server_default='False') can_pay_by_invoice = db.Column(db.Boolean, default=False, nullable=False, server_default='False') can_pay_onsite = db.Column(db.Boolean, default=False, nullable=False, server_default='False') can_pay_by_omise = db.Column(db.Boolean, default=False, nullable=False, server_default='False') can_pay_by_alipay = db.Column(db.Boolean, default=False, nullable=False, server_default='False') can_pay_by_paytm = db.Column(db.Boolean, default=False, nullable=False, server_default='False') cheque_details = db.Column(db.String) bank_details = db.Column(db.String) onsite_details = db.Column(db.String) invoice_details = db.Column(db.String) created_at = db.Column(db.DateTime(timezone=True), default=func.now()) pentabarf_url = db.Column(db.String) ical_url = db.Column(db.String) xcal_url = db.Column(db.String) is_sponsors_enabled = db.Column(db.Boolean, default=False) refund_policy = db.Column(db.String) is_stripe_linked = db.Column(db.Boolean, default=False) completed_order_sales = db.Column(db.Integer) placed_order_sales = db.Column(db.Integer) pending_order_sales = db.Column(db.Integer) completed_order_tickets = db.Column(db.Integer) placed_order_tickets = db.Column(db.Integer) pending_order_tickets = db.Column(db.Integer) discount_code_id = db.Column( db.Integer, db.ForeignKey('discount_codes.id', ondelete='CASCADE')) discount_code = db.relationship('DiscountCode', backref='events', foreign_keys=[discount_code_id]) event_type = db.relationship('EventType', backref='event', foreign_keys=[event_type_id]) event_topic = db.relationship('EventTopic', backref='event', foreign_keys=[event_topic_id]) event_sub_topic = db.relationship('EventSubTopic', backref='event', foreign_keys=[event_sub_topic_id]) group = db.relationship('Group', backref='events', foreign_keys=[group_id]) owner = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "owner"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='owner_events', sync_backref=False, uselist=False, ) organizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "organizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='organizer_events', sync_backref=False, ) coorganizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "coorganizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='coorganizer_events', sync_backref=False, ) track_organizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id,' ' Role.name == "track_organizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='track_organizer_events', sync_backref=False, ) registrars = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "registrar"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='registrar_events', sync_backref=False, ) moderators = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "moderator"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='moderator_events', sync_backref=False, ) # staff users = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='events', sync_backref=False, ) def __init__(self, **kwargs): super().__init__(**kwargs) original_image_url = kwargs.get('original_image_url') self.original_image_url = (self.set_default_event_image( kwargs.get('event_topic_id')) if original_image_url is None else original_image_url) # TODO(Areeb): Test for cleaning up of these on __init__ self.description = clean_up_string(kwargs.get('description')) self.owner_description = clean_up_string( kwargs.get('owner_description')) self.code_of_conduct = clean_up_string(kwargs.get('code_of_conduct')) self.after_order_message = clean_up_string( kwargs.get('after_order_message')) def __repr__(self): return '<Event %r>' % self.name def __setattr__(self, name, value): allow_link = name == 'description' or 'owner_description' or 'after_order_message' if (name == 'owner_description' or name == 'description' or name == 'code_of_conduct' or name == 'after_order_message'): super().__setattr__( name, clean_html(clean_up_string(value), allow_link=allow_link)) else: super().__setattr__(name, value) @classmethod def set_default_event_image(cls, event_topic_id): if event_topic_id is None: return None event_topic = EventTopic.query.filter_by(id=event_topic_id).first() return event_topic.system_image_url @property def fee(self): """ Returns the fee as a percentage from 0 to 100 for this event """ return get_fee(self.payment_country, self.payment_currency) @property def maximum_fee(self): """ Returns the maximum fee for this event """ return get_maximum_fee(self.payment_country, self.payment_currency) def notification_settings(self, user_id): try: return (EmailNotification.query.filter_by( user_id=(login.current_user.id if not user_id else int(user_id) )).filter_by(event_id=self.id).first()) except: return None def get_average_rating(self): avg = (db.session.query(func.avg( Feedback.rating)).filter_by(event_id=self.id).scalar()) if avg is not None: avg = round(avg, 2) return avg def is_payment_enabled(self): return (self.can_pay_by_paypal or self.can_pay_by_stripe or self.can_pay_by_omise or self.can_pay_by_alipay or self.can_pay_by_cheque or self.can_pay_by_bank or self.can_pay_onsite or self.can_pay_by_paytm or self.can_pay_by_invoice) @property def average_rating(self): return self.get_average_rating() def get_owner(self): """returns owner of an event""" for role in self.roles: if role.role.name == Role.OWNER: return role.user return None def as_dict(self): return {c.name: getattr(self, c.name) for c in self.__table__.columns} @property def tickets_sold_object(self): obj = (db.session.query(Order.event_id).filter_by( event_id=self.id, status='completed').join(TicketHolder)) return obj def calc_tickets_sold_count(self): """Calculate total number of tickets sold for the event""" return self.tickets_sold_object.count() def calc_tickets_sold_prev_month(self): """Calculate tickets sold in the previous month""" previous_month = datetime.now().month - 1 return self.tickets_sold_object.filter_by( completed_at=previous_month).count() def calc_total_tickets_count(self): """Calculate total available tickets for all types of tickets""" total_available = (db.session.query(func.sum( Ticket.quantity)).filter_by(event_id=self.id).scalar()) if total_available is None: total_available = 0 return total_available def get_orders_query(self, start=None, end=None): query = Order.query.filter_by(event_id=self.id, status='completed') if start: query = query.filter(Order.completed_at > start) if end: query = query.filter(Order.completed_at < end) return query def calc_revenue(self, start=None, end=None): """Returns total revenues of all completed orders for the given event""" return (self.get_orders_query(start=start, end=end).with_entities( func.sum(Order.amount)).scalar() or 0) @property def chat_room_name(self): return re.sub('[^0-9a-zA-Z!]', '-', self.name) + '-' + self.identifier @property def tickets_available(self): return self.calc_total_tickets_count() @property def tickets_sold(self): return self.calc_tickets_sold_count() @property def revenue(self): return self.calc_revenue() @property def has_sessions(self): return Session.query.filter_by(event_id=self.id).count() > 0 @property def has_speakers(self): return Speaker.query.filter_by(event_id=self.id).count() > 0 @property def order_statistics(self): return Namespace(id=self.id) @property def general_statistics(self): return Namespace(id=self.id) @property def site_link(self): frontend_url = get_settings()['frontend_url'] return f"{frontend_url}/e/{self.identifier}" @property def organizer_site_link(self): frontend_url = get_settings()['frontend_url'] return f"{frontend_url}/events/{self.identifier}" @property def starts_at_tz(self): return self.starts_at.astimezone(pytz.timezone(self.timezone)) @property def ends_at_tz(self): return self.ends_at.astimezone(pytz.timezone(self.timezone)) @property def normalized_location(self): if self.location_name: return self.location_name elif self.online: return self.site_link return 'Location Not Announced' @property def event_location_status(self): if self.online: return 'Online (Please login to the platform to access the video room on the event page)' elif self.location_name: return self.location_name else: return 'Location Not Announced' @property def has_coordinates(self): return self.latitude and self.longitude @property def safe_video_stream(self): """Conditionally return video stream after applying access control""" stream = self.video_stream if stream and stream.user_can_access: return stream return None @property def notify_staff(self): """Who receive notifications about event""" return self.organizers + [self.owner]