@@ -0,0 +1,190 @@
|
||||
"""Уведомления центра зоотехника при событиях синхронизации."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict, Iterable, List, Optional, Set
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from app import db
|
||||
from app.models import (
|
||||
Ingredient,
|
||||
Recipe,
|
||||
SyncClient,
|
||||
SyncClientDisplayName,
|
||||
UnloadingGroup,
|
||||
)
|
||||
from app.models.report import LoadingReport, UnloadingReport
|
||||
from app.models.sync import SyncQueue
|
||||
from app.services.notification_center_service import create_notification, format_detail_timestamp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
RECIPE_DELIVERY_TABLES = frozenset(
|
||||
{"recipe", "ingredient", "unloading_group", "period_recipes"}
|
||||
)
|
||||
REPORT_PARENT_TABLES = frozenset({"loading_report", "unloading_report"})
|
||||
|
||||
|
||||
def resolve_sync_client_label(node_id: Optional[str]) -> str:
|
||||
if not node_id:
|
||||
return "терминал"
|
||||
display = db.session.execute(
|
||||
select(SyncClientDisplayName.display_name).where(
|
||||
SyncClientDisplayName.node_id == node_id
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
if display and str(display).strip():
|
||||
return str(display).strip()
|
||||
client_name = db.session.execute(
|
||||
select(SyncClient.client_name).where(SyncClient.node_id == node_id)
|
||||
).scalar_one_or_none()
|
||||
if client_name and str(client_name).strip():
|
||||
return str(client_name).strip()
|
||||
return f"терминал {node_id[:8]}"
|
||||
|
||||
|
||||
def _q(name: Optional[str]) -> str:
|
||||
text = (name or "").strip()
|
||||
return f"«{text}»" if text else "«без названия»"
|
||||
|
||||
|
||||
def _recipe_name(recipe_id: Optional[str]) -> str:
|
||||
if not recipe_id:
|
||||
return "без названия"
|
||||
recipe = db.session.get(Recipe, recipe_id)
|
||||
if recipe and not getattr(recipe, "is_deleted", False):
|
||||
return recipe.name or "без названия"
|
||||
return "без названия"
|
||||
|
||||
|
||||
def _recipe_id_from_sync_task(task: SyncQueue) -> Optional[str]:
|
||||
table_name = task.table_name
|
||||
record_id = task.record_id
|
||||
if table_name == "recipe":
|
||||
return record_id
|
||||
if table_name == "ingredient":
|
||||
row = db.session.get(Ingredient, record_id)
|
||||
return row.recipe_id if row else None
|
||||
if table_name == "unloading_group":
|
||||
row = db.session.get(UnloadingGroup, record_id)
|
||||
return row.recipe_id if row else None
|
||||
if table_name == "period_recipes":
|
||||
parts = str(record_id).split(":", 1)
|
||||
return parts[1] if len(parts) == 2 else None
|
||||
return None
|
||||
|
||||
|
||||
def notify_tasks_delivered_to_client(
|
||||
client_node_id: str, tasks: Iterable[SyncQueue]
|
||||
) -> None:
|
||||
"""Рейс (или его состав) доставлен на терминал клиента."""
|
||||
terminal = resolve_sync_client_label(client_node_id)
|
||||
when = format_detail_timestamp()
|
||||
notified: Set[str] = set()
|
||||
for task in tasks:
|
||||
if task.table_name not in RECIPE_DELIVERY_TABLES:
|
||||
continue
|
||||
recipe_id = _recipe_id_from_sync_task(task)
|
||||
if not recipe_id or recipe_id in notified:
|
||||
continue
|
||||
notified.add(recipe_id)
|
||||
recipe_label = _q(_recipe_name(recipe_id))
|
||||
try:
|
||||
create_notification(
|
||||
title="Рейс на терминале",
|
||||
detail=(
|
||||
f"Рейс {recipe_label} передан на терминал {terminal} — {when}"
|
||||
),
|
||||
kind="info",
|
||||
category="sync",
|
||||
page="recipes",
|
||||
link_kind="recipe",
|
||||
link_id=recipe_id,
|
||||
)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"[SYNC-NOTIFY] recipe delivered client=%s recipe=%s",
|
||||
client_node_id,
|
||||
recipe_id,
|
||||
)
|
||||
|
||||
|
||||
def _report_notification_from_row(
|
||||
*,
|
||||
table_name: str,
|
||||
report_id: str,
|
||||
client_node_id: str,
|
||||
) -> None:
|
||||
terminal = resolve_sync_client_label(client_node_id)
|
||||
when = format_detail_timestamp()
|
||||
if table_name == "loading_report":
|
||||
report = db.session.get(LoadingReport, report_id)
|
||||
if not report or getattr(report, "is_deleted", False):
|
||||
return
|
||||
recipe_label = _q(report.recipe_name or _recipe_name(report.recipe_id))
|
||||
create_notification(
|
||||
title="Отчёт о загрузке",
|
||||
detail=(
|
||||
f"С терминала {terminal} пришёл отчёт о загрузке по рейсу {recipe_label} — {when}"
|
||||
),
|
||||
kind="success",
|
||||
category="sync",
|
||||
page="reports",
|
||||
link_kind="report_loading",
|
||||
link_id=report_id,
|
||||
)
|
||||
return
|
||||
|
||||
if table_name == "unloading_report":
|
||||
report = db.session.get(UnloadingReport, report_id)
|
||||
if not report or getattr(report, "is_deleted", False):
|
||||
return
|
||||
recipe_label = _q(report.recipe_name or _recipe_name(report.recipe_id))
|
||||
link_id = report.loading_report_id or report_id
|
||||
create_notification(
|
||||
title="Отчёт о выгрузке",
|
||||
detail=(
|
||||
f"С терминала {terminal} пришёл отчёт о выгрузке по рейсу {recipe_label} — {when}"
|
||||
),
|
||||
kind="success",
|
||||
category="sync",
|
||||
page="reports",
|
||||
link_kind="report_loading",
|
||||
link_id=link_id,
|
||||
)
|
||||
|
||||
|
||||
def notify_reports_pushed_from_client(
|
||||
client_node_id: str, applied_changes: Iterable[Dict[str, Any]]
|
||||
) -> None:
|
||||
"""Отчёт с полевого терминала принят на сервер."""
|
||||
notified: Set[str] = set()
|
||||
for change in applied_changes:
|
||||
table_name = change.get("table_name")
|
||||
if table_name not in REPORT_PARENT_TABLES:
|
||||
continue
|
||||
action = change.get("action")
|
||||
if action not in ("create", "update"):
|
||||
continue
|
||||
record_id = change.get("record_id")
|
||||
if not record_id:
|
||||
continue
|
||||
key = f"{table_name}:{record_id}"
|
||||
if key in notified:
|
||||
continue
|
||||
notified.add(key)
|
||||
try:
|
||||
_report_notification_from_row(
|
||||
table_name=table_name,
|
||||
report_id=record_id,
|
||||
client_node_id=client_node_id,
|
||||
)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"[SYNC-NOTIFY] report received client=%s table=%s id=%s",
|
||||
client_node_id,
|
||||
table_name,
|
||||
record_id,
|
||||
)
|
||||
Reference in New Issue
Block a user