Files
comiaunicaty/backend/app/routers/home.py

168 lines
6.3 KiB
Python

from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.core.security import utc_now
from app.db.base import get_db
from app.models import Notification, NotificationPreference
from app.schemas import NotificationPreferencePatch
from app.services.auth import CurrentContext, get_current_context
from app.services.dashboard import calendar_items, file_items, home_dashboard, local_actions_for_context
from app.services.serializers import profile_dict
router = APIRouter(prefix="/api", tags=["home"])
DEFAULT_PREFERENCES = {
"direct_mentions": "immediate",
"event_changes": "immediate",
"urgent_announcements": "immediate",
"tasks_assigned": "immediate",
"discussions": "digest",
"new_files": "digest",
"general_chatter": "digest",
"reactions": "muted",
"off_topic": "muted",
}
@router.get("/home")
def home(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
dashboard = home_dashboard(db, ctx)
return {
"profile": profile_dict(ctx.home_profile, ctx.member),
"sections": dashboard["sections"],
"connections": dashboard["connections"],
}
@router.get("/home/actions")
def home_actions(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
return {"actions": home_dashboard(db, ctx)["sections"]["needs_me"]}
@router.get("/home/calendar")
def home_calendar(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
return {"events": calendar_items(db, ctx)}
@router.get("/home/files")
def home_files(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
return {"files": file_items(db, ctx)}
@router.get("/home/official-updates")
def official_updates(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
return {"official_updates": home_dashboard(db, ctx)["sections"]["official_updates"]}
@router.get("/home/catch-up")
def catch_up(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
return {"catch_up": home_dashboard(db, ctx)["sections"]["catch_up"]}
def _preference_owner(ctx: CurrentContext) -> tuple[str | None, str | None]:
if ctx.home_profile:
return ctx.home_profile.id, None
if ctx.member:
return None, ctx.member.id
return None, None
@router.get("/me/notification-preferences")
def get_notification_preferences(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
home_profile_id, member_id = _preference_owner(ctx)
rows = db.scalars(
select(NotificationPreference).where(
NotificationPreference.home_profile_id == home_profile_id,
NotificationPreference.member_id == member_id,
)
).all()
result = dict(DEFAULT_PREFERENCES)
for row in rows:
result[row.category] = row.delivery
return {
"headline": "Mute the noise, not the group.",
"preferences": result,
"groups": {
"Immediate": ["direct_mentions", "event_changes", "urgent_announcements", "tasks_assigned"],
"Quiet / digest": ["discussions", "new_files", "general_chatter"],
"Mute": ["reactions", "off_topic"],
},
}
@router.patch("/me/notification-preferences")
def patch_notification_preferences(
payload: NotificationPreferencePatch,
ctx: CurrentContext = Depends(get_current_context),
db: Session = Depends(get_db),
) -> dict:
home_profile_id, member_id = _preference_owner(ctx)
if home_profile_id is None and member_id is None:
raise HTTPException(status_code=401, detail={"error": {"code": "not_authenticated", "message": "Open an invite first.", "details": {}}})
for category, delivery in payload.preferences.items():
row = db.scalar(
select(NotificationPreference).where(
NotificationPreference.home_profile_id == home_profile_id,
NotificationPreference.member_id == member_id,
NotificationPreference.category == category,
)
)
if row:
row.delivery = delivery
row.enabled = delivery != "muted"
row.updated_at = utc_now()
else:
db.add(
NotificationPreference(
home_profile_id=home_profile_id,
member_id=member_id,
category=category,
delivery=delivery,
enabled=delivery != "muted",
)
)
db.commit()
return get_notification_preferences(ctx, db)
@router.get("/me/notifications")
def notifications(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
query = select(Notification)
if ctx.home_profile:
query = query.where(Notification.home_profile_id == ctx.home_profile.id)
elif ctx.member:
query = query.where(Notification.member_id == ctx.member.id)
rows = db.scalars(query.order_by(Notification.created_at.desc()).limit(50)).all()
return {
"notifications": [
{
"id": item.id,
"title": item.title,
"body": item.body,
"category": item.category,
"read_at": item.read_at.isoformat() if item.read_at else None,
"created_at": item.created_at.isoformat(),
}
for item in rows
]
}
@router.patch("/me/notifications/{notification_id}/read")
def mark_notification_read(notification_id: str, ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
query = select(Notification).where(Notification.id == notification_id)
if ctx.home_profile:
query = query.where(Notification.home_profile_id == ctx.home_profile.id)
elif ctx.member:
query = query.where(Notification.member_id == ctx.member.id)
notification = db.scalar(query)
if not notification:
raise HTTPException(status_code=404, detail={"error": {"code": "not_found", "message": "Notification not found.", "details": {}}})
notification.read_at = utc_now()
db.commit()
return {"ok": True}