reports
This commit is contained in:
parent
d272ae0bd3
commit
ff01efe4bb
17 changed files with 1378 additions and 21 deletions
180
backend/app/routes_urmiport.py
Normal file
180
backend/app/routes_urmiport.py
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .auth import current_user
|
||||
from .database import get_db
|
||||
from .models import UrmiportReport, UrmiportReportGroup, UserAccount
|
||||
from .urmiport_service import (
|
||||
CreateUrmiportGroupPayload,
|
||||
UpdateUrmiportGroupPayload,
|
||||
build_urmiport_api_docs,
|
||||
get_group_by_slug,
|
||||
get_report,
|
||||
get_user_group,
|
||||
hash_password,
|
||||
normalize_group_slug,
|
||||
serialize_group,
|
||||
serialize_report,
|
||||
verify_password,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/api/v1/urmiport", tags=["urmiport"])
|
||||
|
||||
|
||||
class UnlockReportRequest(BaseModel):
|
||||
password: str = Field(min_length=1, max_length=120)
|
||||
|
||||
|
||||
@router.get("/docs")
|
||||
def urmiport_docs() -> dict:
|
||||
return build_urmiport_api_docs()
|
||||
|
||||
|
||||
@router.get("/groups")
|
||||
def list_report_groups(
|
||||
db: Session = Depends(get_db),
|
||||
user: UserAccount = Depends(current_user),
|
||||
) -> list[dict]:
|
||||
groups = db.scalars(
|
||||
select(UrmiportReportGroup)
|
||||
.where(UrmiportReportGroup.user_id == user.id)
|
||||
.order_by(UrmiportReportGroup.created_at.desc())
|
||||
).all()
|
||||
counts = dict(
|
||||
db.execute(
|
||||
select(UrmiportReport.group_id, func.count(UrmiportReport.id))
|
||||
.where(UrmiportReport.user_id == user.id)
|
||||
.group_by(UrmiportReport.group_id)
|
||||
).all()
|
||||
)
|
||||
return [serialize_group(group, counts.get(group.id, 0)) for group in groups]
|
||||
|
||||
|
||||
@router.post("/groups")
|
||||
def create_report_group(
|
||||
payload: CreateUrmiportGroupPayload,
|
||||
db: Session = Depends(get_db),
|
||||
user: UserAccount = Depends(current_user),
|
||||
) -> dict:
|
||||
try:
|
||||
slug = normalize_group_slug(payload.report_group)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(exc)) from exc
|
||||
|
||||
existing = db.scalar(
|
||||
select(UrmiportReportGroup).where(
|
||||
UrmiportReportGroup.user_id == user.id,
|
||||
UrmiportReportGroup.slug == slug,
|
||||
)
|
||||
)
|
||||
if existing:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Report group already exists")
|
||||
|
||||
if payload.default_access == "password" and not payload.password:
|
||||
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="password is required")
|
||||
|
||||
group = UrmiportReportGroup(
|
||||
user_id=user.id,
|
||||
slug=slug,
|
||||
display_name=payload.display_name or slug,
|
||||
default_access=payload.default_access,
|
||||
password_hash=hash_password(payload.password) if payload.default_access == "password" else None,
|
||||
id_mode=payload.id_mode,
|
||||
)
|
||||
db.add(group)
|
||||
db.commit()
|
||||
db.refresh(group)
|
||||
return serialize_group(group, 0)
|
||||
|
||||
|
||||
@router.patch("/groups/{slug}")
|
||||
def update_report_group(
|
||||
slug: str,
|
||||
payload: UpdateUrmiportGroupPayload,
|
||||
db: Session = Depends(get_db),
|
||||
user: UserAccount = Depends(current_user),
|
||||
) -> dict:
|
||||
group = get_user_group(db, user, slug)
|
||||
if payload.display_name is not None:
|
||||
group.display_name = payload.display_name.strip() or group.slug
|
||||
if payload.default_access is not None:
|
||||
group.default_access = payload.default_access
|
||||
if payload.default_access == "password":
|
||||
if payload.password:
|
||||
group.password_hash = hash_password(payload.password)
|
||||
elif not group.password_hash:
|
||||
raise HTTPException(status_code=422, detail="password is required")
|
||||
else:
|
||||
group.password_hash = None
|
||||
elif payload.password:
|
||||
group.password_hash = hash_password(payload.password)
|
||||
if payload.id_mode is not None:
|
||||
group.id_mode = payload.id_mode
|
||||
db.commit()
|
||||
db.refresh(group)
|
||||
count = db.scalar(
|
||||
select(func.count(UrmiportReport.id)).where(UrmiportReport.group_id == group.id)
|
||||
) or 0
|
||||
return serialize_group(group, count)
|
||||
|
||||
|
||||
@router.delete("/groups/{slug}")
|
||||
def delete_report_group(
|
||||
slug: str,
|
||||
db: Session = Depends(get_db),
|
||||
user: UserAccount = Depends(current_user),
|
||||
) -> dict:
|
||||
group = get_user_group(db, user, slug)
|
||||
db.delete(group)
|
||||
db.commit()
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.get("/groups/{slug}/reports")
|
||||
def list_group_reports(
|
||||
slug: str,
|
||||
db: Session = Depends(get_db),
|
||||
user: UserAccount = Depends(current_user),
|
||||
) -> list[dict]:
|
||||
group = get_user_group(db, user, slug)
|
||||
reports = db.scalars(
|
||||
select(UrmiportReport)
|
||||
.where(UrmiportReport.group_id == group.id)
|
||||
.order_by(UrmiportReport.updated_at.desc())
|
||||
).all()
|
||||
return [serialize_report(report, group) for report in reports]
|
||||
|
||||
|
||||
@router.get("/view/{group_slug}/{report_id}")
|
||||
def view_report_metadata(
|
||||
group_slug: str,
|
||||
report_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
group = get_group_by_slug(db, group_slug)
|
||||
report = get_report(db, group, report_id)
|
||||
return {
|
||||
"report_group": group.slug,
|
||||
"report_id": report.report_id,
|
||||
"access": report.access_mode,
|
||||
"requires_password": report.access_mode == "password",
|
||||
"html": report.html_body if report.access_mode == "public" else None,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/view/{group_slug}/{report_id}/unlock")
|
||||
def unlock_report(
|
||||
group_slug: str,
|
||||
report_id: str,
|
||||
payload: UnlockReportRequest,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
group = get_group_by_slug(db, group_slug)
|
||||
report = get_report(db, group, report_id)
|
||||
if report.access_mode != "password":
|
||||
return {"html": report.html_body}
|
||||
if not verify_password(payload.password, report.password_hash):
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid password")
|
||||
return {"html": report.html_body}
|
||||
Loading…
Add table
Add a link
Reference in a new issue