feat: restore source parity and harden agent runtime
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""报表图表数据聚合(对齐 JNPF ChartUtil)"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import defaultdict
|
||||
from decimal import Decimal
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
|
||||
|
||||
def _parse_field(field: Optional[str]) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""alias.field -> (alias, field_name)"""
|
||||
if not field or not isinstance(field, str):
|
||||
return None, None
|
||||
parts = field.split(".", 1)
|
||||
if len(parts) == 2:
|
||||
return parts[0], parts[1]
|
||||
return None, field
|
||||
|
||||
|
||||
def _collect_rows(datasets: Dict[str, List[Dict[str, Any]]], dataset_names: Set[str]) -> List[Dict[str, Any]]:
|
||||
rows: List[Dict[str, Any]] = []
|
||||
for name in dataset_names:
|
||||
rows.extend(datasets.get(name) or [])
|
||||
return rows
|
||||
|
||||
|
||||
def _aggregate(values: List[Any], summary_type: str) -> str:
|
||||
if not values:
|
||||
return ""
|
||||
st = (summary_type or "none").lower()
|
||||
nums: List[Decimal] = []
|
||||
for v in values:
|
||||
try:
|
||||
nums.append(Decimal(str(v)))
|
||||
except Exception:
|
||||
pass
|
||||
if st == "sum" and nums:
|
||||
return str(sum(nums))
|
||||
if st == "avg" and nums:
|
||||
return str(sum(nums) / len(nums))
|
||||
if st == "max" and nums:
|
||||
return str(max(nums))
|
||||
if st == "min" and nums:
|
||||
return str(min(nums))
|
||||
if st == "count":
|
||||
return str(len(values))
|
||||
return str(values[-1]) if values else ""
|
||||
|
||||
|
||||
def _build_field(
|
||||
data_list: List[Dict[str, Any]],
|
||||
classify_key: Optional[str],
|
||||
series_name_key: Optional[str],
|
||||
series_data_key: Optional[str],
|
||||
max_key: Optional[str],
|
||||
summary_type: str,
|
||||
) -> Dict[str, Any]:
|
||||
chart_map: Dict[Any, Dict[Any, List[Any]]] = defaultdict(lambda: defaultdict(list))
|
||||
max_map: Dict[Any, List[Any]] = defaultdict(list)
|
||||
|
||||
for row in data_list:
|
||||
if not classify_key:
|
||||
continue
|
||||
classify = row.get(classify_key)
|
||||
if classify is None:
|
||||
continue
|
||||
value = row.get(series_data_key) if series_data_key else None
|
||||
if value is None:
|
||||
continue
|
||||
series = row.get(series_name_key) if series_name_key else ""
|
||||
chart_map[series][classify].append(value)
|
||||
if max_key:
|
||||
mx = row.get(max_key)
|
||||
if mx is not None:
|
||||
max_map[classify].append(mx)
|
||||
|
||||
series_name_list: List[str] = []
|
||||
classify_map: Dict[Any, List[List[str]]] = defaultdict(list)
|
||||
max_counts = [0]
|
||||
|
||||
for series, classify_name_map in chart_map.items():
|
||||
series_name_list.append(str(series))
|
||||
for classify, value_list in classify_name_map.items():
|
||||
agg = _aggregate(value_list, summary_type)
|
||||
classify_map[classify].append([agg])
|
||||
max_counts.append(len(classify_map[classify]))
|
||||
|
||||
classify_name_list = sorted(str(k) for k in classify_map.keys())
|
||||
max_field_list: List[str] = []
|
||||
for classify in classify_name_list:
|
||||
objects = max_map.get(classify) or [0]
|
||||
max_field_list.append(_aggregate(objects, "max"))
|
||||
|
||||
max_len = max(max_counts) if max_counts else 0
|
||||
series_data_list: List[List[str]] = []
|
||||
for i in range(max_len):
|
||||
row_data: List[str] = []
|
||||
for category in classify_name_list:
|
||||
category_list = classify_map.get(category) or []
|
||||
category_data = category_list[i] if i < len(category_list) else []
|
||||
row_data.append(category_data[0] if category_data else "")
|
||||
series_data_list.append(row_data)
|
||||
|
||||
result: Dict[str, Any] = {
|
||||
"classifyNameField": classify_name_list,
|
||||
"seriesDataField": series_data_list,
|
||||
}
|
||||
if series_name_key:
|
||||
result["seriesNameField"] = series_name_list
|
||||
if max_key:
|
||||
result["maxField"] = max_field_list
|
||||
return result
|
||||
|
||||
|
||||
def _echart_configs_from_cells(cells: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
configs: List[Dict[str, Any]] = []
|
||||
if not cells:
|
||||
return configs
|
||||
for key, store in (
|
||||
("floatEcharts", cells.get("floatEcharts")),
|
||||
("cellEcharts", cells.get("cellEcharts")),
|
||||
):
|
||||
if not isinstance(store, dict):
|
||||
continue
|
||||
for drawing_id, item in store.items():
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
option = item.get("option") or {}
|
||||
configs.append(
|
||||
{
|
||||
"drawingId": item.get("drawingId") or drawing_id,
|
||||
"option": option,
|
||||
"source": key,
|
||||
}
|
||||
)
|
||||
return configs
|
||||
|
||||
|
||||
def build_chart_data(
|
||||
cells: Dict[str, Any],
|
||||
datasets: Dict[str, List[Dict[str, Any]]],
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成预览用 chartData 列表。
|
||||
每项: { drawingId, field: { classifyNameField, seriesNameField, seriesDataField, maxField? } }
|
||||
"""
|
||||
result: List[Dict[str, Any]] = []
|
||||
for cfg in _echart_configs_from_cells(cells):
|
||||
drawing_id = cfg.get("drawingId")
|
||||
option = cfg.get("option") or {}
|
||||
dataset_names: Set[str] = set()
|
||||
classify_alias, classify_field = _parse_field(option.get("classifyNameField"))
|
||||
series_alias, series_name_field = _parse_field(option.get("seriesNameField"))
|
||||
data_alias, series_data_field = _parse_field(option.get("seriesDataField"))
|
||||
max_alias, max_field = _parse_field(option.get("maxField"))
|
||||
for alias in (classify_alias, series_alias, data_alias, max_alias):
|
||||
if alias:
|
||||
dataset_names.add(alias)
|
||||
if not dataset_names:
|
||||
continue
|
||||
rows = _collect_rows(datasets, dataset_names)
|
||||
field = _build_field(
|
||||
rows,
|
||||
classify_field,
|
||||
series_name_field,
|
||||
series_data_field,
|
||||
max_field,
|
||||
option.get("summaryType") or "none",
|
||||
)
|
||||
result.append({"drawingId": drawing_id, "field": field})
|
||||
return result
|
||||
Reference in New Issue
Block a user