feat: daily suggestion cache, saved ideas (Swipe File), remove Piani Attivi
Backend:
- Suggestions cached in DB per user, regenerated only after 24h
- ?force=true parameter to regenerate on demand
- New endpoints: GET/POST/DELETE /content/ideas for saved ideas
- POST /content/ideas/{id}/mark-used to track usage
Frontend:
- Dashboard: suggestions loaded from cache, not regenerated on every visit
- Dashboard: "Salva idea" button on each suggestion card
- Dashboard: "Dammi altri suggerimenti" CTA to force regeneration
- Dashboard: removed "Piani Attivi" stat card
- SavedIdeas page: list saved ideas, add new, delete, generate from idea
- Sidebar: added "Idee" nav item after "Contenuti"
- App.jsx: added /ideas route
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -342,34 +342,14 @@ def approve_post(
|
||||
return post
|
||||
|
||||
|
||||
@router.get("/suggestions")
|
||||
def get_topic_suggestions(
|
||||
character_id: int | None = Query(None),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Suggest 3 content topics based on character profile and recent posts."""
|
||||
if character_id:
|
||||
character = (
|
||||
db.query(Character)
|
||||
.filter(Character.id == character_id, Character.user_id == current_user.id)
|
||||
.first()
|
||||
)
|
||||
else:
|
||||
character = (
|
||||
db.query(Character)
|
||||
.filter(Character.user_id == current_user.id, Character.is_active == True)
|
||||
.first()
|
||||
)
|
||||
if not character:
|
||||
return {"suggestions": [], "character_id": None}
|
||||
|
||||
def _generate_suggestions(db: Session, current_user: User, character) -> list[str]:
|
||||
"""Internal: call LLM to generate topic suggestions."""
|
||||
provider_name = _get_setting(db, "llm_provider", current_user.id)
|
||||
api_key = _get_setting(db, "llm_api_key", current_user.id)
|
||||
model = _get_setting(db, "llm_model", current_user.id)
|
||||
|
||||
if not provider_name or not api_key:
|
||||
return {"suggestions": [], "character_id": character.id, "needs_setup": True}
|
||||
return []
|
||||
|
||||
recent_posts = (
|
||||
db.query(Post)
|
||||
@@ -406,12 +386,174 @@ def get_topic_suggestions(
|
||||
try:
|
||||
result = llm.generate(prompt, system=system_prompt)
|
||||
lines = [line.strip() for line in result.strip().splitlines() if line.strip()]
|
||||
suggestions = lines[:3]
|
||||
return lines[:3]
|
||||
except Exception:
|
||||
suggestions = []
|
||||
return []
|
||||
|
||||
|
||||
@router.get("/suggestions")
|
||||
def get_topic_suggestions(
|
||||
force: bool = Query(False),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Get cached daily suggestions or generate new ones. Use force=true to regenerate."""
|
||||
character = (
|
||||
db.query(Character)
|
||||
.filter(Character.user_id == current_user.id, Character.is_active == True)
|
||||
.first()
|
||||
)
|
||||
if not character:
|
||||
return {"suggestions": [], "character_id": None}
|
||||
|
||||
provider_name = _get_setting(db, "llm_provider", current_user.id)
|
||||
api_key = _get_setting(db, "llm_api_key", current_user.id)
|
||||
if not provider_name or not api_key:
|
||||
return {"suggestions": [], "character_id": character.id, "needs_setup": True}
|
||||
|
||||
today = date.today().isoformat()
|
||||
|
||||
# Check cache
|
||||
if not force:
|
||||
cached = _get_setting(db, "daily_suggestions", current_user.id)
|
||||
if cached and isinstance(cached, dict) and cached.get("date") == today:
|
||||
return {
|
||||
"suggestions": cached.get("suggestions", []),
|
||||
"character_id": cached.get("character_id", character.id),
|
||||
"character_name": cached.get("character_name", character.name),
|
||||
"cached": True,
|
||||
}
|
||||
|
||||
# Generate new suggestions
|
||||
suggestions = _generate_suggestions(db, current_user, character)
|
||||
|
||||
# Save to cache
|
||||
cache_data = {
|
||||
"date": today,
|
||||
"suggestions": suggestions,
|
||||
"character_id": character.id,
|
||||
"character_name": character.name,
|
||||
}
|
||||
existing = (
|
||||
db.query(SystemSetting)
|
||||
.filter(SystemSetting.key == "daily_suggestions", SystemSetting.user_id == current_user.id)
|
||||
.first()
|
||||
)
|
||||
if existing:
|
||||
existing.value = cache_data
|
||||
existing.updated_at = datetime.utcnow()
|
||||
else:
|
||||
db.add(SystemSetting(key="daily_suggestions", value=cache_data, user_id=current_user.id))
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"suggestions": suggestions,
|
||||
"character_id": character.id,
|
||||
"character_name": character.name,
|
||||
"cached": False,
|
||||
}
|
||||
|
||||
|
||||
# === Saved Ideas (Swipe File) ===
|
||||
|
||||
@router.get("/ideas")
|
||||
def get_saved_ideas(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Get all saved ideas for the user."""
|
||||
setting = (
|
||||
db.query(SystemSetting)
|
||||
.filter(SystemSetting.key == "saved_ideas", SystemSetting.user_id == current_user.id)
|
||||
.first()
|
||||
)
|
||||
ideas = setting.value if setting and isinstance(setting.value, list) else []
|
||||
return {"ideas": ideas, "total": len(ideas)}
|
||||
|
||||
|
||||
@router.post("/ideas")
|
||||
def save_idea(
|
||||
data: dict,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Save an idea for later use."""
|
||||
text = data.get("text", "").strip()
|
||||
if not text:
|
||||
raise HTTPException(status_code=400, detail="Text is required")
|
||||
|
||||
setting = (
|
||||
db.query(SystemSetting)
|
||||
.filter(SystemSetting.key == "saved_ideas", SystemSetting.user_id == current_user.id)
|
||||
.first()
|
||||
)
|
||||
ideas = setting.value if setting and isinstance(setting.value, list) else []
|
||||
|
||||
new_idea = {
|
||||
"id": str(uuid.uuid4())[:8],
|
||||
"text": text,
|
||||
"note": data.get("note", ""),
|
||||
"saved_at": datetime.utcnow().isoformat(),
|
||||
"used": False,
|
||||
}
|
||||
ideas.insert(0, new_idea)
|
||||
|
||||
if setting:
|
||||
setting.value = ideas
|
||||
setting.updated_at = datetime.utcnow()
|
||||
else:
|
||||
db.add(SystemSetting(key="saved_ideas", value=ideas, user_id=current_user.id))
|
||||
db.commit()
|
||||
return new_idea
|
||||
|
||||
|
||||
@router.delete("/ideas/{idea_id}")
|
||||
def delete_idea(
|
||||
idea_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Delete a saved idea."""
|
||||
setting = (
|
||||
db.query(SystemSetting)
|
||||
.filter(SystemSetting.key == "saved_ideas", SystemSetting.user_id == current_user.id)
|
||||
.first()
|
||||
)
|
||||
if not setting or not isinstance(setting.value, list):
|
||||
raise HTTPException(status_code=404, detail="Idea not found")
|
||||
|
||||
ideas = [i for i in setting.value if i.get("id") != idea_id]
|
||||
if len(ideas) == len(setting.value):
|
||||
raise HTTPException(status_code=404, detail="Idea not found")
|
||||
|
||||
setting.value = ideas
|
||||
setting.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.post("/ideas/{idea_id}/mark-used")
|
||||
def mark_idea_used(
|
||||
idea_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Mark an idea as used."""
|
||||
setting = (
|
||||
db.query(SystemSetting)
|
||||
.filter(SystemSetting.key == "saved_ideas", SystemSetting.user_id == current_user.id)
|
||||
.first()
|
||||
)
|
||||
if not setting or not isinstance(setting.value, list):
|
||||
raise HTTPException(status_code=404, detail="Idea not found")
|
||||
|
||||
for idea in setting.value:
|
||||
if idea.get("id") == idea_id:
|
||||
idea["used"] = True
|
||||
break
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail="Idea not found")
|
||||
|
||||
setting.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
return {"ok": True}
|
||||
|
||||
Reference in New Issue
Block a user