feat: add new API Implementation Guide for Modules B, F, H

Co-authored-by: cetinmustafa83 <30570791+cetinmustafa83@users.noreply.github.com>
This commit is contained in:
v0andcetinmustafa83 committed 2026-07-31 13:53:06 +00:00
1 parent 8b33280c20
commit 0c9a46c163
11 files changed
+1800

No files matched your search

@@ -0,0 +1,90 @@
-- CreateTable
CREATE TABLE "exam_sessions" (
"id" TEXT NOT NULL PRIMARY KEY,
"schoolId" TEXT NOT NULL,
"assessmentId" TEXT NOT NULL,
"studentId" TEXT NOT NULL,
"startTime" DATETIME NOT NULL,
"endTime" DATETIME,
"submittedAt" DATETIME,
"status" TEXT NOT NULL DEFAULT 'not_started',
"score" REAL,
"totalPoints" REAL,
"lockdownEnabled" BOOLEAN NOT NULL DEFAULT true,
"cameraMonitor" BOOLEAN NOT NULL DEFAULT false,
"screenShare" BOOLEAN NOT NULL DEFAULT false,
"environmentNotes" TEXT,
"suspiciousActivity" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "exam_sessions_schoolId_fkey" FOREIGN KEY ("schoolId") REFERENCES "School" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "exam_sessions_assessmentId_fkey" FOREIGN KEY ("assessmentId") REFERENCES "Assessment" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "exam_sessions_studentId_fkey" FOREIGN KEY ("studentId") REFERENCES "Student" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "exam_answers" (
"id" TEXT NOT NULL PRIMARY KEY,
"examSessionId" TEXT NOT NULL,
"questionId" TEXT NOT NULL,
"answer" TEXT,
"submittedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isCorrect" BOOLEAN,
"pointsAwarded" REAL,
"reviewedAt" DATETIME,
CONSTRAINT "exam_answers_examSessionId_fkey" FOREIGN KEY ("examSessionId") REFERENCES "exam_sessions" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "exam_events" (
"id" TEXT NOT NULL PRIMARY KEY,
"examSessionId" TEXT NOT NULL,
"eventType" TEXT NOT NULL,
"severity" TEXT NOT NULL DEFAULT 'info',
"description" TEXT,
"timestamp" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"metadata" TEXT,
CONSTRAINT "exam_events_examSessionId_fkey" FOREIGN KEY ("examSessionId") REFERENCES "exam_sessions" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "exam_warnings" (
"id" TEXT NOT NULL PRIMARY KEY,
"examSessionId" TEXT NOT NULL,
"studentId" TEXT NOT NULL,
"warningType" TEXT NOT NULL,
"message" TEXT NOT NULL,
"acknowledged" BOOLEAN NOT NULL DEFAULT false,
"acknowledgedAt" DATETIME,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "exam_warnings_examSessionId_fkey" FOREIGN KEY ("examSessionId") REFERENCES "exam_sessions" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE INDEX "exam_sessions_schoolId_idx" ON "exam_sessions"("schoolId");
-- CreateIndex
CREATE INDEX "exam_sessions_assessmentId_idx" ON "exam_sessions"("assessmentId");
-- CreateIndex
CREATE INDEX "exam_sessions_studentId_idx" ON "exam_sessions"("studentId");
-- CreateIndex
CREATE INDEX "exam_sessions_status_idx" ON "exam_sessions"("status");
-- CreateIndex
CREATE INDEX "exam_answers_examSessionId_idx" ON "exam_answers"("examSessionId");
-- CreateIndex
CREATE INDEX "exam_answers_questionId_idx" ON "exam_answers"("questionId");
-- CreateIndex
CREATE INDEX "exam_events_examSessionId_idx" ON "exam_events"("examSessionId");
-- CreateIndex
CREATE INDEX "exam_events_eventType_idx" ON "exam_events"("eventType");
-- CreateIndex
CREATE INDEX "exam_warnings_examSessionId_idx" ON "exam_warnings"("examSessionId");
-- CreateIndex
CREATE INDEX "exam_warnings_studentId_idx" ON "exam_warnings"("studentId");
@@ -0,0 +1,60 @@
-- CreateTable
CREATE TABLE "notification_hubs" (
"id" TEXT NOT NULL PRIMARY KEY,
"schoolId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"category" TEXT NOT NULL,
"priority" TEXT NOT NULL DEFAULT 'normal',
"title" TEXT NOT NULL,
"message" TEXT NOT NULL,
"actionUrl" TEXT,
"actionLabel" TEXT,
"isRead" BOOLEAN NOT NULL DEFAULT false,
"readAt" DATETIME,
"isDismissed" BOOLEAN NOT NULL DEFAULT false,
"dismissedAt" DATETIME,
"sourceType" TEXT,
"sourceId" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"expiresAt" DATETIME,
CONSTRAINT "notification_hubs_schoolId_fkey" FOREIGN KEY ("schoolId") REFERENCES "School" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "notification_hubs_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "notification_archives" (
"id" TEXT NOT NULL PRIMARY KEY,
"schoolId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"originalId" TEXT,
"title" TEXT NOT NULL,
"message" TEXT NOT NULL,
"category" TEXT NOT NULL,
"archivedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "notification_archives_schoolId_fkey" FOREIGN KEY ("schoolId") REFERENCES "School" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "notification_archives_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateIndex
CREATE INDEX "notification_hubs_schoolId_idx" ON "notification_hubs"("schoolId");
-- CreateIndex
CREATE INDEX "notification_hubs_userId_idx" ON "notification_hubs"("userId");
-- CreateIndex
CREATE INDEX "notification_hubs_isRead_idx" ON "notification_hubs"("isRead");
-- CreateIndex
CREATE INDEX "notification_hubs_category_idx" ON "notification_hubs"("category");
-- CreateIndex
CREATE INDEX "notification_hubs_createdAt_idx" ON "notification_hubs"("createdAt");
-- CreateIndex
CREATE INDEX "notification_archives_schoolId_idx" ON "notification_archives"("schoolId");
-- CreateIndex
CREATE INDEX "notification_archives_userId_idx" ON "notification_archives"("userId");
-- CreateIndex
CREATE INDEX "notification_archives_archivedAt_idx" ON "notification_archives"("archivedAt");
Binary file not shown.
+147
View File
@@ -127,6 +127,11 @@ model School {
signageMessages SignageMessage[]
signageSchedules SignageSchedule[]
signageAuditLogs SignageAuditLog[]
// Module F exam mode relations
examSessions ExamSession[]
// Module H notifications relations
notificationHubs NotificationHub[]
notificationArchives NotificationArchive[]
}
model User {
@@ -226,6 +231,9 @@ model User {
deletionFlagsApproved DeletionFlag[] @relation("DeletionFlagApprover")
// Module B signage relations
signageMessagesCreated SignageMessage[]
// Module H notifications relations
notificationHubs NotificationHub[]
notificationArchives NotificationArchive[]
}
model SchoolYear {
@@ -349,6 +357,8 @@ model Student {
wellnessCheckins WellnessCheckin[]
wellnessScores WellnessScore[]
careerProfile CareerProfile?
// Module F exam mode relations
examSessions ExamSession[]
@@index([userId])
}
@@ -521,6 +531,8 @@ model Assessment {
gradingReviews TeacherGradingReview[]
gradingAnnotations GradingAnnotation[]
examPlan ExamPlan?
// Module F exam mode relations
examSessions ExamSession[]
}
model AssessmentCompetencyLink {
@@ -3425,3 +3437,138 @@ model SignageAuditLog {
@@index([action])
@@map("signage_audit_logs")
}
// ─── Module F: Exam Mode / Digital Test Environment ──────────────────
model ExamSession {
id String @id @default(cuid())
schoolId String
assessmentId String
studentId String
startTime DateTime
endTime DateTime?
submittedAt DateTime?
status String @default("not_started") // not_started, in_progress, submitted, graded
score Float?
totalPoints Float?
lockdownEnabled Boolean @default(true) // Kiosk mode lockdown
cameraMonitor Boolean @default(false) // Enable face monitoring
screenShare Boolean @default(false) // Allow screen sharing for proctoring
environmentNotes String? // JSON: { browser, os, resolution, etc }
suspiciousActivity String? // JSON array of incidents
createdAt DateTime @default(now())
school School @relation(fields: [schoolId], references: [id])
assessment Assessment @relation(fields: [assessmentId], references: [id])
student Student @relation(fields: [studentId], references: [id])
answers ExamAnswer[]
events ExamEvent[]
warnings ExamWarning[]
@@index([schoolId])
@@index([assessmentId])
@@index([studentId])
@@index([status])
@@map("exam_sessions")
}
model ExamAnswer {
id String @id @default(cuid())
examSessionId String
questionId String
answer String? // JSON: selected choice, free text, etc
submittedAt DateTime @default(now())
isCorrect Boolean?
pointsAwarded Float?
reviewedAt DateTime?
examSession ExamSession @relation(fields: [examSessionId], references: [id], onDelete: Cascade)
@@index([examSessionId])
@@index([questionId])
@@map("exam_answers")
}
model ExamEvent {
id String @id @default(cuid())
examSessionId String
eventType String // tab_switch, copy_paste, window_blur, camera_detected_none, suspicious_movement, time_warning
severity String @default("info") // info, warning, critical
description String?
timestamp DateTime @default(now())
metadata String? // JSON: additional context
examSession ExamSession @relation(fields: [examSessionId], references: [id], onDelete: Cascade)
@@index([examSessionId])
@@index([eventType])
@@map("exam_events")
}
model ExamWarning {
id String @id @default(cuid())
examSessionId String
studentId String
warningType String // time_low, behavior_suspicious, multiple_tabs, camera_issue
message String
acknowledged Boolean @default(false)
acknowledgedAt DateTime?
createdAt DateTime @default(now())
examSession ExamSession @relation(fields: [examSessionId], references: [id], onDelete: Cascade)
@@index([examSessionId])
@@index([studentId])
@@map("exam_warnings")
}
// ─── Module H: Centralized Notifications Hub ───────────────────────
model NotificationHub {
id String @id @default(cuid())
schoolId String
userId String
category String // assessment, behavior, communication, wellness, system, announcement
priority String @default("normal") // low, normal, high, critical
title String
message String
actionUrl String? // Link to related resource
actionLabel String? // e.g. "View Grade", "Respond"
isRead Boolean @default(false)
readAt DateTime?
isDismissed Boolean @default(false)
dismissedAt DateTime?
sourceType String? // assessment, incident, message, announcement, etc
sourceId String? // ID of related entity
createdAt DateTime @default(now())
expiresAt DateTime? // Auto-archive if set
school School @relation(fields: [schoolId], references: [id])
user User @relation(fields: [userId], references: [id])
@@index([schoolId])
@@index([userId])
@@index([isRead])
@@index([category])
@@index([createdAt])
@@map("notification_hubs")
}
model NotificationArchive {
id String @id @default(cuid())
schoolId String
userId String
originalId String? // reference to NotificationHub.id if archived
title String
message String
category String
archivedAt DateTime @default(now())
school School @relation(fields: [schoolId], references: [id])
user User @relation(fields: [userId], references: [id])
@@index([schoolId])
@@index([userId])
@@index([archivedAt])
@@map("notification_archives")
}