From 8fe0512608a38eb2301ebc40be725616e2b4ebcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=B1=EA=B2=BD?= Date: Fri, 27 Feb 2026 15:26:38 +0900 Subject: [PATCH] =?UTF-8?q?docs=20YouTube=20Analytics=20API=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=98=A4=EB=94=94=EC=96=B8=EC=8A=A4=20?= =?UTF-8?q?=EC=9D=B8=EC=82=AC=EC=9D=B4=ED=8A=B8=20=EB=8B=A8=EC=9C=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/dashboard/api/routers/v1/dashboard.py | 2 +- app/dashboard/services/data_processor.py | 26 +++---- app/dashboard/services/youtube_analytics.py | 12 +-- docs/dashboard/YouTube Analytics API.md | 86 +++++++++++++++++---- 4 files changed, 84 insertions(+), 42 deletions(-) diff --git a/app/dashboard/api/routers/v1/dashboard.py b/app/dashboard/api/routers/v1/dashboard.py index 3b5908d..4648d4d 100644 --- a/app/dashboard/api/routers/v1/dashboard.py +++ b/app/dashboard/api/routers/v1/dashboard.py @@ -267,7 +267,7 @@ async def get_dashboard_stats( Dashboard.platform == "youtube", Dashboard.platform_user_id == social_account.platform_user_id, Dashboard.uploaded_at >= start_dt, - Dashboard.uploaded_at <= kpi_end_dt, + Dashboard.uploaded_at <= end_dt, ) ) period_video_count = count_result.scalar() or 0 diff --git a/app/dashboard/services/data_processor.py b/app/dashboard/services/data_processor.py index a5c51f1..8214e37 100644 --- a/app/dashboard/services/data_processor.py +++ b/app/dashboard/services/data_processor.py @@ -418,8 +418,8 @@ class DataProcessor: # === 연령/성별 데이터 처리 === demo_rows = demographics_data.get("rows", []) - age_map: dict[str, int] = {} - gender_map = {"male": 0, "female": 0} + age_map: dict[str, float] = {} + gender_map_f: dict[str, float] = {"male": 0.0, "female": 0.0} for row in demo_rows: if len(row) < 3: @@ -427,27 +427,21 @@ class DataProcessor: age_group = row[0] # "age18-24" gender = row[1] # "male" or "female" - views = row[2] + viewer_pct = row[2] # viewerPercentage (이미 % 값, 예: 45.5) - # 연령대별 집계 (age18-24 → 18-24) + # 연령대별 집계: 남녀 비율 합산 (age18-24 → 18-24) age_label = age_group.replace("age", "") - age_map[age_label] = age_map.get(age_label, 0) + views + age_map[age_label] = age_map.get(age_label, 0.0) + viewer_pct # 성별 집계 - if gender in gender_map: - gender_map[gender] += views + if gender in gender_map_f: + gender_map_f[gender] += viewer_pct - # 연령대별 비율 계산 - total_demo_views = sum(age_map.values()) age_groups = [ - { - "label": age, - "percentage": int( - (count / total_demo_views * 100) if total_demo_views > 0 else 0 - ), - } - for age, count in sorted(age_map.items()) + {"label": age, "percentage": int(round(pct))} + for age, pct in sorted(age_map.items()) ] + gender_map = {k: int(round(v)) for k, v in gender_map_f.items()} # === 지역 데이터 처리 === geo_rows = geography_data.get("rows", []) diff --git a/app/dashboard/services/youtube_analytics.py b/app/dashboard/services/youtube_analytics.py index e110ac1..bb3bbf0 100644 --- a/app/dashboard/services/youtube_analytics.py +++ b/app/dashboard/services/youtube_analytics.py @@ -124,7 +124,7 @@ class YouTubeAnalyticsService: self._fetch_monthly_data(video_ids, previous_start, previous_end, access_token), self._fetch_top_videos(video_ids, start_date, _kpi_end, access_token), self._fetch_demographics(start_date, _kpi_end, access_token), - self._fetch_region(video_ids, start_date, _kpi_end, access_token), + self._fetch_region(start_date, _kpi_end, access_token), ] else: # mode == "day" tasks = [ @@ -134,7 +134,7 @@ class YouTubeAnalyticsService: self._fetch_daily_data(video_ids, day_previous_start, day_previous_end, access_token), self._fetch_top_videos(video_ids, start_date, end_date, access_token), self._fetch_demographics(start_date, end_date, access_token), - self._fetch_region(video_ids, start_date, end_date, access_token), + self._fetch_region(start_date, end_date, access_token), ] # 병렬 실행 @@ -206,8 +206,6 @@ class YouTubeAnalyticsService: estimatedMinutesWatched, averageViewDuration, subscribersGained] - Note: - annotationClickThroughRate는 2019년 annotations 기능 제거로 deprecated. """ logger.debug( f"[YouTubeAnalyticsService._fetch_kpi] START - video_count={len(video_ids)}" @@ -218,7 +216,7 @@ class YouTubeAnalyticsService: "startDate": start_date, "endDate": end_date, "metrics": "views,likes,comments,shares,estimatedMinutesWatched,averageViewDuration,subscribersGained", - # "filters": f"video=={','.join(video_ids)}", + "filters": f"video=={','.join(video_ids)}", } result = await self._call_api(params, access_token) @@ -393,7 +391,6 @@ class YouTubeAnalyticsService: async def _fetch_region( self, - video_ids: list[str], start_date: str, end_date: str, access_token: str, @@ -403,7 +400,6 @@ class YouTubeAnalyticsService: 지역별 조회수 분포를 조회합니다 (상위 5개). Args: - video_ids: YouTube 영상 ID 리스트 start_date: 조회 시작일 (YYYY-MM-DD) end_date: 조회 종료일 (YYYY-MM-DD) access_token: OAuth 2.0 액세스 토큰 @@ -421,9 +417,7 @@ class YouTubeAnalyticsService: "endDate": end_date, "dimensions": "country", "metrics": "views", - "filters": f"video=={','.join(video_ids)}", "sort": "-views", - "maxResults": "5", } result = await self._call_api(params, access_token) diff --git a/docs/dashboard/YouTube Analytics API.md b/docs/dashboard/YouTube Analytics API.md index e52e37f..fd934be 100644 --- a/docs/dashboard/YouTube Analytics API.md +++ b/docs/dashboard/YouTube Analytics API.md @@ -70,7 +70,7 @@ API 요청 시 `dimensions` 파라미터에 들어갈 수 있는 값들입니다 | -------------- | ------- | --------------------------- | -------------- | | `**ageGroup`** | **연령대** | 시청자 연령 분포 (18-24, 25-34...) | `video`와 혼용 불가 | | `**gender`** | **성별** | 남녀 성비 (male, female) | `video`와 혼용 불가 | -| `**country`** | **국가** | 국가별 시청자 수 (KR, US...) | 지도 차트용 | +| `**country`** | **국가** | 국가별 시청자 수 (KR, US...) | 지도 차트용, 채널 전체 기준 | ### C. 유입 및 기기 (Traffic Device) @@ -84,22 +84,76 @@ API 요청 시 `dimensions` 파라미터에 들어갈 수 있는 값들입니다 --- -## 3. 자주 쓰는 조합 (Best Practice) +## 3. 현재 사용 중인 API 호출 조합 + +대시보드에서 실제로 사용하는 7가지 호출 조합입니다. 모두 `ids=channel==MINE`으로 고정합니다. + +### 1. KPI 요약 (`_fetch_kpi`) — 현재/이전 기간 각 1회 + +| 파라미터 | 값 | +| ---------- | ------------------------------------------------------------------------------------- | +| dimensions | (없음) | +| metrics | `views, likes, comments, shares, estimatedMinutesWatched, averageViewDuration, subscribersGained` | +| filters | `video==ID1,ID2,...` (업로드된 영상 ID 최대 30개) | + +> 현재/이전 기간을 각각 호출하여 trend(증감률) 계산에 사용. + +--- + +### 2. 월별 추이 차트 (`_fetch_monthly_data`) — 최근 12개월 / 이전 12개월 각 1회 + +| 파라미터 | 값 | +| ---------- | -------------------- | +| dimensions | `month` | +| metrics | `views` | +| filters | `video==ID1,ID2,...` | +| sort | `month` | + +--- + +### 3. 일별 추이 차트 (`_fetch_daily_data`) — 최근 30일 / 이전 30일 각 1회 + +| 파라미터 | 값 | +| ---------- | -------------------- | +| dimensions | `day` | +| metrics | `views` | +| filters | `video==ID1,ID2,...` | +| sort | `day` | + +--- + +### 4. 인기 영상 TOP 4 (`_fetch_top_videos`) + +| 파라미터 | 값 | +| ---------- | ------------------------ | +| dimensions | `video` | +| metrics | `views, likes, comments` | +| filters | `video==ID1,ID2,...` | +| sort | `-views` | +| maxResults | `4` | + +--- + +### 5. 시청자 연령/성별 분포 (`_fetch_demographics`) — 채널 전체 기준 + +| 파라미터 | 값 | +| ---------- | ----------------------- | +| dimensions | `ageGroup, gender` | +| metrics | `viewerPercentage` | + +> `ageGroup`, `gender` 차원은 `video` 필터와 혼용 불가 → 채널 전체 시청자 기준. + +--- + +### 6. 지역별 조회수 TOP 5 (`_fetch_region`) — 채널 전체 기준 + +| 파라미터 | 값 | +| ---------- | -------------------- | +| dimensions | `country` | +| metrics | `views` | +| sort | `-views` | +| maxResults | `5` | -1. **프로젝트 전체 요약 (KPI)** - - `dimensions`: (없음) - - `metrics`: `views,likes,estimatedRevenue` - - `filters`: `video==ID1,ID2...` -2. **일별 성장 그래프 (Line Chart)** - - `dimensions`: `day` - - `metrics`: `views` - - `filters`: `video==ID1,ID2...` - - `sort`: `day` -3. **인기 영상 랭킹 (Table)** - - `dimensions`: `video` - - `metrics`: `views,averageViewDuration` - - `filters`: `video==ID1,ID2...` - - `sort`: `-views` ---