root 656f596d7e feat: implement AI legal assistant system MVP
Core modules:
- Laws: CRUD, search, AI-powered QA
- Analysis: legal research and case management
- Contracts: lifecycle management with templates
- Signatures: electronic signature workflow

Infrastructure:
- FastAPI + SQLite + async SQLAlchemy
- Docker deployment support
- 54 unit tests passing

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 03:34:44 +08:00

132 lines
4.6 KiB
Python

"""Contract model."""
import enum
from datetime import date, datetime
from typing import Optional
from sqlalchemy import String, Text, Date, DateTime, Enum as SQLEnum, Integer, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.core.database import Base
class ContractStatus(str, enum.Enum):
"""Contract status enumeration."""
DRAFT = "draft"
PENDING_APPROVAL = "pending_approval"
APPROVED = "approved"
PENDING_SIGNATURE = "pending_signature"
SIGNED = "signed"
ARCHIVED = "archived"
REJECTED = "rejected"
class Contract(Base):
"""Contract model."""
__tablename__ = "contracts"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
template_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("contract_templates.id"), nullable=True)
title: Mapped[str] = mapped_column(String(200), index=True)
contract_number: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
party_a: Mapped[str] = mapped_column(String(100))
party_b: Mapped[str] = mapped_column(String(100))
content: Mapped[str] = mapped_column(Text)
status: Mapped[ContractStatus] = mapped_column(
SQLEnum(ContractStatus),
default=ContractStatus.DRAFT
)
effective_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
expiry_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
file_path: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
created_by: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"))
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
title: str,
party_a: str,
party_b: str,
content: str,
created_by: int,
template_id: Optional[int] = None,
contract_number: Optional[str] = None,
status: ContractStatus = ContractStatus.DRAFT,
effective_date: Optional[date] = None,
expiry_date: Optional[date] = None,
file_path: Optional[str] = None,
**kwargs
):
self.title = title
self.party_a = party_a
self.party_b = party_b
self.content = content
self.created_by = created_by
self.template_id = template_id
self.contract_number = contract_number
self.status = status
self.effective_date = effective_date
self.expiry_date = expiry_date
self.file_path = file_path
self.created_at = datetime.utcnow()
self.updated_at = datetime.utcnow()
class ContractTemplate(Base):
"""Contract template model."""
__tablename__ = "contract_templates"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(100))
contract_type: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
content: Mapped[str] = mapped_column(Text)
variables: Mapped[Optional[dict]] = mapped_column(String, nullable=True) # JSON string
created_by: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"))
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
name: str,
content: str,
created_by: int,
contract_type: Optional[str] = None,
variables: Optional[dict] = None,
**kwargs
):
self.name = name
self.content = content
self.created_by = created_by
self.contract_type = contract_type
self.variables = variables
self.created_at = datetime.utcnow()
class ContractApproval(Base):
"""Contract approval model."""
__tablename__ = "contract_approvals"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
contract_id: Mapped[int] = mapped_column(Integer, ForeignKey("contracts.id"), index=True)
approver_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True)
status: Mapped[ContractStatus] = mapped_column(SQLEnum(ContractStatus))
comment: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
approved_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
def __init__(
self,
contract_id: int,
approver_id: int,
status: ContractStatus,
comment: Optional[str] = None,
**kwargs
):
self.contract_id = contract_id
self.approver_id = approver_id
self.status = status
self.comment = comment
self.created_at = datetime.utcnow()