A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
279 lines
10 KiB
Go
279 lines
10 KiB
Go
package portfolio
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// ============================================================================
|
|
// Constants / Enums
|
|
// ============================================================================
|
|
|
|
// PortfolioStatus represents the status of a portfolio
|
|
type PortfolioStatus string
|
|
|
|
const (
|
|
PortfolioStatusDraft PortfolioStatus = "DRAFT"
|
|
PortfolioStatusActive PortfolioStatus = "ACTIVE"
|
|
PortfolioStatusReview PortfolioStatus = "REVIEW"
|
|
PortfolioStatusApproved PortfolioStatus = "APPROVED"
|
|
PortfolioStatusArchived PortfolioStatus = "ARCHIVED"
|
|
)
|
|
|
|
// ItemType represents the type of item in a portfolio
|
|
type ItemType string
|
|
|
|
const (
|
|
ItemTypeAssessment ItemType = "ASSESSMENT"
|
|
ItemTypeRoadmap ItemType = "ROADMAP"
|
|
ItemTypeWorkshop ItemType = "WORKSHOP"
|
|
ItemTypeDocument ItemType = "DOCUMENT"
|
|
)
|
|
|
|
// MergeStrategy defines how to merge portfolios
|
|
type MergeStrategy string
|
|
|
|
const (
|
|
MergeStrategyUnion MergeStrategy = "UNION" // Combine all items (default)
|
|
MergeStrategyIntersect MergeStrategy = "INTERSECT" // Only overlapping items
|
|
MergeStrategyReplace MergeStrategy = "REPLACE" // Replace target with source
|
|
)
|
|
|
|
// ============================================================================
|
|
// Main Entities
|
|
// ============================================================================
|
|
|
|
// Portfolio represents a collection of AI use case assessments and related artifacts
|
|
type Portfolio struct {
|
|
ID uuid.UUID `json:"id"`
|
|
TenantID uuid.UUID `json:"tenant_id"`
|
|
NamespaceID *uuid.UUID `json:"namespace_id,omitempty"`
|
|
|
|
// Info
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
Status PortfolioStatus `json:"status"`
|
|
|
|
// Organization
|
|
Department string `json:"department,omitempty"`
|
|
BusinessUnit string `json:"business_unit,omitempty"`
|
|
Owner string `json:"owner,omitempty"`
|
|
OwnerEmail string `json:"owner_email,omitempty"`
|
|
|
|
// Aggregated metrics (computed)
|
|
TotalAssessments int `json:"total_assessments"`
|
|
TotalRoadmaps int `json:"total_roadmaps"`
|
|
TotalWorkshops int `json:"total_workshops"`
|
|
AvgRiskScore float64 `json:"avg_risk_score"`
|
|
HighRiskCount int `json:"high_risk_count"`
|
|
ConditionalCount int `json:"conditional_count"`
|
|
ApprovedCount int `json:"approved_count"`
|
|
ComplianceScore float64 `json:"compliance_score"` // 0-100
|
|
|
|
// Settings
|
|
Settings PortfolioSettings `json:"settings"`
|
|
|
|
// Audit
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
CreatedBy uuid.UUID `json:"created_by"`
|
|
ApprovedAt *time.Time `json:"approved_at,omitempty"`
|
|
ApprovedBy *uuid.UUID `json:"approved_by,omitempty"`
|
|
}
|
|
|
|
// PortfolioSettings contains configuration options
|
|
type PortfolioSettings struct {
|
|
AutoUpdateMetrics bool `json:"auto_update_metrics"` // Recalculate on changes
|
|
RequireApproval bool `json:"require_approval"` // Require approval before active
|
|
NotifyOnHighRisk bool `json:"notify_on_high_risk"` // Alert on high risk items
|
|
AllowExternalShare bool `json:"allow_external_share"` // Share with external users
|
|
}
|
|
|
|
// PortfolioItem represents an item linked to a portfolio
|
|
type PortfolioItem struct {
|
|
ID uuid.UUID `json:"id"`
|
|
PortfolioID uuid.UUID `json:"portfolio_id"`
|
|
ItemType ItemType `json:"item_type"`
|
|
ItemID uuid.UUID `json:"item_id"` // Reference to the actual item
|
|
|
|
// Cached info from the linked item
|
|
Title string `json:"title"`
|
|
Status string `json:"status,omitempty"`
|
|
RiskLevel string `json:"risk_level,omitempty"`
|
|
RiskScore int `json:"risk_score,omitempty"`
|
|
Feasibility string `json:"feasibility,omitempty"`
|
|
|
|
// Ordering and categorization
|
|
SortOrder int `json:"sort_order"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
Notes string `json:"notes,omitempty"`
|
|
|
|
// Audit
|
|
AddedAt time.Time `json:"added_at"`
|
|
AddedBy uuid.UUID `json:"added_by"`
|
|
}
|
|
|
|
// ============================================================================
|
|
// Merge Operations
|
|
// ============================================================================
|
|
|
|
// MergeRequest represents a request to merge portfolios
|
|
type MergeRequest struct {
|
|
SourcePortfolioID uuid.UUID `json:"source_portfolio_id"`
|
|
TargetPortfolioID uuid.UUID `json:"target_portfolio_id"`
|
|
Strategy MergeStrategy `json:"strategy"`
|
|
DeleteSource bool `json:"delete_source"` // Delete source after merge
|
|
IncludeRoadmaps bool `json:"include_roadmaps"`
|
|
IncludeWorkshops bool `json:"include_workshops"`
|
|
}
|
|
|
|
// MergeResult represents the result of a merge operation
|
|
type MergeResult struct {
|
|
TargetPortfolio *Portfolio `json:"target_portfolio"`
|
|
ItemsAdded int `json:"items_added"`
|
|
ItemsSkipped int `json:"items_skipped"` // Duplicates or excluded
|
|
ItemsUpdated int `json:"items_updated"`
|
|
SourceDeleted bool `json:"source_deleted"`
|
|
ConflictsResolved []MergeConflict `json:"conflicts_resolved,omitempty"`
|
|
}
|
|
|
|
// MergeConflict describes a conflict during merge
|
|
type MergeConflict struct {
|
|
ItemID uuid.UUID `json:"item_id"`
|
|
ItemType ItemType `json:"item_type"`
|
|
Reason string `json:"reason"`
|
|
Resolution string `json:"resolution"` // "kept_source", "kept_target", "merged"
|
|
}
|
|
|
|
// ============================================================================
|
|
// Aggregated Views
|
|
// ============================================================================
|
|
|
|
// PortfolioSummary contains aggregated portfolio information
|
|
type PortfolioSummary struct {
|
|
Portfolio *Portfolio `json:"portfolio"`
|
|
Items []PortfolioItem `json:"items"`
|
|
RiskDistribution RiskDistribution `json:"risk_distribution"`
|
|
FeasibilityDist FeasibilityDist `json:"feasibility_distribution"`
|
|
RecentActivity []ActivityEntry `json:"recent_activity,omitempty"`
|
|
}
|
|
|
|
// RiskDistribution shows the distribution of risk levels
|
|
type RiskDistribution struct {
|
|
Minimal int `json:"minimal"`
|
|
Low int `json:"low"`
|
|
Medium int `json:"medium"`
|
|
High int `json:"high"`
|
|
Unacceptable int `json:"unacceptable"`
|
|
}
|
|
|
|
// FeasibilityDist shows the distribution of feasibility verdicts
|
|
type FeasibilityDist struct {
|
|
Yes int `json:"yes"`
|
|
Conditional int `json:"conditional"`
|
|
No int `json:"no"`
|
|
}
|
|
|
|
// ActivityEntry represents recent activity on a portfolio
|
|
type ActivityEntry struct {
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Action string `json:"action"` // "added", "removed", "updated", "merged"
|
|
ItemType ItemType `json:"item_type"`
|
|
ItemID uuid.UUID `json:"item_id"`
|
|
ItemTitle string `json:"item_title"`
|
|
UserID uuid.UUID `json:"user_id"`
|
|
}
|
|
|
|
// PortfolioStats contains statistical information
|
|
type PortfolioStats struct {
|
|
TotalItems int `json:"total_items"`
|
|
ItemsByType map[ItemType]int `json:"items_by_type"`
|
|
RiskDistribution RiskDistribution `json:"risk_distribution"`
|
|
FeasibilityDist FeasibilityDist `json:"feasibility_distribution"`
|
|
AvgRiskScore float64 `json:"avg_risk_score"`
|
|
ComplianceScore float64 `json:"compliance_score"`
|
|
DSFARequired int `json:"dsfa_required"`
|
|
ControlsRequired int `json:"controls_required"`
|
|
LastUpdated time.Time `json:"last_updated"`
|
|
}
|
|
|
|
// ============================================================================
|
|
// API Request/Response Types
|
|
// ============================================================================
|
|
|
|
// CreatePortfolioRequest is the API request for creating a portfolio
|
|
type CreatePortfolioRequest struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
Department string `json:"department,omitempty"`
|
|
BusinessUnit string `json:"business_unit,omitempty"`
|
|
Owner string `json:"owner,omitempty"`
|
|
OwnerEmail string `json:"owner_email,omitempty"`
|
|
Settings PortfolioSettings `json:"settings,omitempty"`
|
|
}
|
|
|
|
// UpdatePortfolioRequest is the API request for updating a portfolio
|
|
type UpdatePortfolioRequest struct {
|
|
Name string `json:"name,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
Status PortfolioStatus `json:"status,omitempty"`
|
|
Department string `json:"department,omitempty"`
|
|
BusinessUnit string `json:"business_unit,omitempty"`
|
|
Owner string `json:"owner,omitempty"`
|
|
OwnerEmail string `json:"owner_email,omitempty"`
|
|
Settings *PortfolioSettings `json:"settings,omitempty"`
|
|
}
|
|
|
|
// AddItemRequest is the API request for adding an item to a portfolio
|
|
type AddItemRequest struct {
|
|
ItemType ItemType `json:"item_type"`
|
|
ItemID uuid.UUID `json:"item_id"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
// BulkAddItemsRequest is the API request for adding multiple items
|
|
type BulkAddItemsRequest struct {
|
|
Items []AddItemRequest `json:"items"`
|
|
}
|
|
|
|
// BulkAddItemsResponse is the API response for bulk adding items
|
|
type BulkAddItemsResponse struct {
|
|
Added int `json:"added"`
|
|
Skipped int `json:"skipped"`
|
|
Errors []string `json:"errors,omitempty"`
|
|
}
|
|
|
|
// PortfolioFilters defines filters for listing portfolios
|
|
type PortfolioFilters struct {
|
|
Status PortfolioStatus
|
|
Department string
|
|
BusinessUnit string
|
|
Owner string
|
|
MinRiskScore *float64
|
|
MaxRiskScore *float64
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
// ComparePortfoliosRequest is the API request for comparing portfolios
|
|
type ComparePortfoliosRequest struct {
|
|
PortfolioIDs []uuid.UUID `json:"portfolio_ids"` // 2-5 portfolios to compare
|
|
}
|
|
|
|
// ComparePortfoliosResponse is the API response for portfolio comparison
|
|
type ComparePortfoliosResponse struct {
|
|
Portfolios []Portfolio `json:"portfolios"`
|
|
Comparison PortfolioComparison `json:"comparison"`
|
|
}
|
|
|
|
// PortfolioComparison contains comparative metrics
|
|
type PortfolioComparison struct {
|
|
RiskScores map[string]float64 `json:"risk_scores"` // portfolio_id -> score
|
|
ComplianceScores map[string]float64 `json:"compliance_scores"`
|
|
ItemCounts map[string]int `json:"item_counts"`
|
|
CommonItems []uuid.UUID `json:"common_items"` // Items in multiple portfolios
|
|
UniqueItems map[string][]uuid.UUID `json:"unique_items"` // portfolio_id -> item_ids
|
|
}
|