CG-Feedback-Monitor/server/db/queries/meetings.ts
2026-07-06 19:08:24 +02:00

70 lines
3.5 KiB
TypeScript

import {
getAllRows, getRowById, getRowsWhereEquals, insertRow, updateRow,
deleteRowsWhereEquals, deleteRowsWhereIn, deleteRowById,
} from '../crud'
import type {
MeetingInstance, PhaseTemplate, Assignment, Consultant,
ConversationEntry, ConversationSkillScore, Assessment,
} from '../../../src/db/types'
import { getConsultantsByIds } from './consultants'
export const getMeetingInstance = (id: number): MeetingInstance | undefined => getRowById<MeetingInstance>('meetingInstances', id)
export const listMeetingInstancesForAssignment = (assignmentId: number): MeetingInstance[] =>
getRowsWhereEquals<MeetingInstance>('meetingInstances', 'assignmentId', assignmentId)
/**
* Kanonischer Filter für Aggregationen über mehrere Meetings: Papierkorb-/unfertige Meetings ausschließen,
* chronologisch sortiert. Siehe CLAUDE.md — immer diese Funktion nutzen, nicht erneut inline filtern.
*/
export function listDoneMeetingsChronological(assignmentId: number): MeetingInstance[] {
const instances = listMeetingInstancesForAssignment(assignmentId)
return instances
.filter(m => !m.deletedAt && m.status === 'done')
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
}
export const addMeetingInstance = (m: Omit<MeetingInstance, 'id'>): number => insertRow('meetingInstances', m)
export const updateMeetingInstance = (id: number, patch: Partial<MeetingInstance>): number =>
updateRow('meetingInstances', id, patch)
export const softDeleteMeetingInstance = (id: number): number =>
updateRow('meetingInstances', id, { deletedAt: new Date().toISOString() })
export const restoreMeetingInstance = (id: number): number =>
updateRow('meetingInstances', id, { deletedAt: null })
/** Cascade: conversationSkillScores der Entries → conversationEntries → assessments → meetingInstance. */
export function hardDeleteMeetingInstance(id: number): void {
const entryIds = getRowsWhereEquals<ConversationEntry>('conversationEntries', 'meetingInstanceId', id).map(e => e.id!)
deleteRowsWhereIn('conversationSkillScores', 'conversationEntryId', entryIds)
deleteRowsWhereEquals('conversationEntries', 'meetingInstanceId', id)
deleteRowsWhereEquals('assessments', 'meetingInstanceId', id)
deleteRowById('meetingInstances', id)
}
export interface MeetingExportData {
meeting: MeetingInstance
template: PhaseTemplate | undefined
assignment: Assignment | undefined
consultants: Consultant[]
entries: ConversationEntry[]
allScores: ConversationSkillScore[]
assessments: Assessment[]
}
/** Gebündelte Rohdaten für Markdown-/JSON-Export eines Meetings — vorher in meetingExport.ts dupliziert. */
export function getMeetingExportData(meetingId: number): MeetingExportData {
const meeting = getRowById<MeetingInstance>('meetingInstances', meetingId)
if (!meeting) throw new Error('Meeting nicht gefunden')
const template = getRowById<PhaseTemplate>('phaseTemplates', meeting.phaseTemplateId)
const assignment = getRowById<Assignment>('assignments', meeting.assignmentId)
const instituteeIds = assignment?.instituteeIds ?? []
const consultants = getConsultantsByIds(instituteeIds)
const entries = getRowsWhereEquals<ConversationEntry>('conversationEntries', 'meetingInstanceId', meetingId)
.sort((a, b) => a.sequenceIndex - b.sequenceIndex)
const allScores = getAllRows<ConversationSkillScore>('conversationSkillScores')
const assessments = getRowsWhereEquals<Assessment>('assessments', 'meetingInstanceId', meetingId)
return { meeting, template, assignment, consultants, entries, allScores, assessments }
}