"""Logic for retrieving top video metrics.""" from datetime import timedelta from typing import Any, Mapping from ddtrace import tracer from analytics.constants import cache, store from analytics.constants.ordering import ORDER_DIRECTIONS from analytics.logic.data_availability import get_videos_max_available_date from analytics.logic.parallel import parallel from analytics.logic.stores import add_outage_error_to_stores from analytics.queries.format import format_row from analytics.queries.top_video_metrics import TopVideoMetrics from analytics.schemas.top_video_metrics import TopVideoMetricsSchema from analytics.utils import store_availability from analytics.utils.cache import cache_in_redis TOP_VIDEO_METRICS_FIELDS = [ "video_id", "channel_id", "subscriber_count", "total_views", "total_estimated_gross_revenue", "average_view_duration_seconds", "views_1_month_back", "growth_2_to_1_months_back", "views_2_months_back", "growth_3_to_2_months_back", "views_3_months_back", "growth_4_to_3_months_back", "views_last_28_days", "views_last_7_days", ] TASK_RESULT = "get_top_video_metrics" SOURCES = "sources" @cache_in_redis(ttl=cache.ONE_DAY) @tracer.wrap(name="get_top_video_metrics") def get_top_video_metrics( query_params: Mapping[str, Any], permissions: Mapping[str, Any], ): """Fetch top video metrics. Args: query_params: Dict with distributors, countries, store_ids, label_ids, subaccount_ids, global_participant_ids, channel_ids, track_types, order_by, order_dir, limit, offset. permissions: Dict with permission_* keys. Returns: oto_response.Response with top video metrics payload. """ order_by = query_params["order_by"] order_dir = query_params["order_dir"] if order_by not in TOP_VIDEO_METRICS_FIELDS: raise Exception("Invalid order_by field") if order_dir.upper() not in ORDER_DIRECTIONS: raise Exception("Invalid order_dir value") store_ids = store_availability.get_query_store_ids( query_params.get("store_ids") or [], store.VIDEO_STORE ) max_available = get_videos_max_available_date() last_available = max_available.replace(day=1) - timedelta(days=1) response_body = { "items": [], "total_results": 0, "last_available_date": last_available, "sources": add_outage_error_to_stores(store_availability.get_video_sources()), } if not store_ids: return TopVideoMetricsSchema.normalized_response(response_body) label_ids, subaccount_ids = _resolve_label_and_subaccount_ids( query_params.get("label_ids") or [], query_params.get("subaccount_ids") or [], permissions.get("permission_label_ids") or [], permissions.get("permission_subaccount_ids") or [], ) artist_ids = permissions.get("permission_artist_ids") or [] track_types = query_params.get("track_types") or ["video"] query_input = { **permissions, "distributors": query_params["distributors"], "store_ids": store_ids, "country_ids": query_params.get("countries") or [], "label_ids": label_ids, "subaccount_ids": subaccount_ids, "artist_ids": artist_ids, "global_participant_ids": query_params.get("global_participant_ids") or [], "channel_ids": query_params.get("channel_ids") or [], "track_types": track_types, "order_by": order_by, "order_dir": order_dir, "limit": query_params["limit"], "offset": query_params["offset"], } requests = { TASK_RESULT: { "func": _fetch_metrics, "args": (query_input,), }, SOURCES: { "func": add_outage_error_to_stores, "args": (store_availability.get_video_sources(),), }, } result = parallel(requests).message items, total_results = result[TASK_RESULT] response_body["items"] = items response_body["total_results"] = total_results response_body["sources"] = result[SOURCES] return TopVideoMetricsSchema.normalized_response(response_body) def _fetch_metrics(query_input): rows = [format_row(row) for row in TopVideoMetrics(query_input).execute()] if not rows: return [], 0 items = [ {key: row[key] for key in TOP_VIDEO_METRICS_FIELDS if key in row} for row in rows ] total_results = rows[0].get("total_results", 0) or 0 return items, total_results def _resolve_label_and_subaccount_ids( label_ids, subaccount_ids, perm_label_ids, perm_subaccount_ids ): """Mirror the legacy auto-population behavior. If the caller did not pass label_ids, fall back to the caller's permission_label_ids. Auto-populate subaccount_ids only when the caller passed neither label_ids nor subaccount_ids — combined-grant callers should see the label catalog rather than the subaccount intersection. """ if not label_ids: label_ids = list(perm_label_ids) if not subaccount_ids and not label_ids: subaccount_ids = list(perm_subaccount_ids) return label_ids, subaccount_ids