fix install diogram

This commit is contained in:
2026-04-02 12:00:11 +05:00
parent 22bbc3a0a6
commit 8a189e310b
23 changed files with 1212 additions and 189 deletions

View File

@@ -24,6 +24,7 @@ from sqlalchemy.orm import Session as SASession
from bd import make_engine
from bd.tables.response import Response, Answer
from bd.tables.poll import Poll
from bd.tables.scale import ScaleDimension, ChoiceScore
from bd.tables.radar_result import RadarResult, RadarResultItem
from route.radar_svg_gen import save_radar_svg
@@ -110,7 +111,8 @@ def compute_radar(response_id: uuid.UUID, session: SASession) -> RadarResult:
session.add(result)
session.flush() # чтобы получить result.id до добавления items
for dim in sorted(dimensions, key=lambda d: (d.position is None, d.position)):
dims_sorted = sorted(dimensions, key=lambda d: (d.position is None, d.position))
for dim in dims_sorted:
item = RadarResultItem(
result_id=result.id,
dimension_id=dim.id,
@@ -121,29 +123,35 @@ def compute_radar(response_id: uuid.UUID, session: SASession) -> RadarResult:
)
session.add(item)
session.commit()
session.refresh(result)
# Сохраняем SVG-файл на сервере
# Генерируем SVG до коммита — result.id уже известен после flush(),
# items_data строим из scores_map/dimensions (они в памяти, не из БД).
# Так result + items + image_path фиксируются в одной транзакции.
items_data = [
{
"dimension_name": item.dimension_name,
"dimension_color": item.dimension_color,
"value": item.value,
"dimension_name": dim.name,
"dimension_color": dim.color,
"value": scores_map[dim.id],
}
for item in sorted(result.items, key=lambda i: (i.dimension_position is None, i.dimension_position))
for dim in dims_sorted
]
max_val = max((it["value"] for it in items_data), default=1.0)
max_val = max(max_val, 14.0)
try:
rel_path = save_radar_svg(str(result.id), items_data, max_val)
with session.begin_nested():
result.image_path = rel_path
session.commit()
session.refresh(result)
poll = session.get(Poll, resp.poll_id)
poll_title = poll.title if poll else "unknown"
user_id_str = str(resp.user_id) if resp.user_id else "anonymous"
rel_path = save_radar_svg(
str(result.id), items_data,
max_val=0.0, # автомасштаб: шкала строится от данных
poll_title=poll_title,
user_id=user_id_str,
scale_padding=0.2, # +20% запаса над максимумом
min_scale=5.0, # шкала не менее 5 даже для малых значений
)
result.image_path = rel_path
except Exception:
pass # не ломаем сдачу, если запись файла не удалась
pass # не ломаем сдачу, если генерация файла не удалась
session.commit() # единственный коммит: result + items + image_path
session.refresh(result)
return result
@@ -186,7 +194,7 @@ def get_radar_result(response_id: str):
@router.get("/responses/{response_id}/radar/image")
def get_radar_image(response_id: str):
"""Отдаёт SVG-файл диаграммы для скачивания."""
"""Отдаёт SVG-файл диаграммы. Если файл отсутствует — пересчитывает автоматически."""
Session = get_session()
try:
rid = uuid.UUID(response_id)
@@ -198,16 +206,21 @@ def get_radar_image(response_id: str):
.filter(RadarResult.response_id == rid)
.first()
)
if not result or not result.image_path:
raise HTTPException(status_code=404, detail="Image not found")
if not result:
raise HTTPException(status_code=404, detail="Radar result not found. Submit the response first.")
app_path = os.getenv("APP_PATH", "/app")
full_path = os.path.join(app_path, result.image_path)
if not os.path.isfile(full_path):
raise HTTPException(status_code=404, detail="Image file missing on server")
full_path = os.path.join(app_path, result.image_path) if result.image_path else None
# Файл удалён или отсутствует — пересчитываем с актуальным алгоритмом
if not full_path or not os.path.isfile(full_path):
result = compute_radar(rid, session)
full_path = os.path.join(app_path, result.image_path) if result.image_path else None
if not full_path or not os.path.isfile(full_path):
raise HTTPException(status_code=500, detail="Failed to generate radar image")
return FileResponse(
full_path,
media_type="image/svg+xml",
filename=f"radar_{response_id}.svg",
headers={"Content-Disposition": f'attachment; filename="radar_{response_id}.svg"'},
)
@@ -237,9 +250,8 @@ def get_user_radar_history(user_id: str):
def _result_to_out(result: RadarResult) -> RadarResultOut:
image_url: Optional[str] = None
if result.image_path:
image_url = f"/responses/{result.response_id}/radar/image"
# image_url возвращаем всегда — endpoint пересоздаст SVG если файл отсутствует
image_url = f"/responses/{result.response_id}/radar/image"
return RadarResultOut(
id=str(result.id),
response_id=str(result.response_id),