Refactor annotation tools and implement quality choice annotation feature

- Updated the annotation label from "bewertet" to "annotiert" in the HomePage component.
- Removed the SingleChoiceView component and replaced it with a new QualityChoiceEntryView component for handling quality choice annotations.
- Introduced QualityChoiceView component to manage the flow of quality choice annotations, including navigation and progress tracking.
- Updated EditableExperimentHeader to default to 'quality-choice' annotation tool.
- Added unique index for annotations in the database schema to prevent duplicate entries.
- Enhanced WaveformPlayer to support audio playback state management.
This commit is contained in:
averel10
2026-03-20 10:01:20 +01:00
parent ea7c75c523
commit f26a0a7906
12 changed files with 1070 additions and 224 deletions

View File

@@ -66,7 +66,7 @@ export async function getAnnotationEntries(experimentId: number): Promise<Datase
/**
* Persists a batch of ratings for the authenticated user.
* Silently ignores duplicates (onConflictDoNothing).
* Updates existing annotations with the same userId, experimentId, and datasetEntryId.
*/
export async function saveAnnotations(
ratings: { entryId: number; rating: number; dialectLabel: string }[],
@@ -81,7 +81,13 @@ export async function saveAnnotations(
await db
.insert(annotation)
.values({experimentId, datasetEntryId: entryId, userId: session.user.id, rating, dialectLabel })
.onConflictDoNothing();
.onConflictDoUpdate({
target: [annotation.userId, annotation.experimentId, annotation.datasetEntryId],
set: {
rating,
dialectLabel,
}
});
}
}

View File

@@ -3,8 +3,8 @@ import { headers } from 'next/headers';
import Link from 'next/link';
import { auth } from '@/lib/auth';
import { getAnnotationEntries } from '@/app/actions/annotations';
import SingleChoiceView from '@/components/AnnotationViews/SingleChoiceView';
import { getExperimentById } from '@/app/actions/experiment';
import QualityChoiceView from '@/components/AnnotationViews/QualityChoiceView';
interface Props {
params: Promise<{ experimentId: string}>;
@@ -55,9 +55,11 @@ export default async function AnnotatePage({ params }: Props) {
// Render based on prototype type
const renderAnnotationView = () => {
switch (prototype) {
case 'single-choice':
return <SingleChoiceView entries={entries} experimentId={experimentId} />;
// Add more prototypes here as needed
case 'quality-choice':
return <QualityChoiceView entries={entries} experimentId={experimentId} />;
case 'binary':
//return <BinaryView entries={entries} experimentId={experimentId} />;
return <div/>;
default:
return (
<div className="max-w-xl mx-auto py-16 text-center">

View File

@@ -73,7 +73,7 @@ export default async function HomePage() {
{/* Progress */}
<div className="mt-3">
<div className="flex justify-between text-xs text-gray-400 mb-1">
<span>{done} / {total} bewertet</span>
<span>{done} / {total} annotiert</span>
<span>{pct}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">