feat: database models and Alembic migrations — User, Recording, Transcription, Protocol, PromptTemplate, ExportFile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
from app.models.base import Base
|
||||
from app.models.export_file import ExportFile, ExportFormat
|
||||
from app.models.prompt_template import PromptTemplate, TemplateType
|
||||
from app.models.protocol import Protocol, ProtocolStatus
|
||||
from app.models.recording import Recording, RecordingSource, RecordingStatus
|
||||
from app.models.transcription import Transcription
|
||||
from app.models.user import User, UserRole
|
||||
|
||||
__all__ = [
|
||||
"Base",
|
||||
"User",
|
||||
"UserRole",
|
||||
"Recording",
|
||||
"RecordingSource",
|
||||
"RecordingStatus",
|
||||
"Transcription",
|
||||
"Protocol",
|
||||
"ProtocolStatus",
|
||||
"PromptTemplate",
|
||||
"TemplateType",
|
||||
"ExportFile",
|
||||
"ExportFormat",
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, func
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class TimestampMixin:
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
)
|
||||
|
||||
|
||||
class UUIDMixin:
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
primary_key=True, default=uuid.uuid4
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
import enum
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import BigInteger, Enum, ForeignKey, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.base import Base, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class ExportFormat(str, enum.Enum):
|
||||
docx = "docx"
|
||||
pdf = "pdf"
|
||||
md = "md"
|
||||
|
||||
|
||||
class ExportFile(UUIDMixin, TimestampMixin, Base):
|
||||
__tablename__ = "export_files"
|
||||
|
||||
protocol_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("protocols.id", ondelete="CASCADE")
|
||||
)
|
||||
format: Mapped[ExportFormat] = mapped_column(Enum(ExportFormat))
|
||||
file_path: Mapped[str] = mapped_column(String(1000))
|
||||
file_size: Mapped[int] = mapped_column(BigInteger)
|
||||
|
||||
protocol: Mapped["Protocol"] = relationship(back_populates="export_files")
|
||||
@@ -0,0 +1,28 @@
|
||||
import enum
|
||||
|
||||
from sqlalchemy import Boolean, Enum, String, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.base import Base, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class TemplateType(str, enum.Enum):
|
||||
client_survey = "client_survey"
|
||||
client_intro = "client_intro"
|
||||
internal = "internal"
|
||||
custom = "custom"
|
||||
|
||||
|
||||
class PromptTemplate(UUIDMixin, TimestampMixin, Base):
|
||||
__tablename__ = "prompt_templates"
|
||||
|
||||
name: Mapped[str] = mapped_column(String(255))
|
||||
description: Mapped[str] = mapped_column(String(1000), default="")
|
||||
type: Mapped[TemplateType] = mapped_column(Enum(TemplateType))
|
||||
system_prompt: Mapped[str] = mapped_column(Text)
|
||||
user_prompt: Mapped[str] = mapped_column(Text)
|
||||
output_schema: Mapped[dict] = mapped_column(JSONB, default=dict)
|
||||
is_default: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
|
||||
protocols: Mapped[list["Protocol"]] = relationship(back_populates="template")
|
||||
@@ -0,0 +1,39 @@
|
||||
import enum
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, Enum, ForeignKey, String, Text, func
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.base import Base, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class ProtocolStatus(str, enum.Enum):
|
||||
generating = "generating"
|
||||
done = "done"
|
||||
error = "error"
|
||||
|
||||
|
||||
class Protocol(UUIDMixin, TimestampMixin, Base):
|
||||
__tablename__ = "protocols"
|
||||
|
||||
transcription_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("transcriptions.id", ondelete="CASCADE")
|
||||
)
|
||||
template_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("prompt_templates.id")
|
||||
)
|
||||
content: Mapped[dict] = mapped_column(JSONB, default=dict)
|
||||
raw_text: Mapped[str] = mapped_column(Text, default="")
|
||||
llm_model: Mapped[str] = mapped_column(String(100))
|
||||
status: Mapped[ProtocolStatus] = mapped_column(
|
||||
Enum(ProtocolStatus), default=ProtocolStatus.generating
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
transcription: Mapped["Transcription"] = relationship(back_populates="protocols")
|
||||
template: Mapped["PromptTemplate"] = relationship(back_populates="protocols")
|
||||
export_files: Mapped[list["ExportFile"]] = relationship(back_populates="protocol")
|
||||
@@ -0,0 +1,40 @@
|
||||
import enum
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import BigInteger, Enum, ForeignKey, Interval, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.base import Base, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class RecordingSource(str, enum.Enum):
|
||||
web = "web"
|
||||
telegram = "telegram"
|
||||
lensa = "lensa"
|
||||
|
||||
|
||||
class RecordingStatus(str, enum.Enum):
|
||||
uploaded = "uploaded"
|
||||
processing = "processing"
|
||||
done = "done"
|
||||
error = "error"
|
||||
|
||||
|
||||
class Recording(UUIDMixin, TimestampMixin, Base):
|
||||
__tablename__ = "recordings"
|
||||
|
||||
user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id"))
|
||||
title: Mapped[str] = mapped_column(String(500))
|
||||
file_path: Mapped[str] = mapped_column(String(1000))
|
||||
file_size: Mapped[int] = mapped_column(BigInteger)
|
||||
duration: Mapped[float | None] = mapped_column(nullable=True)
|
||||
format: Mapped[str] = mapped_column(String(20))
|
||||
source: Mapped[RecordingSource] = mapped_column(Enum(RecordingSource))
|
||||
status: Mapped[RecordingStatus] = mapped_column(
|
||||
Enum(RecordingStatus), default=RecordingStatus.uploaded
|
||||
)
|
||||
|
||||
user: Mapped["User"] = relationship(back_populates="recordings")
|
||||
transcription: Mapped["Transcription | None"] = relationship(
|
||||
back_populates="recording", uselist=False
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import ForeignKey, Interval, String, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.base import Base, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class Transcription(UUIDMixin, TimestampMixin, Base):
|
||||
__tablename__ = "transcriptions"
|
||||
|
||||
recording_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("recordings.id", ondelete="CASCADE"), unique=True
|
||||
)
|
||||
text: Mapped[str] = mapped_column(Text)
|
||||
segments: Mapped[dict] = mapped_column(JSONB, default=list)
|
||||
language: Mapped[str | None] = mapped_column(String(10), nullable=True)
|
||||
whisper_model: Mapped[str] = mapped_column(String(50))
|
||||
processing_time: Mapped[float | None] = mapped_column(nullable=True)
|
||||
|
||||
recording: Mapped["Recording"] = relationship(back_populates="transcription")
|
||||
protocols: Mapped[list["Protocol"]] = relationship(back_populates="transcription")
|
||||
@@ -0,0 +1,25 @@
|
||||
import enum
|
||||
|
||||
from sqlalchemy import Boolean, Enum, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.base import Base, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class UserRole(str, enum.Enum):
|
||||
admin = "admin"
|
||||
user = "user"
|
||||
|
||||
|
||||
class User(UUIDMixin, TimestampMixin, Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
username: Mapped[str] = mapped_column(String(150), unique=True, index=True)
|
||||
email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
|
||||
password_hash: Mapped[str] = mapped_column(String(255))
|
||||
role: Mapped[UserRole] = mapped_column(
|
||||
Enum(UserRole), default=UserRole.user
|
||||
)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
|
||||
recordings: Mapped[list["Recording"]] = relationship(back_populates="user")
|
||||
Reference in New Issue
Block a user