feat(training+controls): interactive video pipeline, training blocks, control generator, CE libraries
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>
This commit is contained in:
Benjamin Admin
2026-03-16 21:41:48 +01:00
parent d2133dbfa2
commit 4f6bc8f6f6
50 changed files with 17299 additions and 198 deletions

View File

@@ -106,7 +106,8 @@ const (
RoleR6 = "R6" // Einkauf
RoleR7 = "R7" // Fachabteilung
RoleR8 = "R8" // IT-Admin
RoleR9 = "R9" // Alle Mitarbeiter
RoleR9 = "R9" // Alle Mitarbeiter
RoleR10 = "R10" // Behoerden / Oeffentlicher Dienst
)
// RoleLabels maps role codes to human-readable labels
@@ -118,8 +119,9 @@ var RoleLabels = map[string]string{
RoleR5: "HR / Personal",
RoleR6: "Einkauf / Beschaffung",
RoleR7: "Fachabteilung",
RoleR8: "IT-Administration",
RoleR9: "Alle Mitarbeiter",
RoleR8: "IT-Administration",
RoleR9: "Alle Mitarbeiter",
RoleR10: "Behoerden / Oeffentlicher Dienst",
}
// NIS2RoleMapping maps internal roles to NIS2 levels
@@ -131,8 +133,38 @@ var NIS2RoleMapping = map[string]string{
RoleR5: "N4", // HR
RoleR6: "N4", // Einkauf
RoleR7: "N5", // Fachabteilung
RoleR8: "N2", // IT-Admin
RoleR9: "N5", // Alle Mitarbeiter
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},
}
// ============================================================================
@@ -498,3 +530,228 @@ type BulkResult struct {
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"`
}