import asyncio from aiohttp import web from aiohttp_apispec import docs, querystring_schema from datetime import timedelta from server.client import services from server.constants import BASE_API_PREFIX from server.constants.charts import ChartGroup, YoutubeDatesInclude, YoutubeFullTrendInclude, YoutubeFullTrendMapping, \ YoutubeVideoPositionsInclude, YoutubeVideoTrendChartFields, YoutubeVideoTrendSummaryFields from server.domains.charts import youtube as youtube_domains from server.scenarios.charts import youtube as youtube_scenarios from server.schemas.charts.youtube import YoutubeDates, YoutubeFullTrend, YoutubeVideoPositions, YoutubeVideoTrend from server.utils.parallel import make_requests from server.utils.response import dump_response_schema router = web.RouteTableDef() @router.get(BASE_API_PREFIX + "/charts/youtube/dates/") @docs(tags=["charts", "youtube"], summary="Get youtube available dates and markets.") @querystring_schema(YoutubeDates.Request) @dump_response_schema(YoutubeDates.Response) async def get(request: web.Request) -> web.Response: params = request["querystring"] video_id = params["video_id"] include_markets = YoutubeDatesInclude.MARKETS.value in params["include_list"] tasks = [youtube_scenarios.get_available_dates()] if video_id: tasks.append(youtube_scenarios.get_video_dates(video_id, params["market"])) if include_markets: tasks.append(youtube_scenarios.get_markets()) responses = await asyncio.gather(*tasks) result = {"charts": {"min": responses[0][0], "max": responses[0][1]}} if video_id: result["video"] = {"min": responses[1][0], "max": responses[1][1]} if include_markets: result["markets"] = responses[-1] return result @router.get(BASE_API_PREFIX + "/charts/youtube/video-trend/") @docs( tags=["charts", "youtube"], summary="Get youtube charts data for a specific date plus summary.", ) @querystring_schema(YoutubeVideoTrend.Request) @dump_response_schema(YoutubeVideoTrend.Response(many=True)) async def get(request: web.Request) -> web.Response: params = request["querystring"] video_id, chart_date, all_markets, field_list = ( params["video_id"], params["chart_date"], params["all_markets"], params["field_list"], ) if not chart_date: _, chart_date = await youtube_scenarios.get_available_dates() (chart_data, summary_data, markets_list) = await make_requests( ( ( services.dsp.get_video_positions_charts, dict( video_id=video_id, start_date=chart_date, end_date=chart_date, chart_group=ChartGroup.YOUTUBE_TRENDING.value, ), [], set(field_list) & set(YoutubeVideoTrendChartFields), ), ( services.dsp.get_video_positions_charts_summary, dict(video_id=video_id, chart_group=ChartGroup.YOUTUBE_TRENDING.value), [], set(field_list) & set(YoutubeVideoTrendSummaryFields), ), (youtube_scenarios.get_markets, {}, [], all_markets), ) ) chart_data = {i["chart"]["country_code"]: i for i in chart_data} summary_data = {i["chart"]["country_code"]: i for i in summary_data} if not all_markets: markets_list = set(chart_data.keys()) | set(summary_data.keys()) result = [] for market in markets_list: chart = chart_data.get(market, {}).get("position", {}) summary = summary_data.get(market, {}).get("summary", {}) result.append( {"country_code": market, "data": youtube_domains.get_video_chart_data(chart, summary, field_list)} ) youtube_domains.sort_video_chart_data(result, params["sort_by"], params["sort_order"]) return result @router.get(BASE_API_PREFIX + "/charts/youtube/video-positions/") @docs( tags=["charts", "youtube"], summary="Get video charts positions per each day within some dates range for chosen market.", ) @querystring_schema(YoutubeVideoPositions.Request) @dump_response_schema(YoutubeVideoPositions.Response) async def get(request: web.Request) -> web.Response: params = request["querystring"] video_id, start_date, end_date = params["video_id"], params.get("start_date"), params.get("end_date") include_chart = YoutubeVideoPositionsInclude.CHART.value in params["include_list"] if not start_date or not end_date: _, end_date = await youtube_scenarios.get_available_dates() start_date = end_date - timedelta(days=28) response = await services.dsp.get_video_positions_charts( video_id=video_id, start_date=start_date, end_date=end_date, chart_group=ChartGroup.YOUTUBE_TRENDING.value, country_code=params["market"], ) result = { "items": [{"date": i["position"]["date"], "position": i["position"]["current"]} for i in response], "count": len(response), } if include_chart and response: result["chart"] = response[0]["chart"] return result @router.get(BASE_API_PREFIX + "/charts/youtube/full-trend/") @docs(tags=["charts", "youtube"], summary="Get full chart for some date and market.") @querystring_schema(YoutubeFullTrend.Request) @dump_response_schema(YoutubeFullTrend.Response(many=True), apply=True) async def get(request: web.Request) -> web.Response: params = request["querystring"] chart_date, market, include_list, image_size = ( params["chart_date"], params["market"], params["include_list"], params["image_size"], ) chart_id = f"{ChartGroup.YOUTUBE_TRENDING.value}_{market}" positions_data = [] summary_mapping = {} if include_list: positions_data = await services.dsp.get_video_positions_charts( chart_id=chart_id, start_date=chart_date, end_date=chart_date, include="video" if YoutubeFullTrendInclude.VIDEO in include_list else None, ) video_id_list = [i["video_id"] for i in positions_data] if YoutubeFullTrendInclude.SUMMARY in include_list: summary_data = await services.dsp.get_video_positions_charts_summary( video_id=video_id_list, chart_id=chart_id, ) summary_mapping = {i["video_id"]: i for i in summary_data} result = [] for position_item in positions_data: video_id = position_item["video_id"] result_item = {"video_id": video_id} for data_type in include_list: source = ( summary_mapping.get(video_id, {}).get("summary", {}) if data_type == YoutubeFullTrendInclude.SUMMARY else position_item.get(data_type.value) ) result_item[data_type.value] = { destination: youtube_domains.calc_field_value(source, origin) for destination, origin in YoutubeFullTrendMapping[data_type].items() } if data_type == YoutubeFullTrendInclude.VIDEO: result_item[data_type.value]["image_url"] = source["thumbnails"].get(image_size.value, {}).get("url") result.append(result_item) return result