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
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:
@@ -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"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user