CG-Feedback-Monitor/server/db/queries/meetings.ts
Lars 234f87d408 V2-Dev-Stand: Skala 1-10, Benefits/Concerns, DEV-Isolation und Gitea-Doku.
Bündelt die Neuentwicklung in AssigmentMonitorV2 (eigene Ports/DB), die Umstellung auf numerische Bewertungen, Meeting-Checklisten, KI-Tagging und die geplante Feedback-Kaskade — als Basis für Versionsverwaltung in Gitea.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 17:35:05 +02:00

84 lines
4.4 KiB
TypeScript

import {
getAllRows, getRowById, getRowsWhereEquals, insertRow, updateRow,
deleteRowsWhereEquals, deleteRowsWhereIn, deleteRowById,
} from '../crud'
import type {
MeetingInstance, PhaseTemplate, Assignment, Consultant,
ConversationEntry, ConversationSkillScore, Assessment,
ChecklistItemDef, ChecklistResponse, MeetingCategoryRating,
} from '../../../src/db/types'
import { getConsultantsByIds } from './consultants'
import { listChecklistItemDefsForPhaseTemplate, listChecklistResponsesForMeeting } from './checklists'
import { listMeetingCategoryRatingsForMeeting } from './meetingCategoryRatings'
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 → meetingCategoryRatings → 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)
deleteRowsWhereEquals('meetingCategoryRatings', 'meetingInstanceId', id)
deleteRowsWhereEquals('checklistResponses', 'meetingInstanceId', id)
deleteRowById('meetingInstances', id)
}
export interface MeetingExportData {
meeting: MeetingInstance
template: PhaseTemplate | undefined
assignment: Assignment | undefined
consultants: Consultant[]
entries: ConversationEntry[]
allScores: ConversationSkillScore[]
assessments: Assessment[]
categoryRatings: MeetingCategoryRating[]
checklistItems: ChecklistItemDef[]
checklistResponses: ChecklistResponse[]
}
/** 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)
const categoryRatings = listMeetingCategoryRatingsForMeeting(meetingId)
const checklistItems = template?.hasChecklist ? listChecklistItemDefsForPhaseTemplate(template.id!) : []
const checklistResponses = template?.hasChecklist ? listChecklistResponsesForMeeting(meetingId) : []
return {
meeting, template, assignment, consultants, entries, allScores,
assessments, categoryRatings, checklistItems, checklistResponses,
}
}