refactor(go/ucca): split license_policy, models, pdf_export, escalation_store, obligations_registry

Split 5 oversized files (501-583 LOC each) into focused units all under 500 LOC:
- license_policy.go → +_types.go (engine logic / type definitions)
- models.go → +_intake.go, +_assessment.go (enums+domains / intake structs / output+DB types)
- pdf_export.go → +_markdown.go (PDF export / markdown export)
- escalation_store.go → +_dsb.go (main escalation ops / DSB pool ops)
- obligations_registry.go → +_grouping.go (registry core / grouping methods)

All files remain in package ucca. Zero behavior changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-19 10:03:51 +02:00
parent 3f1444541f
commit f7a5f9e1ed
11 changed files with 759 additions and 743 deletions

View File

@@ -387,116 +387,3 @@ func (s *EscalationStore) GetEscalationStats(ctx context.Context, tenantID uuid.
return stats, nil
}
// DSB Pool Operations
// AddDSBPoolMember adds a member to the DSB review pool.
func (s *EscalationStore) AddDSBPoolMember(ctx context.Context, m *DSBPoolMember) error {
query := `
INSERT INTO ucca_dsb_pool (
id, tenant_id, user_id, user_name, user_email, role,
is_active, max_concurrent_reviews, created_at, updated_at
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW()
)
ON CONFLICT (tenant_id, user_id) DO UPDATE
SET user_name = $4, user_email = $5, role = $6,
is_active = $7, max_concurrent_reviews = $8, updated_at = NOW()
`
if m.ID == uuid.Nil {
m.ID = uuid.New()
}
_, err := s.pool.Exec(ctx, query,
m.ID, m.TenantID, m.UserID, m.UserName, m.UserEmail, m.Role,
m.IsActive, m.MaxConcurrentReviews,
)
return err
}
// GetDSBPoolMembers retrieves active DSB pool members for a tenant.
func (s *EscalationStore) GetDSBPoolMembers(ctx context.Context, tenantID uuid.UUID, role string) ([]DSBPoolMember, error) {
query := `
SELECT id, tenant_id, user_id, user_name, user_email, role,
is_active, max_concurrent_reviews, current_reviews, created_at, updated_at
FROM ucca_dsb_pool
WHERE tenant_id = $1 AND is_active = true
`
args := []interface{}{tenantID}
if role != "" {
query += " AND role = $2"
args = append(args, role)
}
query += " ORDER BY current_reviews ASC, user_name ASC"
rows, err := s.pool.Query(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
var members []DSBPoolMember
for rows.Next() {
var m DSBPoolMember
err := rows.Scan(
&m.ID, &m.TenantID, &m.UserID, &m.UserName, &m.UserEmail, &m.Role,
&m.IsActive, &m.MaxConcurrentReviews, &m.CurrentReviews, &m.CreatedAt, &m.UpdatedAt,
)
if err != nil {
return nil, err
}
members = append(members, m)
}
return members, nil
}
// GetNextAvailableReviewer finds the next available reviewer for a role.
func (s *EscalationStore) GetNextAvailableReviewer(ctx context.Context, tenantID uuid.UUID, role string) (*DSBPoolMember, error) {
query := `
SELECT id, tenant_id, user_id, user_name, user_email, role,
is_active, max_concurrent_reviews, current_reviews, created_at, updated_at
FROM ucca_dsb_pool
WHERE tenant_id = $1 AND is_active = true AND role = $2
AND current_reviews < max_concurrent_reviews
ORDER BY current_reviews ASC
LIMIT 1
`
var m DSBPoolMember
err := s.pool.QueryRow(ctx, query, tenantID, role).Scan(
&m.ID, &m.TenantID, &m.UserID, &m.UserName, &m.UserEmail, &m.Role,
&m.IsActive, &m.MaxConcurrentReviews, &m.CurrentReviews, &m.CreatedAt, &m.UpdatedAt,
)
if err != nil {
return nil, err
}
return &m, nil
}
// IncrementReviewerCount increments the current review count for a DSB member.
func (s *EscalationStore) IncrementReviewerCount(ctx context.Context, userID uuid.UUID) error {
query := `
UPDATE ucca_dsb_pool
SET current_reviews = current_reviews + 1, updated_at = NOW()
WHERE user_id = $1
`
_, err := s.pool.Exec(ctx, query, userID)
return err
}
// DecrementReviewerCount decrements the current review count for a DSB member.
func (s *EscalationStore) DecrementReviewerCount(ctx context.Context, userID uuid.UUID) error {
query := `
UPDATE ucca_dsb_pool
SET current_reviews = GREATEST(0, current_reviews - 1), updated_at = NOW()
WHERE user_id = $1
`
_, err := s.pool.Exec(ctx, query, userID)
return err
}

View File

@@ -0,0 +1,123 @@
package ucca
import (
"context"
"github.com/google/uuid"
)
// ============================================================================
// DSB Pool Operations
// ============================================================================
// AddDSBPoolMember adds a member to the DSB review pool.
func (s *EscalationStore) AddDSBPoolMember(ctx context.Context, m *DSBPoolMember) error {
query := `
INSERT INTO ucca_dsb_pool (
id, tenant_id, user_id, user_name, user_email, role,
is_active, max_concurrent_reviews, created_at, updated_at
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW()
)
ON CONFLICT (tenant_id, user_id) DO UPDATE
SET user_name = $4, user_email = $5, role = $6,
is_active = $7, max_concurrent_reviews = $8, updated_at = NOW()
`
if m.ID == uuid.Nil {
m.ID = uuid.New()
}
_, err := s.pool.Exec(ctx, query,
m.ID, m.TenantID, m.UserID, m.UserName, m.UserEmail, m.Role,
m.IsActive, m.MaxConcurrentReviews,
)
return err
}
// GetDSBPoolMembers retrieves active DSB pool members for a tenant.
func (s *EscalationStore) GetDSBPoolMembers(ctx context.Context, tenantID uuid.UUID, role string) ([]DSBPoolMember, error) {
query := `
SELECT id, tenant_id, user_id, user_name, user_email, role,
is_active, max_concurrent_reviews, current_reviews, created_at, updated_at
FROM ucca_dsb_pool
WHERE tenant_id = $1 AND is_active = true
`
args := []interface{}{tenantID}
if role != "" {
query += " AND role = $2"
args = append(args, role)
}
query += " ORDER BY current_reviews ASC, user_name ASC"
rows, err := s.pool.Query(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
var members []DSBPoolMember
for rows.Next() {
var m DSBPoolMember
err := rows.Scan(
&m.ID, &m.TenantID, &m.UserID, &m.UserName, &m.UserEmail, &m.Role,
&m.IsActive, &m.MaxConcurrentReviews, &m.CurrentReviews, &m.CreatedAt, &m.UpdatedAt,
)
if err != nil {
return nil, err
}
members = append(members, m)
}
return members, nil
}
// GetNextAvailableReviewer finds the next available reviewer for a role.
func (s *EscalationStore) GetNextAvailableReviewer(ctx context.Context, tenantID uuid.UUID, role string) (*DSBPoolMember, error) {
query := `
SELECT id, tenant_id, user_id, user_name, user_email, role,
is_active, max_concurrent_reviews, current_reviews, created_at, updated_at
FROM ucca_dsb_pool
WHERE tenant_id = $1 AND is_active = true AND role = $2
AND current_reviews < max_concurrent_reviews
ORDER BY current_reviews ASC
LIMIT 1
`
var m DSBPoolMember
err := s.pool.QueryRow(ctx, query, tenantID, role).Scan(
&m.ID, &m.TenantID, &m.UserID, &m.UserName, &m.UserEmail, &m.Role,
&m.IsActive, &m.MaxConcurrentReviews, &m.CurrentReviews, &m.CreatedAt, &m.UpdatedAt,
)
if err != nil {
return nil, err
}
return &m, nil
}
// IncrementReviewerCount increments the current review count for a DSB member.
func (s *EscalationStore) IncrementReviewerCount(ctx context.Context, userID uuid.UUID) error {
query := `
UPDATE ucca_dsb_pool
SET current_reviews = current_reviews + 1, updated_at = NOW()
WHERE user_id = $1
`
_, err := s.pool.Exec(ctx, query, userID)
return err
}
// DecrementReviewerCount decrements the current review count for a DSB member.
func (s *EscalationStore) DecrementReviewerCount(ctx context.Context, userID uuid.UUID) error {
query := `
UPDATE ucca_dsb_pool
SET current_reviews = GREATEST(0, current_reviews - 1), updated_at = NOW()
WHERE user_id = $1
`
_, err := s.pool.Exec(ctx, query, userID)
return err
}

View File

@@ -11,66 +11,6 @@ import (
// Handles license/copyright compliance for standards and norms
// =============================================================================
// LicensedContentFacts represents the license-related facts from the wizard
type LicensedContentFacts struct {
Present bool `json:"present"`
Publisher string `json:"publisher"` // DIN_MEDIA, VDI, VDE, ISO, etc.
LicenseType string `json:"license_type"` // SINGLE_WORKSTATION, NETWORK_INTRANET, etc.
AIUsePermitted string `json:"ai_use_permitted"` // YES, NO, UNKNOWN
ProofUploaded bool `json:"proof_uploaded"`
OperationMode string `json:"operation_mode"` // LINK_ONLY, NOTES_ONLY, FULLTEXT_RAG, TRAINING
DistributionScope string `json:"distribution_scope"` // SINGLE_USER, COMPANY_INTERNAL, etc.
ContentType string `json:"content_type"` // NORM_FULLTEXT, CUSTOMER_NOTES, etc.
}
// LicensePolicyResult represents the evaluation result
type LicensePolicyResult struct {
Allowed bool `json:"allowed"`
EffectiveMode string `json:"effective_mode"` // The mode that will actually be used
Reason string `json:"reason"`
Gaps []LicenseGap `json:"gaps"`
RequiredControls []LicenseControl `json:"required_controls"`
StopLine *LicenseStopLine `json:"stop_line,omitempty"` // If hard blocked
OutputRestrictions *OutputRestrictions `json:"output_restrictions"`
EscalationLevel string `json:"escalation_level"`
RiskScore int `json:"risk_score"`
}
// LicenseGap represents a license-related gap
type LicenseGap struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Controls []string `json:"controls"`
Severity string `json:"severity"`
}
// LicenseControl represents a required control for license compliance
type LicenseControl struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
WhatToDo string `json:"what_to_do"`
Evidence []string `json:"evidence_needed"`
}
// LicenseStopLine represents a hard block
type LicenseStopLine struct {
ID string `json:"id"`
Title string `json:"title"`
Message string `json:"message"`
Outcome string `json:"outcome"` // NOT_ALLOWED, NOT_ALLOWED_UNTIL_LICENSE_CLEARED
}
// OutputRestrictions defines how outputs should be filtered
type OutputRestrictions struct {
AllowQuotes bool `json:"allow_quotes"`
MaxQuoteLength int `json:"max_quote_length"` // in characters
RequireCitation bool `json:"require_citation"`
AllowCopy bool `json:"allow_copy"`
AllowExport bool `json:"allow_export"`
}
// LicensePolicyEngine evaluates license compliance
type LicensePolicyEngine struct {
// Configuration can be added here
@@ -144,7 +84,6 @@ func (e *LicensePolicyEngine) evaluateLinkOnlyMode(facts *LicensedContentFacts,
result.Reason = "Link-only Modus ist ohne spezielle Lizenz erlaubt"
result.RiskScore = 0
// Very restrictive output
result.OutputRestrictions = &OutputRestrictions{
AllowQuotes: false,
MaxQuoteLength: 0,
@@ -153,7 +92,6 @@ func (e *LicensePolicyEngine) evaluateLinkOnlyMode(facts *LicensedContentFacts,
AllowExport: false,
}
// Recommend control for proper setup
result.RequiredControls = append(result.RequiredControls, LicenseControl{
ID: "CTRL-LINK-ONLY-MODE",
Title: "Link-only / Evidence Navigator aktivieren",
@@ -170,7 +108,6 @@ func (e *LicensePolicyEngine) evaluateNotesOnlyMode(facts *LicensedContentFacts,
result.Reason = "Notes-only Modus mit kundeneigenen Zusammenfassungen"
result.RiskScore = 10
// Allow paraphrased content
result.OutputRestrictions = &OutputRestrictions{
AllowQuotes: false, // No direct quotes from norms
MaxQuoteLength: 0,
@@ -244,7 +181,6 @@ func (e *LicensePolicyEngine) evaluateExcerptOnlyMode(facts *LicensedContentFact
func (e *LicensePolicyEngine) evaluateFulltextRAGMode(facts *LicensedContentFacts, result *LicensePolicyResult) {
result.RiskScore = 60
// Check if AI use is explicitly permitted AND proof is uploaded
if facts.AIUsePermitted == "YES" && facts.ProofUploaded {
result.EffectiveMode = "FULLTEXT_RAG"
result.Allowed = true
@@ -290,7 +226,6 @@ func (e *LicensePolicyEngine) evaluateFulltextRAGMode(facts *LicensedContentFact
result.EscalationLevel = "E3"
// Set stop line
result.StopLine = &LicenseStopLine{
ID: "STOP_FULLTEXT_WITHOUT_PROOF",
Title: "Volltext-RAG blockiert",
@@ -312,7 +247,6 @@ func (e *LicensePolicyEngine) evaluateFulltextRAGMode(facts *LicensedContentFact
func (e *LicensePolicyEngine) evaluateTrainingMode(facts *LicensedContentFacts, result *LicensePolicyResult) {
result.RiskScore = 80
// Training is almost always blocked for standards
if facts.AIUsePermitted == "YES" && facts.ProofUploaded && facts.LicenseType == "AI_LICENSE" {
result.EffectiveMode = "TRAINING"
result.Allowed = true
@@ -353,10 +287,8 @@ func (e *LicensePolicyEngine) evaluateTrainingMode(facts *LicensedContentFacts,
// applyPublisherRestrictions applies publisher-specific rules
func (e *LicensePolicyEngine) applyPublisherRestrictions(facts *LicensedContentFacts, result *LicensePolicyResult) {
// DIN Media specific restrictions
if facts.Publisher == "DIN_MEDIA" {
if facts.AIUsePermitted != "YES" {
// DIN Media explicitly prohibits AI use without license
if facts.OperationMode == "FULLTEXT_RAG" || facts.OperationMode == "TRAINING" {
result.Allowed = false
result.EffectiveMode = "LINK_ONLY"
@@ -394,7 +326,6 @@ func (e *LicensePolicyEngine) applyPublisherRestrictions(facts *LicensedContentF
// checkDistributionScope checks if distribution scope matches license type
func (e *LicensePolicyEngine) checkDistributionScope(facts *LicensedContentFacts, result *LicensePolicyResult) {
// Single workstation license with broad distribution
if facts.LicenseType == "SINGLE_WORKSTATION" {
if facts.DistributionScope == "COMPANY_INTERNAL" ||
facts.DistributionScope == "SUBSIDIARIES" ||
@@ -413,7 +344,6 @@ func (e *LicensePolicyEngine) checkDistributionScope(facts *LicensedContentFacts
}
}
// Network license with external distribution
if facts.LicenseType == "NETWORK_INTRANET" {
if facts.DistributionScope == "EXTERNAL_CUSTOMERS" {
result.Gaps = append(result.Gaps, LicenseGap{
@@ -433,16 +363,16 @@ func (e *LicensePolicyEngine) checkDistributionScope(facts *LicensedContentFacts
// CanIngestFulltext checks if fulltext ingestion is allowed
func (e *LicensePolicyEngine) CanIngestFulltext(facts *LicensedContentFacts) bool {
if !facts.Present {
return true // No licensed content, no restrictions
return true
}
switch facts.OperationMode {
case "LINK_ONLY":
return false // Only metadata/references
return false
case "NOTES_ONLY":
return false // Only customer notes, not fulltext
return false
case "EXCERPT_ONLY":
return false // Only short excerpts
return false
case "FULLTEXT_RAG":
return facts.AIUsePermitted == "YES" && facts.ProofUploaded
case "TRAINING":
@@ -457,8 +387,6 @@ func (e *LicensePolicyEngine) CanIngestNotes(facts *LicensedContentFacts) bool {
if !facts.Present {
return true
}
// Notes are allowed in most modes
return facts.OperationMode == "NOTES_ONLY" ||
facts.OperationMode == "EXCERPT_ONLY" ||
facts.OperationMode == "FULLTEXT_RAG" ||
@@ -471,40 +399,17 @@ func (e *LicensePolicyEngine) GetEffectiveMode(facts *LicensedContentFacts) stri
return result.EffectiveMode
}
// LicenseIngestDecision represents the decision for ingesting a document
type LicenseIngestDecision struct {
AllowFulltext bool `json:"allow_fulltext"`
AllowNotes bool `json:"allow_notes"`
AllowMetadata bool `json:"allow_metadata"`
Reason string `json:"reason"`
EffectiveMode string `json:"effective_mode"`
}
// DecideIngest returns the ingest decision for a document
func (e *LicensePolicyEngine) DecideIngest(facts *LicensedContentFacts) *LicenseIngestDecision {
result := e.Evaluate(facts)
decision := &LicenseIngestDecision{
return &LicenseIngestDecision{
AllowMetadata: true, // Metadata is always allowed
AllowNotes: e.CanIngestNotes(facts),
AllowFulltext: e.CanIngestFulltext(facts),
Reason: result.Reason,
EffectiveMode: result.EffectiveMode,
}
return decision
}
// LicenseAuditEntry represents an audit log entry for license decisions
type LicenseAuditEntry struct {
Timestamp time.Time `json:"timestamp"`
TenantID string `json:"tenant_id"`
DocumentID string `json:"document_id,omitempty"`
Facts *LicensedContentFacts `json:"facts"`
Decision string `json:"decision"` // ALLOW, DENY, DOWNGRADE
EffectiveMode string `json:"effective_mode"`
Reason string `json:"reason"`
StopLineID string `json:"stop_line_id,omitempty"`
}
// FormatAuditEntry creates an audit entry for logging

View File

@@ -0,0 +1,88 @@
package ucca
import "time"
// =============================================================================
// License Policy Types
// =============================================================================
// LicensedContentFacts represents the license-related facts from the wizard
type LicensedContentFacts struct {
Present bool `json:"present"`
Publisher string `json:"publisher"` // DIN_MEDIA, VDI, VDE, ISO, etc.
LicenseType string `json:"license_type"` // SINGLE_WORKSTATION, NETWORK_INTRANET, etc.
AIUsePermitted string `json:"ai_use_permitted"` // YES, NO, UNKNOWN
ProofUploaded bool `json:"proof_uploaded"`
OperationMode string `json:"operation_mode"` // LINK_ONLY, NOTES_ONLY, FULLTEXT_RAG, TRAINING
DistributionScope string `json:"distribution_scope"` // SINGLE_USER, COMPANY_INTERNAL, etc.
ContentType string `json:"content_type"` // NORM_FULLTEXT, CUSTOMER_NOTES, etc.
}
// LicensePolicyResult represents the evaluation result
type LicensePolicyResult struct {
Allowed bool `json:"allowed"`
EffectiveMode string `json:"effective_mode"` // The mode that will actually be used
Reason string `json:"reason"`
Gaps []LicenseGap `json:"gaps"`
RequiredControls []LicenseControl `json:"required_controls"`
StopLine *LicenseStopLine `json:"stop_line,omitempty"` // If hard blocked
OutputRestrictions *OutputRestrictions `json:"output_restrictions"`
EscalationLevel string `json:"escalation_level"`
RiskScore int `json:"risk_score"`
}
// LicenseGap represents a license-related gap
type LicenseGap struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Controls []string `json:"controls"`
Severity string `json:"severity"`
}
// LicenseControl represents a required control for license compliance
type LicenseControl struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
WhatToDo string `json:"what_to_do"`
Evidence []string `json:"evidence_needed"`
}
// LicenseStopLine represents a hard block
type LicenseStopLine struct {
ID string `json:"id"`
Title string `json:"title"`
Message string `json:"message"`
Outcome string `json:"outcome"` // NOT_ALLOWED, NOT_ALLOWED_UNTIL_LICENSE_CLEARED
}
// OutputRestrictions defines how outputs should be filtered
type OutputRestrictions struct {
AllowQuotes bool `json:"allow_quotes"`
MaxQuoteLength int `json:"max_quote_length"` // in characters
RequireCitation bool `json:"require_citation"`
AllowCopy bool `json:"allow_copy"`
AllowExport bool `json:"allow_export"`
}
// LicenseIngestDecision represents the decision for ingesting a document
type LicenseIngestDecision struct {
AllowFulltext bool `json:"allow_fulltext"`
AllowNotes bool `json:"allow_notes"`
AllowMetadata bool `json:"allow_metadata"`
Reason string `json:"reason"`
EffectiveMode string `json:"effective_mode"`
}
// LicenseAuditEntry represents an audit log entry for license decisions
type LicenseAuditEntry struct {
Timestamp time.Time `json:"timestamp"`
TenantID string `json:"tenant_id"`
DocumentID string `json:"document_id,omitempty"`
Facts *LicensedContentFacts `json:"facts"`
Decision string `json:"decision"` // ALLOW, DENY, DOWNGRADE
EffectiveMode string `json:"effective_mode"`
Reason string `json:"reason"`
StopLineID string `json:"stop_line_id,omitempty"`
}

View File

@@ -1,11 +1,5 @@
package ucca
import (
"time"
"github.com/google/uuid"
)
// ============================================================================
// Constants / Enums
// ============================================================================
@@ -177,347 +171,6 @@ const (
TrainingNO TrainingAllowed = "NO"
)
// ============================================================================
// Input Structs
// ============================================================================
// UseCaseIntake represents the user's input describing their planned AI use case
type UseCaseIntake struct {
// Free-text description of the use case
UseCaseText string `json:"use_case_text"`
// Business domain
Domain Domain `json:"domain"`
// Title for the assessment (optional)
Title string `json:"title,omitempty"`
// Data types involved
DataTypes DataTypes `json:"data_types"`
// Purpose of the processing
Purpose Purpose `json:"purpose"`
// Level of automation
Automation AutomationLevel `json:"automation"`
// Output characteristics
Outputs Outputs `json:"outputs"`
// Hosting configuration
Hosting Hosting `json:"hosting"`
// Model usage configuration
ModelUsage ModelUsage `json:"model_usage"`
// Retention configuration
Retention Retention `json:"retention"`
// Financial regulations context (DORA, MaRisk, BAIT)
// Only applicable for financial domains (banking, finance, insurance, investment)
FinancialContext *FinancialContext `json:"financial_context,omitempty"`
// Opt-in to store raw text (otherwise only hash)
StoreRawText bool `json:"store_raw_text,omitempty"`
}
// DataTypes specifies what kinds of data are processed
type DataTypes struct {
PersonalData bool `json:"personal_data"`
Article9Data bool `json:"article_9_data"` // Special categories (health, religion, etc.)
MinorData bool `json:"minor_data"` // Data of children
LicensePlates bool `json:"license_plates"` // KFZ-Kennzeichen
Images bool `json:"images"` // Photos/images of persons
Audio bool `json:"audio"` // Voice recordings
LocationData bool `json:"location_data"` // GPS/location tracking
BiometricData bool `json:"biometric_data"` // Fingerprints, face recognition
FinancialData bool `json:"financial_data"` // Bank accounts, salaries
EmployeeData bool `json:"employee_data"` // HR/employment data
CustomerData bool `json:"customer_data"` // Customer information
PublicData bool `json:"public_data"` // Publicly available data only
}
// Purpose specifies the processing purpose
type Purpose struct {
CustomerSupport bool `json:"customer_support"`
Marketing bool `json:"marketing"`
Analytics bool `json:"analytics"`
Automation bool `json:"automation"`
EvaluationScoring bool `json:"evaluation_scoring"` // Scoring/ranking of persons
DecisionMaking bool `json:"decision_making"` // Automated decisions
Profiling bool `json:"profiling"`
Research bool `json:"research"`
InternalTools bool `json:"internal_tools"`
PublicService bool `json:"public_service"`
}
// Outputs specifies output characteristics
type Outputs struct {
RecommendationsToUsers bool `json:"recommendations_to_users"`
RankingsOrScores bool `json:"rankings_or_scores"` // Outputs rankings/scores
LegalEffects bool `json:"legal_effects"` // Has legal consequences
AccessDecisions bool `json:"access_decisions"` // Grants/denies access
ContentGeneration bool `json:"content_generation"` // Generates text/media
DataExport bool `json:"data_export"` // Exports data externally
}
// Hosting specifies where the AI runs
type Hosting struct {
Provider string `json:"provider,omitempty"` // e.g., "Azure", "AWS", "Hetzner", "On-Prem"
Region string `json:"region"` // "eu", "third_country", "on_prem"
DataResidency string `json:"data_residency,omitempty"` // Where data is stored
}
// ModelUsage specifies how the model is used
type ModelUsage struct {
RAG bool `json:"rag"` // Retrieval-Augmented Generation only
Finetune bool `json:"finetune"` // Fine-tuning with data
Training bool `json:"training"` // Full training with data
Inference bool `json:"inference"` // Inference only
}
// Retention specifies data retention
type Retention struct {
StorePrompts bool `json:"store_prompts"`
StoreResponses bool `json:"store_responses"`
RetentionDays int `json:"retention_days,omitempty"`
AnonymizeAfterUse bool `json:"anonymize_after_use"`
}
// ============================================================================
// Financial Regulations Structs (DORA, MaRisk, BAIT)
// ============================================================================
// FinancialEntityType represents the type of financial institution
type FinancialEntityType string
const (
FinancialEntityCreditInstitution FinancialEntityType = "CREDIT_INSTITUTION"
FinancialEntityPaymentServiceProvider FinancialEntityType = "PAYMENT_SERVICE_PROVIDER"
FinancialEntityEMoneyInstitution FinancialEntityType = "E_MONEY_INSTITUTION"
FinancialEntityInvestmentFirm FinancialEntityType = "INVESTMENT_FIRM"
FinancialEntityInsuranceCompany FinancialEntityType = "INSURANCE_COMPANY"
FinancialEntityCryptoAssetProvider FinancialEntityType = "CRYPTO_ASSET_PROVIDER"
FinancialEntityOther FinancialEntityType = "OTHER_FINANCIAL"
)
// SizeCategory represents the significance category of a financial institution
type SizeCategory string
const (
SizeCategorySignificant SizeCategory = "SIGNIFICANT"
SizeCategoryLessSignificant SizeCategory = "LESS_SIGNIFICANT"
SizeCategorySmall SizeCategory = "SMALL"
)
// ProviderLocation represents the location of an ICT service provider
type ProviderLocation string
const (
ProviderLocationEU ProviderLocation = "EU"
ProviderLocationEEA ProviderLocation = "EEA"
ProviderLocationAdequacyDecision ProviderLocation = "ADEQUACY_DECISION"
ProviderLocationThirdCountry ProviderLocation = "THIRD_COUNTRY"
)
// FinancialEntity describes the financial institution context
type FinancialEntity struct {
Type FinancialEntityType `json:"type"`
Regulated bool `json:"regulated"`
SizeCategory SizeCategory `json:"size_category"`
}
// ICTService describes ICT service characteristics for DORA compliance
type ICTService struct {
IsCritical bool `json:"is_critical"`
IsOutsourced bool `json:"is_outsourced"`
ProviderLocation ProviderLocation `json:"provider_location"`
ConcentrationRisk bool `json:"concentration_risk"`
}
// FinancialAIApplication describes financial-specific AI application characteristics
type FinancialAIApplication struct {
AffectsCustomerDecisions bool `json:"affects_customer_decisions"`
AlgorithmicTrading bool `json:"algorithmic_trading"`
RiskAssessment bool `json:"risk_assessment"`
AMLKYC bool `json:"aml_kyc"`
ModelValidationDone bool `json:"model_validation_done"`
}
// FinancialContext aggregates all financial regulation-specific information
type FinancialContext struct {
FinancialEntity FinancialEntity `json:"financial_entity"`
ICTService ICTService `json:"ict_service"`
AIApplication FinancialAIApplication `json:"ai_application"`
}
// ============================================================================
// Output Structs
// ============================================================================
// AssessmentResult represents the complete evaluation result
type AssessmentResult struct {
// Overall verdict
Feasibility Feasibility `json:"feasibility"`
RiskLevel RiskLevel `json:"risk_level"`
Complexity Complexity `json:"complexity"`
RiskScore int `json:"risk_score"` // 0-100
// Triggered rules
TriggeredRules []TriggeredRule `json:"triggered_rules"`
// Required controls/mitigations
RequiredControls []RequiredControl `json:"required_controls"`
// Recommended architecture patterns
RecommendedArchitecture []PatternRecommendation `json:"recommended_architecture"`
// Patterns that must NOT be used
ForbiddenPatterns []ForbiddenPattern `json:"forbidden_patterns"`
// Matching didactic examples
ExampleMatches []ExampleMatch `json:"example_matches"`
// Special flags
DSFARecommended bool `json:"dsfa_recommended"`
Art22Risk bool `json:"art22_risk"` // Art. 22 GDPR automated decision risk
TrainingAllowed TrainingAllowed `json:"training_allowed"`
// Summary for humans
Summary string `json:"summary"`
Recommendation string `json:"recommendation"`
AlternativeApproach string `json:"alternative_approach,omitempty"`
}
// TriggeredRule represents a rule that was triggered during evaluation
type TriggeredRule struct {
Code string `json:"code"` // e.g., "R-001"
Category string `json:"category"` // e.g., "A. Datenklassifikation"
Title string `json:"title"`
Description string `json:"description"`
Severity Severity `json:"severity"`
ScoreDelta int `json:"score_delta"`
GDPRRef string `json:"gdpr_ref,omitempty"` // e.g., "Art. 9 DSGVO"
Rationale string `json:"rationale"` // Why this rule triggered
}
// RequiredControl represents a control that must be implemented
type RequiredControl struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Severity Severity `json:"severity"`
Category string `json:"category"` // "technical" or "organizational"
GDPRRef string `json:"gdpr_ref,omitempty"`
}
// PatternRecommendation represents a recommended architecture pattern
type PatternRecommendation struct {
PatternID string `json:"pattern_id"` // e.g., "P-RAG-ONLY"
Title string `json:"title"`
Description string `json:"description"`
Rationale string `json:"rationale"`
Priority int `json:"priority"` // 1=highest
}
// ForbiddenPattern represents a pattern that must NOT be used
type ForbiddenPattern struct {
PatternID string `json:"pattern_id"`
Title string `json:"title"`
Description string `json:"description"`
Reason string `json:"reason"`
GDPRRef string `json:"gdpr_ref,omitempty"`
}
// ExampleMatch represents a matching didactic example
type ExampleMatch struct {
ExampleID string `json:"example_id"`
Title string `json:"title"`
Description string `json:"description"`
Similarity float64 `json:"similarity"` // 0.0 - 1.0
Outcome string `json:"outcome"` // What happened / recommendation
Lessons string `json:"lessons"` // Key takeaways
}
// ============================================================================
// Database Entity
// ============================================================================
// Assessment represents a stored assessment in the database
type Assessment struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenant_id"`
NamespaceID *uuid.UUID `json:"namespace_id,omitempty"`
Title string `json:"title"`
PolicyVersion string `json:"policy_version"`
Status string `json:"status"` // "completed", "draft"
// Input
Intake UseCaseIntake `json:"intake"`
UseCaseTextStored bool `json:"use_case_text_stored"`
UseCaseTextHash string `json:"use_case_text_hash"`
// Results
Feasibility Feasibility `json:"feasibility"`
RiskLevel RiskLevel `json:"risk_level"`
Complexity Complexity `json:"complexity"`
RiskScore int `json:"risk_score"`
TriggeredRules []TriggeredRule `json:"triggered_rules"`
RequiredControls []RequiredControl `json:"required_controls"`
RecommendedArchitecture []PatternRecommendation `json:"recommended_architecture"`
ForbiddenPatterns []ForbiddenPattern `json:"forbidden_patterns"`
ExampleMatches []ExampleMatch `json:"example_matches"`
DSFARecommended bool `json:"dsfa_recommended"`
Art22Risk bool `json:"art22_risk"`
TrainingAllowed TrainingAllowed `json:"training_allowed"`
// Corpus Versioning (RAG)
CorpusVersionID *uuid.UUID `json:"corpus_version_id,omitempty"`
CorpusVersion string `json:"corpus_version,omitempty"`
// LLM Explanation (optional)
ExplanationText *string `json:"explanation_text,omitempty"`
ExplanationGeneratedAt *time.Time `json:"explanation_generated_at,omitempty"`
ExplanationModel *string `json:"explanation_model,omitempty"`
// Domain
Domain Domain `json:"domain"`
// Audit
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedBy uuid.UUID `json:"created_by"`
}
// ============================================================================
// API Request/Response Types
// ============================================================================
// AssessRequest is the API request for creating an assessment
type AssessRequest struct {
Intake UseCaseIntake `json:"intake"`
}
// AssessResponse is the API response for an assessment
type AssessResponse struct {
Assessment Assessment `json:"assessment"`
Result AssessmentResult `json:"result"`
Escalation *Escalation `json:"escalation,omitempty"`
}
// ExplainRequest is the API request for generating an explanation
type ExplainRequest struct {
Language string `json:"language,omitempty"` // "de" or "en", default "de"
}
// ExplainResponse is the API response for an explanation
type ExplainResponse struct {
ExplanationText string `json:"explanation_text"`
GeneratedAt time.Time `json:"generated_at"`
Model string `json:"model"`
LegalContext *LegalContext `json:"legal_context,omitempty"`
}
// ExportFormat specifies the export format
type ExportFormat string

View File

@@ -0,0 +1,174 @@
package ucca
import (
"time"
"github.com/google/uuid"
)
// ============================================================================
// Output Structs
// ============================================================================
// AssessmentResult represents the complete evaluation result
type AssessmentResult struct {
// Overall verdict
Feasibility Feasibility `json:"feasibility"`
RiskLevel RiskLevel `json:"risk_level"`
Complexity Complexity `json:"complexity"`
RiskScore int `json:"risk_score"` // 0-100
// Triggered rules
TriggeredRules []TriggeredRule `json:"triggered_rules"`
// Required controls/mitigations
RequiredControls []RequiredControl `json:"required_controls"`
// Recommended architecture patterns
RecommendedArchitecture []PatternRecommendation `json:"recommended_architecture"`
// Patterns that must NOT be used
ForbiddenPatterns []ForbiddenPattern `json:"forbidden_patterns"`
// Matching didactic examples
ExampleMatches []ExampleMatch `json:"example_matches"`
// Special flags
DSFARecommended bool `json:"dsfa_recommended"`
Art22Risk bool `json:"art22_risk"` // Art. 22 GDPR automated decision risk
TrainingAllowed TrainingAllowed `json:"training_allowed"`
// Summary for humans
Summary string `json:"summary"`
Recommendation string `json:"recommendation"`
AlternativeApproach string `json:"alternative_approach,omitempty"`
}
// TriggeredRule represents a rule that was triggered during evaluation
type TriggeredRule struct {
Code string `json:"code"` // e.g., "R-001"
Category string `json:"category"` // e.g., "A. Datenklassifikation"
Title string `json:"title"`
Description string `json:"description"`
Severity Severity `json:"severity"`
ScoreDelta int `json:"score_delta"`
GDPRRef string `json:"gdpr_ref,omitempty"` // e.g., "Art. 9 DSGVO"
Rationale string `json:"rationale"` // Why this rule triggered
}
// RequiredControl represents a control that must be implemented
type RequiredControl struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Severity Severity `json:"severity"`
Category string `json:"category"` // "technical" or "organizational"
GDPRRef string `json:"gdpr_ref,omitempty"`
}
// PatternRecommendation represents a recommended architecture pattern
type PatternRecommendation struct {
PatternID string `json:"pattern_id"` // e.g., "P-RAG-ONLY"
Title string `json:"title"`
Description string `json:"description"`
Rationale string `json:"rationale"`
Priority int `json:"priority"` // 1=highest
}
// ForbiddenPattern represents a pattern that must NOT be used
type ForbiddenPattern struct {
PatternID string `json:"pattern_id"`
Title string `json:"title"`
Description string `json:"description"`
Reason string `json:"reason"`
GDPRRef string `json:"gdpr_ref,omitempty"`
}
// ExampleMatch represents a matching didactic example
type ExampleMatch struct {
ExampleID string `json:"example_id"`
Title string `json:"title"`
Description string `json:"description"`
Similarity float64 `json:"similarity"` // 0.0 - 1.0
Outcome string `json:"outcome"` // What happened / recommendation
Lessons string `json:"lessons"` // Key takeaways
}
// ============================================================================
// Database Entity
// ============================================================================
// Assessment represents a stored assessment in the database
type Assessment struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenant_id"`
NamespaceID *uuid.UUID `json:"namespace_id,omitempty"`
Title string `json:"title"`
PolicyVersion string `json:"policy_version"`
Status string `json:"status"` // "completed", "draft"
// Input
Intake UseCaseIntake `json:"intake"`
UseCaseTextStored bool `json:"use_case_text_stored"`
UseCaseTextHash string `json:"use_case_text_hash"`
// Results
Feasibility Feasibility `json:"feasibility"`
RiskLevel RiskLevel `json:"risk_level"`
Complexity Complexity `json:"complexity"`
RiskScore int `json:"risk_score"`
TriggeredRules []TriggeredRule `json:"triggered_rules"`
RequiredControls []RequiredControl `json:"required_controls"`
RecommendedArchitecture []PatternRecommendation `json:"recommended_architecture"`
ForbiddenPatterns []ForbiddenPattern `json:"forbidden_patterns"`
ExampleMatches []ExampleMatch `json:"example_matches"`
DSFARecommended bool `json:"dsfa_recommended"`
Art22Risk bool `json:"art22_risk"`
TrainingAllowed TrainingAllowed `json:"training_allowed"`
// Corpus Versioning (RAG)
CorpusVersionID *uuid.UUID `json:"corpus_version_id,omitempty"`
CorpusVersion string `json:"corpus_version,omitempty"`
// LLM Explanation (optional)
ExplanationText *string `json:"explanation_text,omitempty"`
ExplanationGeneratedAt *time.Time `json:"explanation_generated_at,omitempty"`
ExplanationModel *string `json:"explanation_model,omitempty"`
// Domain
Domain Domain `json:"domain"`
// Audit
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedBy uuid.UUID `json:"created_by"`
}
// ============================================================================
// API Request/Response Types
// ============================================================================
// AssessRequest is the API request for creating an assessment
type AssessRequest struct {
Intake UseCaseIntake `json:"intake"`
}
// AssessResponse is the API response for an assessment
type AssessResponse struct {
Assessment Assessment `json:"assessment"`
Result AssessmentResult `json:"result"`
Escalation *Escalation `json:"escalation,omitempty"`
}
// ExplainRequest is the API request for generating an explanation
type ExplainRequest struct {
Language string `json:"language,omitempty"` // "de" or "en", default "de"
}
// ExplainResponse is the API response for an explanation
type ExplainResponse struct {
ExplanationText string `json:"explanation_text"`
GeneratedAt time.Time `json:"generated_at"`
Model string `json:"model"`
LegalContext *LegalContext `json:"legal_context,omitempty"`
}

View File

@@ -0,0 +1,175 @@
package ucca
// ============================================================================
// Input Structs
// ============================================================================
// UseCaseIntake represents the user's input describing their planned AI use case
type UseCaseIntake struct {
// Free-text description of the use case
UseCaseText string `json:"use_case_text"`
// Business domain
Domain Domain `json:"domain"`
// Title for the assessment (optional)
Title string `json:"title,omitempty"`
// Data types involved
DataTypes DataTypes `json:"data_types"`
// Purpose of the processing
Purpose Purpose `json:"purpose"`
// Level of automation
Automation AutomationLevel `json:"automation"`
// Output characteristics
Outputs Outputs `json:"outputs"`
// Hosting configuration
Hosting Hosting `json:"hosting"`
// Model usage configuration
ModelUsage ModelUsage `json:"model_usage"`
// Retention configuration
Retention Retention `json:"retention"`
// Financial regulations context (DORA, MaRisk, BAIT)
// Only applicable for financial domains (banking, finance, insurance, investment)
FinancialContext *FinancialContext `json:"financial_context,omitempty"`
// Opt-in to store raw text (otherwise only hash)
StoreRawText bool `json:"store_raw_text,omitempty"`
}
// DataTypes specifies what kinds of data are processed
type DataTypes struct {
PersonalData bool `json:"personal_data"`
Article9Data bool `json:"article_9_data"` // Special categories (health, religion, etc.)
MinorData bool `json:"minor_data"` // Data of children
LicensePlates bool `json:"license_plates"` // KFZ-Kennzeichen
Images bool `json:"images"` // Photos/images of persons
Audio bool `json:"audio"` // Voice recordings
LocationData bool `json:"location_data"` // GPS/location tracking
BiometricData bool `json:"biometric_data"` // Fingerprints, face recognition
FinancialData bool `json:"financial_data"` // Bank accounts, salaries
EmployeeData bool `json:"employee_data"` // HR/employment data
CustomerData bool `json:"customer_data"` // Customer information
PublicData bool `json:"public_data"` // Publicly available data only
}
// Purpose specifies the processing purpose
type Purpose struct {
CustomerSupport bool `json:"customer_support"`
Marketing bool `json:"marketing"`
Analytics bool `json:"analytics"`
Automation bool `json:"automation"`
EvaluationScoring bool `json:"evaluation_scoring"` // Scoring/ranking of persons
DecisionMaking bool `json:"decision_making"` // Automated decisions
Profiling bool `json:"profiling"`
Research bool `json:"research"`
InternalTools bool `json:"internal_tools"`
PublicService bool `json:"public_service"`
}
// Outputs specifies output characteristics
type Outputs struct {
RecommendationsToUsers bool `json:"recommendations_to_users"`
RankingsOrScores bool `json:"rankings_or_scores"` // Outputs rankings/scores
LegalEffects bool `json:"legal_effects"` // Has legal consequences
AccessDecisions bool `json:"access_decisions"` // Grants/denies access
ContentGeneration bool `json:"content_generation"` // Generates text/media
DataExport bool `json:"data_export"` // Exports data externally
}
// Hosting specifies where the AI runs
type Hosting struct {
Provider string `json:"provider,omitempty"` // e.g., "Azure", "AWS", "Hetzner", "On-Prem"
Region string `json:"region"` // "eu", "third_country", "on_prem"
DataResidency string `json:"data_residency,omitempty"` // Where data is stored
}
// ModelUsage specifies how the model is used
type ModelUsage struct {
RAG bool `json:"rag"` // Retrieval-Augmented Generation only
Finetune bool `json:"finetune"` // Fine-tuning with data
Training bool `json:"training"` // Full training with data
Inference bool `json:"inference"` // Inference only
}
// Retention specifies data retention
type Retention struct {
StorePrompts bool `json:"store_prompts"`
StoreResponses bool `json:"store_responses"`
RetentionDays int `json:"retention_days,omitempty"`
AnonymizeAfterUse bool `json:"anonymize_after_use"`
}
// ============================================================================
// Financial Regulations Structs (DORA, MaRisk, BAIT)
// ============================================================================
// FinancialEntityType represents the type of financial institution
type FinancialEntityType string
const (
FinancialEntityCreditInstitution FinancialEntityType = "CREDIT_INSTITUTION"
FinancialEntityPaymentServiceProvider FinancialEntityType = "PAYMENT_SERVICE_PROVIDER"
FinancialEntityEMoneyInstitution FinancialEntityType = "E_MONEY_INSTITUTION"
FinancialEntityInvestmentFirm FinancialEntityType = "INVESTMENT_FIRM"
FinancialEntityInsuranceCompany FinancialEntityType = "INSURANCE_COMPANY"
FinancialEntityCryptoAssetProvider FinancialEntityType = "CRYPTO_ASSET_PROVIDER"
FinancialEntityOther FinancialEntityType = "OTHER_FINANCIAL"
)
// SizeCategory represents the significance category of a financial institution
type SizeCategory string
const (
SizeCategorySignificant SizeCategory = "SIGNIFICANT"
SizeCategoryLessSignificant SizeCategory = "LESS_SIGNIFICANT"
SizeCategorySmall SizeCategory = "SMALL"
)
// ProviderLocation represents the location of an ICT service provider
type ProviderLocation string
const (
ProviderLocationEU ProviderLocation = "EU"
ProviderLocationEEA ProviderLocation = "EEA"
ProviderLocationAdequacyDecision ProviderLocation = "ADEQUACY_DECISION"
ProviderLocationThirdCountry ProviderLocation = "THIRD_COUNTRY"
)
// FinancialEntity describes the financial institution context
type FinancialEntity struct {
Type FinancialEntityType `json:"type"`
Regulated bool `json:"regulated"`
SizeCategory SizeCategory `json:"size_category"`
}
// ICTService describes ICT service characteristics for DORA compliance
type ICTService struct {
IsCritical bool `json:"is_critical"`
IsOutsourced bool `json:"is_outsourced"`
ProviderLocation ProviderLocation `json:"provider_location"`
ConcentrationRisk bool `json:"concentration_risk"`
}
// FinancialAIApplication describes financial-specific AI application characteristics
type FinancialAIApplication struct {
AffectsCustomerDecisions bool `json:"affects_customer_decisions"`
AlgorithmicTrading bool `json:"algorithmic_trading"`
RiskAssessment bool `json:"risk_assessment"`
AMLKYC bool `json:"aml_kyc"`
ModelValidationDone bool `json:"model_validation_done"`
}
// FinancialContext aggregates all financial regulation-specific information
type FinancialContext struct {
FinancialEntity FinancialEntity `json:"financial_entity"`
ICTService ICTService `json:"ict_service"`
AIApplication FinancialAIApplication `json:"ai_application"`
}

View File

@@ -428,74 +428,3 @@ func (r *ObligationsRegistry) generateExecutiveSummary(overview *ManagementOblig
return summary
}
// containsString checks if a slice contains a string
func containsString(slice []string, s string) bool {
for _, item := range slice {
if item == s {
return true
}
}
return false
}
// ============================================================================
// Grouping Methods
// ============================================================================
// GroupByRegulation groups obligations by their regulation ID
func (r *ObligationsRegistry) GroupByRegulation(obligations []Obligation) map[string][]Obligation {
result := make(map[string][]Obligation)
for _, obl := range obligations {
result[obl.RegulationID] = append(result[obl.RegulationID], obl)
}
return result
}
// GroupByDeadline groups obligations by deadline timeframe
func (r *ObligationsRegistry) GroupByDeadline(obligations []Obligation) ObligationsByDeadlineResponse {
result := ObligationsByDeadlineResponse{
Overdue: []Obligation{},
ThisWeek: []Obligation{},
ThisMonth: []Obligation{},
NextQuarter: []Obligation{},
Later: []Obligation{},
NoDeadline: []Obligation{},
}
now := time.Now()
oneWeek := now.AddDate(0, 0, 7)
oneMonth := now.AddDate(0, 1, 0)
threeMonths := now.AddDate(0, 3, 0)
for _, obl := range obligations {
if obl.Deadline == nil || obl.Deadline.Type != DeadlineAbsolute || obl.Deadline.Date == nil {
result.NoDeadline = append(result.NoDeadline, obl)
continue
}
deadline := *obl.Deadline.Date
switch {
case deadline.Before(now):
result.Overdue = append(result.Overdue, obl)
case deadline.Before(oneWeek):
result.ThisWeek = append(result.ThisWeek, obl)
case deadline.Before(oneMonth):
result.ThisMonth = append(result.ThisMonth, obl)
case deadline.Before(threeMonths):
result.NextQuarter = append(result.NextQuarter, obl)
default:
result.Later = append(result.Later, obl)
}
}
return result
}
// GroupByResponsible groups obligations by responsible role
func (r *ObligationsRegistry) GroupByResponsible(obligations []Obligation) map[ResponsibleRole][]Obligation {
result := make(map[ResponsibleRole][]Obligation)
for _, obl := range obligations {
result[obl.Responsible] = append(result[obl.Responsible], obl)
}
return result
}

View File

@@ -0,0 +1,75 @@
package ucca
import "time"
// ============================================================================
// Grouping Methods
// ============================================================================
// GroupByRegulation groups obligations by their regulation ID
func (r *ObligationsRegistry) GroupByRegulation(obligations []Obligation) map[string][]Obligation {
result := make(map[string][]Obligation)
for _, obl := range obligations {
result[obl.RegulationID] = append(result[obl.RegulationID], obl)
}
return result
}
// GroupByDeadline groups obligations by deadline timeframe
func (r *ObligationsRegistry) GroupByDeadline(obligations []Obligation) ObligationsByDeadlineResponse {
result := ObligationsByDeadlineResponse{
Overdue: []Obligation{},
ThisWeek: []Obligation{},
ThisMonth: []Obligation{},
NextQuarter: []Obligation{},
Later: []Obligation{},
NoDeadline: []Obligation{},
}
now := time.Now()
oneWeek := now.AddDate(0, 0, 7)
oneMonth := now.AddDate(0, 1, 0)
threeMonths := now.AddDate(0, 3, 0)
for _, obl := range obligations {
if obl.Deadline == nil || obl.Deadline.Type != DeadlineAbsolute || obl.Deadline.Date == nil {
result.NoDeadline = append(result.NoDeadline, obl)
continue
}
deadline := *obl.Deadline.Date
switch {
case deadline.Before(now):
result.Overdue = append(result.Overdue, obl)
case deadline.Before(oneWeek):
result.ThisWeek = append(result.ThisWeek, obl)
case deadline.Before(oneMonth):
result.ThisMonth = append(result.ThisMonth, obl)
case deadline.Before(threeMonths):
result.NextQuarter = append(result.NextQuarter, obl)
default:
result.Later = append(result.Later, obl)
}
}
return result
}
// GroupByResponsible groups obligations by responsible role
func (r *ObligationsRegistry) GroupByResponsible(obligations []Obligation) map[ResponsibleRole][]Obligation {
result := make(map[ResponsibleRole][]Obligation)
for _, obl := range obligations {
result[obl.Responsible] = append(result[obl.Responsible], obl)
}
return result
}
// containsString checks if a slice contains a string
func containsString(slice []string, s string) bool {
for _, item := range slice {
if item == s {
return true
}
}
return false
}

View File

@@ -408,103 +408,3 @@ func truncateString(s string, maxLen int) string {
}
return s[:maxLen-3] + "..."
}
// ExportMarkdown exports the overview as Markdown (for compatibility)
func (e *PDFExporter) ExportMarkdown(overview *ManagementObligationsOverview) (*ExportMemoResponse, error) {
var buf bytes.Buffer
// Title
buf.WriteString(fmt.Sprintf("# Regulatorische Pflichten-Uebersicht\n\n"))
if overview.OrganizationName != "" {
buf.WriteString(fmt.Sprintf("**Organisation:** %s\n\n", overview.OrganizationName))
}
buf.WriteString(fmt.Sprintf("**Stand:** %s\n\n", overview.AssessmentDate.Format("02.01.2006")))
// Executive Summary
buf.WriteString("## Executive Summary\n\n")
summary := overview.ExecutiveSummary
buf.WriteString(fmt.Sprintf("| Metrik | Wert |\n"))
buf.WriteString(fmt.Sprintf("|--------|------|\n"))
buf.WriteString(fmt.Sprintf("| Anwendbare Regulierungen | %d |\n", summary.TotalRegulations))
buf.WriteString(fmt.Sprintf("| Gesamtzahl Pflichten | %d |\n", summary.TotalObligations))
buf.WriteString(fmt.Sprintf("| Kritische Pflichten | %d |\n", summary.CriticalObligations))
buf.WriteString(fmt.Sprintf("| Kommende Fristen (30 Tage) | %d |\n", summary.UpcomingDeadlines))
buf.WriteString(fmt.Sprintf("| Compliance Score | %d%% |\n\n", summary.ComplianceScore))
// Key Risks
if len(summary.KeyRisks) > 0 {
buf.WriteString("### Wesentliche Risiken\n\n")
for _, risk := range summary.KeyRisks {
buf.WriteString(fmt.Sprintf("- %s\n", risk))
}
buf.WriteString("\n")
}
// Recommended Actions
if len(summary.RecommendedActions) > 0 {
buf.WriteString("### Empfohlene Massnahmen\n\n")
for _, action := range summary.RecommendedActions {
buf.WriteString(fmt.Sprintf("- %s\n", action))
}
buf.WriteString("\n")
}
// Applicable Regulations
buf.WriteString("## Anwendbare Regulierungen\n\n")
buf.WriteString("| Regulierung | Klassifizierung | Pflichten | Grund |\n")
buf.WriteString("|-------------|-----------------|-----------|-------|\n")
for _, reg := range overview.ApplicableRegulations {
buf.WriteString(fmt.Sprintf("| %s | %s | %d | %s |\n", reg.Name, reg.Classification, reg.ObligationCount, reg.Reason))
}
buf.WriteString("\n")
// Sanctions Summary
buf.WriteString("## Sanktionsrisiken\n\n")
sanctions := overview.SanctionsSummary
if sanctions.MaxFinancialRisk != "" {
buf.WriteString(fmt.Sprintf("- **Max. Finanzrisiko:** %s\n", sanctions.MaxFinancialRisk))
}
buf.WriteString(fmt.Sprintf("- **Persoenliche Haftung:** %v\n", sanctions.PersonalLiabilityRisk))
buf.WriteString(fmt.Sprintf("- **Strafrechtliche Konsequenzen:** %v\n\n", sanctions.CriminalLiabilityRisk))
if sanctions.Summary != "" {
buf.WriteString(fmt.Sprintf("*%s*\n\n", sanctions.Summary))
}
// Obligations
buf.WriteString("## Pflichten-Uebersicht\n\n")
for _, obl := range overview.Obligations {
buf.WriteString(fmt.Sprintf("### %s - %s\n\n", obl.ID, obl.Title))
buf.WriteString(fmt.Sprintf("**Prioritaet:** %s | **Verantwortlich:** %s\n\n", obl.Priority, obl.Responsible))
if len(obl.LegalBasis) > 0 {
buf.WriteString("**Rechtsgrundlage:** ")
for i, lb := range obl.LegalBasis {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(lb.Norm)
}
buf.WriteString("\n\n")
}
buf.WriteString(fmt.Sprintf("%s\n\n", obl.Description))
}
// Incident Deadlines
if len(overview.IncidentDeadlines) > 0 {
buf.WriteString("## Meldepflichten bei Vorfaellen\n\n")
for _, dl := range overview.IncidentDeadlines {
buf.WriteString(fmt.Sprintf("- **%s:** %s an %s\n", dl.Phase, dl.Deadline, dl.Recipient))
}
buf.WriteString("\n")
}
// Footer
buf.WriteString("---\n\n")
buf.WriteString(fmt.Sprintf("*Generiert am %s mit BreakPilot AI Compliance SDK*\n", time.Now().Format("02.01.2006 15:04")))
return &ExportMemoResponse{
Content: buf.String(),
ContentType: "text/markdown",
Filename: fmt.Sprintf("pflichten-uebersicht-%s.md", time.Now().Format("2006-01-02")),
GeneratedAt: time.Now(),
}, nil
}

View File

@@ -0,0 +1,107 @@
package ucca
import (
"bytes"
"fmt"
"time"
)
// ExportMarkdown exports the overview as Markdown (for compatibility)
func (e *PDFExporter) ExportMarkdown(overview *ManagementObligationsOverview) (*ExportMemoResponse, error) {
var buf bytes.Buffer
// Title
buf.WriteString(fmt.Sprintf("# Regulatorische Pflichten-Uebersicht\n\n"))
if overview.OrganizationName != "" {
buf.WriteString(fmt.Sprintf("**Organisation:** %s\n\n", overview.OrganizationName))
}
buf.WriteString(fmt.Sprintf("**Stand:** %s\n\n", overview.AssessmentDate.Format("02.01.2006")))
// Executive Summary
buf.WriteString("## Executive Summary\n\n")
summary := overview.ExecutiveSummary
buf.WriteString(fmt.Sprintf("| Metrik | Wert |\n"))
buf.WriteString(fmt.Sprintf("|--------|------|\n"))
buf.WriteString(fmt.Sprintf("| Anwendbare Regulierungen | %d |\n", summary.TotalRegulations))
buf.WriteString(fmt.Sprintf("| Gesamtzahl Pflichten | %d |\n", summary.TotalObligations))
buf.WriteString(fmt.Sprintf("| Kritische Pflichten | %d |\n", summary.CriticalObligations))
buf.WriteString(fmt.Sprintf("| Kommende Fristen (30 Tage) | %d |\n", summary.UpcomingDeadlines))
buf.WriteString(fmt.Sprintf("| Compliance Score | %d%% |\n\n", summary.ComplianceScore))
// Key Risks
if len(summary.KeyRisks) > 0 {
buf.WriteString("### Wesentliche Risiken\n\n")
for _, risk := range summary.KeyRisks {
buf.WriteString(fmt.Sprintf("- %s\n", risk))
}
buf.WriteString("\n")
}
// Recommended Actions
if len(summary.RecommendedActions) > 0 {
buf.WriteString("### Empfohlene Massnahmen\n\n")
for _, action := range summary.RecommendedActions {
buf.WriteString(fmt.Sprintf("- %s\n", action))
}
buf.WriteString("\n")
}
// Applicable Regulations
buf.WriteString("## Anwendbare Regulierungen\n\n")
buf.WriteString("| Regulierung | Klassifizierung | Pflichten | Grund |\n")
buf.WriteString("|-------------|-----------------|-----------|-------|\n")
for _, reg := range overview.ApplicableRegulations {
buf.WriteString(fmt.Sprintf("| %s | %s | %d | %s |\n", reg.Name, reg.Classification, reg.ObligationCount, reg.Reason))
}
buf.WriteString("\n")
// Sanctions Summary
buf.WriteString("## Sanktionsrisiken\n\n")
sanctions := overview.SanctionsSummary
if sanctions.MaxFinancialRisk != "" {
buf.WriteString(fmt.Sprintf("- **Max. Finanzrisiko:** %s\n", sanctions.MaxFinancialRisk))
}
buf.WriteString(fmt.Sprintf("- **Persoenliche Haftung:** %v\n", sanctions.PersonalLiabilityRisk))
buf.WriteString(fmt.Sprintf("- **Strafrechtliche Konsequenzen:** %v\n\n", sanctions.CriminalLiabilityRisk))
if sanctions.Summary != "" {
buf.WriteString(fmt.Sprintf("*%s*\n\n", sanctions.Summary))
}
// Obligations
buf.WriteString("## Pflichten-Uebersicht\n\n")
for _, obl := range overview.Obligations {
buf.WriteString(fmt.Sprintf("### %s - %s\n\n", obl.ID, obl.Title))
buf.WriteString(fmt.Sprintf("**Prioritaet:** %s | **Verantwortlich:** %s\n\n", obl.Priority, obl.Responsible))
if len(obl.LegalBasis) > 0 {
buf.WriteString("**Rechtsgrundlage:** ")
for i, lb := range obl.LegalBasis {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(lb.Norm)
}
buf.WriteString("\n\n")
}
buf.WriteString(fmt.Sprintf("%s\n\n", obl.Description))
}
// Incident Deadlines
if len(overview.IncidentDeadlines) > 0 {
buf.WriteString("## Meldepflichten bei Vorfaellen\n\n")
for _, dl := range overview.IncidentDeadlines {
buf.WriteString(fmt.Sprintf("- **%s:** %s an %s\n", dl.Phase, dl.Deadline, dl.Recipient))
}
buf.WriteString("\n")
}
// Footer
buf.WriteString("---\n\n")
buf.WriteString(fmt.Sprintf("*Generiert am %s mit BreakPilot AI Compliance SDK*\n", time.Now().Format("02.01.2006 15:04")))
return &ExportMemoResponse{
Content: buf.String(),
ContentType: "text/markdown",
Filename: fmt.Sprintf("pflichten-uebersicht-%s.md", time.Now().Format("2006-01-02")),
GeneratedAt: time.Now(),
}, nil
}