Files
site/WESP_REL/app/routes/reports_legacy.py
T
2026-07-17 12:57:18 +03:00

318 lines
11 KiB
Python

import logging
from datetime import datetime
from flask import Blueprint, jsonify, request
logger = logging.getLogger(__name__)
from sqlalchemy import func, select
from app import db
from app.models import (
ComponentLoadingTime,
LoadingReport,
LoadingReportComponent,
Recipe,
UnloadingReport,
UnloadingReportGroup,
)
from app.routes.auth_decorators import require_auth, require_paired_terminal
bp = Blueprint("reports_legacy", __name__, url_prefix="/api")
def _error(message: str, status_code: int = 400):
return jsonify({"error": True, "message": message}), status_code
@bp.post("/save_report")
@require_paired_terminal
def save_report_legacy():
data = request.get_json() or {}
recipe_id = data.get("recipe_id")
if not recipe_id:
return _error("Не указан ID рецепта", 400)
recipe = db.session.execute(
select(Recipe).where(Recipe.id == recipe_id, Recipe.is_deleted.is_(False))
).scalar_one_or_none()
if not recipe:
return _error("Рецепт не найден", 404)
try:
total_weight = float(data.get("total_weight", 0))
target_mixing_time = int(data.get("target_mixing_time", 0))
actual_mixing_time = int(data.get("actual_mixing_time", 0))
except (TypeError, ValueError):
return _error("Некорректные числовые поля отчёта", 400)
components = data.get("components", []) or []
if not components:
return _error("Нет данных о компонентах", 400)
report = LoadingReport(
recipe_id=recipe_id,
recipe_name=recipe.name,
start_time=datetime.now(),
end_time=datetime.now(),
target_mixing_time=target_mixing_time,
actual_mixing_time=actual_mixing_time,
total_weight=total_weight,
dispenser_type=(data.get("dispenser_type") or "dispenser"),
created_by="system",
updated_by="system",
)
db.session.add(report)
db.session.flush()
for idx, comp in enumerate(components, start=1):
db.session.add(
LoadingReportComponent(
report_id=report.id,
component_id=comp.get("component_id") or comp.get("componentId"),
component_name=str(comp.get("name") or comp.get("component_name") or "—"),
target_weight=float(comp.get("target_weight", 0) or 0),
actual_weight=float(comp.get("actual_weight", 0) or 0),
overload=float(comp.get("overload", 0) or 0),
loading_order=idx,
created_by="system",
updated_by="system",
)
)
for item in data.get("component_loading_times", []) or []:
try:
start_time = datetime.fromisoformat(item["start_time"])
end_time = datetime.fromisoformat(item["end_time"])
except Exception:
continue
db.session.add(
ComponentLoadingTime(
report_id=report.id,
component_name=str(item.get("component_name") or "—"),
start_time=start_time,
end_time=end_time,
loading_duration=float(item.get("loading_duration", 0) or 0),
loading_order=int(item.get("loading_order", 0) or 0),
created_by="system",
updated_by="system",
)
)
db.session.commit()
try:
from app.services.feed_quality.evaluator import evaluate_loading_report
evaluate_loading_report(report.id, send_notifications=True)
except Exception:
logger.exception("[FEED-QUALITY] save_report evaluate failed report_id=%s", report.id)
return jsonify({"status": "success", "message": "Отчёт успешно сохранён", "report_id": report.id})
@bp.get("/consumption_by_component")
@require_auth
def consumption_by_component():
rows = db.session.execute(
select(
LoadingReportComponent.component_id,
LoadingReportComponent.component_name,
func.coalesce(
func.sum(
LoadingReportComponent.actual_weight
+ func.coalesce(LoadingReportComponent.overload, 0)
),
0,
).label("total_actual_weight"),
func.count(LoadingReportComponent.id).label("report_count"),
)
.join(LoadingReport, LoadingReport.id == LoadingReportComponent.report_id)
.where(LoadingReportComponent.is_deleted.is_(False))
.where(LoadingReport.is_deleted.is_(False))
.group_by(
LoadingReportComponent.component_id,
LoadingReportComponent.component_name,
)
).all()
return jsonify(
[
{
"component_id": r.component_id,
"component_name": r.component_name or "—",
"total_actual_weight": round(float(r.total_actual_weight or 0), 2),
"report_count": int(r.report_count or 0),
}
for r in rows
]
)
@bp.get("/reports/<string:report_id>/loading_times")
@require_auth
def report_loading_times(report_id: str):
report = db.session.execute(
select(LoadingReport).where(LoadingReport.id == report_id, LoadingReport.is_deleted.is_(False))
).scalar_one_or_none()
if not report:
return _error("Отчёт не найден", 404)
rows = db.session.execute(
select(ComponentLoadingTime)
.where(
ComponentLoadingTime.report_id == report_id,
ComponentLoadingTime.is_deleted.is_(False),
)
.order_by(ComponentLoadingTime.loading_order.asc())
).scalars().all()
return jsonify(
{
"status": "success",
"loading_times": [
{
"component_name": lt.component_name,
"start_time": lt.start_time.isoformat() if lt.start_time else None,
"end_time": lt.end_time.isoformat() if lt.end_time else None,
"loading_duration": float(lt.loading_duration or 0),
"loading_order": lt.loading_order,
}
for lt in rows
],
}
)
@bp.post("/save_unloading_report")
@require_paired_terminal
def save_unloading_report_legacy():
data = request.get_json() or {}
loading_report_id = data.get("loading_report_id")
recipe_id = data.get("recipe_id")
if not loading_report_id:
return _error("Не указан ID отчёта о загрузке", 400)
if not recipe_id:
return _error("Не указан ID рецепта", 400)
recipe = db.session.execute(
select(Recipe).where(Recipe.id == recipe_id, Recipe.is_deleted.is_(False))
).scalar_one_or_none()
if not recipe:
return _error("Рецепт не найден", 404)
report = UnloadingReport(
recipe_id=recipe_id,
recipe_name=recipe.name,
loading_report_id=loading_report_id,
start_time=datetime.now(),
end_time=datetime.now(),
total_weight=float(data.get("total_weight", 0) or 0),
total_unloaded_weight=float(data.get("total_unloaded_weight", 0) or 0),
remaining_weight=float(data.get("remaining_weight", 0) or 0),
created_by="system",
updated_by="system",
)
db.session.add(report)
db.session.flush()
for group in data.get("unloading_groups", []) or []:
db.session.add(
UnloadingReportGroup(
report_id=report.id,
name=str(group.get("name") or "—"),
target_weight=float(group.get("target_weight", 0) or 0),
unloaded_weight=float(group.get("unloaded_weight", 0) or 0),
remaining_weight=float(group.get("remaining_weight", 0) or 0),
distribution_type=str(group.get("distribution_type") or "percent"),
distribution_value=float(group.get("distribution_value", 0) or 0),
order=int(group.get("order", 0) or 0),
created_by="system",
updated_by="system",
)
)
db.session.commit()
try:
from app.services.feed_quality.evaluator import evaluate_unloading_report
evaluate_unloading_report(report.id, send_notifications=True)
except Exception:
logger.exception(
"[FEED-QUALITY] save_unloading_report evaluate failed unloading_id=%s", report.id
)
return jsonify(
{
"status": "success",
"message": "Отчёт о выгрузке сохранён",
"unloading_report_id": report.id,
}
)
@bp.get("/unloading_reports")
@require_auth
def unloading_reports_legacy():
rows = db.session.execute(
select(UnloadingReport)
.where(UnloadingReport.is_deleted.is_(False))
.order_by(UnloadingReport.start_time.desc())
).scalars().all()
return jsonify(
[
{
"id": r.id,
"recipe_id": r.recipe_id,
"recipe_name": r.recipe_name,
"loading_report_id": r.loading_report_id,
"start_time": r.start_time.isoformat() if r.start_time else None,
"end_time": r.end_time.isoformat() if r.end_time else None,
"total_weight": r.total_weight,
"total_unloaded_weight": r.total_unloaded_weight,
"remaining_weight": r.remaining_weight,
"unloading_groups": [
{
"name": g.name,
"target_weight": g.target_weight,
"unloaded_weight": g.unloaded_weight,
"remaining_weight": g.remaining_weight,
"distribution_type": g.distribution_type,
"distribution_value": g.distribution_value,
"order": g.order,
}
for g in sorted(r.groups, key=lambda x: x.order)
if not g.is_deleted
],
}
for r in rows
]
)
@bp.get("/reports/<string:report_id>/unloading_groups")
@require_auth
def report_unloading_groups(report_id: str):
report = db.session.execute(
select(UnloadingReport).where(
UnloadingReport.id == report_id, UnloadingReport.is_deleted.is_(False)
)
).scalar_one_or_none()
if not report:
return _error("Отчёт выгрузки не найден", 404)
return jsonify(
{
"status": "success",
"recipe_name": report.recipe_name,
"total_weight": report.total_weight,
"total_unloaded_weight": report.total_unloaded_weight,
"remaining_weight": report.remaining_weight,
"unloading_groups": [
{
"name": g.name,
"target_weight": g.target_weight,
"unloaded_weight": g.unloaded_weight,
"remaining_weight": g.remaining_weight,
"distribution_type": g.distribution_type,
"distribution_value": g.distribution_value,
"order": g.order,
}
for g in sorted(report.groups, key=lambda x: x.order)
if not g.is_deleted
],
}
)