"""Logic for Charts.""" import json from typing import List from charts import features from charts.connectors import snowflake from charts.logic.permissions import get_permissions from charts.logic import sql_loader CHART_COLUMN_NAMES = [ 'chartId', 'name', 'platform', 'target', 'country', 'type', 'frequency', 'genre', 'lastAvailableTimestamp', 'createdAt', 'modifiedAt', ] SONG_HISTORY_COLUMN_NAMES = [ 'uuid', 'position', 'positionChange', 'streams', 'totalStreams', 'totalShazams', 'views', 'totalUnits', 'trackId', 'isrc', 'publicSoundRecordingId', 'upc', 'videoId', 'chartDate', 'chartId' ] PRODUCT_HISTORY_COLUMN_NAMES = [ 'uuid', 'position', 'positionChange', 'streams', 'totalStreams', 'totalShazams', 'views', 'totalUnits', 'trackId', 'isrc', 'publicProductId', 'upc', 'videoId', 'chartDate', 'chartId' ] CHART_RANKINGS_COLUMN_NAMES = [ 'uuid', 'chartId', 'position', 'positionChange', 'streams', 'totalStreams', 'totalShazams', 'views', 'numTracks', 'totalUnits', 'trackId', 'isrc', 'publicSoundRecordingId', 'upc', 'publicProductId', 'videoId', 'channelId', 'channelName', 'chartDate', 'peakTimestamp', 'peakPosition', 'daysOnChart', 'artistNames' ] CHART_RANKINGS_ARRAY_NAMES = [ 'offerTypes', 'globalParticipants', 'spotifyIds', ] # Ranking position limit for chart rankings in catalog IN_CATALOG_POSITION_LIMIT = 1000 def get_charts(): """Get list of charts """ where_clause = '' controlled_charts_filter = features.get_ff_charts_sql_filter() if controlled_charts_filter is not None: where_clause = 'WHERE ' + controlled_charts_filter sql = sql_loader.load_query('get_charts') sql = sql.format(where_clause=where_clause) rows = snowflake.fetchall(sql, {}) result = [{c: v for c, v in zip(CHART_COLUMN_NAMES, row)} for row in rows] return result def get_chart(chart_id): sql = sql_loader.load_query('get_chart') params = { 'chart_id': chart_id } row = snowflake.fetchone(sql, params) result = {c: v for c, v in zip(CHART_COLUMN_NAMES, row)} return result def get_chart_available_dates(chart_id: str) -> List[str]: """ Get all available chart dates by chart_id Args: chart_id: unique string chart identifier Returns: List of string chart dates """ sql = sql_loader.load_query('get_chart_available_dates') params = { 'chart_id': chart_id } rows = snowflake.fetchall(sql, params) result = [row[0] for row in rows] return result def get_chart_rankings(chart_id, chart_date, limit, offset): assert limit > 0 assert offset >= 0 params = { 'chart_id': chart_id, 'chart_date': chart_date, 'limit': limit, 'offset': offset, } sql = sql_loader.load_query('get_chart_rankings') rows = snowflake.fetchall(sql, params) result = [{c: v for c, v in zip(CHART_RANKINGS_COLUMN_NAMES, row)} for row in rows] return result def get_chart_rankings_in_catalog(request_context, chart_id, chart_date, limit, offset): assert limit > 0 assert offset >= 0 permissions = get_permissions(request_context) params = { 'position_limit': IN_CATALOG_POSITION_LIMIT, 'chart_id': chart_id, 'chart_date': chart_date, 'limit': limit, 'offset': offset, **permissions } sql = sql_loader.load_query('get_chart_rankings_in_catalog') # Add permission filters sql = _format_with_permissions_filter(sql, permissions) rows = snowflake.fetchall(sql, params) result = [{c: v for c, v in zip(CHART_RANKINGS_COLUMN_NAMES, row)} for row in rows] return result def get_chart_song_history(chart_id, public_sound_recording_id): params = { 'chart_id': chart_id, 'public_sound_recording_id': public_sound_recording_id, } sql = sql_loader.load_query('get_chart_song_history') rows = snowflake.fetchall(sql, params) result = [{c: v for c, v in zip(SONG_HISTORY_COLUMN_NAMES, row)} for row in rows] return result def get_chart_product_history(chart_id, public_product_id): params = { 'chart_id': chart_id, 'public_product_id': public_product_id, } sql = sql_loader.load_query('get_chart_product_history') rows = snowflake.fetchall(sql, params) result = [{c: v for c, v in zip(PRODUCT_HISTORY_COLUMN_NAMES, row)} for row in rows] return result def _parse_rows(c, v): if c in CHART_RANKINGS_ARRAY_NAMES and v is not None: return json.loads(v) return v def _format_with_permissions_filter(sql, permissions_filter): """Format query with filter_clause for permissions Args: sql(str): SQL containing a {filter_clause} to be filled in permissions_filter (dict): dict containing resources a profile can access Returns: str: SQL formatted with appropiate permissions filter """ # If the permission dict were to be empty, we force the query to return nothing # by setting the filter clause to just FALSE if all(value is None for value in permissions_filter.values()): return sql.format(filter_clause='FALSE') # If starVendor response, return everything by setting clause to TRUE if all(value == ['*'] for value in permissions_filter.values()): return sql.format(filter_clause='TRUE') filters = [] if permissions_filter['vendor_ids']: filters.append('VENDOR_ID IN (:vendor_ids)') if permissions_filter['subaccount_ids']: filters.append('SUBACCOUNT_ID IN (:subaccount_ids)') if permissions_filter['label_participant_ids']: filters.append('LABEL_PARTICIPANT_ID IN (:label_participant_ids)') clause = ' OR '.join(filters) return sql.format(filter_clause=clause)