feat: added simple statistics to backend to track progress on experiment
This commit is contained in:
@@ -5,9 +5,11 @@ import { experiment } from '@/lib/model/experiment';
|
||||
import { experiment_calibration } from '@/lib/model/experiment_calibration';
|
||||
import { annotation } from '@/lib/model/annotation';
|
||||
import { participant } from '@/lib/model/participant';
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
import { dataset_entry } from '@/lib/model/dataset_entry';
|
||||
import { and, eq, count, sql, isNotNull } from 'drizzle-orm';
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { auth, requireAdmin } from '@/lib/auth';
|
||||
import { isParticipantCalibrationDone } from './calibration-scoring';
|
||||
import { headers } from 'next/headers';
|
||||
|
||||
export async function createExperiment({
|
||||
@@ -183,3 +185,108 @@ export async function clearExperimentData(id: number) {
|
||||
throw new Error('Failed to clear experiment data');
|
||||
}
|
||||
}
|
||||
|
||||
export async function getExperimentStatistics(id: number) {
|
||||
const result = await requireAdmin();
|
||||
if (!result.authenticated || !result.admin) {
|
||||
throw new Error('Unauthorized');
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the experiment with dataset
|
||||
const exp = await db
|
||||
.select()
|
||||
.from(experiment)
|
||||
.where(eq(experiment.id, id))
|
||||
.limit(1);
|
||||
|
||||
if (!exp || exp.length === 0) {
|
||||
throw new Error('Experiment not found');
|
||||
}
|
||||
|
||||
const datasetId = exp[0].datasetId;
|
||||
|
||||
// Get all participants
|
||||
const allParticipants = await db
|
||||
.select()
|
||||
.from(participant)
|
||||
.where(eq(participant.experimentId, id));
|
||||
|
||||
// Count onboarding completion (simple check for non-null)
|
||||
const completedOnboarding = allParticipants.filter(
|
||||
(p) => p.onboardingAnswers !== null && p.onboardingAnswers !== undefined
|
||||
).length;
|
||||
|
||||
// Check calibration completion using the proper validation
|
||||
let completedCalibration = 0;
|
||||
for (const p of allParticipants) {
|
||||
if (await isParticipantCalibrationDone(id, p)) {
|
||||
completedCalibration++;
|
||||
}
|
||||
}
|
||||
|
||||
// Count total dataset entries
|
||||
const totalEntriesResult = await db
|
||||
.select({ total: count().as('total') })
|
||||
.from(dataset_entry)
|
||||
.where(eq(dataset_entry.datasetId, datasetId));
|
||||
|
||||
const totalEntries = totalEntriesResult[0]?.total || 0;
|
||||
|
||||
// Get annotation statistics
|
||||
const annotationStats = await db
|
||||
.select({
|
||||
total: count().as('total'),
|
||||
entriesWithAnnotations: count(sql`DISTINCT ${annotation.datasetEntryId}`).as('entriesWithAnnotations'),
|
||||
})
|
||||
.from(annotation)
|
||||
.where(eq(annotation.experimentId, id));
|
||||
|
||||
// Get per-entry annotation counts
|
||||
const perEntryAnnotations = await db
|
||||
.select({
|
||||
datasetEntryId: annotation.datasetEntryId,
|
||||
annotationCount: count().as('annotationCount'),
|
||||
})
|
||||
.from(annotation)
|
||||
.where(eq(annotation.experimentId, id))
|
||||
.groupBy(annotation.datasetEntryId);
|
||||
|
||||
// Calculate statistics
|
||||
const totalAnnotations = annotationStats[0]?.total || 0;
|
||||
const entriesWithAnnotations = annotationStats[0]?.entriesWithAnnotations || 0;
|
||||
const entriesWithoutAnnotations = Math.max(0, totalEntries - entriesWithAnnotations);
|
||||
const averageAnnotationsPerEntry = totalEntries > 0 ? (totalAnnotations / totalEntries).toFixed(2) : '0';
|
||||
|
||||
// Find max and min annotations per entry
|
||||
let maxAnnotationsPerEntry = 0;
|
||||
let minAnnotationsPerEntry = 0;
|
||||
if (perEntryAnnotations.length > 0) {
|
||||
const counts = perEntryAnnotations.map(p => p.annotationCount || 0);
|
||||
maxAnnotationsPerEntry = Math.max(...counts);
|
||||
minAnnotationsPerEntry = Math.min(...counts);
|
||||
}
|
||||
|
||||
return {
|
||||
participants: {
|
||||
total: allParticipants.length,
|
||||
completedOnboarding,
|
||||
completedCalibration,
|
||||
},
|
||||
entries: {
|
||||
total: totalEntries,
|
||||
},
|
||||
annotations: {
|
||||
total: totalAnnotations,
|
||||
entriesWithAnnotations,
|
||||
entriesWithoutAnnotations,
|
||||
averagePerEntry: parseFloat(averageAnnotationsPerEntry),
|
||||
maxPerEntry: maxAnnotationsPerEntry,
|
||||
minPerEntry: minAnnotationsPerEntry,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching experiment statistics:', error);
|
||||
throw new Error('Failed to fetch experiment statistics');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user