From 09bb7a71ee194a2e2ac7f817afdf1ee69a7aa027 Mon Sep 17 00:00:00 2001 From: jaehwang Date: Wed, 20 May 2026 17:16:19 +0900 Subject: [PATCH] =?UTF-8?q?report=20=ED=8C=A8=EC=B9=98=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80.=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=EC=A7=81=EA=B2=B0=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/common/db.py | 12 +++---- app/services/analysis.py | 78 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/app/common/db.py b/app/common/db.py index 29259e9..ca79de8 100644 --- a/app/common/db.py +++ b/app/common/db.py @@ -148,7 +148,7 @@ async def is_done(table: str, row_id: int | None) -> bool: return r["status"] == "done" -async def _fetch_raw(table: str, row_id: int | None) -> dict | None: +async def fetch_raw(table: str, row_id: int | None) -> dict | None: if row_id is None: return None row = await fetchone(f"SELECT raw_data FROM {table} WHERE id = %s", (row_id,)) @@ -164,11 +164,11 @@ async def get_analysis_raw_data(analysis_run_id: str) -> dict: (analysis_run_id,), ) return { - "instagram": await _fetch_raw("instagram_data", run["instagram_data_id"]), - "facebook": await _fetch_raw("facebook_data", run["facebook_data_id"]), - "naver_blog": await _fetch_raw("naver_blog_data", run["naver_blog_data_id"]), - "youtube": await _fetch_raw("youtube_data", run["youtube_data_id"]), - "gangnam_unni": await _fetch_raw("gangnam_unni_data", run["gangnam_unni_data_id"]), + "instagram": await fetch_raw("instagram_data", run["instagram_data_id"]), + "facebook": await fetch_raw("facebook_data", run["facebook_data_id"]), + "naver_blog": await fetch_raw("naver_blog_data", run["naver_blog_data_id"]), + "youtube": await fetch_raw("youtube_data", run["youtube_data_id"]), + "gangnam_unni": await fetch_raw("gangnam_unni_data", run["gangnam_unni_data_id"]), } diff --git a/app/services/analysis.py b/app/services/analysis.py index c040eff..0a2450e 100644 --- a/app/services/analysis.py +++ b/app/services/analysis.py @@ -1,6 +1,6 @@ import json import logging -from common.db import fetchone, execute, get_analysis_raw_data, save_analysis_report, get_market_analysis +from common.db import fetchone, execute, fetch_raw, get_analysis_raw_data, save_analysis_report, get_market_analysis from integrations.llm.llm_service import LLMService from integrations.llm.prompt import report_prompt, plan_prompt from integrations.llm.schemas.report import ReportOutput @@ -47,7 +47,6 @@ async def generate_report(analysis_run_id: str) -> ReportOutput: return await LLMService(provider="perplexity").generate(report_prompt, input_data) - async def generate_plan(analysis_run_id: str) -> PlanOutput: run = await fetchone( "SELECT hospital_id, report_data FROM analysis_runs WHERE analysis_run_id = %s", @@ -84,9 +83,84 @@ async def generate_plan(analysis_run_id: str) -> PlanOutput: return await LLMService(provider="perplexity").generate(plan_prompt, input_data) +async def _build_overrides(analysis_run_id: str) -> dict: + run = await fetchone( + "SELECT hospital_id, instagram_data_id, facebook_data_id," + " naver_blog_data_id, youtube_data_id, gangnam_unni_data_id" + " FROM analysis_runs WHERE analysis_run_id = %s", + (analysis_run_id,), + ) + if not run: + return {} + + hospital_row = await fetchone( + "SELECT raw_data FROM hospital_baseinfo WHERE hospital_id = %s", + (run["hospital_id"],), + ) + hospital = json.loads(hospital_row["raw_data"]) if hospital_row and isinstance(hospital_row.get("raw_data"), str) else (hospital_row or {}).get("raw_data") or {} + instagram = await fetch_raw("instagram_data", run["instagram_data_id"]) or {} + facebook = await fetch_raw("facebook_data", run["facebook_data_id"]) or {} + naver_blog = await fetch_raw("naver_blog_data", run["naver_blog_data_id"]) or {} + youtube = await fetch_raw("youtube_data", run["youtube_data_id"]) or {} + gangnam_unni = await fetch_raw("gangnam_unni_data", run["gangnam_unni_data_id"]) or {} + + snapshot: dict = {} + + # ── gangnam_unni ────────────────────────────────────────────────────────── + doctors = gangnam_unni.get("doctors", []) + lead = max(doctors, key=lambda d: d.get("reviews", 0)) if doctors else None + if gangnam_unni.get("name"): snapshot["name"] = gangnam_unni["name"] + if gangnam_unni.get("rating"): snapshot["overall_rating"] = gangnam_unni["rating"] + if gangnam_unni.get("totalReviews"): snapshot["total_reviews"] = gangnam_unni["totalReviews"] + if gangnam_unni.get("address"): snapshot["location"] = gangnam_unni["address"] + if gangnam_unni.get("badges"): snapshot["certifications"] = gangnam_unni["badges"] + if doctors: snapshot["staff_count"] = len(doctors) + if lead: + snapshot["lead_doctor"] = { + "name": lead.get("name"), + "credentials": lead.get("specialty"), + "rating": lead.get("rating"), + "review_count": lead.get("reviews"), + } + + # ── instagram ───────────────────────────────────────────────────────────── + ig_patch: dict = {} + if instagram.get("username"): ig_patch["handle"] = instagram["username"] + if instagram.get("posts"): ig_patch["posts"] = instagram["posts"] + if instagram.get("followers"): ig_patch["followers"] = instagram["followers"] + if instagram.get("following"): ig_patch["following"] = instagram["following"] + if instagram.get("bio"): ig_patch["bio"] = instagram["bio"] + if instagram.get("username"): ig_patch["profile_link"] = f"https://www.instagram.com/{instagram['username']}/" + + overrides: dict = {} + if snapshot: + overrides["clinic_snapshot"] = snapshot + if ig_patch: + overrides["instagram_audit"] = {"accounts": [ig_patch]} + return overrides + + +def _deep_merge(base: dict, overrides: dict) -> dict: + for k, v in overrides.items(): + if isinstance(v, dict) and isinstance(base.get(k), dict): + _deep_merge(base[k], v) + elif isinstance(v, list) and isinstance(base.get(k), list): + for i, item in enumerate(v): + if i < len(base[k]) and isinstance(item, dict) and isinstance(base[k][i], dict): + _deep_merge(base[k][i], item) + else: + base[k] = v + return base + +def _patch_report(result: ReportOutput, overrides: dict) -> ReportOutput: + merged = _deep_merge(result.model_dump(), overrides) + return ReportOutput(**merged) + + async def run_report_task(analysis_run_id: str) -> None: logger.info("[report] start run=%s", analysis_run_id) result = await generate_report(analysis_run_id) + result = _patch_report(result, await _build_overrides(analysis_run_id)) await save_analysis_report(analysis_run_id, result.model_dump()) logger.info("[report] done run=%s", analysis_run_id)