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
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:
164
backend/content_service/models.py
Normal file
164
backend/content_service/models.py
Normal file
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
BreakPilot Content Service - Database Models
|
||||
Educational Content Management mit Creative Commons Lizenzen
|
||||
"""
|
||||
from sqlalchemy import Column, String, Integer, Float, DateTime, JSON, ForeignKey, Enum, Table
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
import enum
|
||||
import uuid
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
# Content Type Enum
|
||||
class ContentType(str, enum.Enum):
|
||||
VIDEO = "video"
|
||||
PDF = "pdf"
|
||||
IMAGE_GALLERY = "image_gallery"
|
||||
MARKDOWN = "markdown"
|
||||
AUDIO = "audio"
|
||||
H5P = "h5p"
|
||||
|
||||
# CC License Enum
|
||||
class CCLicense(str, enum.Enum):
|
||||
CC_BY = "CC-BY-4.0"
|
||||
CC_BY_SA = "CC-BY-SA-4.0"
|
||||
CC_BY_NC = "CC-BY-NC-4.0"
|
||||
CC_BY_NC_SA = "CC-BY-NC-SA-4.0"
|
||||
CC0 = "CC0-1.0"
|
||||
|
||||
# Category Enum
|
||||
class ContentCategory(str, enum.Enum):
|
||||
MOVEMENT = "movement"
|
||||
MATH = "math"
|
||||
STEAM = "steam"
|
||||
LANGUAGE = "language"
|
||||
ARTS = "arts"
|
||||
SOCIAL = "social"
|
||||
MINDFULNESS = "mindfulness"
|
||||
|
||||
# Content Status
|
||||
class ContentStatus(str, enum.Enum):
|
||||
DRAFT = "draft"
|
||||
REVIEW = "review"
|
||||
PUBLISHED = "published"
|
||||
ARCHIVED = "archived"
|
||||
|
||||
# Many-to-Many: Content <-> Tags
|
||||
content_tags = Table(
|
||||
'content_tags',
|
||||
Base.metadata,
|
||||
Column('content_id', String, ForeignKey('contents.id')),
|
||||
Column('tag_id', String, ForeignKey('tags.id'))
|
||||
)
|
||||
|
||||
class Content(Base):
|
||||
"""Educational Content Model"""
|
||||
__tablename__ = 'contents'
|
||||
|
||||
# Primary Key
|
||||
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
|
||||
# Creator Info
|
||||
creator_id = Column(String, nullable=False, index=True) # User ID from consent-service
|
||||
creator_name = Column(String, nullable=False)
|
||||
creator_email = Column(String)
|
||||
|
||||
# Content Metadata
|
||||
title = Column(String(500), nullable=False, index=True)
|
||||
description = Column(String(5000))
|
||||
content_type = Column(Enum(ContentType), nullable=False)
|
||||
category = Column(Enum(ContentCategory), nullable=False, index=True)
|
||||
|
||||
# License
|
||||
license = Column(Enum(CCLicense), nullable=False, default=CCLicense.CC_BY_SA)
|
||||
|
||||
# Age Range
|
||||
age_min = Column(Integer, default=6)
|
||||
age_max = Column(Integer, default=18)
|
||||
|
||||
# Files & URLs
|
||||
files = Column(JSON, default=list) # List of file URLs in MinIO
|
||||
thumbnail_url = Column(String)
|
||||
embed_url = Column(String) # For YouTube, Vimeo, etc.
|
||||
|
||||
# H5P Specific
|
||||
h5p_content_id = Column(String) # H5P content ID if type=h5p
|
||||
|
||||
# Matrix Integration
|
||||
matrix_room_id = Column(String) # Associated Matrix room for discussion
|
||||
matrix_event_id = Column(String) # Matrix message event ID
|
||||
|
||||
# Status
|
||||
status = Column(Enum(ContentStatus), default=ContentStatus.DRAFT, index=True)
|
||||
|
||||
# Analytics
|
||||
downloads = Column(Integer, default=0)
|
||||
views = Column(Integer, default=0)
|
||||
avg_rating = Column(Float, default=0.0)
|
||||
rating_count = Column(Integer, default=0)
|
||||
impact_score = Column(Float, default=0.0) # Future: Impact-Scoring
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
published_at = Column(DateTime)
|
||||
|
||||
# Relationships
|
||||
ratings = relationship("Rating", back_populates="content", cascade="all, delete-orphan")
|
||||
tags = relationship("Tag", secondary=content_tags, back_populates="contents")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Content {self.title} ({self.content_type})>"
|
||||
|
||||
|
||||
class Rating(Base):
|
||||
"""Content Ratings by Teachers"""
|
||||
__tablename__ = 'ratings'
|
||||
|
||||
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
content_id = Column(String, ForeignKey('contents.id'), nullable=False)
|
||||
user_id = Column(String, nullable=False, index=True) # Teacher ID
|
||||
user_name = Column(String)
|
||||
|
||||
stars = Column(Integer, nullable=False) # 1-5
|
||||
comment = Column(String(2000))
|
||||
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
# Relationship
|
||||
content = relationship("Content", back_populates="ratings")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Rating {self.stars}★ for Content {self.content_id}>"
|
||||
|
||||
|
||||
class Tag(Base):
|
||||
"""Content Tags for Search/Filter"""
|
||||
__tablename__ = 'tags'
|
||||
|
||||
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
name = Column(String(100), unique=True, nullable=False, index=True)
|
||||
category = Column(String(100)) # Optional grouping
|
||||
|
||||
# Relationship
|
||||
contents = relationship("Content", secondary=content_tags, back_populates="tags")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Tag {self.name}>"
|
||||
|
||||
|
||||
class Download(Base):
|
||||
"""Download Tracking (für Impact-Scoring)"""
|
||||
__tablename__ = 'downloads'
|
||||
|
||||
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
content_id = Column(String, ForeignKey('contents.id'), nullable=False, index=True)
|
||||
user_id = Column(String, nullable=False, index=True) # Teacher ID
|
||||
|
||||
downloaded_at = Column(DateTime, default=datetime.utcnow, index=True)
|
||||
ip_address = Column(String) # Optional, anonymisiert nach 7 Tagen
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Download {self.content_id} by {self.user_id}>"
|
||||
Reference in New Issue
Block a user