Files

90 lines
4.0 KiB
Python

import uuid
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any
from pydantic import BaseModel, Field, validator
from sqlalchemy import String, Text, func
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy.sql import delete
class Base(DeclarativeBase):
pass
class Metadata(BaseModel):
authority_id: str = Field(..., description="Authority ID for routing")
reference_number: str = Field(..., description="Aktenzeichen/Kassenzeichen")
doc_type: str = Field(..., enum=["NOTICE", "COURT_ORDER", "GENERAL_INQUIRY"])
issued_at: datetime
@validator('doc_type')
def validate_doc_type(cls, v):
if v not in ["NOTICE", "COURT_ORDER", "GENERAL_INQUIRY"]:
raise ValueError("Invalid document type")
return v
class DocumentEnvelope(BaseModel):
id: Optional[str] = Field(default_factory=lambda: str(uuid.uuid4()), read_only=True)
metadata: Metadata
encrypted_payload: str = Field(..., description="Base64 E2E Encrypted PDF")
status: str = Field(default="RECEIVED", enum=["RECEIVED", "ROUTED", "ASSIGNED", "CLOSED"], read_only=True)
retention_date: Optional[datetime] = None
class ThreadType(BaseModel):
type: str = Field(..., enum=["TEXT_CHAT", "CALLBACK_REQUEST", "APPOINTMENT"])
initial_message: Optional[str] = None
class InteractionThread(BaseModel):
id: Optional[str] = Field(default_factory=lambda: str(uuid.uuid4()), read_only=True)
document_id: str
type: str = Field(..., enum=["TEXT_CHAT", "CALLBACK_REQUEST", "APPOINTMENT"])
status: str = Field(default="OPEN", enum=["OPEN", "PENDING_OFFICIAL", "PENDING_CITIZEN", "RESOLVED"])
assigned_official_id: Optional[str] = None
class Message(BaseModel):
id: Optional[str] = Field(default_factory=lambda: str(uuid.uuid4()), read_only=True)
thread_id: str
sender_role: str = Field(..., enum=["CITIZEN", "OFFICIAL"])
content: str = Field(..., description="Encrypted message content")
timestamp: datetime = Field(default_factory=datetime.now)
class ExportRequest(BaseModel):
case_id: str
target_system: str = "eAkte-Standard-V2"
include_attachments: bool = True
class ExportJob(BaseModel):
job_id: str
status: str = "QUEUED"
# DB Models
class Document(Base):
__tablename__ = "documents"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
reference_number: Mapped[str] = mapped_column(String(50), index=True)
authority_id: Mapped[str] = mapped_column(String(50), index=True)
doc_type: Mapped[str] = mapped_column(String(20))
status: Mapped[str] = mapped_column(String(20), default="RECEIVED")
storage_path: Mapped[str] = mapped_column(String(255)) # S3 key
issued_at: Mapped[datetime]
retention_date: Mapped[Optional[datetime]]
threads: relationship("Thread", back_populates="document")
class Thread(Base):
__tablename__ = "threads"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
document_id: Mapped[str] = mapped_column(String(36), index=True)
type: Mapped[str] = mapped_column(String(20))
status: Mapped[str] = mapped_column(String(20), default="OPEN")
assigned_official_id: Mapped[Optional[str]] = mapped_column(String(100))
document: relationship("Document", back_populates="threads")
messages: relationship("Message", back_populates="thread")
class Message(Base):
__tablename__ = "messages"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
thread_id: Mapped[str] = mapped_column(String(36), index=True)
sender_role: Mapped[str] = mapped_column(String(20))
content: Mapped[str] = mapped_column(Text) # Encrypted
timestamp: Mapped[datetime] = mapped_column(default=func.now())
thread: relationship("Thread", back_populates="messages")