"""Demographics model.""" import collections import json from oto import response as oto_response from oto import status from analytics.connectors import redis from analytics.connectors import snowflake from analytics.consts import analytics as consts from analytics.consts import demographics as demographics_consts from analytics.models import utils LABELID_CLAUSE = 'AND labelid = :labelid' SUBACCOUNT_CLAUSE = 'AND subaccountid = :subaccountid' ISRC_CLAUSE = 'AND isrc in (:isrcs)' STOREID_CLAUSE = 'AND storeid in (:store_ids)' FEEDID_CLAUSE = ' AND feedid in (:feed_ids)' ARTISTID_CLAUSE = 'AND artistid in (:artist_ids)' TERRITORY_CODE_CLAUSE = 'AND territory_code = :territory_code' DISTRIBUTOR_CLAUSE = 'AND distributor in (:distributors)' _sql_loader = snowflake.SQLLoader('analytics/queries') BY_LABEL = 'get_summary_demographics_by_label_feed' BY_ARTIST = 'get_summary_demographics_by_artist_feed' BY_IRSC = 'get_summary_demographics_by_isrc_feed' class Cohort(collections.namedtuple( 'Cohort', ['age_group', 'gender', 'streams'])): """Cohort stores data about a group with fixed age range and gender.""" def _choose_demographics_query(query_params): """Choose which demographics insights SQL query to use. Args: params (dict): the params to filter by Returns: str: Sql query file name """ if not query_params['artist_ids'] and not query_params['isrcs']: return BY_LABEL elif not query_params['isrcs']: return BY_ARTIST else: return BY_IRSC def _get_summary_demographics_sql(params=None): query_name = _choose_demographics_query(params) raw_sql = _sql_loader.load_query(query_name) return raw_sql.format( labelid_clause=LABELID_CLAUSE if params['labelid'] else '', subaccount_clause=SUBACCOUNT_CLAUSE if params['subaccountid'] else '', isrc_clause=ISRC_CLAUSE if ( params['isrcs'] and params['isrcs'][0] is not None) else '', storeid_clause=STOREID_CLAUSE if ( params['store_ids'] and params['store_ids'][0] is not None) else '', feedid_clause=FEEDID_CLAUSE, artistids_clause=ARTISTID_CLAUSE if params['artist_ids'] else '', territory_code_clause=TERRITORY_CODE_CLAUSE if params[ 'territory_code'] else '', distributor_clause=DISTRIBUTOR_CLAUSE if (params[ 'distributors'] and params['distributors'][0] is not None) else '') def get_cohorts( store_ids, feed_ids, start_date, end_date, labelid, *, subaccountid=None, isrcs=None, artist_ids=None, territory_code=None, distributors=None): """Query cohorts for a store. Args: store_ids (list[int]): list of store ids feed_ids (list): list of feed ids start_date (str): start date (YYYY-MM-DD) end_date (str): end date (YYYY-MM-DD) labelid (int): user label id subaccountid (int): user label id isrcs (list[str]): list of track ISRCs artist_ids (list[int]): list of artist ids territory_code (str): territory code distributors(list[str]): list of distributors names. Returns: oto_response.Response[Cohort] """ query_params = { 'start_date': start_date, 'end_date': end_date, 'labelid': labelid, 'subaccountid': subaccountid, 'isrcs': isrcs, 'store_ids': store_ids, 'feed_ids': feed_ids, 'artist_ids': artist_ids, 'territory_code': territory_code, 'distributors': distributors, 'query': 'get_summary_demographics'} # respond with cached result, if available cache_key = utils.get_cache_key(query_params) cached_result = redis.client.get(cache_key) if cached_result: raw_cohorts = json.loads(cached_result.decode('utf8')) return oto_response.Response( [Cohort(*c) for c in raw_cohorts]) if set(store_ids) == set(consts.AVAILABLE_DEMOGRAPHICS_STORE_IDS): store_id = None elif len(store_ids) != 1: raise ValueError('Unexpected store_ids value') else: store_id = store_ids[0] query_params['store_ids'] = [store_id] sql = _get_summary_demographics_sql(query_params) raw_cohorts = snowflake.fetchall(sql, query_params) # if result is empty, return 204 if not raw_cohorts or raw_cohorts == ((),): return oto_response.Response(status=status.NO_CONTENT) cohorts = [Cohort(*c) for c in raw_cohorts] sorted_cohorts = sorted( cohorts, key=lambda cohort: ( demographics_consts.age_group_order.get(cohort.age_group, 0), cohort.gender)) # store cohorts in cache for later lookup redis.client.set(cache_key, json.dumps(sorted_cohorts), ex=60*60) return oto_response.Response(sorted_cohorts)