315 lines
9.1 KiB
Python
315 lines
9.1 KiB
Python
"""
|
|
ⒸAngelaMos | 2026
|
|
repository.py
|
|
"""
|
|
|
|
from typing import Any
|
|
from collections.abc import Sequence
|
|
from datetime import UTC, datetime, timedelta
|
|
from uuid import UUID
|
|
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from config import ProgramStatus, ReportStatus, UserRole
|
|
from program.Program import Program
|
|
from report.Report import Report
|
|
from user.User import User
|
|
|
|
|
|
class AdminRepository:
|
|
"""
|
|
Repository for admin-specific database operations
|
|
"""
|
|
@classmethod
|
|
async def get_all_programs(
|
|
cls,
|
|
session: AsyncSession,
|
|
skip: int = 0,
|
|
limit: int = 20,
|
|
status_filter: ProgramStatus | None = None,
|
|
) -> Sequence[tuple[Program,
|
|
User,
|
|
int]]:
|
|
"""
|
|
Get all programs with company info and report count
|
|
"""
|
|
report_count_subq = (
|
|
select(
|
|
Report.program_id,
|
|
func.count(Report.id).label("report_count")
|
|
).group_by(Report.program_id).subquery()
|
|
)
|
|
|
|
query = (
|
|
select(
|
|
Program,
|
|
User,
|
|
func.coalesce(report_count_subq.c.report_count,
|
|
0).label("report_count")
|
|
).join(User,
|
|
Program.company_id == User.id).outerjoin(
|
|
report_count_subq,
|
|
Program.id == report_count_subq.c.program_id
|
|
)
|
|
)
|
|
|
|
if status_filter:
|
|
query = query.where(Program.status == status_filter)
|
|
|
|
result = await session.execute(
|
|
query.order_by(Program.created_at.desc()
|
|
).offset(skip).limit(limit)
|
|
)
|
|
return [tuple(row) for row in result.all()]
|
|
|
|
@classmethod
|
|
async def count_all_programs(
|
|
cls,
|
|
session: AsyncSession,
|
|
status_filter: ProgramStatus | None = None,
|
|
) -> int:
|
|
"""
|
|
Count all programs
|
|
"""
|
|
query = select(func.count()).select_from(Program)
|
|
|
|
if status_filter:
|
|
query = query.where(Program.status == status_filter)
|
|
|
|
result = await session.execute(query)
|
|
return result.scalar_one()
|
|
|
|
@classmethod
|
|
async def get_program_by_id(
|
|
cls,
|
|
session: AsyncSession,
|
|
program_id: UUID,
|
|
) -> Program | None:
|
|
"""
|
|
Get program by ID for admin operations
|
|
"""
|
|
return await session.get(Program, program_id)
|
|
|
|
@classmethod
|
|
async def get_all_reports(
|
|
cls,
|
|
session: AsyncSession,
|
|
skip: int = 0,
|
|
limit: int = 20,
|
|
status_filter: ReportStatus | None = None,
|
|
severity_filter: str | None = None,
|
|
) -> Sequence[tuple[Report,
|
|
Program,
|
|
User]]:
|
|
"""
|
|
Get all reports with program and researcher info
|
|
"""
|
|
query = (
|
|
select(Report,
|
|
Program,
|
|
User).join(Program,
|
|
Report.program_id == Program.id).join(
|
|
User,
|
|
Report.researcher_id == User.id
|
|
)
|
|
)
|
|
|
|
if status_filter:
|
|
query = query.where(Report.status == status_filter)
|
|
|
|
if severity_filter:
|
|
query = query.where(
|
|
Report.severity_submitted == severity_filter
|
|
)
|
|
|
|
result = await session.execute(
|
|
query.order_by(Report.created_at.desc()
|
|
).offset(skip).limit(limit)
|
|
)
|
|
return [tuple(row) for row in result.all()]
|
|
|
|
@classmethod
|
|
async def count_all_reports(
|
|
cls,
|
|
session: AsyncSession,
|
|
status_filter: ReportStatus | None = None,
|
|
severity_filter: str | None = None,
|
|
) -> int:
|
|
"""
|
|
Count all reports
|
|
"""
|
|
query = select(func.count()).select_from(Report)
|
|
|
|
if status_filter:
|
|
query = query.where(Report.status == status_filter)
|
|
|
|
if severity_filter:
|
|
query = query.where(
|
|
Report.severity_submitted == severity_filter
|
|
)
|
|
|
|
result = await session.execute(query)
|
|
return result.scalar_one()
|
|
|
|
@classmethod
|
|
async def get_report_by_id(
|
|
cls,
|
|
session: AsyncSession,
|
|
report_id: UUID,
|
|
) -> Report | None:
|
|
"""
|
|
Get report by ID for admin operations
|
|
"""
|
|
return await session.get(Report, report_id)
|
|
|
|
@classmethod
|
|
async def get_all_users_with_stats(
|
|
cls,
|
|
session: AsyncSession,
|
|
skip: int = 0,
|
|
limit: int = 20,
|
|
role_filter: UserRole | None = None,
|
|
) -> Sequence[tuple[User,
|
|
int,
|
|
int]]:
|
|
"""
|
|
Get all users with program and report counts
|
|
"""
|
|
program_count_subq = (
|
|
select(
|
|
Program.company_id,
|
|
func.count(Program.id).label("program_count")
|
|
).group_by(Program.company_id).subquery()
|
|
)
|
|
|
|
report_count_subq = (
|
|
select(
|
|
Report.researcher_id,
|
|
func.count(Report.id).label("report_count")
|
|
).group_by(Report.researcher_id).subquery()
|
|
)
|
|
|
|
query = (
|
|
select(
|
|
User,
|
|
func.coalesce(program_count_subq.c.program_count,
|
|
0).label("program_count"),
|
|
func.coalesce(report_count_subq.c.report_count,
|
|
0).label("report_count")
|
|
).outerjoin(
|
|
program_count_subq,
|
|
User.id == program_count_subq.c.company_id
|
|
).outerjoin(
|
|
report_count_subq,
|
|
User.id == report_count_subq.c.researcher_id
|
|
)
|
|
)
|
|
|
|
if role_filter:
|
|
query = query.where(User.role == role_filter)
|
|
|
|
result = await session.execute(
|
|
query.order_by(User.created_at.desc()
|
|
).offset(skip).limit(limit)
|
|
)
|
|
return [tuple(row) for row in result.all()]
|
|
|
|
@classmethod
|
|
async def count_all_users(
|
|
cls,
|
|
session: AsyncSession,
|
|
role_filter: UserRole | None = None,
|
|
) -> int:
|
|
"""
|
|
Count all users
|
|
"""
|
|
query = select(func.count()).select_from(User)
|
|
|
|
if role_filter:
|
|
query = query.where(User.role == role_filter)
|
|
|
|
result = await session.execute(query)
|
|
return result.scalar_one()
|
|
|
|
@classmethod
|
|
async def get_platform_stats(
|
|
cls,
|
|
session: AsyncSession,
|
|
) -> dict[str,
|
|
Any]:
|
|
"""
|
|
Get platform-wide statistics
|
|
"""
|
|
now = datetime.now(UTC)
|
|
month_ago = now - timedelta(days = 30)
|
|
|
|
total_users = await session.execute(
|
|
select(func.count()).select_from(User)
|
|
)
|
|
|
|
users_by_role = await session.execute(
|
|
select(User.role,
|
|
func.count()).group_by(User.role)
|
|
)
|
|
role_counts = {
|
|
str(role): count
|
|
for role, count in users_by_role.all()
|
|
}
|
|
|
|
total_programs = await session.execute(
|
|
select(func.count()).select_from(Program)
|
|
)
|
|
|
|
active_programs = await session.execute(
|
|
select(func.count()).select_from(Program).where(
|
|
Program.status == ProgramStatus.ACTIVE
|
|
)
|
|
)
|
|
|
|
total_reports = await session.execute(
|
|
select(func.count()).select_from(Report)
|
|
)
|
|
|
|
reports_by_status = await session.execute(
|
|
select(Report.status,
|
|
func.count()).group_by(Report.status)
|
|
)
|
|
status_counts = {
|
|
str(status): count
|
|
for status, count in reports_by_status.all()
|
|
}
|
|
|
|
total_bounties = await session.execute(
|
|
select(func.coalesce(func.sum(Report.bounty_amount),
|
|
0)).where(
|
|
Report.bounty_amount.isnot(None)
|
|
)
|
|
)
|
|
|
|
reports_this_month = await session.execute(
|
|
select(func.count()).select_from(Report).where(
|
|
Report.created_at >= month_ago
|
|
)
|
|
)
|
|
|
|
new_users_this_month = await session.execute(
|
|
select(func.count()
|
|
).select_from(User).where(User.created_at >= month_ago)
|
|
)
|
|
|
|
return {
|
|
"total_users": total_users.scalar_one(),
|
|
"total_researchers": role_counts.get("user",
|
|
0),
|
|
"total_companies": role_counts.get("company",
|
|
0),
|
|
"total_programs": total_programs.scalar_one(),
|
|
"active_programs": active_programs.scalar_one(),
|
|
"total_reports": total_reports.scalar_one(),
|
|
"reports_by_status": status_counts,
|
|
"total_bounties_paid": total_bounties.scalar_one(),
|
|
"reports_this_month": reports_this_month.scalar_one(),
|
|
"new_users_this_month": new_users_this_month.scalar_one(),
|
|
}
|