# retoor <retoor@molodetz.nl>
import logging
from typing import Annotated
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from devplacepy.database import (
REPORTABLE_TARGETS,
REPORT_SEVERITIES,
REPORT_STATUSES,
report_reason_options,
)
from devplacepy.dependencies import json_or_form
from devplacepy.models import ReportForm
from devplacepy.responses import action_result, json_error, respond
from devplacepy.schemas import ReportListOut, ReportReasonsOut
from devplacepy.seo import base_seo_context, site_url, website_schema
from devplacepy.services.audit import record as audit
from devplacepy.services.moderation import queue, sla
from devplacepy.utils import create_notification, get_current_user, require_user
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("/reasons")
async def report_reasons(request: Request):
base = site_url(request)
return respond(
request,
"report_reasons.html",
{
**base_seo_context(
request,
title="Report reasons",
description="The categories DevPlace accepts content reports under.",
breadcrumbs=[
{"name": "Home", "url": "/feed"},
{"name": "Report reasons", "url": "/reports/reasons"},
],
schemas=[website_schema(base)],
),
"request": request,
"user": get_current_user(request),
"reasons": report_reason_options(),
"severities": list(REPORT_SEVERITIES),
},
model=ReportReasonsOut,
)
@router.get("/mine", response_class=HTMLResponse)
async def my_reports(request: Request, status: str = "", page: int = 1):
user = require_user(request)
if status not in REPORT_STATUSES:
status = ""
reports, pagination = queue.list_reports(
status=status, reporter_uid=user["uid"], page=page
)
base = site_url(request)
seo_ctx = base_seo_context(
request,
title="Your reports",
description="The reports you filed and the outcome of each.",
robots="noindex,nofollow",
breadcrumbs=[
{"name": "Home", "url": "/feed"},
{"name": "Your reports", "url": "/reports/mine"},
],
schemas=[website_schema(base)],
)
return respond(
request,
"reports_mine.html",
{
**seo_ctx,
"request": request,
"user": user,
"reports": reports,
"pagination": pagination,
"status": status,
"reasons": report_reason_options(),
},
model=ReportListOut,
)
@router.post("/{target_type}/{target_uid}")
async def submit_report(
request: Request,
target_type: str,
target_uid: str,
data: Annotated[ReportForm, Depends(json_or_form(ReportForm))],
):
user = require_user(request)
if target_type not in REPORTABLE_TARGETS:
return json_error(400, "Unknown report target")
owner_uid = queue.owner_uid_for(target_type, target_uid)
if owner_uid and owner_uid == user["uid"]:
return json_error(400, "You cannot report your own content")
report = queue.raise_report(
target_type=target_type,
target_uid=target_uid,
reporter_uid=user["uid"],
reason=data.reason,
detail=data.detail,
origin="member",
)
if not report:
return json_error(400, "Report could not be filed")
hours = sla.sla_hours()
logger.info(
f"{user['username']} reported {target_type} {target_uid} as {data.reason}"
)
audit.record(
request,
"report.create",
user=user,
target_type=target_type,
target_uid=target_uid,
metadata={"reason": data.reason, "severity": report["severity"]},
summary=f"{user['username']} reported {target_type} {target_uid} as {data.reason}",
links=[audit.target(target_type, target_uid)],
)
create_notification(
user["uid"],
"moderation",
f"Report received. A moderator reviews it within {hours} hours.",
user["uid"],
"/reports/mine",
)
return action_result(
request,
"/reports/mine",
data={
"uid": report["uid"],
"status": report["status"],
"severity": report["severity"],
"sla_hours": hours,
},
)