feat(training): add Media Pipeline — TTS Audio, Presentation Video, Bulk Generation
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 48s
CI / test-python-backend-compliance (push) Successful in 35s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 20s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 48s
CI / test-python-backend-compliance (push) Successful in 35s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 20s
Phase A: 8 new IT-Security training modules (SEC-PWD, SEC-DESK, SEC-KIAI, SEC-BYOD, SEC-VIDEO, SEC-USB, SEC-INC, SEC-HOME) with CTM entries. Bulk content and quiz generation endpoints for all 28 modules. Phase B: Piper TTS service (Python/FastAPI) for local German speech synthesis. training_media table, TTSClient in Go backend, audio generation endpoints, AudioPlayer component in frontend. MinIO storage integration. Phase C: FFmpeg presentation video pipeline — LLM generates slide scripts, ImageMagick renders 1920x1080 slides, FFmpeg combines with audio to MP4. VideoPlayer and ScriptPreview components in frontend. New files: 15 created, 9 modified - compliance-tts-service/ (Dockerfile, main.py, tts_engine.py, storage.py, slide_renderer.py, video_generator.py) - migrations 014-016 (training engine, IT-security modules, media table) - training package (models, store, content_generator, media, handlers) - frontend (AudioPlayer, VideoPlayer, ScriptPreview, api, types, page) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
500
ai-compliance-sdk/internal/training/models.go
Normal file
500
ai-compliance-sdk/internal/training/models.go
Normal file
@@ -0,0 +1,500 @@
|
||||
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
|
||||
)
|
||||
|
||||
// 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",
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 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"`
|
||||
}
|
||||
Reference in New Issue
Block a user