fix: Restore all files lost during destructive rebase
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>
This commit is contained in:
191
backend/content_service/schemas.py
Normal file
191
backend/content_service/schemas.py
Normal 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
|
||||
Reference in New Issue
Block a user