"""Podcast Model. This model represents a Podcast """ import sqlalchemy from sqlalchemy import ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.sql import func from podcast.connectors import mysql from podcast.constants import error from podcast.constants.common import FEED_TYPES, PRIVATE_RSS, PUBLIC_RSS from podcast.models import episode as episode_model from podcast.models import network from podcast.models.participant import Participant from podcast.models.podcast_season import PodcastSeason from podcast.utils import api_utils from podcast.utils import exc from podcast.utils import uuid class Podcast(mysql.BaseModel): """Podcast model.""" __tablename__ = 'podcast' id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # noqa network_id = sqlalchemy.Column( sqlalchemy.Integer, ForeignKey(network.Network.id), ) title = sqlalchemy.Column(sqlalchemy.VARCHAR(255)) description = sqlalchemy.Column(sqlalchemy.VARCHAR(4000)) slug = sqlalchemy.Column(sqlalchemy.VARCHAR(255), nullable=True) host = sqlalchemy.Column(sqlalchemy.VARCHAR(255)) owner = sqlalchemy.Column(sqlalchemy.VARCHAR(255)) copyright = sqlalchemy.Column(sqlalchemy.VARCHAR(255)) # noqa link = sqlalchemy.Column(sqlalchemy.VARCHAR(4000)) email = sqlalchemy.Column(sqlalchemy.VARCHAR(255)) explicit = sqlalchemy.Column( sqlalchemy.Enum(*['explicit', 'clean'])) show_type = sqlalchemy.Column( sqlalchemy.Enum(*['episodic', 'serial'])) created_by = sqlalchemy.Column(sqlalchemy.Integer) updated_by = sqlalchemy.Column(sqlalchemy.Integer) created_date = sqlalchemy.Column(sqlalchemy.DateTime, default=func.now()) updated_date = sqlalchemy.Column( sqlalchemy.DateTime, default=func.now(), onupdate=func.now()) is_deleted = sqlalchemy.Column( sqlalchemy.Boolean, nullable=False, default=False) language = sqlalchemy.Column(sqlalchemy.VARCHAR(255)) apple_id = sqlalchemy.Column(sqlalchemy.VARCHAR(255)) megaphone_id = sqlalchemy.Column(sqlalchemy.VARCHAR(50)) categories = sqlalchemy.Column(sqlalchemy.JSON) uuid = sqlalchemy.Column(sqlalchemy.VARCHAR(36), default=uuid.get_uuid) participants = relationship(Participant, lazy='select') favorited_users = relationship( 'User', secondary='user_podcast_favorite', secondaryjoin='and_(User.id==user_podcast_favorite.c.user_id, User.active.is_(True))', lazy='select' ) chartable_rss_link = sqlalchemy.Column(sqlalchemy.VARCHAR(255)) seasons = relationship(PodcastSeason, lazy='select') feed_type = sqlalchemy.Column(sqlalchemy.Enum(*FEED_TYPES), default=PUBLIC_RSS) show_family_id = sqlalchemy.Column( sqlalchemy.Integer, ForeignKey('show_family.id'), nullable=True ) channel_id = sqlalchemy.Column(sqlalchemy.VARCHAR(50), nullable=True) def to_dict(self, isSeasons=True): """Return the object as dictionary.""" participants = [participant.to_dict() for participant in self.participants] podcast_result = dict( id=self.id, network_id=self.network_id, title=self.title, description=self.description, slug=self.slug, host=self.host, owner=self.owner, copyright=self.copyright, link=self.link, email=self.email, explicit=self.explicit, show_type=self.show_type, language=self.language, megaphone_id=self.megaphone_id, categories=self.categories, uuid=self.uuid, created_by=self.created_by, updated_by=self.updated_by, created_date=self.created_date, updated_date=self.updated_date, apple_id=self.apple_id, participants=participants, feed_type=self.feed_type, show_family_id=self.show_family_id, channel_id=self.channel_id ) if isSeasons: podcast_result['seasons'] = [season.to_dict() for season in self.seasons] return podcast_result def get_podcasts(limit=0, offset=0, podcast_ids=[]): """Return all the podcasts. If podcast ids filters by podcast ids else query none. Ordered by podcast title and paginated by limit and offset. Args: limit (int): how many podcasts to retrieve. offset (int): the offset (for pagination). ids (list of int): podcast ids to fetch podcasts. Returns: dict: containing the paginated podcasts. """ with mysql.pod_db_session(read_only=True) as session: query = session.query(Podcast).filter( Podcast.is_deleted.isnot(True), Podcast.feed_type.in_([PUBLIC_RSS, PRIVATE_RSS])) if podcast_ids: query = query.filter(Podcast.id.in_(podcast_ids)) else: query = query.filter(None) query = query.order_by(Podcast.title) limited_query = query.offset(offset) if limit != 0: limited_query = limited_query.limit(limit).offset(offset) rows = limited_query.all() items = [row.to_dict() for row in rows] total_records = query.count() return { 'items': items, 'pagination': { 'total_records': total_records } } def get_podcasts_by_ids(ids, raw=False): """Return all the podcasts by ids. Args: ids (list): ids to fetch Returns: dict: containing the podcasts. """ with mysql.pod_db_session(read_only=True) as session: rows = session.query(Podcast).filter(Podcast.id.in_(ids)) \ .filter(Podcast.is_deleted.isnot(True)).all() if raw: return {'items': rows} items = [row.to_dict() for row in rows] return {'items': items} def get_podcasts_by_network_ids(network_ids): """Return podcasts by network ids. Args: network_ids (list of int): The networks ids. Returns: dict: containing the podcasts. """ with mysql.pod_db_session(read_only=True) as session: rows = session.query(Podcast).filter( Podcast.network_id.in_(network_ids), Podcast.is_deleted.isnot(True), ).all() return {'items': [row.to_dict() for row in rows]} def get_podcasts_by_megaphone_ids(ids): """Return all the podcasts by mp ids. Args: ids (list): megaphone ids to fetch Returns: dict: containing the podcasts. """ with mysql.pod_db_session(read_only=True) as session: rows = session.query(Podcast).filter(Podcast.megaphone_id.in_(ids)) \ .filter(Podcast.is_deleted.isnot(True)).all() items = [row.to_dict() for row in rows] return {'items': items} def get_podcast_by_id(podcast_id, raw=False): """Return a podcast by id. Args: podcast_id (int): The podcast unique identifier. Returns: dict: containing the paginated podcasts. """ with mysql.pod_db_session(read_only=True) as session: podcast = session.query(Podcast).get(podcast_id) if not podcast or podcast.is_deleted: raise exc.OwsError.not_found(error.ERROR_PODCAST_NOT_FOUND) if raw: return podcast podcast_dict = podcast.to_dict() return podcast_dict def get_podcast_by_megaphone_id(megaphone_id): """Return a podcast by megaphone id. Args: megaphone_id (str): The podcast unique identifier. Returns: dict: the podcast. """ with mysql.pod_db_session(read_only=True) as session: podcast = session.query(Podcast).filter(Podcast.is_deleted.isnot(True)) \ .filter(Podcast.megaphone_id == megaphone_id).first() if not podcast: raise exc.OwsError.not_found(error.ERROR_PODCAST_NOT_FOUND) return podcast.to_dict() def get_podcast_ids_by_network_ids(network_ids): """Return podcast ids by network ids. Args: network_ids (list of int): The networks ids. Returns: list of ints: podcast ids. """ with mysql.pod_db_session(read_only=True) as session: podcasts = session.query(Podcast.id).filter( Podcast.network_id.in_(network_ids), Podcast.is_deleted.isnot(True), Podcast.feed_type.in_([PUBLIC_RSS, PRIVATE_RSS])) return [podcast.id for podcast in podcasts] def get_podcast_ids_by_ids_and_network_ids(podcast_ids=[], network_ids=[]): """Return podcast ids by podcast and network ids. Args: network_ids (list of int): The networks ids. podcast_ids (list of int): the podcast_ids Returns: list of ints: podcast ids. """ with mysql.pod_db_session(read_only=True) as session: podcasts = session.query(Podcast.id).filter( sqlalchemy.or_( Podcast.network_id.in_(network_ids), Podcast.id.in_(podcast_ids) ), Podcast.is_deleted.isnot(True), Podcast.feed_type.in_([PUBLIC_RSS, PRIVATE_RSS])) return [podcast.id for podcast in podcasts] def get_podcast_ids_by_show_family_ids_and_network_ids(show_family_ids=[], network_ids=[]): """Return podcast ids by show family ids and network ids. Args: network_ids (list of int): The networks ids. show_family_ids (list of int): The show_family_ids Returns: list of ints: podcast ids. """ with mysql.pod_db_session(read_only=True) as session: podcasts = session.query(Podcast.id).filter( sqlalchemy.or_( Podcast.network_id.in_(network_ids), Podcast.show_family_id.in_(show_family_ids) ), Podcast.is_deleted.isnot(True)) return [podcast.id for podcast in podcasts] def search_podcasts(title, podcast_ids): """Return all the podcasts where the titles match the search query. Args: title (string): search text to query the podcasts on. podcast_ids (list): podcast_ids user has access too Returns: list: list of podcasts """ with mysql.pod_db_session(read_only=True) as session: query = session.query(Podcast).filter(Podcast.is_deleted.isnot(True)) query = query.filter( Podcast.id.in_(podcast_ids), Podcast.title.ilike(f'%{title}%', escape='/') ) query = query.order_by(Podcast.title) rows = query.all() return [row.to_dict() for row in rows] def create_podcast(data, session=None): """Create a new Podcast. Args: data (dict): the data from which to create the podcast. Returns: dict: containing the created podcast dict. """ if session: return _create_podcast(data, session) with mysql.pod_db_session() as session: return _create_podcast(data, session) def _create_podcast(data, session): user_id = api_utils.get_user_id() data['chartable_rss_link'] = data.get('slug') data['created_by'] = user_id data['updated_by'] = user_id podcast = Podcast(**data) session.add(podcast) session.flush() return podcast.to_dict() def update_podcast(podcast_id, data, session=None): """Update a Podcast. Args: podcast_id (int): the podcast id. data (dict): the data with which to update the podcast. session (sqlalchemy.session.Session): connection to db. Returns: dict: containing the updated podcast dict. """ if not isinstance(podcast_id, int): raise exc.OwsError.bad_request(error.ERROR_MESSAGE_BAD_PARAMS) if not data: raise exc.OwsError.bad_request(error.ERROR_MESSAGE_EMPTY_BODY) if session: return _update_podcast(podcast_id, data, session) with mysql.pod_db_session() as session: return _update_podcast(podcast_id, data, session) def _update_podcast(podcast_id, data, session): query = session.query( Podcast).filter( Podcast.id == podcast_id) podcast = query.first() if not podcast: raise exc.OwsError.not_found(error.ERROR_PODCAST_NOT_FOUND) data['updated_by'] = api_utils.get_user_id() query.update(data) result = query.first() return result.to_dict() def delete_podcast(podcast_id, session): """Delete a podcast. Args: podcast_id (int): the podcast id. session (sqlalchemy.session.Session): live connection to database Returns: dict: containing the deleted podcast dict. """ if not isinstance(podcast_id, int): raise exc.OwsError.bad_request(error.ERROR_MESSAGE_BAD_PARAMS) podcast = session.query( Podcast).get(podcast_id) if not podcast: raise exc.OwsError.not_found(error.ERROR_PODCAST_NOT_FOUND) if podcast.is_deleted: raise exc.OwsError.not_found(error.ERROR_MESSAGE_PODCAST_IS_DELETED) episode_model.delete_episodes_by_podcast_id(podcast_id, session) podcast.updated_by = api_utils.get_user_id() podcast.is_deleted = True return podcast.to_dict() def get_feeds_by_show_family_id(show_family_id, session=None): """Return all the feeds(podcasts) by show family id. Args: show_family_id (int): Show family id to fetch details for session (sqlalchemy.session.Session): live connection to database Returns: list: containing the podcasts. """ if session: return _fetch_feeds_by_show_family_id(show_family_id, session) with mysql.pod_db_session() as session: return _fetch_feeds_by_show_family_id(show_family_id, session) def _fetch_feeds_by_show_family_id(show_family_id, session): rows = session.query(Podcast).filter( Podcast.show_family_id == show_family_id, Podcast.is_deleted.isnot(True) ).all() return {'items': [row.to_dict(isSeasons=False) for row in rows]} def get_feeds_count_for_show_family_ids(show_family_ids): """Get feeds count per show_family_id. Args: show_family_ids (list( of ints): the show_family_ids to fetch num of feeds. Returns: dict: {[show_family_id]: 5} i.e {1: 5, 2:3} """ with mysql.pod_db_session(read_only=True) as session: query_result = session.query( Podcast.show_family_id, func.count(Podcast.show_family_id) ).filter( Podcast.show_family_id.in_(show_family_ids), Podcast.is_deleted.isnot(True) ).group_by(Podcast.show_family_id).all() final_result = {} for result in query_result: final_result[result[0]] = result[1] return final_result def get_earliest_feeds_by_show_family_ids(show_family_ids): """Return earliest podcasts per show family ids. Args: show_family_ids (list of int): Show family ids to fetch earliest podcasts for. Returns: list of dict: containing the earliest poodcasts. """ with mysql.pod_db_session(read_only=True) as session: subquery = session.query( Podcast.show_family_id, func.min(Podcast.id).label('earliest_podcast_id') ).group_by(Podcast.show_family_id).subquery() earliest_podcasts = session.query(Podcast).\ join(subquery, subquery.c.earliest_podcast_id == Podcast.id).\ filter( Podcast.show_family_id.in_(show_family_ids), Podcast.is_deleted.isnot(True) ).all() return {'items': [row.to_dict(isSeasons=False) for row in earliest_podcasts]} def get_public_and_private_rss_podcasts_by_network_ids_and_show_family_ids( network_ids=[], show_family_ids=[]): """Return podcasts by network ids and show family ids having feed-type as 'public-rss' or 'private-rss'. Args: network_ids (list of int): The networks ids. show_family_ids (list of int): The show_family_ids. Returns: dict: contains list of podcasts. """ with mysql.pod_db_session(read_only=True) as session: rows = session.query(Podcast.id, Podcast.title).filter( sqlalchemy.or_( Podcast.network_id.in_(network_ids), Podcast.show_family_id.in_(show_family_ids) ), Podcast.feed_type.in_((PUBLIC_RSS, PRIVATE_RSS)), Podcast.is_deleted.isnot(True) ) return {'items': [{'id': row.id, 'title': row.title} for row in rows]} def get_all_podcasts(): """Return all the podcasts. Returns: dict: containing the podcasts. """ with mysql.pod_db_session(read_only=True) as session: rows = session.query(Podcast).filter( Podcast.is_deleted.isnot(True)) return {'items': [{'id': row.id, 'title': row.title, 'network_id': row.network_id} for row in rows]}