228 lines
8.2 KiB
Python
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()
|