마켓 분석 데이터 추가
parent
cda518c027
commit
20fdf53264
|
|
@ -59,6 +59,17 @@ async def fetchone(sql: str, args: tuple = ()) -> dict | None:
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def fetchall(sql: str, args: tuple = ()) -> list[dict]:
|
||||||
|
pool = await get_pool()
|
||||||
|
async with pool.acquire() as conn:
|
||||||
|
try:
|
||||||
|
async with conn.cursor(aiomysql.DictCursor) as cur:
|
||||||
|
await cur.execute(sql, args)
|
||||||
|
return await cur.fetchall()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
async def insert_instagram_row(hospital_id: str, url: str) -> int:
|
async def insert_instagram_row(hospital_id: str, url: str) -> int:
|
||||||
return await execute("INSERT INTO instagram_data (hospital_id, url) VALUES (%s, %s)", (hospital_id, url))
|
return await execute("INSERT INTO instagram_data (hospital_id, url) VALUES (%s, %s)", (hospital_id, url))
|
||||||
|
|
||||||
|
|
@ -247,3 +258,14 @@ async def save_hospital_raw_data(hospital_id: str, data: dict, analysis_run_id:
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
await _insert_hospital_history(hospital_id, analysis_run_id)
|
await _insert_hospital_history(hospital_id, analysis_run_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_market_analysis(analysis_run_id: str) -> dict:
|
||||||
|
rows = await fetchall(
|
||||||
|
"SELECT analysis_type, data FROM market_analysis WHERE analysis_run_id = %s AND status = 'done'",
|
||||||
|
(analysis_run_id,),
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
row["analysis_type"]: json.loads(row["data"]) if isinstance(row["data"], str) else row["data"]
|
||||||
|
for row in rows
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@ class PlanInput(BaseModel):
|
||||||
services: str | None = None
|
services: str | None = None
|
||||||
doctors: str | None = None
|
doctors: str | None = None
|
||||||
report: str | None = None
|
report: str | None = None
|
||||||
|
market_competitors: str | None = None
|
||||||
|
market_keywords: str | None = None
|
||||||
|
market_trend: str | None = None
|
||||||
|
market_target_audience: str | None = None
|
||||||
|
|
||||||
|
|
||||||
# --- BrandGuide ---
|
# --- BrandGuide ---
|
||||||
|
|
|
||||||
|
|
@ -317,6 +317,10 @@ class ReportInput(BaseModel):
|
||||||
naver_blog: str | None = None
|
naver_blog: str | None = None
|
||||||
youtube: str | None = None
|
youtube: str | None = None
|
||||||
gangnam_unni: str | None = None
|
gangnam_unni: str | None = None
|
||||||
|
market_competitors: str | None = None
|
||||||
|
market_keywords: str | None = None
|
||||||
|
market_trend: str | None = None
|
||||||
|
market_target_audience: str | None = None
|
||||||
|
|
||||||
|
|
||||||
# --- MarketingReport ---
|
# --- MarketingReport ---
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,20 @@
|
||||||
- 시술: {services}
|
- 시술: {services}
|
||||||
- 의료진: {doctors}
|
- 의료진: {doctors}
|
||||||
|
|
||||||
|
## 시장 분석 데이터
|
||||||
|
|
||||||
|
### 경쟁 병원
|
||||||
|
{market_competitors}
|
||||||
|
|
||||||
|
### 검색 키워드 트렌드
|
||||||
|
{market_keywords}
|
||||||
|
|
||||||
|
### 시장 트렌드
|
||||||
|
{market_trend}
|
||||||
|
|
||||||
|
### 잠재 고객 분석
|
||||||
|
{market_target_audience}
|
||||||
|
|
||||||
## 분석 리포트
|
## 분석 리포트
|
||||||
{report}
|
{report}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,20 @@
|
||||||
- 시술: {services}
|
- 시술: {services}
|
||||||
- 의료진: {doctors}
|
- 의료진: {doctors}
|
||||||
|
|
||||||
|
## 시장 분석 데이터
|
||||||
|
|
||||||
|
### 경쟁 병원
|
||||||
|
{market_competitors}
|
||||||
|
|
||||||
|
### 검색 키워드 트렌드
|
||||||
|
{market_keywords}
|
||||||
|
|
||||||
|
### 시장 트렌드
|
||||||
|
{market_trend}
|
||||||
|
|
||||||
|
### 잠재 고객 분석
|
||||||
|
{market_target_audience}
|
||||||
|
|
||||||
## 채널 데이터
|
## 채널 데이터
|
||||||
|
|
||||||
### 인스타그램
|
### 인스타그램
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from common.db import fetchone, execute, get_analysis_raw_data, save_analysis_report
|
from common.db import fetchone, execute, get_analysis_raw_data, save_analysis_report, get_market_analysis
|
||||||
from integrations.llm.llm_service import LLMService
|
from integrations.llm.llm_service import LLMService
|
||||||
from integrations.llm.prompt import report_prompt, plan_prompt
|
from integrations.llm.prompt import report_prompt, plan_prompt
|
||||||
from integrations.llm.schemas.report import ReportOutput
|
from integrations.llm.schemas.report import ReportOutput
|
||||||
|
|
@ -22,6 +22,10 @@ async def generate_report(analysis_run_id: str) -> ReportOutput:
|
||||||
raw_data = clinic_row["raw_data"] if clinic_row else None
|
raw_data = clinic_row["raw_data"] if clinic_row else None
|
||||||
clinic = json.loads(raw_data) if isinstance(raw_data, str) else (raw_data or {})
|
clinic = json.loads(raw_data) if isinstance(raw_data, str) else (raw_data or {})
|
||||||
raw = await get_analysis_raw_data(analysis_run_id)
|
raw = await get_analysis_raw_data(analysis_run_id)
|
||||||
|
market = await get_market_analysis(analysis_run_id)
|
||||||
|
|
||||||
|
def _json(v) -> str | None:
|
||||||
|
return json.dumps(v, ensure_ascii=False) if v else None
|
||||||
|
|
||||||
input_data = {
|
input_data = {
|
||||||
"clinic_name": clinic.get("clinicName"),
|
"clinic_name": clinic.get("clinicName"),
|
||||||
|
|
@ -31,8 +35,12 @@ async def generate_report(analysis_run_id: str) -> ReportOutput:
|
||||||
"slogan": clinic.get("slogan"),
|
"slogan": clinic.get("slogan"),
|
||||||
"services": json.dumps(clinic.get("services", []), ensure_ascii=False),
|
"services": json.dumps(clinic.get("services", []), ensure_ascii=False),
|
||||||
"doctors": json.dumps(clinic.get("doctors", []), ensure_ascii=False),
|
"doctors": json.dumps(clinic.get("doctors", []), ensure_ascii=False),
|
||||||
|
"market_competitors": _json(market.get("competitors")),
|
||||||
|
"market_keywords": _json(market.get("keywords")),
|
||||||
|
"market_trend": _json(market.get("trend")),
|
||||||
|
"market_target_audience": _json(market.get("target_audience")),
|
||||||
**{
|
**{
|
||||||
channel: json.dumps(data, ensure_ascii=False) if data else None
|
channel: _json(data)
|
||||||
for channel, data in raw.items()
|
for channel, data in raw.items()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +61,10 @@ async def generate_plan(analysis_run_id: str) -> PlanOutput:
|
||||||
clinic = json.loads(raw_data) if isinstance(raw_data, str) else (raw_data or {})
|
clinic = json.loads(raw_data) if isinstance(raw_data, str) else (raw_data or {})
|
||||||
report_data = run["report_data"]
|
report_data = run["report_data"]
|
||||||
report = json.loads(report_data) if isinstance(report_data, str) else report_data
|
report = json.loads(report_data) if isinstance(report_data, str) else report_data
|
||||||
|
market = await get_market_analysis(analysis_run_id)
|
||||||
|
|
||||||
|
def _json(v) -> str | None:
|
||||||
|
return json.dumps(v, ensure_ascii=False) if v else None
|
||||||
|
|
||||||
input_data = {
|
input_data = {
|
||||||
"clinic_name": clinic.get("clinicName"),
|
"clinic_name": clinic.get("clinicName"),
|
||||||
|
|
@ -62,7 +74,11 @@ async def generate_plan(analysis_run_id: str) -> PlanOutput:
|
||||||
"slogan": clinic.get("slogan"),
|
"slogan": clinic.get("slogan"),
|
||||||
"services": json.dumps(clinic.get("services", []), ensure_ascii=False),
|
"services": json.dumps(clinic.get("services", []), ensure_ascii=False),
|
||||||
"doctors": json.dumps(clinic.get("doctors", []), ensure_ascii=False),
|
"doctors": json.dumps(clinic.get("doctors", []), ensure_ascii=False),
|
||||||
"report": json.dumps(report, ensure_ascii=False) if report else None,
|
"report": _json(report),
|
||||||
|
"market_competitors": _json(market.get("competitors")),
|
||||||
|
"market_keywords": _json(market.get("keywords")),
|
||||||
|
"market_trend": _json(market.get("trend")),
|
||||||
|
"market_target_audience": _json(market.get("target_audience")),
|
||||||
}
|
}
|
||||||
|
|
||||||
return await LLMService(provider="perplexity").generate(plan_prompt, input_data)
|
return await LLMService(provider="perplexity").generate(plan_prompt, input_data)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue