fix: harden admin access, repair ORM joins, and add migration/tests

This commit is contained in:
dwindown
2026-04-01 14:59:54 +07:00
parent de592d140e
commit 16ab13e911
21 changed files with 1275 additions and 368 deletions

View File

@@ -18,7 +18,7 @@ from dataclasses import dataclass, field
import logging
import pandas as pd
from sqlalchemy import select, func, and_, or_
from sqlalchemy import Integer, and_, cast, func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
@@ -415,7 +415,7 @@ async def generate_item_analysis_report(
resp_result = await db.execute(
select(
func.count().label("total"),
func.sum(func.cast(UserAnswer.is_correct, type_=func.INTEGER)).label("correct")
func.sum(cast(UserAnswer.is_correct, Integer)).label("correct")
).where(UserAnswer.item_id == item.id)
)
resp_stats = resp_result.first()
@@ -678,7 +678,7 @@ async def generate_tryout_comparison_report(
cal_result = await db.execute(
select(
func.count().label("total"),
func.sum(func.cast(Item.calibrated, type_=func.INTEGER)).label("calibrated")
func.sum(cast(Item.calibrated, Integer)).label("calibrated")
).where(
Item.tryout_id == tryout_id,
Item.website_id == website_id,
@@ -704,15 +704,56 @@ async def generate_tryout_comparison_report(
if tryout:
date_str = tryout.created_at.strftime("%Y-%m-%d")
session_result = await db.execute(
select(
func.count(Session.id).label("participant_count"),
func.avg(Session.NM).label("avg_nm"),
func.avg(Session.NN).label("avg_nn"),
func.avg(Session.theta).label("avg_theta"),
func.stddev_pop(Session.NM).label("std_nm"),
).where(
Session.tryout_id == tryout_id,
Session.website_id == website_id,
Session.is_completed.is_(True),
)
)
session_stats = session_result.first()
participant_count = (
int(session_stats.participant_count)
if session_stats and session_stats.participant_count
else (stats.participant_count if stats else 0)
)
avg_nm = (
round(float(session_stats.avg_nm), 2)
if session_stats and session_stats.avg_nm is not None
else (round(float(stats.rataan), 2) if stats and stats.rataan is not None else None)
)
avg_nn = (
round(float(session_stats.avg_nn), 2)
if session_stats and session_stats.avg_nn is not None
else None
)
avg_theta = (
round(float(session_stats.avg_theta), 4)
if session_stats and session_stats.avg_theta is not None
else None
)
std_nm = (
round(float(session_stats.std_nm), 2)
if session_stats and session_stats.std_nm is not None
else (round(float(stats.sb), 2) if stats and stats.sb is not None else None)
)
record = TryoutComparisonRecord(
tryout_id=tryout_id,
date=date_str,
subject=subject,
participant_count=stats.participant_count if stats else 0,
avg_nm=round(stats.rataan, 2) if stats and stats.rataan else None,
avg_nn=round(stats.rataan + 500, 2) if stats and stats.rataan else None,
avg_theta=None, # Would need to calculate from sessions
std_nm=round(stats.sb, 2) if stats and stats.sb else None,
participant_count=participant_count,
avg_nm=avg_nm,
avg_nn=avg_nn,
avg_theta=avg_theta,
std_nm=std_nm,
calibration_percentage=round(cal_percentage, 2),
)
comparison_records.append(record)