"""Snowflake Model for metrics.""" from oto import response from oto import status import sqlalchemy from sqlalchemy import Column from sqlalchemy import Integer from social_analytics.connectors import snowflake from social_analytics.connectors.sentry import sentry_client from social_analytics.constants import error from social_analytics.constants.collectors import ( PLATFORM_AND_SOCIAL_METRIC_REFS as platforms_and_metrics) class Metric(snowflake.BaseModel): """Snowflake metric model.""" __tablename__ = 'FACT_SOCIAL_PROFILE_METRICS' social_profile_id = Column(Integer, primary_key=True) platform_id = Column(Integer) label_id = Column(Integer) metric_id = Column(Integer) metric_value = Column(sqlalchemy.types.FLOAT) processed_datetime = Column(sqlalchemy.types.TIMESTAMP, primary_key=True) activity_datetime = Column(sqlalchemy.types.TIMESTAMP) def to_dict(self): """Return the object as dictionary.""" return dict( social_profile_id=self.social_profile_id, platform_id=self.platform_id, label_id=self.label_id, metric_id=self.metric_id, metric_value=self.metric_value, processed_datetime=str(self.processed_datetime), activity_datetime=str(self.activity_datetime)) @snowflake.db_session_wrap def get_metrics_by_social_profile_id(social_profile_id, session): """Fetch metrics for a social profile id. Args: social_profile_id (int): The social profile id for which to fetch latest metrics. session (Session): The snowflake session. Returns: response.Response: containing the latest metrics for given social profile id as a list of dictionaries. """ if not social_profile_id: return response.create_error_response( status.BAD_REQUEST, error.ERROR_MESSAGE_BAD_PARAMS) try: res = session.query( Metric).filter_by(social_profile_id=social_profile_id).all() metrics = [row.to_dict() for row in res] if not metrics: return response.create_not_found_response() return response.Response({'items': metrics}) except Exception: sentry_client.captureException() return response.create_error_response( code=error.INTERNAL_ERROR, message='Snowflake error', status=500) @snowflake.db_session_wrap def get_time_series_data_for_period(params, session): """Fetch metrics by social profile, platform and metric id for a period. Args: params (dict) : a dict containing the following params { 'social_profile_id', 'platform_id', 'metric_id', 'start_date', 'end_date' } where: social_profile_id (int): The social profile id for which to fetch time series data. platform_id (int): The platform id for which to fetch time series data. metric_id (int): The metric id for which to fetch time series data. start_date (datetime): The period start for which to return data. end_date (datetime): The period end for which to return data. session (Session): The snowflake session. Returns: response.Response: containing a list of metric dicts. """ required_fields = [ 'social_profile_id', 'platform_id', 'metric_id', 'start_date', 'end_date'] if not all(field_name in params for field_name in required_fields): return response.create_error_response( status.BAD_REQUEST, error.ERROR_MESSAGE_BAD_PARAMS) try: rows = ( session.query( Metric).filter( Metric.social_profile_id == params['social_profile_id'], Metric.platform_id == params['platform_id'], Metric.metric_id == params['metric_id'], Metric.activity_datetime >= params['start_date'], Metric.activity_datetime <= params['end_date']) .all()) metrics = [row.to_dict() for row in rows] if not metrics: return response.create_not_found_response() platform = _get_platform_name(params['platform_id']) metric_name = _get_metric_name(params['metric_id']) results = [ { 'activity_datetime': metric['activity_datetime'], 'platform': platform, 'metric_name': metric_name, 'metric_value': metric['metric_value'] } for metric in metrics] return response.Response({'items': results}) except Exception: sentry_client.captureException() return response.create_error_response( code=error.INTERNAL_ERROR, message='snowflake error', status=status.INTERNAL_ERROR) def _get_platform_name(platform_id): """Get platform name from platform constants.""" return list( filter(lambda x: x['platform_id'] == platform_id, platforms_and_metrics['platforms'] ))[0]['platform'] def _get_metric_name(metric_id): """Get metric name from metric constants.""" return list( filter(lambda x: x['metric_id'] == metric_id, platforms_and_metrics['social_metrics'] ))[0]['metric_name'] @snowflake.db_session_wrap def get_latest_metrics_by_social_profile_ids(social_profile_ids, session): """Get latest metrics by social profile ids. Args: social_profile_ids (list): The social profile ids for which to fetch the latest metrics. session (Session): The Snowflake session. Returns: response.Response: containing a list of artist ids and the related social profiles with the latest metrics. e.g. {'items': [ { 'artist_id': 1, 'social_profiles': [ { 'social_profile_id': 69, 'platform_id': '3', 'platform_name': 'Artist 1', 'platform': 'facebook', 'metric': 'fan_count', 'metric_value': 5319794 } ] }, { 'artist_id': 2, 'social_profiles': [ { 'social_profile_id': 5, 'platform_id': '6', 'platform_name': 'Artist 2', 'platform': 'facebook', 'metric': 'fan_count', 'metric_value': 12912184 } ] } """ if not social_profile_ids: return response.create_error_response( status.BAD_REQUEST, error.ERROR_MESSAGE_BAD_PARAMS) metric_ids = [ metric['metric_id'] for metric in platforms_and_metrics['social_metrics']] metric_ids_param = ','.join(map(str, metric_ids)) social_profile_ids_param = ','.join(map(str, social_profile_ids)) query = '''SELECT DISTINCT (social_profile_id), metric_id, platform_id, FIRST_VALUE(metric_value) OVER (PARTITION BY social_profile_id, metric_id ORDER BY activity_datetime DESC ) AS last_collected_value FROM FACT_SOCIAL_PROFILE_METRICS WHERE social_profile_id IN ({social_profile_ids}) AND metric_id IN ({metric_ids})'''\ .format(metric_ids=metric_ids_param, social_profile_ids=social_profile_ids_param) try: res = session.execute(query).fetchall() if not res: return response.create_not_found_response() metrics = [{ 'social_profile_id': row['social_profile_id'], 'platform_id': row['platform_id'], 'metric_id': row['metric_id'], 'metric_value': row['last_collected_value'] } for row in res] results = [] for metric in metrics: # TODO this is a workaround for some bad data in qa # Remove once this is fixed try: platform_name = _get_platform_name(metric['platform_id']) metric_name = _get_metric_name(metric['metric_id']) except IndexError: continue result = { 'social_profile_id': metric['social_profile_id'], 'platform': platform_name, 'metric': metric_name, 'metric_value': metric['metric_value'] } results.append(result) return response.Response({'items': results}) except Exception: sentry_client.captureException() return response.create_error_response( code=error.INTERNAL_ERROR, message='Snowflake error', status=status.INTERNAL_ERROR) @snowflake.db_session_wrap def get_latest_metric_by_social_profile_id(social_profile_id, session): """Fetch metric for the social profile id provided. Args: social_profile_id (int): The social profile id for which to fetch latest metric. session (Session): The snowflake session. Returns: response.Response: containing a dict with social_profile_id, metric and metric_value. e.g. { 'social_profile_id': 3, 'platform': 'facebook', 'metric': 'likes', 'metric_value': 12345 } """ if not social_profile_id: return response.create_error_response( status.BAD_REQUEST, error.ERROR_MESSAGE_BAD_PARAMS) try: # TODO when dim_social_profile and dim_platform are filled do the join # to get the metric name. res = ( session.query(Metric) .filter(Metric.social_profile_id == social_profile_id) .order_by(Metric.activity_datetime.desc()) .first() ) if not res: return response.create_not_found_response() metric = res.to_dict() platform_name = _get_platform_name(metric['platform_id']) metric_name = _get_metric_name(metric['metric_id']) result = { 'social_profile_id': metric['social_profile_id'], 'platform': platform_name, 'metric': metric_name, 'metric_value': metric['metric_value'] } return response.Response(result) except Exception: sentry_client.captureException() return response.create_error_response( code=error.INTERNAL_ERROR, message='Snowflake error', status=status.INTERNAL_ERROR) @snowflake.db_session_wrap def get_metrics_by_social_profile_id_platform_id_and_metric_id( social_profile_id, platform_id, metric_id, session): """Fetch metrics by profile_id, platform_id and metric_id. Args: social_profile_id (int): The social profile id for which to fetch metrics. platform_id (int): The platform id for which to fetch metrics. metric_id (int): The metric id for which to fetch metrics. Returns: response.Response: containing the metrics for given social profile id, platform id and metric id as a list of dictionaries. """ if not social_profile_id or not platform_id or not metric_id: return response.create_error_response( status.BAD_REQUEST, error.ERROR_MESSAGE_BAD_PARAMS) try: res = session.query( Metric).filter_by( social_profile_id=social_profile_id, platform_id=platform_id, metric_id=metric_id).all() metrics = [row.to_dict() for row in res] if not metrics: return response.create_not_found_response() return response.Response({'items': metrics}) except Exception as e: sentry_client.captureException() return response.create_error_response( code=error.INTERNAL_ERROR, message=e, status=status.INTERNAL_ERROR)