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>
185 lines
4.7 KiB
Python
185 lines
4.7 KiB
Python
"""Law API endpoints."""
|
|
from typing import Optional
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.database import get_db
|
|
from app.models.law import LawType, LawStatus
|
|
from app.schemas.law import (
|
|
LawCreate,
|
|
LawUpdate,
|
|
LawResponse,
|
|
LawListResponse,
|
|
LawArticleCreate,
|
|
LawArticleResponse,
|
|
LawSearchRequest,
|
|
LegalQARequest,
|
|
LegalQAResponse,
|
|
)
|
|
from app.services.law_service import LawService
|
|
from app.services.llm_service import llm_service
|
|
|
|
router = APIRouter(prefix="/laws", tags=["laws"])
|
|
|
|
|
|
@router.post("", response_model=LawResponse)
|
|
async def create_law(
|
|
law_data: LawCreate,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Create a new law."""
|
|
service = LawService(db)
|
|
law = await service.create_law(
|
|
title=law_data.title,
|
|
law_type=LawType(law_data.law_type.value),
|
|
promulgation_date=law_data.promulgation_date,
|
|
effective_date=law_data.effective_date,
|
|
issuing_authority=law_data.issuing_authority,
|
|
content=law_data.content,
|
|
status=LawStatus(law_data.status.value),
|
|
document_number=law_data.document_number,
|
|
)
|
|
return law
|
|
|
|
|
|
@router.get("", response_model=LawListResponse)
|
|
async def list_laws(
|
|
skip: int = Query(0, ge=0),
|
|
limit: int = Query(20, ge=1, le=100),
|
|
law_type: Optional[str] = Query(None),
|
|
status: Optional[str] = Query(None),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""List laws with optional filters."""
|
|
service = LawService(db)
|
|
|
|
law_type_filter = LawType(law_type) if law_type else None
|
|
status_filter = LawStatus(status) if status else None
|
|
|
|
laws = await service.get_laws_list(
|
|
skip=skip,
|
|
limit=limit,
|
|
law_type=law_type_filter,
|
|
status=status_filter,
|
|
)
|
|
|
|
return LawListResponse(
|
|
items=laws,
|
|
total=len(laws),
|
|
skip=skip,
|
|
limit=limit,
|
|
)
|
|
|
|
|
|
@router.get("/{law_id}", response_model=LawResponse)
|
|
async def get_law(
|
|
law_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Get a law by ID."""
|
|
service = LawService(db)
|
|
law = await service.get_law_by_id(law_id)
|
|
|
|
if not law:
|
|
raise HTTPException(status_code=404, detail="Law not found")
|
|
|
|
return law
|
|
|
|
|
|
@router.put("/{law_id}", response_model=LawResponse)
|
|
async def update_law(
|
|
law_id: int,
|
|
law_data: LawUpdate,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Update a law."""
|
|
service = LawService(db)
|
|
|
|
update_dict = law_data.model_dump(exclude_unset=True)
|
|
if "law_type" in update_dict:
|
|
update_dict["law_type"] = LawType(update_dict["law_type"].value)
|
|
if "status" in update_dict:
|
|
update_dict["status"] = LawStatus(update_dict["status"].value)
|
|
|
|
law = await service.update_law(law_id, **update_dict)
|
|
|
|
if not law:
|
|
raise HTTPException(status_code=404, detail="Law not found")
|
|
|
|
return law
|
|
|
|
|
|
@router.delete("/{law_id}")
|
|
async def delete_law(
|
|
law_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Delete a law."""
|
|
service = LawService(db)
|
|
success = await service.delete_law(law_id)
|
|
|
|
if not success:
|
|
raise HTTPException(status_code=404, detail="Law not found")
|
|
|
|
return {"message": "Law deleted successfully"}
|
|
|
|
|
|
@router.post("/search", response_model=LawListResponse)
|
|
async def search_laws(
|
|
search_data: LawSearchRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Search laws by keyword."""
|
|
service = LawService(db)
|
|
laws = await service.search_laws_by_keyword(
|
|
keyword=search_data.keyword,
|
|
limit=search_data.limit,
|
|
)
|
|
|
|
return LawListResponse(
|
|
items=laws,
|
|
total=len(laws),
|
|
skip=0,
|
|
limit=search_data.limit,
|
|
)
|
|
|
|
|
|
@router.post("/qa", response_model=LegalQAResponse)
|
|
async def legal_qa(
|
|
qa_data: LegalQARequest,
|
|
):
|
|
"""Ask a legal question and get AI-powered answer."""
|
|
answer = await llm_service.legal_qa(
|
|
question=qa_data.question,
|
|
context=qa_data.context,
|
|
)
|
|
|
|
return LegalQAResponse(
|
|
question=qa_data.question,
|
|
answer=answer,
|
|
references=[],
|
|
)
|
|
|
|
|
|
@router.post("/{law_id}/articles", response_model=LawArticleResponse)
|
|
async def create_article(
|
|
law_id: int,
|
|
article_data: LawArticleCreate,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Create a law article."""
|
|
service = LawService(db)
|
|
|
|
# Verify law exists
|
|
law = await service.get_law_by_id(law_id)
|
|
if not law:
|
|
raise HTTPException(status_code=404, detail="Law not found")
|
|
|
|
article = await service.create_article(
|
|
law_id=law_id,
|
|
article_number=article_data.article_number,
|
|
content=article_data.content,
|
|
)
|
|
|
|
return article
|