"""Models for tables related to Apollo Project.""" import datetime import enum from sqlalchemy import BigInteger, Boolean, CHAR, Column, Date, Enum, ForeignKey, Integer, String, Text, TIMESTAMP,\ UniqueConstraint, func from sqlalchemy.dialects.mysql import BIT, INTEGER, VARCHAR, LONGTEXT from sqlalchemy.ext.hybrid import hybrid_method from sqlalchemy.orm import relationship, backref from sqlalchemy.sql import expression from apollo_main_db.base import Base class MarketRegionTypeEnum(enum.IntEnum): GENERAL = 0 HH = 1 NMF = 2 CHARTS_SPOTIFY = 3 CHARTS_APPLE = 4 class RegionDetailTypeEnum(enum.IntEnum): HH = 1 NMF = 2 CHARTS_SPOTIFY = 3 CHARTS_APPLE = 4 class ApolloVisitLog(Base): """Log visited tracks. Attributes: id: Primary key user_id: User ID. dsp_id: DSP ID (1 - Spotify, 2 - Apple, etc) type_id: Type ID (1 - Track, 2 - Playlist, etc) instance_id: Object instance ID. created_at: Timestamp when the entry was created. """ __tablename__ = "tblApolloVisitLog" id = Column("Id", BigInteger, primary_key=True) user_id = Column("UserId", CHAR(37), nullable=False) dsp_id = Column("DspId", Integer, nullable=False) type_id = Column("TypeId", Integer, nullable=False) instance_id = Column("InstanceId", CHAR(22), nullable=False) created_at = Column("CreatedAt", TIMESTAMP(), nullable=False, default=func.utc_timestamp()) def __str__(self) -> str: return "Apollo Visit Log Entry {} {} {} {}".format(self.user_id, self.dsp_id, self.type_id, self.instance_id) class ApolloUserOnboarding(Base): """Log user onboarding. Attributes: user_id: Primary key created_at: Timestamp when the entry was created. """ __tablename__ = 'tblApolloUserOnboarding' user_id = Column('UserId', CHAR(37), primary_key=True) is_mobile = Column('IsMobile', Boolean) is_portal = Column('IsPortal', Boolean) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) def __str__(self) -> str: return 'Apollo User Onboarding {}'.format(self.user_id) class StarredContent(Base): """Table with user's starred tracks. Attributes: id: Primary key user_id: User ID. uri: Track Uri. isrc: Track ISRC. """ __tablename__ = 'tblStarredContent' id = Column('Id', BigInteger, primary_key=True) user_id = Column('UserId', String(100)) uri = Column('Uri', String(100)) isrc = Column('ISRC', String(45), nullable=False) def __str__(self) -> str: return 'User starred {} Track {}'.format(self.user_id, self.uri) class UserMarket(Base): """Table with user's markets. Attributes: user_id: User ID Primary Key. market_id: Market ID. """ __tablename__ = "tblApolloUserMarket" user_id = Column("UserId", String(100), primary_key=True) market_id = Column("MarketId", BigInteger) email = Column("Email", String(255)) def __str__(self): return "User {} market {} email {}".format(self.user_id, self.market_id, self.email) class Market(Base): """Table with market data. Attributes: market_id: Market ID Primary Key. market_name: Short market name. market_fullname: Full market name. """ __tablename__ = 'tblApplicationInstance' market_id = Column('intApplicationInstanceID', Integer, primary_key=True, autoincrement=True) market_name = Column('strSpotifyRegionID', String(50)) market_fullname = Column('strName', String(45)) language_code = Column('strLanguageID', String(15)) active = Column('blnActive', Boolean, default=True) ga_country_name = Column('strGACountryName', String(45)) workout = Column('blnWorkout', BIT(1), default=0) include_other_playlists = Column('blnIncludeOtherPlaylists', BIT(1), default=0) default_service = Column('strDefaultService', String(10), nullable=True) services = Column('strServiceList', String(255), nullable=True) fallback = Column('blnFallbackApplication', Boolean, default=False) def __str__(self): return 'Market ID {} - {} ({})'.format(self.market_id, self.market_fullname, self.market_name) class Globalization(Base): """Globalization data. Attributes: key: Field name. culture: Culture code. value: Field value. """ __tablename__ = 'tblGlobalization' key = Column('strKey', String(45), primary_key=True) language_code = Column('strCulture', String(5), primary_key=True) value = Column('strValue', Text) class Genre(Base): """Music genres. Attributes: id: Table ID. market_id: Market reference. name: Genre name. last_fm_tag: FM tag. active: Is active or not. category_id: Category reference. """ __tablename__ = 'tblGenre' id = Column('intID', Integer, primary_key=True, autoincrement=True) market_id = Column('intApplicationInstanceID', Integer) name = Column('strName', String(50), nullable=True) last_fm_tag = Column('strLastFmTag', VARCHAR(100, charset='latin1')) active = Column('blnActive', Boolean) category_id = Column('intCategoryID', INTEGER(unsigned=True), nullable=True) class ApolloRecentSearch(Base): """Table with user's recent searches. Attributes: id: Primary key user_id: User ID. uri: URI of track in search results. """ __tablename__ = 'tblAdminSearchHistory' id = Column('Id', BigInteger, primary_key=True) user_id = Column('AdminUserId', String(100), nullable=False) uri = Column('SearchResultUri', String(100)) created_at = Column('Timestamp', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) search_term = Column('SearchTerm', String(100)) search_type = Column('SearchResultType', BigInteger) source = Column("Source", String(30)) country_code = Column("CountryCode", String(15)) def __str__(self) -> str: return 'User {} searched track uri {}'.format(self.user_id, self.uri) class BuzzCategory(Base): """Table with Buzz categories. Attributes: id: Primary key. name: Category name value. """ __tablename__ = 'BuzzCategory' id = Column('Id', BigInteger, primary_key=True) name = Column('Name', String(255), nullable=True) def __str__(self) -> str: return 'BuzzCategory {}'.format(self.name) class BuzzUser(Base): """Table with Buzz users. Attributes: id: Primary key. user_name: User name value. display_name: URI of track in search results. category_id: User category identifier. country_code: Country code value. service_type: Service type value. music_service: Music DSP id value. created_at: Creation timestamp. """ __tablename__ = "BuzzUser" id = Column("Id", BigInteger, primary_key=True) user_name = Column("Username", String(45), nullable=True) display_name = Column("DisplayName", String(255), nullable=True) category_id = Column("CategoryID", Integer, nullable=True) country_code = Column("CountryCode", String(100), nullable=True) service_type = Column("ServiceType", Integer, nullable=True) music_service = Column("MusicServiceId", Integer, nullable=False) created_at = Column("Created", TIMESTAMP(), nullable=False, default=func.utc_timestamp()) subscribers = Column("Subscribers", Integer, nullable=True) playlist_subscribers = Column("PlaylistSubscribers", Integer, nullable=True) image = Column("Image", String(100), nullable=True) def __str__(self) -> str: return "BuzzUser ({}) - {}".format(self.username, self.country_code) class ApolloGoVersion(Base): """Table with Apollo Go versions update. Attributes: id: Primary key. ios: IOS version. android: Android version. created_at: Creation timestamp. """ __tablename__ = 'tblApolloGoVersion' id = Column('ID', BigInteger, primary_key=True) os = Column('OS', String(50), nullable=True) version = Column('Version', String(50), nullable=True) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) def __str__(self) -> str: return 'os: {}, version: {}'.format(self.os, self.version) class IsrcRelation(Base): """Table with relations beyween parent and children isrc in terms of isrc merging. Attributes: id: Primary key. created_at: Creation timestamp. isrc: related isrc. main_isrc: parent isrc """ __tablename__ = 'tblIsrcRelation' __table_arg__ = (UniqueConstraint('ISRC', 'MainISRC')) id = Column('Id', BigInteger, primary_key=True) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) isrc = Column('ISRC', String(45), nullable=False) main_isrc = Column('MainISRC', String(45), nullable=False, index=True) def __str__(self) -> str: return 'Isrc relation: {}: child {} -> parent {}'.format(self.id, self.isrc, self.main_isrc) class ApolloMarket(Base): """Table with Apollo Markets. Attributes: id: Primary key. code: Market code (e.g. us, au). name: Market name (e.g. Norway, United States). full_name: Market full name (e.g. The United States). is_primary: Boolean key reflects market is primary or not. Primaries could be selected by users. vendors: byte flag of having information in that market for all vendors. """ __tablename__ = 'tblApolloMarket' id = Column('ID', BigInteger, primary_key=True) code = Column('Code', String(10), nullable=False, unique=True) name = Column('Name', String(255), nullable=False) full_name = Column('FullName', String(255), nullable=False) is_primary = Column('IsPrimary', Boolean) vendors = Column('Vendors', Integer) def __str__(self) -> str: return '{} - {}'.format(self.code, self.name) class ApolloCopies(Base): """Table with Apollo Copies data. Attributes: id: Primary key. os: OS value (e.g. iOS, Android etc). version: Version in semanting format (e.g. 2.1.12). data: Dumped to str copies data. created_at: Creation timestamp. updated_at: Updating timestamp. """ __tablename__ = 'tblApolloCopies' id = Column('ID', BigInteger, primary_key=True) os = Column('OS', String(30), nullable=False) version = Column('Version', String(50), nullable=False) data = Column('Data', Text) created_at = Column('CreatedAt', TIMESTAMP()) updated_at = Column('UpdatedAt', TIMESTAMP()) def __str__(self) -> str: return '{} - {}'.format(self.version, self.os) class PlaylistBlacklist(Base): """Table with list of playlists ids that should be blacklisted. Attributes: id: Primary key. vendor: Vendor value (e.g. Spotify, Apple). playlist_id: Playlist ID value. """ __tablename__ = 'tblPlaylistsBlacklist' id = Column('ID', BigInteger, primary_key=True) vendor = Column('Vendor', String(20), nullable=False) playlist_id = Column('PlaylistId', String(50), nullable=False) def __str__(self) -> str: return '{} - {}'.format(self.vendor, self.playlist_id) class ApolloGlobalTrackPrioritiesHistory(Base): """Global track priorities playlists content by date. Attributes: id: Primary key. date: History date. type_id: GTP list type. playlist_id: Playlist ID, container of specific GTP tracks. """ __tablename__ = 'tblApolloGlobalTrackPrioritiesHistory' id = Column('Id', Integer, primary_key=True) date = Column('Date', Date, nullable=False) playlist_id = Column('PlaylistId', String(22), nullable=False) type_id = Column('TypeId', Integer, nullable=False) tracks = relationship("ApolloGlobalTrackPrioritiesHistoryTrack", back_populates="history") def __str__(self) -> str: return 'Apollo GTP History {}'.format(self.id) class ApolloGlobalTrackPrioritiesHistoryTrack(Base): """Global track priorities playlists content by date. Attributes: id: Primary key. history_id: Ref to parent history record. track_id: Spotify track ID. isrc: Track ISRC. """ __tablename__ = 'tblApolloGlobalTrackPrioritiesHistoryTrack' id = Column('Id', BigInteger, primary_key=True) history_id = Column('HistoryId', Integer, ForeignKey('tblApolloGlobalTrackPrioritiesHistory.Id'), nullable=False) track_id = Column('TrackId', String(22), nullable=False) isrc = Column('ISRC', String(15), nullable=False) history = relationship("ApolloGlobalTrackPrioritiesHistory", back_populates="tracks") def __str__(self) -> str: return 'Apollo GTP History Track {}'.format(self.id) class ApolloKeyValueStorage(Base): """Apollo key value storage. Attributes: id: Primary key. key: Key code. value: Value. updated_at: Value in datetime format. """ __tablename__ = 'tblApolloKeyValueStorage' id = Column('Id', Integer, primary_key=True) key = Column('Key', String(30), nullable=False) value = Column('Value', String(30), nullable=True) updated_at = Column('UpdatedAt', String(30), nullable=True) def __str__(self) -> str: return 'Apollo Storage: Key {} Value'.format(self.key, self.value) class ApolloUserVersionCarousel(Base): """User carousel viewed data for specific version. Attributes: id: Primary key. user_id: Auth0 user identifier. carousel_id: FK on ApolloVersionCarousel model. created_at: Creation timestamp. updated_at: Updating timestamp. """ __tablename__ = "tblApolloUserVersionCarousel" __table_args__ = (UniqueConstraint('UserId', 'CarouselId'),) id = Column('Id', BigInteger, primary_key=True) user_id = Column('UserId', CHAR(37)) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) updated_at = Column('UpdatedAt', TIMESTAMP()) carousel_id = Column("CarouselId", BigInteger, ForeignKey("tblApolloVersionCarousel.Id"), nullable=True) version_carousel = relationship("ApolloVersionCarousel", backref="viewed_users") def __str__(self) -> str: return f"Carousel user viewed data: User -> ({self.user_id}), Version -> ({self.version_carousel})" class ApolloVersionCarousel(Base): """Carousel version model. Attributes: id: Primary key. os: OS value (e.g. ios, android). version: Semver version value. created_at: Creation timestamp. updated_at: Updating timestamp. """ __tablename__ = "tblApolloVersionCarousel" id = Column('Id', BigInteger, primary_key=True) os = Column('OS', String(30), nullable=True) version = Column('Version', String(50), nullable=True) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) updated_at = Column( 'UpdatedAt', TIMESTAMP(), nullable=False, onupdate=func.utc_timestamp(), default=func.utc_timestamp()) def __str__(self): return f"{self.os} - {self.version}" class ApolloVersionCarouselSlide(Base): """Slide model for carousel version object. Attributes: id: Primary key. carousel_id: FK on ApolloVersionCarousel model. slide_num: Number of slide (min=1, max=10). title_1: First title value. title_2: Second title value. img_url: Slide image URL. description: Slide description. created_at: Creation timestamp. updated_at: Updating timestamp. """ __tablename__ = "tblApolloVersionCarouselSlide" id = Column('Id', BigInteger, primary_key=True) carousel_id = Column("CarouselId", BigInteger, ForeignKey("tblApolloVersionCarousel.Id"), nullable=True) slide_num = Column("SlideNum", Integer, nullable=False) title_1 = Column("Title1", String(30), nullable=True) title_2 = Column("Title2", String(30), nullable=True) img_url = Column("ImageUrl", String(512), nullable=True) description = Column("Description", String(200), nullable=True) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) updated_at = Column('UpdatedAt', TIMESTAMP()) version_carousel = relationship("ApolloVersionCarousel", backref=backref("slides", order_by="ApolloVersionCarouselSlide.slide_num.asc()")) def __str__(self): name_str = f"Slide #{self.slide_num}" if not self.version_carousel: return name_str return f"{name_str} - {self.version_carousel}" class NPSSurvey(Base): """NPS survey model. Attributes: id: Primary key. name: Survey name. active: Boolean flag is survey active. start_date: Survey start date. end_date: Survey end date. created_at: Creation timestamp. updated_at: Updating timestamp. """ __tablename__ = "tblApolloNPSSurvey" id = Column("Id", BigInteger, primary_key=True) name = Column("Name", String(40), nullable=True) active = Column("Active", Boolean) start_date = Column("StartDate", Date(), nullable=False) end_date = Column("EndDate", Date(), nullable=False) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) updated_at = Column('UpdatedAt', TIMESTAMP()) def __str__(self): dates_str = f"({self.start_date.isoformat()} - {self.end_date.isoformat()})" return f"Survey '{self.name}' {dates_str}" if self.name else f"Survey ID={self.id} {dates_str}" class NPSSurveyQuestion(Base): """NPS survey question model. Attributes: id: Primary key. survey_id: FK on NPSSurvey model. question_num: Question number in survey. message: Question. type: Question type. created_at: Creation timestamp. updated_at: Updating timestamp. """ __tablename__ = "tblApolloNPSQuestion" id = Column("Id", BigInteger, primary_key=True) survey_id = Column("SurveyId", BigInteger, ForeignKey("tblApolloNPSSurvey.Id"), nullable=True) question_num = Column("QuestionNum", Integer, nullable=False) message = Column("Message", String(120), nullable=False) type = Column("Type", String(30), nullable=False) labels = Column("Labels", Text) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) updated_at = Column('UpdatedAt', TIMESTAMP()) survey = relationship("NPSSurvey", backref=backref("questions", order_by="NPSSurveyQuestion.question_num.asc()")) def __str__(self): return f"Question #{self.question_num} - Survey ID={self.survey_id}" class NPSSurveyUser(Base): """NPS survey target user model. Attributes: id: Primary key. survey_id: FK on NPSSurvey model. user_id: Auth0 user identifier. created_at: Creation timestamp. updated_at: Updating timestamp. """ __tablename__ = "tblApolloNPSTargetUser" id = Column("Id", BigInteger, primary_key=True) survey_id = Column("SurveyId", BigInteger, ForeignKey("tblApolloNPSSurvey.Id"), nullable=True) user_id = Column("UserId", String(40), nullable=False) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=func.utc_timestamp()) updated_at = Column('UpdatedAt', TIMESTAMP()) survey = relationship("NPSSurvey", backref=backref("target_users")) def __str__(self): return f"{self.user_id} - Survey ID={self.survey_id}" class NPSSurveyResult(Base): """NPS survey result model. Attributes: id: Primary key. survey_id: FK on NPSSurvey model. user_id: Auth0 user identifier. name: User name. email: User email. status: NPS survey result status. answers: User answers. created_at: Creation timestamp. updated_at: Updating timestamp. """ __tablename__ = "tblApolloNPSResult" id = Column("Id", BigInteger, primary_key=True) survey_id = Column("SurveyId", BigInteger, ForeignKey("tblApolloNPSSurvey.Id"), nullable=True) user_id = Column("UserId", String(40), nullable=False) name = Column("Name", String(50), nullable=False) email = Column("Email", String(50), nullable=False) status = Column("Status", String(40), nullable=False) answers = Column("Answers", Text) skips_num = Column("SkipsNum", Integer, default=0, nullable=False) created_at = Column('CreatedAt', TIMESTAMP(), nullable=False, default=datetime.datetime.utcnow) updated_at = Column( 'UpdatedAt', TIMESTAMP(), nullable=False, default=datetime.datetime.utcnow, onupdate=func.utc_timestamp()) def __str__(self): return f"{self.email} results - Survey ID={self.survey_id}" class ChartsMonitoring(Base): """Delphi charts update monitoring model. Is used by apollo-charts-monitoring-job to check and process any activities/changes like additions/removals/ moves in charts for event creating. Attributes: id: Primary key. dsp: DSP value (e.g. Spotify, Apple). chart_type: chart type (e.g. viral, regional (for spotify)). breakdown: daily or weekly market: Market code (e.g. us, au). chart_id: full (Delphi) chart_id (e.g. regional_daily_us (for spotify), charts_daily_us (for apple)). updated_at: Datetime when chart was processed chart_datetime: Datetime of delphi chart last update == to "max_date" from delphi charts/data-health/status data: previous chart state got from delphi (Done specially for apple because delphi updates apple charts more than once a day and does not save the previous state but just overrides existing one. As a result we can`t get the previous chart state and forced to store it in our database) """ __tablename__ = "tblChartsMonitoring" id = Column("Id", BigInteger, primary_key=True) dsp = Column("Dsp", String(50), nullable=False) chart_type = Column("ChartType", String(50), nullable=False) breakdown = Column("Breakdown", String(50), nullable=False) market = Column("Market", String(50), nullable=False) chart_id = Column("ChartId", String(100)) updated_at = Column("UpdatedAt", TIMESTAMP(), nullable=False, default=datetime.datetime.utcnow) chart_datetime = Column("ChartDatetime", TIMESTAMP(), nullable=False, default=datetime.datetime.utcnow) data = Column("Data", LONGTEXT) def __str__(self): return f"{self.dsp} - {self.chart_id}" class ApolloRegion(Base): """Apollo regions list. Attributes: code: Region code. name: Region name. is_primary: Primary boolean flag. dsp_flag: DSP bit flag. """ __tablename__ = "tblApolloRegion" code = Column("Code", String(10), primary_key=True) name = Column("Name", String(50), nullable=False) is_primary = Column("IsPrimary", Boolean, nullable=False) dsp_flag = Column("DspFlag", Integer, nullable=False) def __str__(self) -> str: return f"Region {self.code} {self.name} {self.is_primary} {self.dsp_flag}" class ApolloRegionDetail(Base): """Apollo region's details. Attributes: type: Region for specific type. code: Region code. rank: Rank (sorting order). name_override: Region name override for specific type. """ __tablename__ = "tblApolloRegionDetail" type = Column("Type", Enum(RegionDetailTypeEnum), nullable=False, primary_key=True) code = Column("Code", String(10), nullable=False, primary_key=True) rank = Column("Rank", Integer, nullable=True) name_override = Column("NameOverride", String(50), nullable=True) def __str__(self) -> str: return f"Region details {self.type} {self.code} {self.rank} {self.name_override}" class ApolloMarketRegion(Base): """Apollo type and market code to region code mapping. Attributes: type: Type code. market_code: Market code. region_code: Region code. market_name_override: Market name override for specific case. """ __tablename__ = "tblApolloMarketRegion" type = Column("Type", Enum(MarketRegionTypeEnum), nullable=False, primary_key=True) market_code = Column("MarketCode", String(10), ForeignKey("tblApolloMarket.Code"), nullable=False, primary_key=True) region_code = Column("RegionCode", String(10), ForeignKey("tblApolloRegion.Code"), nullable=False) market_name_override = Column("MarketNameOverride", String(50), nullable=True) market = relationship("ApolloMarket") region = relationship("ApolloRegion") def __str__(self) -> str: return f"Market Region {self.type.name} {self.market_code} {self.region_code}" class ApolloMarketDetail(Base): """Apollo market's details. Attributes: code: Region code. rank: Rank (sorting order). """ __tablename__ = "tblApolloMarketDetail" code = Column("Code", String(10), ForeignKey("tblApolloMarket.Code"), nullable=False, primary_key=True) rank = Column("Rank", Integer, nullable=True) def __str__(self) -> str: return f"Market details {self.code} {self.rank}"