Update ui.py
Browse files
ui.py
CHANGED
|
@@ -1,66 +1,126 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
import httpx
|
| 3 |
import json
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
def build_ui(fastapi_app):
|
| 6 |
-
base = "" # 同一プロセスなので相対パスでOK
|
| 7 |
-
|
| 8 |
async def do_summarize(platforms, keywords_csv, brand, limit, language):
|
| 9 |
keywords = [k.strip() for k in keywords_csv.split(",") if k.strip()]
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
"platforms": platforms, "keywords": keywords, "brand": brand,
|
| 13 |
-
"limit": int(limit), "language": language
|
| 14 |
-
})
|
| 15 |
-
r.raise_for_status()
|
| 16 |
-
data = r.json()
|
| 17 |
-
items = data["items"]
|
| 18 |
-
summary = data["summary"]
|
| 19 |
return json.dumps(items, ensure_ascii=False, indent=2), summary
|
| 20 |
|
| 21 |
async def do_generate_plan(brand, language, platforms, keywords_csv, start_date, tone, cta, image_style_hint):
|
| 22 |
keywords = [k.strip() for k in keywords_csv.split(",") if k.strip()]
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
async def do_list_calendar():
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
async def do_approve(post_id):
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
async def do_schedule(post_id, iso):
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
async def save_keywords(keywords_csv):
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
async def load_keywords():
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
|
|
|
| 64 |
|
| 65 |
with gr.Blocks(title="SNS運用AIライト") as demo:
|
| 66 |
gr.Markdown("## SNS運用AIライト — 競合/トレンド要約 → 1週間案 → 承認 → 予約投稿")
|
|
@@ -93,7 +153,7 @@ def build_ui(fastapi_app):
|
|
| 93 |
gr.Markdown("承認→予約→公開(APScheduler)が動きます。")
|
| 94 |
post_id = gr.Number(label="Post ID")
|
| 95 |
approve_btn = gr.Button("承認する")
|
| 96 |
-
schedule_iso = gr.Textbox(label="予約日時(ISO, 例: 2025-09-01T09:00:00Z)")
|
| 97 |
schedule_btn = gr.Button("予約に登録")
|
| 98 |
out = gr.Code(label="レスポンス", language="json")
|
| 99 |
approve_btn.click(do_approve, [post_id], [out])
|
|
|
|
| 1 |
import gradio as gr
|
|
|
|
| 2 |
import json
|
| 3 |
+
from db import SessionLocal, Post, Keyword
|
| 4 |
+
from services.llm import summarize_trends_llm, generate_week_plan_llm
|
| 5 |
+
from services.trend_monitor import fetch_trend_samples
|
| 6 |
+
from services.scheduler import schedule_post_job
|
| 7 |
|
| 8 |
def build_ui(fastapi_app):
|
|
|
|
|
|
|
| 9 |
async def do_summarize(platforms, keywords_csv, brand, limit, language):
|
| 10 |
keywords = [k.strip() for k in keywords_csv.split(",") if k.strip()]
|
| 11 |
+
items = await fetch_trend_samples(platforms, keywords, int(limit))
|
| 12 |
+
summary = await summarize_trends_llm(items, brand, language)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
return json.dumps(items, ensure_ascii=False, indent=2), summary
|
| 14 |
|
| 15 |
async def do_generate_plan(brand, language, platforms, keywords_csv, start_date, tone, cta, image_style_hint):
|
| 16 |
keywords = [k.strip() for k in keywords_csv.split(",") if k.strip()]
|
| 17 |
+
posts = await generate_week_plan_llm(
|
| 18 |
+
brand=brand,
|
| 19 |
+
language=language,
|
| 20 |
+
platforms=platforms,
|
| 21 |
+
keywords=keywords,
|
| 22 |
+
start_date=(start_date or None),
|
| 23 |
+
tone=tone,
|
| 24 |
+
cta=cta,
|
| 25 |
+
image_style_hint=image_style_hint,
|
| 26 |
+
)
|
| 27 |
+
# DB保存
|
| 28 |
+
session = SessionLocal()
|
| 29 |
+
try:
|
| 30 |
+
out = []
|
| 31 |
+
for p in posts:
|
| 32 |
+
post = Post(
|
| 33 |
+
platform=p.platform,
|
| 34 |
+
scheduled_at=p.scheduled_at,
|
| 35 |
+
text=p.text,
|
| 36 |
+
image_prompt=p.image_prompt,
|
| 37 |
+
status="draft",
|
| 38 |
+
)
|
| 39 |
+
session.add(post)
|
| 40 |
+
session.flush()
|
| 41 |
+
out.append({
|
| 42 |
+
"id": post.id,
|
| 43 |
+
"platform": post.platform,
|
| 44 |
+
"scheduled_at": post.scheduled_at,
|
| 45 |
+
"text": post.text,
|
| 46 |
+
"image_prompt": post.image_prompt,
|
| 47 |
+
"status": post.status,
|
| 48 |
+
})
|
| 49 |
+
session.commit()
|
| 50 |
+
return json.dumps(out, ensure_ascii=False, indent=2)
|
| 51 |
+
finally:
|
| 52 |
+
session.close()
|
| 53 |
|
| 54 |
async def do_list_calendar():
|
| 55 |
+
session = SessionLocal()
|
| 56 |
+
try:
|
| 57 |
+
posts = session.query(Post).order_by(Post.scheduled_at.asc().nulls_last()).all()
|
| 58 |
+
out = [{
|
| 59 |
+
"id": p.id,
|
| 60 |
+
"platform": p.platform,
|
| 61 |
+
"scheduled_at": p.scheduled_at,
|
| 62 |
+
"text": p.text,
|
| 63 |
+
"image_prompt": p.image_prompt,
|
| 64 |
+
"status": p.status,
|
| 65 |
+
} for p in posts]
|
| 66 |
+
return json.dumps({"events": out}, ensure_ascii=False, indent=2)
|
| 67 |
+
finally:
|
| 68 |
+
session.close()
|
| 69 |
|
| 70 |
async def do_approve(post_id):
|
| 71 |
+
session = SessionLocal()
|
| 72 |
+
try:
|
| 73 |
+
post = session.get(Post, int(post_id))
|
| 74 |
+
if not post:
|
| 75 |
+
return json.dumps({"error": "post not found"}, ensure_ascii=False)
|
| 76 |
+
post.status = "approved"
|
| 77 |
+
session.commit()
|
| 78 |
+
return json.dumps({
|
| 79 |
+
"id": post.id, "platform": post.platform, "scheduled_at": post.scheduled_at,
|
| 80 |
+
"text": post.text, "image_prompt": post.image_prompt, "status": post.status
|
| 81 |
+
}, ensure_ascii=False, indent=2)
|
| 82 |
+
finally:
|
| 83 |
+
session.close()
|
| 84 |
|
| 85 |
async def do_schedule(post_id, iso):
|
| 86 |
+
session = SessionLocal()
|
| 87 |
+
try:
|
| 88 |
+
post = session.get(Post, int(post_id))
|
| 89 |
+
if not post:
|
| 90 |
+
return json.dumps({"error": "post not found"}, ensure_ascii=False)
|
| 91 |
+
if post.status != "approved":
|
| 92 |
+
return json.dumps({"error": "post must be approved before scheduling"}, ensure_ascii=False)
|
| 93 |
+
post.scheduled_at = iso
|
| 94 |
+
post.status = "scheduled"
|
| 95 |
+
session.commit()
|
| 96 |
+
# 予約ジョブ登録
|
| 97 |
+
schedule_post_job(post.id, post.platform, post.text, post.image_prompt, post.scheduled_at)
|
| 98 |
+
return json.dumps({
|
| 99 |
+
"id": post.id, "platform": post.platform, "scheduled_at": post.scheduled_at,
|
| 100 |
+
"text": post.text, "image_prompt": post.image_prompt, "status": post.status
|
| 101 |
+
}, ensure_ascii=False, indent=2)
|
| 102 |
+
finally:
|
| 103 |
+
session.close()
|
| 104 |
|
| 105 |
async def save_keywords(keywords_csv):
|
| 106 |
+
kws = [k.strip() for k in keywords_csv.split(",") if k.strip()]
|
| 107 |
+
session = SessionLocal()
|
| 108 |
+
try:
|
| 109 |
+
session.query(Keyword).delete()
|
| 110 |
+
for kw in kws:
|
| 111 |
+
session.add(Keyword(text=kw))
|
| 112 |
+
session.commit()
|
| 113 |
+
return "保存しました"
|
| 114 |
+
finally:
|
| 115 |
+
session.close()
|
| 116 |
|
| 117 |
async def load_keywords():
|
| 118 |
+
session = SessionLocal()
|
| 119 |
+
try:
|
| 120 |
+
kws = [k.text for k in session.query(Keyword).all()]
|
| 121 |
+
return ", ".join(kws)
|
| 122 |
+
finally:
|
| 123 |
+
session.close()
|
| 124 |
|
| 125 |
with gr.Blocks(title="SNS運用AIライト") as demo:
|
| 126 |
gr.Markdown("## SNS運用AIライト — 競合/トレンド要約 → 1週間案 → 承認 → 予約投稿")
|
|
|
|
| 153 |
gr.Markdown("承認→予約→公開(APScheduler)が動きます。")
|
| 154 |
post_id = gr.Number(label="Post ID")
|
| 155 |
approve_btn = gr.Button("承認する")
|
| 156 |
+
schedule_iso = gr.Textbox(label="予約日時(ISO, 例: 2025-09-01T09:00:00Z または +09:00)")
|
| 157 |
schedule_btn = gr.Button("予約に登録")
|
| 158 |
out = gr.Code(label="レスポンス", language="json")
|
| 159 |
approve_btn.click(do_approve, [post_id], [out])
|