feat(music-lab): market_trends·trend_reports DB + market.py + /api/music/market 5개 API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -137,6 +137,38 @@ def init_db() -> None:
|
||||
""")
|
||||
conn.execute("CREATE INDEX IF NOT EXISTS idx_rr_month ON revenue_records(record_month DESC)")
|
||||
|
||||
# ── market_trends 테이블 ──────────────────────────────────────────
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS market_trends (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
source TEXT NOT NULL DEFAULT '',
|
||||
country TEXT NOT NULL DEFAULT '',
|
||||
genre TEXT NOT NULL DEFAULT '',
|
||||
keyword TEXT NOT NULL DEFAULT '',
|
||||
score REAL NOT NULL DEFAULT 0.0,
|
||||
rank INTEGER,
|
||||
metadata TEXT NOT NULL DEFAULT '{}',
|
||||
collected_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
||||
)
|
||||
""")
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_mt_country_source "
|
||||
"ON market_trends(country, source, collected_at DESC)"
|
||||
)
|
||||
|
||||
# ── trend_reports 테이블 ──────────────────────────────────────────
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS trend_reports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
report_date TEXT UNIQUE NOT NULL DEFAULT '',
|
||||
top_genres TEXT NOT NULL DEFAULT '[]',
|
||||
top_keywords TEXT NOT NULL DEFAULT '[]',
|
||||
recommended_styles TEXT NOT NULL DEFAULT '[]',
|
||||
insights TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
# ── music_tasks CRUD ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -583,3 +615,88 @@ def get_revenue_dashboard() -> dict:
|
||||
"by_month": [dict(r) for r in by_month],
|
||||
"by_country": [dict(r) for r in by_country],
|
||||
}
|
||||
|
||||
|
||||
# ── market_trends CRUD ────────────────────────────────────────────────────────
|
||||
|
||||
def insert_market_trends(trends: list) -> None:
|
||||
with _conn() as conn:
|
||||
conn.executemany(
|
||||
"""INSERT INTO market_trends (source, country, genre, keyword, score, rank, metadata)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
||||
[(t.get("source",""), t.get("country",""), t.get("genre",""),
|
||||
t.get("keyword",""), t.get("score", 0.0), t.get("rank"),
|
||||
json.dumps(t.get("metadata", {})))
|
||||
for t in trends],
|
||||
)
|
||||
|
||||
|
||||
def get_market_trends(
|
||||
country: str = None, genre: str = None, source: str = None, days: int = 7
|
||||
) -> list:
|
||||
with _conn() as conn:
|
||||
q = "SELECT * FROM market_trends WHERE collected_at >= datetime('now', ?)"
|
||||
params: list = [f"-{days} days"]
|
||||
if country:
|
||||
q += " AND country=?"; params.append(country)
|
||||
if genre:
|
||||
q += " AND genre=?"; params.append(genre)
|
||||
if source:
|
||||
q += " AND source=?"; params.append(source)
|
||||
q += " ORDER BY collected_at DESC LIMIT 500"
|
||||
rows = conn.execute(q, params).fetchall()
|
||||
return [
|
||||
{"id": r["id"], "source": r["source"], "country": r["country"],
|
||||
"genre": r["genre"], "keyword": r["keyword"], "score": r["score"],
|
||||
"rank": r["rank"], "metadata": json.loads(r["metadata"]),
|
||||
"collected_at": r["collected_at"]}
|
||||
for r in rows
|
||||
]
|
||||
|
||||
|
||||
# ── trend_reports CRUD ────────────────────────────────────────────────────────
|
||||
|
||||
def upsert_trend_report(data: dict) -> None:
|
||||
with _conn() as conn:
|
||||
conn.execute(
|
||||
"""INSERT INTO trend_reports
|
||||
(report_date, top_genres, top_keywords, recommended_styles, insights)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
ON CONFLICT(report_date) DO UPDATE SET
|
||||
top_genres=excluded.top_genres,
|
||||
top_keywords=excluded.top_keywords,
|
||||
recommended_styles=excluded.recommended_styles,
|
||||
insights=excluded.insights""",
|
||||
(data["report_date"], json.dumps(data["top_genres"]),
|
||||
json.dumps(data["top_keywords"]), json.dumps(data["recommended_styles"]),
|
||||
data["insights"]),
|
||||
)
|
||||
|
||||
|
||||
def get_latest_trend_report() -> Optional[Dict[str, Any]]:
|
||||
with _conn() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM trend_reports ORDER BY report_date DESC LIMIT 1"
|
||||
).fetchone()
|
||||
if not row:
|
||||
return None
|
||||
return {
|
||||
"id": row["id"],
|
||||
"report_date": row["report_date"],
|
||||
"top_genres": json.loads(row["top_genres"]),
|
||||
"top_keywords": json.loads(row["top_keywords"]),
|
||||
"recommended_styles": json.loads(row["recommended_styles"]),
|
||||
"insights": row["insights"],
|
||||
"created_at": row["created_at"],
|
||||
}
|
||||
|
||||
|
||||
def get_trend_reports(limit: int = 10) -> list:
|
||||
with _conn() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT id, report_date, insights, created_at FROM trend_reports "
|
||||
"ORDER BY report_date DESC LIMIT ?", (limit,)
|
||||
).fetchall()
|
||||
return [{"id": r["id"], "report_date": r["report_date"],
|
||||
"insights": r["insights"][:100], "created_at": r["created_at"]}
|
||||
for r in rows]
|
||||
|
||||
Reference in New Issue
Block a user