optimize avatar consistency

This commit is contained in:
2026-02-12 10:20:21 +01:00
parent 811aed522d
commit cfd1d25b17
4 changed files with 169 additions and 36 deletions

View File

@@ -50,10 +50,46 @@ progress_store = {}
async def get_user_profile_picture(user_id: UUID) -> Optional[str]:
"""Get profile picture URL from user profile record (cached)."""
"""Get profile picture URL with priority: LinkedInAccount > Profile.profile_picture.
Priority:
1. LinkedInAccount.linkedin_picture (if account exists and is active)
2. Profile.profile_picture (from setup process)
Note: session.linkedin_picture (OAuth login) should be checked by caller first.
"""
# Check for connected LinkedIn account first
linkedin_account = await db.get_linkedin_account(user_id)
if linkedin_account and linkedin_account.is_active and linkedin_account.linkedin_picture:
return linkedin_account.linkedin_picture
# Fall back to profile picture from setup process
profile = await db.get_profile(user_id)
if profile and profile.profile_picture:
return profile.profile_picture
return None
async def get_user_avatar(session: UserSession, user_id: UUID) -> Optional[str]:
"""Get user avatar URL with complete priority order.
Priority:
1. LinkedInAccount.linkedin_picture (connected LinkedIn account for auto-posting)
2. Profile.profile_picture (from setup process)
3. session.linkedin_picture (from OAuth login)
Returns None if no avatar is available (caller should show initials).
"""
# First check LinkedInAccount and Profile
avatar = await get_user_profile_picture(user_id)
if avatar:
return avatar
# Fall back to session picture (OAuth login)
if session and session.linkedin_picture:
return session.linkedin_picture
return None
@@ -1197,11 +1233,36 @@ async def dashboard(request: Request):
# Company accounts have a different dashboard
if session.account_type == "company":
company = await db.get_company(UUID(session.company_id)) if session.company_id else None
employees = await db.get_company_employees(UUID(session.company_id)) if session.company_id else []
employees_raw = await db.get_company_employees(UUID(session.company_id)) if session.company_id else []
pending_invitations = await db.get_pending_invitations(UUID(session.company_id)) if session.company_id else []
quota = await db.get_company_daily_quota(UUID(session.company_id)) if session.company_id else None
license_key = await db.get_company_limits(UUID(session.company_id)) if session.company_id else None
# Add avatar URLs to employees
employees = []
for emp in employees_raw:
emp_session = UserSession(
user_id=str(emp.id),
linkedin_picture=emp.linkedin_picture,
email=emp.email,
account_type=emp.account_type.value if hasattr(emp.account_type, 'value') else emp.account_type,
display_name=emp.display_name
)
avatar_url = await get_user_avatar(emp_session, emp.id)
# Create employee dict with avatar
emp_dict = {
"id": emp.id,
"email": emp.email,
"display_name": emp.display_name or emp.linkedin_name or emp.email,
"onboarding_status": emp.onboarding_status,
"avatar_url": avatar_url
}
employees.append(emp_dict)
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("company_dashboard.html", {
"request": request,
"page": "home",
@@ -1211,14 +1272,15 @@ async def dashboard(request: Request):
"total_employees": len(employees),
"pending_invitations": pending_invitations,
"quota": quota,
"license_key": license_key
"license_key": license_key,
"profile_picture": profile_picture
})
# Employee accounts have their own dashboard
user_id = UUID(session.user_id)
profile = await db.get_profile(user_id)
posts = await db.get_generated_posts(user_id)
profile_picture = session.linkedin_picture or await get_user_profile_picture(user_id)
profile_picture = await get_user_avatar(session, user_id)
if session.account_type == "employee":
# Count post statuses
@@ -1268,7 +1330,7 @@ async def posts_page(request: Request):
user_id = UUID(session.user_id)
profile = await db.get_profile(user_id)
posts = await db.get_generated_posts(user_id)
profile_picture = session.linkedin_picture or await get_user_profile_picture(user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("posts.html", {
"request": request,
@@ -1320,6 +1382,8 @@ async def post_types_page(request: Request):
categorized_by_type[type_name] = []
categorized_by_type[type_name].append(post)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("post_types.html", {
"request": request,
"page": "post_types",
@@ -1327,10 +1391,12 @@ async def post_types_page(request: Request):
"post_types": post_types,
"uncategorized_posts": uncategorized_posts,
"categorized_posts": categorized_posts,
"categorized_by_type": categorized_by_type
"categorized_by_type": categorized_by_type,
"profile_picture": profile_picture
})
except Exception as e:
logger.error(f"Error loading post types page: {e}")
profile_picture = await get_user_avatar(session, UUID(session.user_id))
return templates.TemplateResponse("post_types.html", {
"request": request,
"page": "post_types",
@@ -1339,7 +1405,8 @@ async def post_types_page(request: Request):
"uncategorized_posts": [],
"categorized_posts": [],
"categorized_by_type": {},
"error": str(e)
"error": str(e),
"profile_picture": profile_picture
})
@@ -1509,15 +1576,8 @@ async def post_detail_page(request: Request, post_id: str):
linkedin_posts = await db.get_linkedin_posts(post.user_id)
reference_posts = [p.post_text for p in linkedin_posts if p.post_text and len(p.post_text) > 100][:10]
profile_picture_url = session.linkedin_picture
if not profile_picture_url:
for lp in linkedin_posts:
if lp.raw_data and isinstance(lp.raw_data, dict):
author = lp.raw_data.get("author", {})
if author and isinstance(author, dict):
profile_picture_url = author.get("profile_picture")
if profile_picture_url:
break
# Get avatar with priority: LinkedInAccount > profile_picture > session.linkedin_picture
profile_picture_url = await get_user_avatar(session, post.user_id)
profile_analysis_record = await db.get_profile_analysis(post.user_id)
profile_analysis = profile_analysis_record.full_analysis if profile_analysis_record else None
@@ -1575,13 +1635,17 @@ async def research_page(request: Request):
limit_reached = not can_create
limit_message = error_msg
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("research.html", {
"request": request,
"page": "research",
"session": session,
"user_id": session.user_id,
"limit_reached": limit_reached,
"limit_message": limit_message
"limit_message": limit_message,
"profile_picture": profile_picture
})
@@ -1600,13 +1664,17 @@ async def create_post_page(request: Request):
limit_reached = not can_create
limit_message = error_msg
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("create_post.html", {
"request": request,
"page": "create",
"session": session,
"user_id": session.user_id,
"limit_reached": limit_reached,
"limit_message": limit_message
"limit_message": limit_message,
"profile_picture": profile_picture
})
@@ -1621,7 +1689,7 @@ async def status_page(request: Request):
user_id = UUID(session.user_id)
profile = await db.get_profile(user_id)
status = await orchestrator.get_user_status(user_id)
profile_picture = session.linkedin_picture or await get_user_profile_picture(user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("status.html", {
"request": request,
@@ -2218,11 +2286,12 @@ async def settings_page(request: Request):
return RedirectResponse(url="/login", status_code=302)
try:
profile = await db.get_profile(UUID(session.user_id))
profile_picture = session.linkedin_picture or await get_user_profile_picture(UUID(session.user_id))
user_id = UUID(session.user_id)
profile = await db.get_profile(user_id)
profile_picture = await get_user_avatar(session, user_id)
# Get LinkedIn account if linked
linkedin_account = await db.get_linkedin_account(UUID(session.user_id))
linkedin_account = await db.get_linkedin_account(user_id)
return templates.TemplateResponse("settings.html", {
"request": request,
@@ -2480,13 +2549,17 @@ async def company_strategy_page(request: Request, success: bool = False):
company = await db.get_company(UUID(session.company_id))
strategy = company.company_strategy if company else {}
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("company_strategy.html", {
"request": request,
"page": "strategy",
"session": session,
"company": company,
"strategy": strategy,
"success": success
"success": success,
"profile_picture": profile_picture
})
@@ -2545,7 +2618,9 @@ async def company_accounts_page(request: Request):
company_id = UUID(session.company_id)
employees = await db.get_company_employees(company_id)
pending_invitations = await db.get_pending_invitations(company_id)
profile_picture = session.linkedin_picture
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("company_accounts.html", {
"request": request,
@@ -2576,15 +2651,25 @@ async def company_manage_page(request: Request, employee_id: str = None):
all_employees = await db.get_company_employees(company_id)
active_employees = [emp for emp in all_employees if emp.onboarding_status == "completed"]
# Build display info for employees
# Build display info for employees with correct avatar URLs
# Note: emp is a User object from get_company_employees which has linkedin_name and linkedin_picture
active_employees_info = []
for emp in active_employees:
# Create minimal session for employee to get avatar
emp_session = UserSession(
user_id=str(emp.id),
linkedin_picture=emp.linkedin_picture,
email=emp.email,
account_type=emp.account_type.value if hasattr(emp.account_type, 'value') else emp.account_type,
display_name=emp.display_name
)
avatar_url = await get_user_avatar(emp_session, emp.id)
active_employees_info.append({
"id": str(emp.id),
"email": emp.email,
"display_name": emp.linkedin_name or emp.display_name or emp.email,
"linkedin_picture": emp.linkedin_picture,
"linkedin_picture": avatar_url, # Now contains the correct avatar with priority
"onboarding_status": emp.onboarding_status
})
@@ -2609,6 +2694,9 @@ async def company_manage_page(request: Request, employee_id: str = None):
pending_posts = len([p for p in employee_posts if p.status in ['draft', 'pending']])
approved_posts = len([p for p in employee_posts if p.status in ['approved', 'published']])
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("company_manage.html", {
"request": request,
"page": "manage",
@@ -2617,7 +2705,8 @@ async def company_manage_page(request: Request, employee_id: str = None):
"selected_employee": selected_employee,
"employee_posts": employee_posts,
"pending_posts": pending_posts,
"approved_posts": approved_posts
"approved_posts": approved_posts,
"profile_picture": profile_picture
})
@@ -2647,6 +2736,9 @@ async def company_manage_posts(request: Request, employee_id: str = None):
profile = await db.get_profile(emp_profile.id)
posts = await db.get_generated_posts(emp_profile.id)
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("company_manage_posts.html", {
"request": request,
"page": "manage",
@@ -2655,7 +2747,8 @@ async def company_manage_posts(request: Request, employee_id: str = None):
"employee_name": emp_user.linkedin_name or emp_profile.display_name or emp_user.email,
"profile": profile,
"posts": posts,
"total_posts": len(posts)
"total_posts": len(posts),
"profile_picture": profile_picture
})
@@ -2688,6 +2781,17 @@ async def company_manage_post_detail(request: Request, post_id: str, employee_id
profile = await db.get_profile(emp_profile.id)
# Get employee's avatar
# Note: Create minimal session for the employee to use get_user_avatar
emp_session = UserSession(
user_id=str(emp_profile.id),
linkedin_picture=emp_user.linkedin_picture,
email=emp_user.email,
account_type=emp_user.account_type.value if hasattr(emp_user.account_type, 'value') else emp_user.account_type,
display_name=emp_profile.display_name
)
profile_picture_url = await get_user_avatar(emp_session, emp_profile.id)
# Convert media_items to dicts for JSON serialization in template
media_items_dict = []
if post.media_items:
@@ -2704,7 +2808,8 @@ async def company_manage_post_detail(request: Request, post_id: str, employee_id
"employee_name": emp_user.linkedin_name or emp_profile.display_name or emp_user.email,
"profile": profile,
"post": post,
"media_items_dict": media_items_dict
"media_items_dict": media_items_dict,
"profile_picture_url": profile_picture_url
})
@@ -2739,6 +2844,9 @@ async def company_manage_research(request: Request, employee_id: str = None):
limit_reached = not can_create
limit_message = error_msg
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("company_manage_research.html", {
"request": request,
"page": "manage",
@@ -2747,7 +2855,8 @@ async def company_manage_research(request: Request, employee_id: str = None):
"employee_name": emp_user.linkedin_name or emp_profile.display_name or emp_user.email,
"user_id": str(emp_profile.id),
"limit_reached": limit_reached,
"limit_message": limit_message
"limit_message": limit_message,
"profile_picture": profile_picture
})
@@ -2782,6 +2891,9 @@ async def company_manage_create(request: Request, employee_id: str = None):
limit_reached = not can_create
limit_message = error_msg
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("company_manage_create.html", {
"request": request,
"page": "manage",
@@ -2790,7 +2902,8 @@ async def company_manage_create(request: Request, employee_id: str = None):
"employee_name": emp_user.linkedin_name or emp_profile.display_name or emp_user.email,
"user_id": str(emp_profile.id),
"limit_reached": limit_reached,
"limit_message": limit_message
"limit_message": limit_message,
"profile_picture": profile_picture
})
@@ -2810,12 +2923,16 @@ async def employee_strategy_page(request: Request):
company = await db.get_company(UUID(session.company_id))
strategy = company.company_strategy if company else {}
user_id = UUID(session.user_id)
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("employee_strategy.html", {
"request": request,
"page": "strategy",
"session": session,
"company": company,
"strategy": strategy
"strategy": strategy,
"profile_picture": profile_picture
})