feat: BreakPilot PWA - Full codebase (clean push without large binaries)
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed

All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
This commit is contained in:
BreakPilot Dev
2026-02-11 13:25:58 +01:00
commit 19855efacc
2512 changed files with 933814 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
"""
Pydantic Schemas für Content Service API
"""
from pydantic import BaseModel, Field, validator
from typing import Optional, List
from datetime import datetime
from models import ContentType, CCLicense, ContentCategory, ContentStatus
# ============= REQUEST SCHEMAS =============
class ContentCreate(BaseModel):
"""Schema für Content Creation"""
title: str = Field(..., min_length=3, max_length=500)
description: Optional[str] = Field(None, max_length=5000)
content_type: ContentType
category: ContentCategory
license: CCLicense = CCLicense.CC_BY_SA
age_min: int = Field(6, ge=3, le=18)
age_max: int = Field(18, ge=3, le=18)
embed_url: Optional[str] = None
tags: List[str] = []
@validator('age_max')
def age_max_must_be_greater(cls, v, values):
if 'age_min' in values and v < values['age_min']:
raise ValueError('age_max must be >= age_min')
return v
class ContentUpdate(BaseModel):
"""Schema für Content Update"""
title: Optional[str] = Field(None, min_length=3, max_length=500)
description: Optional[str] = Field(None, max_length=5000)
category: Optional[ContentCategory] = None
license: Optional[CCLicense] = None
age_min: Optional[int] = Field(None, ge=3, le=18)
age_max: Optional[int] = Field(None, ge=3, le=18)
embed_url: Optional[str] = None
tags: Optional[List[str]] = None
status: Optional[ContentStatus] = None
class RatingCreate(BaseModel):
"""Schema für Rating Creation"""
stars: int = Field(..., ge=1, le=5)
comment: Optional[str] = Field(None, max_length=2000)
# ============= RESPONSE SCHEMAS =============
class TagResponse(BaseModel):
"""Tag Response"""
id: str
name: str
category: Optional[str]
class Config:
from_attributes = True
class RatingResponse(BaseModel):
"""Rating Response"""
id: str
user_id: str
user_name: Optional[str]
stars: int
comment: Optional[str]
created_at: datetime
class Config:
from_attributes = True
class ContentResponse(BaseModel):
"""Content Response (Full)"""
id: str
creator_id: str
creator_name: str
creator_email: Optional[str]
title: str
description: Optional[str]
content_type: ContentType
category: ContentCategory
license: CCLicense
age_min: int
age_max: int
files: List[str]
thumbnail_url: Optional[str]
embed_url: Optional[str]
h5p_content_id: Optional[str]
matrix_room_id: Optional[str]
matrix_event_id: Optional[str]
status: ContentStatus
downloads: int
views: int
avg_rating: float
rating_count: int
impact_score: float
created_at: datetime
updated_at: datetime
published_at: Optional[datetime]
tags: List[TagResponse] = []
class Config:
from_attributes = True
class ContentListItem(BaseModel):
"""Content Response (List View - simplified)"""
id: str
creator_name: str
title: str
description: Optional[str]
content_type: ContentType
category: ContentCategory
license: CCLicense
thumbnail_url: Optional[str]
avg_rating: float
rating_count: int
downloads: int
created_at: datetime
tags: List[str] = []
class Config:
from_attributes = True
class ContentWithRatings(ContentResponse):
"""Content with ratings included"""
ratings: List[RatingResponse] = []
class Config:
from_attributes = True
# ============= FILTER SCHEMAS =============
class ContentFilter(BaseModel):
"""Search/Filter Parameters"""
search: Optional[str] = None
category: Optional[ContentCategory] = None
content_type: Optional[ContentType] = None
license: Optional[CCLicense] = None
age_min: Optional[int] = None
age_max: Optional[int] = None
tags: Optional[List[str]] = None
min_rating: Optional[float] = None
status: Optional[ContentStatus] = ContentStatus.PUBLISHED
creator_id: Optional[str] = None
# Pagination
skip: int = Field(0, ge=0)
limit: int = Field(20, ge=1, le=100)
# Sorting
sort_by: str = Field("created_at", pattern="^(created_at|avg_rating|downloads|title)$")
sort_desc: bool = True
# ============= ANALYTICS SCHEMAS =============
class ContentStats(BaseModel):
"""Content Statistics"""
total_contents: int
total_downloads: int
total_views: int
avg_rating: float
by_category: dict
by_type: dict
by_license: dict
class CreatorStats(BaseModel):
"""Creator Statistics"""
creator_id: str
creator_name: str
total_contents: int
total_downloads: int
total_views: int
avg_rating: float
impact_score: float
content_breakdown: dict
# ============= UPLOAD SCHEMAS =============
class FileUploadResponse(BaseModel):
"""File Upload Response"""
file_url: str
file_name: str
file_size: int
content_type: str