feat: 设备模具统计按归属日期汇总

This commit is contained in:
souplearn
2026-07-25 05:28:44 +08:00
parent 9674554b98
commit 37f2b2dcb2
3 changed files with 256 additions and 68 deletions
+47 -4
View File
@@ -1,6 +1,7 @@
import json
import re
from dataclasses import dataclass, field
from types import SimpleNamespace
from typing import Any
from app.services.cleaning import is_cleaning_item
@@ -33,6 +34,7 @@ class UsageStatRow:
object_type: str
value: float = 0
report_count: int = 0
report_ids: set[int] = field(default_factory=set, repr=False)
tags: list[str] = field(default_factory=list)
attendance_point_name: str = ""
product_name: str = ""
@@ -58,6 +60,7 @@ class UsageStatAccumulator:
product_name: str = "",
process_name: str = "",
stamping_method: str = "",
report_id: int | None = None,
) -> None:
row = self._rows.get(key)
if row is None:
@@ -86,7 +89,11 @@ class UsageStatAccumulator:
row.object_type = object_type
row.value += as_float(value)
row.report_count += 1
if report_id is None:
row.report_count += 1
elif report_id not in row.report_ids:
row.report_ids.add(report_id)
row.report_count += 1
for tag in tags or []:
if tag and tag not in row.tags:
row.tags.append(tag)
@@ -119,12 +126,42 @@ def build_usage_stats(
raise ValueError("category must be device or mold")
def build_usage_stats_from_allocations(
*,
allocations: list,
category: str,
equipment_type_by_key: dict[tuple[str, str], str],
) -> list[UsageStatRow]:
reports = []
for allocation in allocations:
item = allocation.item
if item is None:
continue
proxy = SimpleNamespace(
attendance_point_name=item.attendance_point_name,
product_name=item.product_name,
process_name=item.process_name,
stamping_method=item.stamping_method,
operator_count=item.operator_count,
device_no=item.device_no,
allocated_minutes=allocation.effective_minutes,
good_qty=allocation.good_qty,
report_id=allocation.report_id,
)
reports.append(SimpleNamespace(items=[proxy]))
return build_usage_stats(
reports=reports,
category=category,
equipment_type_by_key=equipment_type_by_key,
)
def _build_device_usage_stats(
reports: list,
equipment_type_by_key: dict[tuple[str, str], str],
) -> list[UsageStatRow]:
acc = UsageStatAccumulator(category="device")
for item in _iter_report_items(reports):
for item, report_id in _iter_report_items(reports):
if is_misc_item(item):
continue
@@ -144,6 +181,7 @@ def _build_device_usage_stats(
value=value,
tags=[TAG_CLEANING],
attendance_point_name=point_name,
report_id=report_id,
)
continue
@@ -157,13 +195,14 @@ def _build_device_usage_stats(
object_type=equipment_type_by_key.get((point_name, device_no), DEVICE_TYPE_STAMPING),
value=as_float(getattr(item, "allocated_minutes", 0)),
attendance_point_name=point_name,
report_id=report_id,
)
return acc.rows()
def _build_mold_usage_stats(reports: list) -> list[UsageStatRow]:
acc = UsageStatAccumulator(category="mold")
for item in _iter_report_items(reports):
for item, report_id in _iter_report_items(reports):
if is_misc_item(item):
continue
@@ -187,13 +226,17 @@ def _build_mold_usage_stats(reports: list) -> list[UsageStatRow]:
product_name=product_name,
process_name=process_name,
stamping_method=stamping_method,
report_id=report_id,
)
return acc.rows()
def _iter_report_items(reports: list):
for report in reports:
yield from getattr(report, "items", []) or []
report_id = getattr(report, "id", None)
for item in getattr(report, "items", []) or []:
item_report_id = getattr(item, "report_id", None)
yield item, item_report_id if item_report_id is not None else report_id
def _item_tags(item) -> list[str]: