Some checks failed
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Failing after 37s
CI/CD / test-python-backend-compliance (push) Successful in 39s
CI/CD / test-python-document-crawler (push) Successful in 26s
CI/CD / test-python-dsms-gateway (push) Successful in 23s
CI/CD / validate-canonical-controls (push) Successful in 12s
CI/CD / Deploy (push) Has been skipped
Interactive Training Videos (CP-TRAIN): - DB migration 022: training_checkpoints + checkpoint_progress tables - NarratorScript generation via Anthropic (AI Teacher persona, German) - TTS batch synthesis + interactive video pipeline (slides + checkpoint slides + FFmpeg) - 4 new API endpoints: generate-interactive, interactive-manifest, checkpoint submit, checkpoint progress - InteractiveVideoPlayer component (HTML5 Video, quiz overlay, seek protection, progress tracking) - Learner portal integration with automatic completion on all checkpoints passed - 30 new tests (handler validation + grading logic + manifest/progress + seek protection) Training Blocks: - Block generator, block store, block config CRUD + preview/generate endpoints - Migration 021: training_blocks schema Control Generator + Canonical Library: - Control generator routes + service enhancements - Canonical control library helpers, sidebar entry - Citation backfill service + tests - CE libraries data (hazard, protection, evidence, lifecycle, components) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
758 lines
30 KiB
Go
758 lines
30 KiB
Go
package training
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// ============================================================================
|
|
// Constants / Enums
|
|
// ============================================================================
|
|
|
|
// RegulationArea represents a compliance regulation area
|
|
type RegulationArea string
|
|
|
|
const (
|
|
RegulationDSGVO RegulationArea = "dsgvo"
|
|
RegulationNIS2 RegulationArea = "nis2"
|
|
RegulationISO27001 RegulationArea = "iso27001"
|
|
RegulationAIAct RegulationArea = "ai_act"
|
|
RegulationGeschGehG RegulationArea = "geschgehg"
|
|
RegulationHinSchG RegulationArea = "hinschg"
|
|
)
|
|
|
|
// FrequencyType represents the training frequency
|
|
type FrequencyType string
|
|
|
|
const (
|
|
FrequencyOnboarding FrequencyType = "onboarding"
|
|
FrequencyAnnual FrequencyType = "annual"
|
|
FrequencyEventTrigger FrequencyType = "event_trigger"
|
|
FrequencyMicro FrequencyType = "micro"
|
|
)
|
|
|
|
// AssignmentStatus represents the status of a training assignment
|
|
type AssignmentStatus string
|
|
|
|
const (
|
|
AssignmentStatusPending AssignmentStatus = "pending"
|
|
AssignmentStatusInProgress AssignmentStatus = "in_progress"
|
|
AssignmentStatusCompleted AssignmentStatus = "completed"
|
|
AssignmentStatusOverdue AssignmentStatus = "overdue"
|
|
AssignmentStatusExpired AssignmentStatus = "expired"
|
|
)
|
|
|
|
// TriggerType represents how a training was assigned
|
|
type TriggerType string
|
|
|
|
const (
|
|
TriggerOnboarding TriggerType = "onboarding"
|
|
TriggerAnnual TriggerType = "annual"
|
|
TriggerEvent TriggerType = "event"
|
|
TriggerManual TriggerType = "manual"
|
|
)
|
|
|
|
// ContentFormat represents the format of module content
|
|
type ContentFormat string
|
|
|
|
const (
|
|
ContentFormatMarkdown ContentFormat = "markdown"
|
|
ContentFormatHTML ContentFormat = "html"
|
|
)
|
|
|
|
// Difficulty represents the difficulty level of a quiz question
|
|
type Difficulty string
|
|
|
|
const (
|
|
DifficultyEasy Difficulty = "easy"
|
|
DifficultyMedium Difficulty = "medium"
|
|
DifficultyHard Difficulty = "hard"
|
|
)
|
|
|
|
// AuditAction represents an action in the audit trail
|
|
type AuditAction string
|
|
|
|
const (
|
|
AuditActionAssigned AuditAction = "assigned"
|
|
AuditActionStarted AuditAction = "started"
|
|
AuditActionCompleted AuditAction = "completed"
|
|
AuditActionQuizSubmitted AuditAction = "quiz_submitted"
|
|
AuditActionEscalated AuditAction = "escalated"
|
|
AuditActionCertificateIssued AuditAction = "certificate_issued"
|
|
AuditActionContentGenerated AuditAction = "content_generated"
|
|
)
|
|
|
|
// AuditEntityType represents the type of entity in audit log
|
|
type AuditEntityType string
|
|
|
|
const (
|
|
AuditEntityAssignment AuditEntityType = "assignment"
|
|
AuditEntityModule AuditEntityType = "module"
|
|
AuditEntityQuiz AuditEntityType = "quiz"
|
|
AuditEntityCertificate AuditEntityType = "certificate"
|
|
)
|
|
|
|
// ============================================================================
|
|
// Role Constants
|
|
// ============================================================================
|
|
|
|
const (
|
|
RoleR1 = "R1" // Geschaeftsfuehrung
|
|
RoleR2 = "R2" // IT-Leitung
|
|
RoleR3 = "R3" // DSB
|
|
RoleR4 = "R4" // ISB
|
|
RoleR5 = "R5" // HR
|
|
RoleR6 = "R6" // Einkauf
|
|
RoleR7 = "R7" // Fachabteilung
|
|
RoleR8 = "R8" // IT-Admin
|
|
RoleR9 = "R9" // Alle Mitarbeiter
|
|
RoleR10 = "R10" // Behoerden / Oeffentlicher Dienst
|
|
)
|
|
|
|
// RoleLabels maps role codes to human-readable labels
|
|
var RoleLabels = map[string]string{
|
|
RoleR1: "Geschaeftsfuehrung",
|
|
RoleR2: "IT-Leitung",
|
|
RoleR3: "Datenschutzbeauftragter",
|
|
RoleR4: "Informationssicherheitsbeauftragter",
|
|
RoleR5: "HR / Personal",
|
|
RoleR6: "Einkauf / Beschaffung",
|
|
RoleR7: "Fachabteilung",
|
|
RoleR8: "IT-Administration",
|
|
RoleR9: "Alle Mitarbeiter",
|
|
RoleR10: "Behoerden / Oeffentlicher Dienst",
|
|
}
|
|
|
|
// NIS2RoleMapping maps internal roles to NIS2 levels
|
|
var NIS2RoleMapping = map[string]string{
|
|
RoleR1: "N1", // Geschaeftsfuehrung
|
|
RoleR2: "N2", // IT-Leitung
|
|
RoleR3: "N3", // DSB
|
|
RoleR4: "N3", // ISB
|
|
RoleR5: "N4", // HR
|
|
RoleR6: "N4", // Einkauf
|
|
RoleR7: "N5", // Fachabteilung
|
|
RoleR8: "N2", // IT-Admin
|
|
RoleR9: "N5", // Alle Mitarbeiter
|
|
RoleR10: "N4", // Behoerden
|
|
}
|
|
|
|
// TargetAudienceRoleMapping maps canonical control target_audience values to CTM roles
|
|
var TargetAudienceRoleMapping = map[string][]string{
|
|
"enterprise": {RoleR1, RoleR4, RoleR5, RoleR6, RoleR7, RoleR9}, // Unternehmen
|
|
"authority": {RoleR10}, // Behoerden
|
|
"provider": {RoleR2, RoleR8}, // IT-Dienstleister
|
|
"all": {RoleR1, RoleR2, RoleR3, RoleR4, RoleR5, RoleR6, RoleR7, RoleR8, RoleR9, RoleR10},
|
|
}
|
|
|
|
// CategoryRoleMapping provides additional role hints based on control category
|
|
var CategoryRoleMapping = map[string][]string{
|
|
"encryption": {RoleR2, RoleR8},
|
|
"authentication": {RoleR2, RoleR8, RoleR9},
|
|
"network": {RoleR2, RoleR8},
|
|
"data_protection": {RoleR3, RoleR5, RoleR9},
|
|
"logging": {RoleR2, RoleR4, RoleR8},
|
|
"incident": {RoleR1, RoleR4},
|
|
"continuity": {RoleR1, RoleR2, RoleR4},
|
|
"compliance": {RoleR1, RoleR3, RoleR4},
|
|
"supply_chain": {RoleR6},
|
|
"physical": {RoleR7},
|
|
"personnel": {RoleR5, RoleR9},
|
|
"application": {RoleR8},
|
|
"system": {RoleR2, RoleR8},
|
|
"risk": {RoleR1, RoleR4},
|
|
"governance": {RoleR1, RoleR4},
|
|
"hardware": {RoleR2, RoleR8},
|
|
"identity": {RoleR2, RoleR3, RoleR8},
|
|
}
|
|
|
|
// ============================================================================
|
|
// Main Entities
|
|
// ============================================================================
|
|
|
|
// TrainingModule represents a compliance training module
|
|
type TrainingModule struct {
|
|
ID uuid.UUID `json:"id"`
|
|
TenantID uuid.UUID `json:"tenant_id"`
|
|
AcademyCourseID *uuid.UUID `json:"academy_course_id,omitempty"`
|
|
ModuleCode string `json:"module_code"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description,omitempty"`
|
|
RegulationArea RegulationArea `json:"regulation_area"`
|
|
NIS2Relevant bool `json:"nis2_relevant"`
|
|
ISOControls []string `json:"iso_controls"` // JSONB
|
|
FrequencyType FrequencyType `json:"frequency_type"`
|
|
ValidityDays int `json:"validity_days"`
|
|
RiskWeight float64 `json:"risk_weight"`
|
|
ContentType string `json:"content_type"`
|
|
DurationMinutes int `json:"duration_minutes"`
|
|
PassThreshold int `json:"pass_threshold"`
|
|
IsActive bool `json:"is_active"`
|
|
SortOrder int `json:"sort_order"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// TrainingMatrixEntry represents a role-to-module mapping in the CTM
|
|
type TrainingMatrixEntry struct {
|
|
ID uuid.UUID `json:"id"`
|
|
TenantID uuid.UUID `json:"tenant_id"`
|
|
RoleCode string `json:"role_code"`
|
|
ModuleID uuid.UUID `json:"module_id"`
|
|
IsMandatory bool `json:"is_mandatory"`
|
|
Priority int `json:"priority"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
// Joined fields (optional, populated in queries)
|
|
ModuleCode string `json:"module_code,omitempty"`
|
|
ModuleTitle string `json:"module_title,omitempty"`
|
|
}
|
|
|
|
// TrainingAssignment represents a user's training assignment
|
|
type TrainingAssignment struct {
|
|
ID uuid.UUID `json:"id"`
|
|
TenantID uuid.UUID `json:"tenant_id"`
|
|
ModuleID uuid.UUID `json:"module_id"`
|
|
UserID uuid.UUID `json:"user_id"`
|
|
UserName string `json:"user_name"`
|
|
UserEmail string `json:"user_email"`
|
|
RoleCode string `json:"role_code,omitempty"`
|
|
TriggerType TriggerType `json:"trigger_type"`
|
|
TriggerEvent string `json:"trigger_event,omitempty"`
|
|
Status AssignmentStatus `json:"status"`
|
|
ProgressPercent int `json:"progress_percent"`
|
|
QuizScore *float64 `json:"quiz_score,omitempty"`
|
|
QuizPassed *bool `json:"quiz_passed,omitempty"`
|
|
QuizAttempts int `json:"quiz_attempts"`
|
|
StartedAt *time.Time `json:"started_at,omitempty"`
|
|
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
|
Deadline time.Time `json:"deadline"`
|
|
CertificateID *uuid.UUID `json:"certificate_id,omitempty"`
|
|
EscalationLevel int `json:"escalation_level"`
|
|
LastEscalationAt *time.Time `json:"last_escalation_at,omitempty"`
|
|
EnrollmentID *uuid.UUID `json:"enrollment_id,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
// Joined fields
|
|
ModuleCode string `json:"module_code,omitempty"`
|
|
ModuleTitle string `json:"module_title,omitempty"`
|
|
}
|
|
|
|
// QuizQuestion represents a persistent quiz question for a module
|
|
type QuizQuestion struct {
|
|
ID uuid.UUID `json:"id"`
|
|
ModuleID uuid.UUID `json:"module_id"`
|
|
Question string `json:"question"`
|
|
Options []string `json:"options"` // JSONB
|
|
CorrectIndex int `json:"correct_index"`
|
|
Explanation string `json:"explanation,omitempty"`
|
|
Difficulty Difficulty `json:"difficulty"`
|
|
IsActive bool `json:"is_active"`
|
|
SortOrder int `json:"sort_order"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// QuizAttempt represents a single quiz attempt by a user
|
|
type QuizAttempt struct {
|
|
ID uuid.UUID `json:"id"`
|
|
AssignmentID uuid.UUID `json:"assignment_id"`
|
|
UserID uuid.UUID `json:"user_id"`
|
|
Answers []QuizAnswer `json:"answers"` // JSONB
|
|
Score float64 `json:"score"`
|
|
Passed bool `json:"passed"`
|
|
CorrectCount int `json:"correct_count"`
|
|
TotalCount int `json:"total_count"`
|
|
DurationSeconds *int `json:"duration_seconds,omitempty"`
|
|
AttemptedAt time.Time `json:"attempted_at"`
|
|
}
|
|
|
|
// QuizAnswer represents a single answer within a quiz attempt
|
|
type QuizAnswer struct {
|
|
QuestionID uuid.UUID `json:"question_id"`
|
|
SelectedIndex int `json:"selected_index"`
|
|
Correct bool `json:"correct"`
|
|
}
|
|
|
|
// AuditLogEntry represents an entry in the training audit trail
|
|
type AuditLogEntry struct {
|
|
ID uuid.UUID `json:"id"`
|
|
TenantID uuid.UUID `json:"tenant_id"`
|
|
UserID *uuid.UUID `json:"user_id,omitempty"`
|
|
Action AuditAction `json:"action"`
|
|
EntityType AuditEntityType `json:"entity_type"`
|
|
EntityID *uuid.UUID `json:"entity_id,omitempty"`
|
|
Details map[string]interface{} `json:"details"` // JSONB
|
|
IPAddress string `json:"ip_address,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// ModuleContent represents LLM-generated or manual content for a module
|
|
type ModuleContent struct {
|
|
ID uuid.UUID `json:"id"`
|
|
ModuleID uuid.UUID `json:"module_id"`
|
|
Version int `json:"version"`
|
|
ContentFormat ContentFormat `json:"content_format"`
|
|
ContentBody string `json:"content_body"`
|
|
Summary string `json:"summary,omitempty"`
|
|
GeneratedBy string `json:"generated_by,omitempty"`
|
|
LLMModel string `json:"llm_model,omitempty"`
|
|
IsPublished bool `json:"is_published"`
|
|
ReviewedBy *uuid.UUID `json:"reviewed_by,omitempty"`
|
|
ReviewedAt *time.Time `json:"reviewed_at,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// TrainingStats contains aggregated training metrics
|
|
type TrainingStats struct {
|
|
TotalModules int `json:"total_modules"`
|
|
TotalAssignments int `json:"total_assignments"`
|
|
CompletionRate float64 `json:"completion_rate"`
|
|
OverdueCount int `json:"overdue_count"`
|
|
PendingCount int `json:"pending_count"`
|
|
InProgressCount int `json:"in_progress_count"`
|
|
CompletedCount int `json:"completed_count"`
|
|
AvgQuizScore float64 `json:"avg_quiz_score"`
|
|
AvgCompletionDays float64 `json:"avg_completion_days"`
|
|
UpcomingDeadlines int `json:"upcoming_deadlines"` // within 7 days
|
|
}
|
|
|
|
// ComplianceGap represents a missing or overdue training requirement
|
|
type ComplianceGap struct {
|
|
ModuleID uuid.UUID `json:"module_id"`
|
|
ModuleCode string `json:"module_code"`
|
|
ModuleTitle string `json:"module_title"`
|
|
RegulationArea RegulationArea `json:"regulation_area"`
|
|
RoleCode string `json:"role_code"`
|
|
IsMandatory bool `json:"is_mandatory"`
|
|
AssignmentID *uuid.UUID `json:"assignment_id,omitempty"`
|
|
Status string `json:"status"` // "missing", "overdue", "expired"
|
|
Deadline *time.Time `json:"deadline,omitempty"`
|
|
}
|
|
|
|
// EscalationResult represents the result of an escalation check
|
|
type EscalationResult struct {
|
|
AssignmentID uuid.UUID `json:"assignment_id"`
|
|
UserID uuid.UUID `json:"user_id"`
|
|
UserName string `json:"user_name"`
|
|
UserEmail string `json:"user_email"`
|
|
ModuleTitle string `json:"module_title"`
|
|
PreviousLevel int `json:"previous_level"`
|
|
NewLevel int `json:"new_level"`
|
|
DaysOverdue int `json:"days_overdue"`
|
|
EscalationLabel string `json:"escalation_label"`
|
|
}
|
|
|
|
// DeadlineInfo represents upcoming deadline information
|
|
type DeadlineInfo struct {
|
|
AssignmentID uuid.UUID `json:"assignment_id"`
|
|
ModuleCode string `json:"module_code"`
|
|
ModuleTitle string `json:"module_title"`
|
|
UserID uuid.UUID `json:"user_id"`
|
|
UserName string `json:"user_name"`
|
|
Deadline time.Time `json:"deadline"`
|
|
DaysLeft int `json:"days_left"`
|
|
Status AssignmentStatus `json:"status"`
|
|
}
|
|
|
|
// ============================================================================
|
|
// Filter Types
|
|
// ============================================================================
|
|
|
|
// ModuleFilters defines filters for listing modules
|
|
type ModuleFilters struct {
|
|
RegulationArea RegulationArea
|
|
FrequencyType FrequencyType
|
|
IsActive *bool
|
|
NIS2Relevant *bool
|
|
Search string
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
// AssignmentFilters defines filters for listing assignments
|
|
type AssignmentFilters struct {
|
|
ModuleID *uuid.UUID
|
|
UserID *uuid.UUID
|
|
RoleCode string
|
|
Status AssignmentStatus
|
|
Overdue *bool
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
// AuditLogFilters defines filters for listing audit log entries
|
|
type AuditLogFilters struct {
|
|
UserID *uuid.UUID
|
|
Action AuditAction
|
|
EntityType AuditEntityType
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
// ============================================================================
|
|
// API Request/Response Types
|
|
// ============================================================================
|
|
|
|
// CreateModuleRequest is the API request for creating a training module
|
|
type CreateModuleRequest struct {
|
|
ModuleCode string `json:"module_code" binding:"required"`
|
|
Title string `json:"title" binding:"required"`
|
|
Description string `json:"description,omitempty"`
|
|
RegulationArea RegulationArea `json:"regulation_area" binding:"required"`
|
|
NIS2Relevant bool `json:"nis2_relevant"`
|
|
ISOControls []string `json:"iso_controls,omitempty"`
|
|
FrequencyType FrequencyType `json:"frequency_type" binding:"required"`
|
|
ValidityDays int `json:"validity_days"`
|
|
RiskWeight float64 `json:"risk_weight"`
|
|
ContentType string `json:"content_type"`
|
|
DurationMinutes int `json:"duration_minutes"`
|
|
PassThreshold int `json:"pass_threshold"`
|
|
}
|
|
|
|
// UpdateModuleRequest is the API request for updating a training module
|
|
type UpdateModuleRequest struct {
|
|
Title *string `json:"title,omitempty"`
|
|
Description *string `json:"description,omitempty"`
|
|
NIS2Relevant *bool `json:"nis2_relevant,omitempty"`
|
|
ISOControls []string `json:"iso_controls,omitempty"`
|
|
ValidityDays *int `json:"validity_days,omitempty"`
|
|
RiskWeight *float64 `json:"risk_weight,omitempty"`
|
|
DurationMinutes *int `json:"duration_minutes,omitempty"`
|
|
PassThreshold *int `json:"pass_threshold,omitempty"`
|
|
IsActive *bool `json:"is_active,omitempty"`
|
|
}
|
|
|
|
// SetMatrixEntryRequest is the API request for setting a CTM entry
|
|
type SetMatrixEntryRequest struct {
|
|
RoleCode string `json:"role_code" binding:"required"`
|
|
ModuleID uuid.UUID `json:"module_id" binding:"required"`
|
|
IsMandatory bool `json:"is_mandatory"`
|
|
Priority int `json:"priority"`
|
|
}
|
|
|
|
// ComputeAssignmentsRequest is the API request for computing assignments
|
|
type ComputeAssignmentsRequest struct {
|
|
UserID uuid.UUID `json:"user_id" binding:"required"`
|
|
UserName string `json:"user_name" binding:"required"`
|
|
UserEmail string `json:"user_email" binding:"required"`
|
|
Roles []string `json:"roles" binding:"required"`
|
|
Trigger string `json:"trigger"`
|
|
}
|
|
|
|
// UpdateAssignmentProgressRequest updates progress on an assignment
|
|
type UpdateAssignmentProgressRequest struct {
|
|
Progress int `json:"progress" binding:"required"`
|
|
}
|
|
|
|
// SubmitTrainingQuizRequest is the API request for submitting a quiz
|
|
type SubmitTrainingQuizRequest struct {
|
|
AssignmentID uuid.UUID `json:"assignment_id" binding:"required"`
|
|
Answers []QuizAnswer `json:"answers" binding:"required"`
|
|
DurationSeconds *int `json:"duration_seconds,omitempty"`
|
|
}
|
|
|
|
// SubmitTrainingQuizResponse is the API response for quiz submission
|
|
type SubmitTrainingQuizResponse struct {
|
|
AttemptID uuid.UUID `json:"attempt_id"`
|
|
Score float64 `json:"score"`
|
|
Passed bool `json:"passed"`
|
|
CorrectCount int `json:"correct_count"`
|
|
TotalCount int `json:"total_count"`
|
|
Threshold int `json:"threshold"`
|
|
}
|
|
|
|
// GenerateContentRequest is the API request for LLM content generation
|
|
type GenerateContentRequest struct {
|
|
ModuleID uuid.UUID `json:"module_id" binding:"required"`
|
|
Language string `json:"language"`
|
|
}
|
|
|
|
// GenerateQuizRequest is the API request for LLM quiz generation
|
|
type GenerateQuizRequest struct {
|
|
ModuleID uuid.UUID `json:"module_id" binding:"required"`
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
// PublishContentRequest is the API request for publishing content
|
|
type PublishContentRequest struct {
|
|
ReviewedBy uuid.UUID `json:"reviewed_by"`
|
|
}
|
|
|
|
// BulkAssignRequest is the API request for bulk assigning a module
|
|
type BulkAssignRequest struct {
|
|
ModuleID uuid.UUID `json:"module_id" binding:"required"`
|
|
RoleCodes []string `json:"role_codes" binding:"required"`
|
|
Trigger string `json:"trigger"`
|
|
Deadline time.Time `json:"deadline" binding:"required"`
|
|
}
|
|
|
|
// ModuleListResponse is the API response for listing modules
|
|
type ModuleListResponse struct {
|
|
Modules []TrainingModule `json:"modules"`
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
// AssignmentListResponse is the API response for listing assignments
|
|
type AssignmentListResponse struct {
|
|
Assignments []TrainingAssignment `json:"assignments"`
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
// MatrixResponse is the API response for the full training matrix
|
|
type MatrixResponse struct {
|
|
Entries map[string][]TrainingMatrixEntry `json:"entries"` // role_code -> entries
|
|
Roles map[string]string `json:"roles"` // role_code -> label
|
|
}
|
|
|
|
// AuditLogResponse is the API response for listing audit log entries
|
|
type AuditLogResponse struct {
|
|
Entries []AuditLogEntry `json:"entries"`
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
// EscalationResponse is the API response for escalation check
|
|
type EscalationResponse struct {
|
|
Results []EscalationResult `json:"results"`
|
|
TotalChecked int `json:"total_checked"`
|
|
Escalated int `json:"escalated"`
|
|
}
|
|
|
|
// DeadlineListResponse is the API response for listing deadlines
|
|
type DeadlineListResponse struct {
|
|
Deadlines []DeadlineInfo `json:"deadlines"`
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
// BulkResult holds the result of a bulk generation operation
|
|
type BulkResult struct {
|
|
Generated int `json:"generated"`
|
|
Skipped int `json:"skipped"`
|
|
Errors []string `json:"errors"`
|
|
}
|
|
|
|
// ============================================================================
|
|
// Training Block Types (Controls → Schulungsmodule Pipeline)
|
|
// ============================================================================
|
|
|
|
// TrainingBlockConfig defines how canonical controls are grouped into training modules
|
|
type TrainingBlockConfig struct {
|
|
ID uuid.UUID `json:"id"`
|
|
TenantID uuid.UUID `json:"tenant_id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
DomainFilter string `json:"domain_filter,omitempty"` // "AUTH", "CRYP", etc.
|
|
CategoryFilter string `json:"category_filter,omitempty"` // "authentication", etc.
|
|
SeverityFilter string `json:"severity_filter,omitempty"` // "high", "critical"
|
|
TargetAudienceFilter string `json:"target_audience_filter,omitempty"` // "enterprise", "authority", "provider", "all"
|
|
RegulationArea RegulationArea `json:"regulation_area"`
|
|
ModuleCodePrefix string `json:"module_code_prefix"`
|
|
FrequencyType FrequencyType `json:"frequency_type"`
|
|
DurationMinutes int `json:"duration_minutes"`
|
|
PassThreshold int `json:"pass_threshold"`
|
|
MaxControlsPerModule int `json:"max_controls_per_module"`
|
|
IsActive bool `json:"is_active"`
|
|
LastGeneratedAt *time.Time `json:"last_generated_at,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// TrainingBlockControlLink tracks which canonical controls are linked to which module
|
|
type TrainingBlockControlLink struct {
|
|
ID uuid.UUID `json:"id"`
|
|
BlockConfigID uuid.UUID `json:"block_config_id"`
|
|
ModuleID uuid.UUID `json:"module_id"`
|
|
ControlID string `json:"control_id"`
|
|
ControlTitle string `json:"control_title"`
|
|
ControlObjective string `json:"control_objective"`
|
|
ControlRequirements []string `json:"control_requirements"`
|
|
SortOrder int `json:"sort_order"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// CanonicalControlSummary is a lightweight view on canonical_controls for the training pipeline
|
|
type CanonicalControlSummary struct {
|
|
ControlID string `json:"control_id"`
|
|
Title string `json:"title"`
|
|
Objective string `json:"objective"`
|
|
Rationale string `json:"rationale"`
|
|
Requirements []string `json:"requirements"`
|
|
Severity string `json:"severity"`
|
|
Category string `json:"category"`
|
|
TargetAudience string `json:"target_audience"`
|
|
Tags []string `json:"tags"`
|
|
}
|
|
|
|
// CanonicalControlMeta provides aggregated metadata about canonical controls
|
|
type CanonicalControlMeta struct {
|
|
Domains []DomainCount `json:"domains"`
|
|
Categories []CategoryCount `json:"categories"`
|
|
Audiences []AudienceCount `json:"audiences"`
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
// DomainCount is a domain with its control count
|
|
type DomainCount struct {
|
|
Domain string `json:"domain"`
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
// CategoryCount is a category with its control count
|
|
type CategoryCount struct {
|
|
Category string `json:"category"`
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
// AudienceCount is a target audience with its control count
|
|
type AudienceCount struct {
|
|
Audience string `json:"audience"`
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
// CreateBlockConfigRequest is the API request for creating a block config
|
|
type CreateBlockConfigRequest struct {
|
|
Name string `json:"name" binding:"required"`
|
|
Description string `json:"description,omitempty"`
|
|
DomainFilter string `json:"domain_filter,omitempty"`
|
|
CategoryFilter string `json:"category_filter,omitempty"`
|
|
SeverityFilter string `json:"severity_filter,omitempty"`
|
|
TargetAudienceFilter string `json:"target_audience_filter,omitempty"`
|
|
RegulationArea RegulationArea `json:"regulation_area" binding:"required"`
|
|
ModuleCodePrefix string `json:"module_code_prefix" binding:"required"`
|
|
FrequencyType FrequencyType `json:"frequency_type"`
|
|
DurationMinutes int `json:"duration_minutes"`
|
|
PassThreshold int `json:"pass_threshold"`
|
|
MaxControlsPerModule int `json:"max_controls_per_module"`
|
|
}
|
|
|
|
// UpdateBlockConfigRequest is the API request for updating a block config
|
|
type UpdateBlockConfigRequest struct {
|
|
Name *string `json:"name,omitempty"`
|
|
Description *string `json:"description,omitempty"`
|
|
DomainFilter *string `json:"domain_filter,omitempty"`
|
|
CategoryFilter *string `json:"category_filter,omitempty"`
|
|
SeverityFilter *string `json:"severity_filter,omitempty"`
|
|
TargetAudienceFilter *string `json:"target_audience_filter,omitempty"`
|
|
MaxControlsPerModule *int `json:"max_controls_per_module,omitempty"`
|
|
DurationMinutes *int `json:"duration_minutes,omitempty"`
|
|
PassThreshold *int `json:"pass_threshold,omitempty"`
|
|
IsActive *bool `json:"is_active,omitempty"`
|
|
}
|
|
|
|
// ============================================================================
|
|
// Interactive Video / Checkpoint Types
|
|
// ============================================================================
|
|
|
|
// NarratorScript is an extended VideoScript with narrator persona and checkpoints
|
|
type NarratorScript struct {
|
|
Title string `json:"title"`
|
|
Intro string `json:"intro"`
|
|
Sections []NarratorSection `json:"sections"`
|
|
Outro string `json:"outro"`
|
|
TotalDurationEstimate int `json:"total_duration_estimate"`
|
|
}
|
|
|
|
// NarratorSection is one narrative section with optional checkpoint
|
|
type NarratorSection struct {
|
|
Heading string `json:"heading"`
|
|
NarratorText string `json:"narrator_text"`
|
|
BulletPoints []string `json:"bullet_points"`
|
|
Transition string `json:"transition"`
|
|
Checkpoint *CheckpointDefinition `json:"checkpoint,omitempty"`
|
|
}
|
|
|
|
// CheckpointDefinition defines a quiz checkpoint within a video
|
|
type CheckpointDefinition struct {
|
|
Title string `json:"title"`
|
|
Questions []CheckpointQuestion `json:"questions"`
|
|
}
|
|
|
|
// CheckpointQuestion is a quiz question within a checkpoint
|
|
type CheckpointQuestion struct {
|
|
Question string `json:"question"`
|
|
Options []string `json:"options"`
|
|
CorrectIndex int `json:"correct_index"`
|
|
Explanation string `json:"explanation"`
|
|
}
|
|
|
|
// Checkpoint is a DB record for a video checkpoint
|
|
type Checkpoint struct {
|
|
ID uuid.UUID `json:"id"`
|
|
ModuleID uuid.UUID `json:"module_id"`
|
|
CheckpointIndex int `json:"checkpoint_index"`
|
|
Title string `json:"title"`
|
|
TimestampSeconds float64 `json:"timestamp_seconds"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// CheckpointProgress tracks a user's progress on a checkpoint
|
|
type CheckpointProgress struct {
|
|
ID uuid.UUID `json:"id"`
|
|
AssignmentID uuid.UUID `json:"assignment_id"`
|
|
CheckpointID uuid.UUID `json:"checkpoint_id"`
|
|
Passed bool `json:"passed"`
|
|
Attempts int `json:"attempts"`
|
|
LastAttemptAt *time.Time `json:"last_attempt_at,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// InteractiveVideoManifest is returned to the frontend player
|
|
type InteractiveVideoManifest struct {
|
|
MediaID uuid.UUID `json:"media_id"`
|
|
StreamURL string `json:"stream_url"`
|
|
Checkpoints []CheckpointManifestEntry `json:"checkpoints"`
|
|
}
|
|
|
|
// CheckpointManifestEntry is one checkpoint in the manifest
|
|
type CheckpointManifestEntry struct {
|
|
CheckpointID uuid.UUID `json:"checkpoint_id"`
|
|
Index int `json:"index"`
|
|
Title string `json:"title"`
|
|
TimestampSeconds float64 `json:"timestamp_seconds"`
|
|
Questions []CheckpointQuestion `json:"questions"`
|
|
Progress *CheckpointProgress `json:"progress,omitempty"`
|
|
}
|
|
|
|
// SubmitCheckpointQuizRequest is the API request for submitting a checkpoint quiz
|
|
type SubmitCheckpointQuizRequest struct {
|
|
AssignmentID string `json:"assignment_id"`
|
|
Answers []int `json:"answers"`
|
|
}
|
|
|
|
// SubmitCheckpointQuizResponse is the API response for a checkpoint quiz submission
|
|
type SubmitCheckpointQuizResponse struct {
|
|
Passed bool `json:"passed"`
|
|
Score float64 `json:"score"`
|
|
Feedback []CheckpointQuizFeedback `json:"feedback"`
|
|
}
|
|
|
|
// CheckpointQuizFeedback is feedback for a single question
|
|
type CheckpointQuizFeedback struct {
|
|
Question string `json:"question"`
|
|
Correct bool `json:"correct"`
|
|
Explanation string `json:"explanation"`
|
|
}
|
|
|
|
// GenerateBlockRequest is the API request for generating modules from a block config
|
|
type GenerateBlockRequest struct {
|
|
Language string `json:"language"`
|
|
AutoMatrix bool `json:"auto_matrix"`
|
|
}
|
|
|
|
// PreviewBlockResponse shows what would be generated without writing to DB
|
|
type PreviewBlockResponse struct {
|
|
ControlCount int `json:"control_count"`
|
|
ModuleCount int `json:"module_count"`
|
|
Controls []CanonicalControlSummary `json:"controls"`
|
|
ProposedRoles []string `json:"proposed_roles"`
|
|
}
|
|
|
|
// GenerateBlockResponse shows the result of a block generation
|
|
type GenerateBlockResponse struct {
|
|
ModulesCreated int `json:"modules_created"`
|
|
ControlsLinked int `json:"controls_linked"`
|
|
MatrixEntriesCreated int `json:"matrix_entries_created"`
|
|
ContentGenerated int `json:"content_generated"`
|
|
Errors []string `json:"errors,omitempty"`
|
|
}
|