feat: protocol and template API endpoints, auto-pipeline on upload
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from sqlalchemy import func, select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.orm import selectinload
|
||||||
|
|
||||||
|
from app.api.deps import get_current_user
|
||||||
|
from app.database import get_db
|
||||||
|
from app.models.protocol import Protocol
|
||||||
|
from app.models.user import User
|
||||||
|
from app.schemas.protocol import ProtocolList, ProtocolRegenerate, ProtocolResponse
|
||||||
|
from app.tasks.protocol import generate_protocol
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api/protocols", tags=["protocols"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", response_model=ProtocolList)
|
||||||
|
async def list_protocols(
|
||||||
|
skip: int = 0,
|
||||||
|
limit: int = 50,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
total = (await db.execute(select(func.count(Protocol.id)))).scalar()
|
||||||
|
result = await db.execute(
|
||||||
|
select(Protocol)
|
||||||
|
.order_by(Protocol.created_at.desc())
|
||||||
|
.offset(skip)
|
||||||
|
.limit(limit)
|
||||||
|
)
|
||||||
|
items = list(result.scalars().all())
|
||||||
|
return ProtocolList(items=items, total=total)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{protocol_id}", response_model=ProtocolResponse)
|
||||||
|
async def get_protocol(
|
||||||
|
protocol_id: uuid.UUID,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
result = await db.execute(
|
||||||
|
select(Protocol).where(Protocol.id == protocol_id)
|
||||||
|
)
|
||||||
|
protocol = result.scalar_one_or_none()
|
||||||
|
if not protocol:
|
||||||
|
raise HTTPException(404, "Protocol not found")
|
||||||
|
return protocol
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{protocol_id}/regenerate", status_code=202)
|
||||||
|
async def regenerate_protocol(
|
||||||
|
protocol_id: uuid.UUID,
|
||||||
|
data: ProtocolRegenerate,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
result = await db.execute(
|
||||||
|
select(Protocol).where(Protocol.id == protocol_id)
|
||||||
|
)
|
||||||
|
protocol = result.scalar_one_or_none()
|
||||||
|
if not protocol:
|
||||||
|
raise HTTPException(404, "Protocol not found")
|
||||||
|
|
||||||
|
template_id = str(data.template_id) if data.template_id else None
|
||||||
|
generate_protocol.delay(str(protocol.transcription_id), template_id)
|
||||||
|
return {"message": "Regeneration started"}
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{protocol_id}", response_model=ProtocolResponse)
|
||||||
|
async def update_protocol_content(
|
||||||
|
protocol_id: uuid.UUID,
|
||||||
|
content: dict,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
result = await db.execute(
|
||||||
|
select(Protocol).where(Protocol.id == protocol_id)
|
||||||
|
)
|
||||||
|
protocol = result.scalar_one_or_none()
|
||||||
|
if not protocol:
|
||||||
|
raise HTTPException(404, "Protocol not found")
|
||||||
|
|
||||||
|
protocol.content = content
|
||||||
|
await db.commit()
|
||||||
|
await db.refresh(protocol)
|
||||||
|
return protocol
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{protocol_id}", status_code=204)
|
||||||
|
async def delete_protocol(
|
||||||
|
protocol_id: uuid.UUID,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
result = await db.execute(
|
||||||
|
select(Protocol).where(Protocol.id == protocol_id)
|
||||||
|
)
|
||||||
|
protocol = result.scalar_one_or_none()
|
||||||
|
if not protocol:
|
||||||
|
raise HTTPException(404, "Protocol not found")
|
||||||
|
await db.delete(protocol)
|
||||||
|
await db.commit()
|
||||||
@@ -19,6 +19,7 @@ from app.services.recording import (
|
|||||||
get_recordings,
|
get_recordings,
|
||||||
get_upload_path,
|
get_upload_path,
|
||||||
)
|
)
|
||||||
|
from app.tasks.transcription import transcribe_recording
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/recordings", tags=["recordings"])
|
router = APIRouter(prefix="/api/recordings", tags=["recordings"])
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ async def upload_recording(
|
|||||||
source=RecordingSource.web,
|
source=RecordingSource.web,
|
||||||
duration=duration,
|
duration=duration,
|
||||||
)
|
)
|
||||||
|
transcribe_recording.delay(str(recording.id))
|
||||||
return recording
|
return recording
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.api.deps import get_current_user
|
||||||
|
from app.database import get_db
|
||||||
|
from app.models.prompt_template import PromptTemplate
|
||||||
|
from app.models.user import User
|
||||||
|
from app.schemas.prompt_template import TemplateCreate, TemplateResponse, TemplateUpdate
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api/templates", tags=["templates"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", response_model=list[TemplateResponse])
|
||||||
|
async def list_templates(
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
result = await db.execute(
|
||||||
|
select(PromptTemplate).order_by(PromptTemplate.created_at)
|
||||||
|
)
|
||||||
|
return list(result.scalars().all())
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("", response_model=TemplateResponse, status_code=201)
|
||||||
|
async def create_template(
|
||||||
|
data: TemplateCreate,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
template = PromptTemplate(**data.model_dump())
|
||||||
|
db.add(template)
|
||||||
|
await db.commit()
|
||||||
|
await db.refresh(template)
|
||||||
|
return template
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{template_id}", response_model=TemplateResponse)
|
||||||
|
async def get_template(
|
||||||
|
template_id: uuid.UUID,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
result = await db.execute(
|
||||||
|
select(PromptTemplate).where(PromptTemplate.id == template_id)
|
||||||
|
)
|
||||||
|
template = result.scalar_one_or_none()
|
||||||
|
if not template:
|
||||||
|
raise HTTPException(404, "Template not found")
|
||||||
|
return template
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{template_id}", response_model=TemplateResponse)
|
||||||
|
async def update_template(
|
||||||
|
template_id: uuid.UUID,
|
||||||
|
data: TemplateUpdate,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
result = await db.execute(
|
||||||
|
select(PromptTemplate).where(PromptTemplate.id == template_id)
|
||||||
|
)
|
||||||
|
template = result.scalar_one_or_none()
|
||||||
|
if not template:
|
||||||
|
raise HTTPException(404, "Template not found")
|
||||||
|
|
||||||
|
for field, value in data.model_dump(exclude_unset=True).items():
|
||||||
|
setattr(template, field, value)
|
||||||
|
await db.commit()
|
||||||
|
await db.refresh(template)
|
||||||
|
return template
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{template_id}", status_code=204)
|
||||||
|
async def delete_template(
|
||||||
|
template_id: uuid.UUID,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
result = await db.execute(
|
||||||
|
select(PromptTemplate).where(PromptTemplate.id == template_id)
|
||||||
|
)
|
||||||
|
template = result.scalar_one_or_none()
|
||||||
|
if not template:
|
||||||
|
raise HTTPException(404, "Template not found")
|
||||||
|
await db.delete(template)
|
||||||
|
await db.commit()
|
||||||
@@ -1,19 +1,38 @@
|
|||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
from app.api.auth import router as auth_router
|
from app.api.auth import router as auth_router
|
||||||
|
from app.api.protocols import router as protocols_router
|
||||||
from app.api.recordings import router as recordings_router
|
from app.api.recordings import router as recordings_router
|
||||||
|
from app.api.templates import router as templates_router
|
||||||
|
from app.database import async_session
|
||||||
|
from app.models.prompt_template import PromptTemplate
|
||||||
|
from app.prompts.defaults import DEFAULT_TEMPLATES
|
||||||
|
|
||||||
|
|
||||||
|
async def seed_default_templates():
|
||||||
|
async with async_session() as db:
|
||||||
|
result = await db.execute(select(PromptTemplate))
|
||||||
|
if result.scalars().first() is not None:
|
||||||
|
return
|
||||||
|
for tpl in DEFAULT_TEMPLATES:
|
||||||
|
db.add(PromptTemplate(**tpl))
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
|
await seed_default_templates()
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(title="Meeting Protocol Service", lifespan=lifespan)
|
app = FastAPI(title="Meeting Protocol Service", lifespan=lifespan)
|
||||||
app.include_router(auth_router)
|
app.include_router(auth_router)
|
||||||
app.include_router(recordings_router)
|
app.include_router(recordings_router)
|
||||||
|
app.include_router(protocols_router)
|
||||||
|
app.include_router(templates_router)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/health")
|
@app.get("/api/health")
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ def transcribe_recording(self, recording_id: str):
|
|||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(transcription)
|
db.refresh(transcription)
|
||||||
|
|
||||||
|
from app.tasks.protocol import generate_protocol
|
||||||
|
generate_protocol.delay(str(transcription.id))
|
||||||
|
|
||||||
return {"transcription_id": str(transcription.id), "status": "done"}
|
return {"transcription_id": str(transcription.id), "status": "done"}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user