Files
api-copp/route/scale_crud.py
2026-04-01 14:18:52 +05:00

228 lines
8.2 KiB
Python

"""
CRUD для осей (ScaleDimension) и расшифровок вариантов ответов (ChoiceScore).
Эндпоинты:
POST /polls/{poll_id}/dimensions — создать ось для теста
GET /polls/{poll_id}/dimensions — список осей теста
PUT /dimensions/{dimension_id} — изменить ось
DELETE /dimensions/{dimension_id} — удалить ось
POST /dimensions/{dimension_id}/scores — задать расшифровку для варианта ответа
GET /dimensions/{dimension_id}/scores — список расшифровок по оси
GET /choices/{choice_id}/scores — расшифровки конкретного варианта
DELETE /choice-scores/{score_id} — удалить расшифровку
"""
import uuid
from typing import List, Optional
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from bd import make_engine
from bd.tables.poll import Poll
from bd.tables.choice import Choice
from bd.tables.scale import ScaleDimension, ChoiceScore
router = APIRouter(tags=["scale"])
def get_session():
from sqlalchemy.orm import sessionmaker
return sessionmaker(bind=make_engine(), future=True)
# ---------------------------------------------------------------------------
# Схемы
# ---------------------------------------------------------------------------
class DimensionIn(BaseModel):
name: str
description: Optional[str] = None
color: Optional[str] = None
position: Optional[int] = None
class DimensionOut(BaseModel):
id: str
poll_id: str
name: str
description: Optional[str]
color: Optional[str]
position: Optional[int]
class ChoiceScoreIn(BaseModel):
choice_id: str
score: float = 1.0
class ChoiceScoreOut(BaseModel):
id: str
choice_id: str
dimension_id: str
score: float
# ---------------------------------------------------------------------------
# Оси (ScaleDimension)
# ---------------------------------------------------------------------------
@router.post("/polls/{poll_id}/dimensions", response_model=DimensionOut, status_code=201,
tags=["scale"])
def create_dimension(poll_id: str, payload: DimensionIn):
Session = get_session()
try:
pid = uuid.UUID(poll_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid poll UUID")
with Session() as session:
if not session.get(Poll, pid):
raise HTTPException(status_code=404, detail="Poll not found")
dim = ScaleDimension(
poll_id=pid,
name=payload.name,
description=payload.description,
color=payload.color,
position=payload.position,
)
session.add(dim)
session.commit()
session.refresh(dim)
return DimensionOut(
id=str(dim.id), poll_id=str(dim.poll_id),
name=dim.name, description=dim.description,
color=dim.color, position=dim.position,
)
@router.get("/polls/{poll_id}/dimensions", response_model=List[DimensionOut],
tags=["scale"])
def list_dimensions(poll_id: str):
Session = get_session()
try:
pid = uuid.UUID(poll_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid poll UUID")
with Session() as session:
dims = (
session.query(ScaleDimension)
.filter(ScaleDimension.poll_id == pid)
.order_by(ScaleDimension.position.asc().nullslast())
.all()
)
return [
DimensionOut(id=str(d.id), poll_id=str(d.poll_id),
name=d.name, description=d.description,
color=d.color, position=d.position)
for d in dims
]
@router.put("/dimensions/{dimension_id}", response_model=DimensionOut, tags=["scale"])
def update_dimension(dimension_id: str, payload: DimensionIn):
Session = get_session()
try:
did = uuid.UUID(dimension_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid dimension UUID")
with Session() as session:
dim = session.get(ScaleDimension, did)
if not dim:
raise HTTPException(status_code=404, detail="Dimension not found")
dim.name = payload.name
dim.description = payload.description
dim.color = payload.color
dim.position = payload.position
session.commit()
session.refresh(dim)
return DimensionOut(id=str(dim.id), poll_id=str(dim.poll_id),
name=dim.name, description=dim.description,
color=dim.color, position=dim.position)
@router.delete("/dimensions/{dimension_id}", status_code=204, tags=["scale"])
def delete_dimension(dimension_id: str):
Session = get_session()
try:
did = uuid.UUID(dimension_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid dimension UUID")
with Session() as session:
dim = session.get(ScaleDimension, did)
if not dim:
raise HTTPException(status_code=404, detail="Dimension not found")
session.delete(dim)
session.commit()
# ---------------------------------------------------------------------------
# Расшифровки вариантов ответов (ChoiceScore)
# ---------------------------------------------------------------------------
@router.post("/dimensions/{dimension_id}/scores", response_model=ChoiceScoreOut,
status_code=201, tags=["scale"])
def create_choice_score(dimension_id: str, payload: ChoiceScoreIn):
Session = get_session()
try:
did = uuid.UUID(dimension_id)
cid = uuid.UUID(payload.choice_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid UUID")
with Session() as session:
if not session.get(ScaleDimension, did):
raise HTTPException(status_code=404, detail="Dimension not found")
if not session.get(Choice, cid):
raise HTTPException(status_code=404, detail="Choice not found")
cs = ChoiceScore(choice_id=cid, dimension_id=did, score=payload.score)
session.add(cs)
session.commit()
session.refresh(cs)
return ChoiceScoreOut(id=str(cs.id), choice_id=str(cs.choice_id),
dimension_id=str(cs.dimension_id), score=cs.score)
@router.get("/dimensions/{dimension_id}/scores", response_model=List[ChoiceScoreOut],
tags=["scale"])
def list_scores_by_dimension(dimension_id: str):
Session = get_session()
try:
did = uuid.UUID(dimension_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid dimension UUID")
with Session() as session:
rows = session.query(ChoiceScore).filter(ChoiceScore.dimension_id == did).all()
return [ChoiceScoreOut(id=str(r.id), choice_id=str(r.choice_id),
dimension_id=str(r.dimension_id), score=r.score)
for r in rows]
@router.get("/choices/{choice_id}/scores", response_model=List[ChoiceScoreOut],
tags=["scale"])
def list_scores_by_choice(choice_id: str):
Session = get_session()
try:
cid = uuid.UUID(choice_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid choice UUID")
with Session() as session:
rows = session.query(ChoiceScore).filter(ChoiceScore.choice_id == cid).all()
return [ChoiceScoreOut(id=str(r.id), choice_id=str(r.choice_id),
dimension_id=str(r.dimension_id), score=r.score)
for r in rows]
@router.delete("/choice-scores/{score_id}", status_code=204, tags=["scale"])
def delete_choice_score(score_id: str):
Session = get_session()
try:
sid = uuid.UUID(score_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid UUID")
with Session() as session:
cs = session.get(ChoiceScore, sid)
if not cs:
raise HTTPException(status_code=404, detail="Score not found")
session.delete(cs)
session.commit()