Fix AI filtering bugs, add special award shortlist integration
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m20s

Part 1 - Bug Fixes:
- Fix toProjectWithRelations() stripping file fields needed by AI (detectedLang, textContent, etc.)
- Fix parseAIData() reading flat when aiScreeningJson is nested under rule ID
- Fix getAIConfidenceScore() with same nesting issue (always returned 0)

Part 2 - Special Award Track Integration:
- Add shortlistSize to SpecialAward, qualityScore/shortlisted/confirmed fields to AwardEligibility
- Add specialAwardId to Round for award-owned rounds
- Update AI eligibility service to return qualityScore (0-100) for ranking
- Update eligibility job with filteringRoundId scoping and auto-shortlist top N
- Add 8 new specialAward router procedures (listForRound, runEligibilityForRound,
  listShortlist, toggleShortlisted, confirmShortlist, listRounds, createRound, deleteRound)
- Create award-shortlist.tsx component with ranked table, shortlist checkboxes, confirm dialog
- Add "Special Award Tracks" section to filtering dashboard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-02-17 15:38:31 +01:00
parent 6743119c4d
commit a02ed59158
10 changed files with 1308 additions and 309 deletions

View File

@@ -378,8 +378,9 @@ model User {
filteringOverrides FilteringResult[] @relation("FilteringOverriddenBy")
// Award overrides
awardEligibilityOverrides AwardEligibility[] @relation("AwardEligibilityOverriddenBy")
awardWinnerOverrides SpecialAward[] @relation("AwardOverriddenBy")
awardEligibilityOverrides AwardEligibility[] @relation("AwardEligibilityOverriddenBy")
awardEligibilityConfirms AwardEligibility[] @relation("AwardEligibilityConfirmer")
awardWinnerOverrides SpecialAward[] @relation("AwardOverriddenBy")
// In-app notifications
notifications InAppNotification[] @relation("UserNotifications")
@@ -1507,6 +1508,7 @@ model SpecialAward {
juryGroupId String?
eligibilityMode AwardEligibilityMode @default(STAY_IN_MAIN)
decisionMode String? // "JURY_VOTE" | "AWARD_MASTER_DECISION" | "ADMIN_DECISION"
shortlistSize Int @default(10)
// Eligibility job tracking
eligibilityJobStatus String? // PENDING, PROCESSING, COMPLETED, FAILED
@@ -1530,6 +1532,7 @@ model SpecialAward {
competition Competition? @relation(fields: [competitionId], references: [id], onDelete: SetNull)
evaluationRound Round? @relation(fields: [evaluationRoundId], references: [id], onDelete: SetNull)
awardJuryGroup JuryGroup? @relation(fields: [juryGroupId], references: [id], onDelete: SetNull)
rounds Round[] @relation("AwardRounds")
@@index([programId])
@@index([status])
@@ -1545,11 +1548,17 @@ model AwardEligibility {
method EligibilityMethod @default(AUTO)
eligible Boolean @default(false)
aiReasoningJson Json? @db.JsonB
qualityScore Float?
shortlisted Boolean @default(false)
// Admin override
overriddenBy String?
overriddenAt DateTime?
// Shortlist confirmation
confirmedAt DateTime?
confirmedBy String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -1557,6 +1566,7 @@ model AwardEligibility {
award SpecialAward @relation(fields: [awardId], references: [id], onDelete: Cascade)
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
overriddenByUser User? @relation("AwardEligibilityOverriddenBy", fields: [overriddenBy], references: [id], onDelete: SetNull)
confirmer User? @relation("AwardEligibilityConfirmer", fields: [confirmedBy], references: [id], onDelete: SetNull)
@@unique([awardId, projectId])
@@index([awardId])
@@ -2118,12 +2128,14 @@ model Round {
// Links to other entities
juryGroupId String?
submissionWindowId String?
specialAwardId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)
specialAward SpecialAward? @relation("AwardRounds", fields: [specialAwardId], references: [id], onDelete: SetNull)
juryGroup JuryGroup? @relation(fields: [juryGroupId], references: [id], onDelete: SetNull)
submissionWindow SubmissionWindow? @relation(fields: [submissionWindowId], references: [id], onDelete: SetNull)
projectRoundStates ProjectRoundState[]
@@ -2157,6 +2169,7 @@ model Round {
@@index([competitionId])
@@index([roundType])
@@index([status])
@@index([specialAwardId])
}
model ProjectRoundState {