Wire up Errors tab in Auditing: auto-create ErrorItem on Analysis Error

- Create ErrorItem record when proof analysis results in "Analysis Error" status
- Add submitter_name/submitter_agency fields to ErrorItemResponse schema
- Eager-load proof creator and agency in error items query to avoid N+1
- Populate submitter fields from proof creator in the API route
- Update frontend ErrorItemResponse type and conversion to map submitter fields
- Fix ErrorsTable proof name styling to blue link (text-active-blue) matching Flags tab

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
michael 2026-02-12 13:55:16 -06:00
parent 9ec892b46b
commit 220a97ab57
6 changed files with 24 additions and 5 deletions

View file

@ -475,6 +475,8 @@ async def list_error_items(
id=item.id,
proof_version_id=item.proof_version_id,
error_summary=item.error_summary,
submitter_name=item.proof_version.proof.created_by_user.name if item.proof_version and item.proof_version.proof and item.proof_version.proof.created_by_user else None,
submitter_agency=item.proof_version.proof.created_by_user.agency.name if item.proof_version and item.proof_version.proof and item.proof_version.proof.created_by_user and item.proof_version.proof.created_by_user.agency else None,
campaign_name=item.proof_version.proof.campaign.name if item.proof_version and item.proof_version.proof and item.proof_version.proof.campaign else None,
proof_name=item.proof_version.proof.proof_name if item.proof_version and item.proof_version.proof else None,
version=item.proof_version.version if item.proof_version else None,

View file

@ -129,6 +129,8 @@ class ErrorItemResponse(BaseModel):
id: uuid.UUID
proof_version_id: uuid.UUID
error_summary: Optional[str]
submitter_name: Optional[str]
submitter_agency: Optional[str]
campaign_name: Optional[str]
proof_name: Optional[str]
version: Optional[int]

View file

@ -5,7 +5,7 @@ from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.models.models import FlaggedItem, ResolvedItem, ErrorItem, ProofVersion, Proof, Campaign
from app.models.models import FlaggedItem, ResolvedItem, ErrorItem, ProofVersion, Proof, Campaign, User
class AuditRepository:
@ -134,6 +134,10 @@ class AuditRepository:
selectinload(ErrorItem.proof_version)
.selectinload(ProofVersion.proof)
.selectinload(Proof.campaign),
selectinload(ErrorItem.proof_version)
.selectinload(ProofVersion.proof)
.selectinload(Proof.created_by_user)
.selectinload(User.agency),
)
.join(ProofVersion)
.join(Proof)

View file

@ -9,7 +9,7 @@ from app.websocket.manager import ConnectionManager
from app.services.analysis_service import AnalysisService
from app.models.schemas import SubReview
from app.models.database import async_session_factory
from app.repositories import ProofRepository, CampaignRepository, UserRepository
from app.repositories import ProofRepository, CampaignRepository, UserRepository, AuditRepository
from app.services.storage_service import storage_service
logger = logging.getLogger(__name__)
@ -231,6 +231,15 @@ async def handle_analyze_message(
is_identical_file=is_identical_file,
)
# Auto-create ErrorItem when analysis results in an error
if result.overallStatus == "Analysis Error":
audit_repo = AuditRepository(session)
await audit_repo.create_error_item(
proof_version_id=version.id,
error_summary=result.leadAgentSummary,
)
logger.info(f"[WEBSOCKET] Created ErrorItem for analysis error on version {version.id}")
await session.commit()
proof_id = str(proof.id)
version_id = str(version.id)

View file

@ -135,7 +135,7 @@ const ErrorsTable: React.FC<{ items: ErrorItem[], onNavigate: AuditingProps['onN
onClick={() => onNavigate(item)}
title={`Click to view Version ${item.version} of ${item.proofName}`}
>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-black-title">{item.proofName}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-active-blue">{item.proofName}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">Version {item.version}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitter}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitAgency}</td>

View file

@ -81,6 +81,8 @@ export interface ErrorItemResponse {
id: string;
proof_version_id: string;
error_summary: string | null;
submitter_name: string | null;
submitter_agency: string | null;
campaign_name: string | null;
proof_name: string | null;
version: number | null;
@ -333,8 +335,8 @@ class ApiService {
campaignName: item.campaign_name || '',
proofName: item.proof_name || '',
version: item.version || 1,
submitter: '',
submitAgency: '',
submitter: item.submitter_name || '',
submitAgency: item.submitter_agency || '',
errorSummary: item.error_summary || '',
timestamp: item.created_at,
};