klausur-service (7 monoliths): - grid_editor_helpers.py (1,737 → 5 files: columns, filters, headers, zones) - cv_cell_grid.py (1,675 → 7 files: build, legacy, streaming, merge, vocab) - worksheet_editor_api.py (1,305 → 4 files: models, AI, reconstruct, routes) - legal_corpus_ingestion.py (1,280 → 3 files: registry, chunking, ingestion) - cv_review.py (1,248 → 4 files: pipeline, spell, LLM, barrel) - cv_preprocessing.py (1,166 → 3 files: deskew, dewarp, barrel) - rbac.py, admin_api.py, routes/eh.py remain (next batch) backend-lehrer (1 monolith): - classroom_engine/repository.py (1,705 → 7 files by domain) All re-export barrels preserve backward compatibility. Zero import errors verified. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
134 lines
3.8 KiB
Python
134 lines
3.8 KiB
Python
"""
|
|
Worksheet Editor Models — Enums, Pydantic models, and configuration.
|
|
"""
|
|
|
|
import os
|
|
import logging
|
|
from typing import Optional, List, Dict
|
|
from enum import Enum
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# =============================================
|
|
# CONFIGURATION
|
|
# =============================================
|
|
|
|
OLLAMA_URL = os.getenv("OLLAMA_URL", "http://host.docker.internal:11434")
|
|
SD_MODEL = os.getenv("SD_MODEL", "stable-diffusion") # or specific SD model
|
|
WORKSHEET_STORAGE_DIR = os.getenv("WORKSHEET_STORAGE_DIR",
|
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), "worksheet-storage"))
|
|
|
|
# Ensure storage directory exists
|
|
os.makedirs(WORKSHEET_STORAGE_DIR, exist_ok=True)
|
|
|
|
# =============================================
|
|
# ENUMS & MODELS
|
|
# =============================================
|
|
|
|
class AIImageStyle(str, Enum):
|
|
REALISTIC = "realistic"
|
|
CARTOON = "cartoon"
|
|
SKETCH = "sketch"
|
|
CLIPART = "clipart"
|
|
EDUCATIONAL = "educational"
|
|
|
|
class WorksheetStatus(str, Enum):
|
|
DRAFT = "draft"
|
|
PUBLISHED = "published"
|
|
ARCHIVED = "archived"
|
|
|
|
# Style prompt modifiers
|
|
STYLE_PROMPTS = {
|
|
AIImageStyle.REALISTIC: "photorealistic, high detail, professional photography",
|
|
AIImageStyle.CARTOON: "cartoon style, colorful, child-friendly, simple shapes",
|
|
AIImageStyle.SKETCH: "pencil sketch, hand-drawn, black and white, artistic",
|
|
AIImageStyle.CLIPART: "clipart style, flat design, simple, vector-like",
|
|
AIImageStyle.EDUCATIONAL: "educational illustration, clear, informative, textbook style"
|
|
}
|
|
|
|
# =============================================
|
|
# REQUEST/RESPONSE MODELS
|
|
# =============================================
|
|
|
|
class AIImageRequest(BaseModel):
|
|
prompt: str = Field(..., min_length=3, max_length=500)
|
|
style: AIImageStyle = AIImageStyle.EDUCATIONAL
|
|
width: int = Field(512, ge=256, le=1024)
|
|
height: int = Field(512, ge=256, le=1024)
|
|
|
|
class AIImageResponse(BaseModel):
|
|
image_base64: str
|
|
prompt_used: str
|
|
error: Optional[str] = None
|
|
|
|
class PageData(BaseModel):
|
|
id: str
|
|
index: int
|
|
canvasJSON: str
|
|
|
|
class PageFormat(BaseModel):
|
|
width: float = 210
|
|
height: float = 297
|
|
orientation: str = "portrait"
|
|
margins: Dict[str, float] = {"top": 15, "right": 15, "bottom": 15, "left": 15}
|
|
|
|
class WorksheetSaveRequest(BaseModel):
|
|
id: Optional[str] = None
|
|
title: str
|
|
description: Optional[str] = None
|
|
pages: List[PageData]
|
|
pageFormat: Optional[PageFormat] = None
|
|
|
|
class WorksheetResponse(BaseModel):
|
|
id: str
|
|
title: str
|
|
description: Optional[str]
|
|
pages: List[PageData]
|
|
pageFormat: PageFormat
|
|
createdAt: str
|
|
updatedAt: str
|
|
|
|
class AIModifyRequest(BaseModel):
|
|
prompt: str = Field(..., min_length=3, max_length=1000)
|
|
canvas_json: str
|
|
model: str = "qwen2.5vl:32b"
|
|
|
|
class AIModifyResponse(BaseModel):
|
|
modified_canvas_json: Optional[str] = None
|
|
message: str
|
|
error: Optional[str] = None
|
|
|
|
class ReconstructRequest(BaseModel):
|
|
session_id: str
|
|
page_number: int = 1
|
|
include_images: bool = True
|
|
regenerate_graphics: bool = False
|
|
|
|
class ReconstructResponse(BaseModel):
|
|
canvas_json: str
|
|
page_width: int
|
|
page_height: int
|
|
elements_count: int
|
|
vocabulary_matched: int
|
|
message: str
|
|
error: Optional[str] = None
|
|
|
|
# =============================================
|
|
# IN-MEMORY STORAGE (Development)
|
|
# =============================================
|
|
|
|
worksheets_db: Dict[str, Dict] = {}
|
|
|
|
# PDF Generation availability
|
|
try:
|
|
from reportlab.lib import colors # noqa: F401
|
|
from reportlab.lib.pagesizes import A4 # noqa: F401
|
|
from reportlab.lib.units import mm # noqa: F401
|
|
from reportlab.pdfgen import canvas # noqa: F401
|
|
from reportlab.lib.styles import getSampleStyleSheet # noqa: F401
|
|
REPORTLAB_AVAILABLE = True
|
|
except ImportError:
|
|
REPORTLAB_AVAILABLE = False
|