added post insight feature

This commit is contained in:
2026-02-25 15:07:53 +01:00
parent 7c9866c0a6
commit a3ea774b58
9 changed files with 992 additions and 1 deletions

View File

@@ -44,6 +44,7 @@ from src.services.storage_service import storage
from src.services.link_extractor import LinkExtractor, LinkExtractionError
from src.services.file_extractor import FileExtractor, FileExtractionError
from src.agents.link_topic_builder import LinkTopicBuilderAgent
from src.services.post_insights_service import compute_post_insights, refresh_post_insights_for_account
# Router for user frontend
user_router = APIRouter(tags=["user"])
@@ -2928,7 +2929,7 @@ async def linkedin_callback(
linkedin_vanity_name = None
try:
profile_response = await client.get(
"https://api.linkedin.com/v2/me",
"https://api.linkedin.com/v2/me?projection=(id,vanityName,localizedFirstName,localizedLastName)",
headers={"Authorization": f"Bearer {access_token}"}
)
if profile_response.status_code == 200:
@@ -3016,6 +3017,41 @@ async def linkedin_disconnect(request: Request):
raise HTTPException(status_code=500, detail=str(e))
# ==================== POST INSIGHTS ====================
@user_router.post("/api/insights/refresh")
async def refresh_post_insights(request: Request):
"""Manually refresh post insights (max once per day)."""
session = require_user_session(request)
if not session:
raise HTTPException(status_code=401, detail="Not authenticated")
try:
user_id = UUID(session.user_id)
linkedin_account = await db.get_linkedin_account(user_id)
if not linkedin_account:
raise HTTPException(status_code=400, detail="LinkedIn account not connected")
profile = await db.get_profile(user_id)
metadata = profile.metadata or {}
today = datetime.now(timezone.utc).date().isoformat()
last_refresh = metadata.get("post_insights_manual_refresh_date")
if last_refresh == today:
raise HTTPException(status_code=429, detail="Manual refresh already used today")
await refresh_post_insights_for_account(db, linkedin_account)
metadata["post_insights_manual_refresh_date"] = today
await db.update_profile(user_id, {"metadata": metadata})
return {"success": True, "refreshed_at": today}
except HTTPException:
raise
except Exception as e:
logger.exception(f"Failed to refresh post insights: {e}")
raise HTTPException(status_code=500, detail=str(e))
# ==================== COMPANY MANAGEMENT ENDPOINTS ====================
@user_router.get("/company/strategy", response_class=HTMLResponse)
@@ -3579,6 +3615,50 @@ async def employee_strategy_page(request: Request):
})
@user_router.get("/employee/insights", response_class=HTMLResponse)
async def employee_insights_page(request: Request):
"""Employee post insights page."""
session = require_user_session(request)
if not session:
return RedirectResponse(url="/login", status_code=302)
if session.account_type != "employee":
return RedirectResponse(url="/", status_code=302)
try:
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
linkedin_account = await db.get_linkedin_account(user_id)
post_insights = {"has_data": False}
if linkedin_account:
try:
from datetime import date, timedelta
since = (date.today() - timedelta(days=90)).isoformat()
insights_posts = await db.get_post_insights_posts(user_id)
insights_daily = await db.get_post_insights_daily(user_id, since_date=since)
post_insights = compute_post_insights(insights_posts, insights_daily)
except Exception as e:
logger.error(f"Error computing post insights: {e}")
return templates.TemplateResponse("employee_insights.html", {
"request": request,
"page": "insights",
"session": session,
"profile_picture": profile_picture,
"linkedin_account": linkedin_account,
"post_insights": post_insights
})
except Exception as e:
logger.error(f"Error loading insights: {e}")
return templates.TemplateResponse("employee_insights.html", {
"request": request,
"page": "insights",
"session": session,
"error": str(e)
})
# ============================================================================
# EMPLOYEE POST TYPES MANAGEMENT
# ============================================================================