generated from coulomb/repo-seed
90 lines
4.0 KiB
Python
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") |