@@ -0,0 +1,317 @@
|
||||
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
|
||||
],
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user