participant-calibration flow implemented

This commit is contained in:
averel10
2026-03-27 07:28:35 +01:00
parent 5503a1812c
commit 9a54d91a4f
7 changed files with 606 additions and 3 deletions

View File

@@ -9,6 +9,8 @@ import { auth } from '@/lib/auth';
import { headers } from 'next/headers';
import type { DatasetEntryForAnnotation } from '@/lib/dialects';
import { experiment } from '@/lib/model/experiment';
import { experiment_calibration } from '@/lib/model/experiment_calibration';
import { participant } from '@/lib/model/participant';
/**
* Returns unannotated entries for the given dataset and authenticated user.
@@ -133,3 +135,60 @@ export async function getAnnotationProgress(
return { total: allEntries.length, done: annotated.length };
}
/**
* Checks if calibration is required and completed for the current user in an experiment.
* Returns true if calibration is completed or not required, false if calibration is pending.
*/
export async function isCalibrationDone(experimentId: number): Promise<boolean> {
console.log('Checking calibration status for experimentId:', experimentId);
const session = await auth.api.getSession({ headers: await headers() });
if (!session) throw new Error('Nicht angemeldet');
// Get calibration items for this experiment
const calibrationItems = await db
.select()
.from(experiment_calibration)
.where(eq(experiment_calibration.experimentId, experimentId));
// If no calibration items are defined, calibration is not required
if (calibrationItems.length === 0) {
return true;
}
// Check if participant exists for this user and experiment
const participantRecord = await db
.select()
.from(participant)
.where(
and(
eq(participant.experimentId, experimentId),
eq(participant.userId, session.user.id)
)
)
.limit(1);
// If no participant record exists, calibration is not done
if (participantRecord.length === 0) {
return false;
}
// Check if calibration answers are filled
const calibrationAnswers = participantRecord[0].calibrationAnswers as Record<number, { dialectLabel: string; confidence: number }> | null;
if (!calibrationAnswers) {
return false;
}
// Verify that all calibration items have complete answers
for (const item of calibrationItems) {
const answer = calibrationAnswers[item.id];
// Check if answer exists and has both required fields
if (!answer || !answer.dialectLabel || !answer.confidence) {
return false;
}
}
return true;
}

View File

@@ -2,9 +2,12 @@
import db from '@/lib/db';
import { experiment_calibration } from '@/lib/model/experiment_calibration';
import { participant } from '@/lib/model/participant';
import { eq, and } from 'drizzle-orm';
import { revalidatePath } from 'next/cache';
import { requireAdmin } from '@/lib/auth';
import { auth } from '@/lib/auth';
import { headers } from 'next/headers';
import { unlink } from 'fs/promises';
import { join } from 'path';
@@ -22,6 +25,33 @@ export async function getCalibrationEntries(experimentId: number) {
}
}
export async function getCalibrationAnswers(experimentId: number) {
try {
const session = await auth.api.getSession({ headers: await headers() });
if (!session) throw new Error('Nicht angemeldet');
const participantRecord = await db
.select()
.from(participant)
.where(
and(
eq(participant.experimentId, experimentId),
eq(participant.userId, session.user.id)
)
)
.limit(1);
if (participantRecord.length === 0) {
return { success: true, data: null };
}
return { success: true, data: participantRecord[0].calibrationAnswers };
} catch (error) {
console.error('Error fetching calibration answers:', error);
throw new Error('Failed to fetch calibration answers');
}
}
export async function deleteCalibrationEntry(calibrationId: number, experimentId: number) {
const result = await requireAdmin();
if (!result.authenticated || !result.admin) {
@@ -133,3 +163,35 @@ export async function deleteAllCalibrationEntries(experimentId: number) {
throw new Error('Failed to delete all calibration entries');
}
}
/**
* Saves calibration answers for the current user in an experiment.
* Creates or updates a participant record with the calibration answers as JSON.
*/
export async function saveCalibrationAnswers(
experimentId: number,
answers: Record<number, { calibrationItemId: number; dialectLabel: string; confidence: number }>
): Promise<void> {
const session = await auth.api.getSession({ headers: await headers() });
if (!session) throw new Error('Nicht angemeldet');
try {
// Upsert participant record with calibration answers
await db
.insert(participant)
.values({
experimentId,
userId: session.user.id,
calibrationAnswers: answers,
})
.onConflictDoUpdate({
target: [participant.experimentId, participant.userId],
set: {
calibrationAnswers: answers,
},
});
} catch (error) {
console.error('Error saving calibration answers:', error);
throw new Error('Failed to save calibration answers');
}
}