feat: restore source parity and harden agent runtime
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
数据源聚合(polymerizationType)
|
||||
1 / select — 列表
|
||||
2 / group — 分组
|
||||
3 / summary — 汇总(summaryType: sum|avg|max|min|count)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from decimal import Decimal
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from online_dev.report_manager.engine.parent_cells import _get_nested_value, resolve_field_path
|
||||
|
||||
|
||||
@dataclass
|
||||
class BindData:
|
||||
value: Any
|
||||
data_list: List[Dict[str, Any]]
|
||||
|
||||
|
||||
def _field_name(cell: Dict[str, Any]) -> str:
|
||||
custom = cell.get("custom") or {}
|
||||
return str(custom.get("field") or custom.get("bindField") or "")
|
||||
|
||||
|
||||
def _poly_type(cell: Dict[str, Any]) -> str:
|
||||
custom = cell.get("custom") or {}
|
||||
raw = custom.get("polymerizationType")
|
||||
if raw is None:
|
||||
return "1"
|
||||
return str(raw)
|
||||
|
||||
|
||||
def _summary_type(cell: Dict[str, Any]) -> str:
|
||||
custom = cell.get("custom") or {}
|
||||
return str(custom.get("summaryType") or "sum").lower()
|
||||
|
||||
|
||||
def _group_type(cell: Dict[str, Any]) -> str:
|
||||
custom = cell.get("custom") or {}
|
||||
return str(custom.get("groupType") or "default")
|
||||
|
||||
|
||||
def _dataset_name(cell: Dict[str, Any]) -> str:
|
||||
custom = cell.get("custom") or {}
|
||||
name = str(
|
||||
custom.get("dataSetName") or custom.get("dataSet") or custom.get("alias") or ""
|
||||
)
|
||||
if name:
|
||||
return name
|
||||
field = str(custom.get("field") or custom.get("bindField") or "")
|
||||
if "." in field:
|
||||
return field.split(".", 1)[0]
|
||||
return ""
|
||||
|
||||
|
||||
def build_bind_list(cell: Dict[str, Any], rows: List[Dict[str, Any]]) -> List[BindData]:
|
||||
poly = _poly_type(cell)
|
||||
field = resolve_field_path(_field_name(cell), _dataset_name(cell))
|
||||
prop = field.split(".")[-1] if "." in field else field
|
||||
|
||||
if poly == "3":
|
||||
return [_summary_bind(cell, rows, field)]
|
||||
|
||||
if poly == "2":
|
||||
return _group_bind(rows, prop, _group_type(cell))
|
||||
|
||||
return _list_bind(rows, field)
|
||||
|
||||
|
||||
def _list_bind(rows: List[Dict[str, Any]], field: str) -> List[BindData]:
|
||||
out: List[BindData] = []
|
||||
for row in rows:
|
||||
val = _get_nested_value(row, field)
|
||||
out.append(BindData(value=val, data_list=[row]))
|
||||
if not out:
|
||||
out.append(BindData(value="", data_list=[{}]))
|
||||
return out
|
||||
|
||||
|
||||
def _group_bind(
|
||||
rows: List[Dict[str, Any]], prop: str, group_type: str
|
||||
) -> List[BindData]:
|
||||
if group_type == "adjacent":
|
||||
return _group_adjacent(rows, prop)
|
||||
ordered: Dict[Any, List[Dict[str, Any]]] = {}
|
||||
for row in rows:
|
||||
key = _get_nested_value(row, prop)
|
||||
if key is None:
|
||||
key = ""
|
||||
ordered.setdefault(key, []).append(row)
|
||||
return [
|
||||
BindData(value=k, data_list=ordered[k]) for k in ordered.keys()
|
||||
]
|
||||
|
||||
|
||||
def _group_adjacent(rows: List[Dict[str, Any]], prop: str) -> List[BindData]:
|
||||
out: List[BindData] = []
|
||||
bucket: List[Dict[str, Any]] = []
|
||||
last_key: Any = object()
|
||||
for row in rows:
|
||||
key = _get_nested_value(row, prop)
|
||||
if key is None:
|
||||
key = ""
|
||||
if bucket and key != last_key:
|
||||
out.append(BindData(value=last_key, data_list=bucket))
|
||||
bucket = []
|
||||
bucket.append(row)
|
||||
last_key = key
|
||||
if bucket:
|
||||
out.append(BindData(value=last_key, data_list=bucket))
|
||||
if not out:
|
||||
out.append(BindData(value="", data_list=[{}]))
|
||||
return out
|
||||
|
||||
|
||||
def _summary_bind(cell: Dict[str, Any], rows: List[Dict[str, Any]], field: str) -> BindData:
|
||||
st = _summary_type(cell)
|
||||
nums: List[Decimal] = []
|
||||
for row in rows:
|
||||
v = _get_nested_value(row, field)
|
||||
try:
|
||||
nums.append(Decimal(str(v)))
|
||||
except Exception:
|
||||
pass
|
||||
if st == "count":
|
||||
val: Any = len(rows)
|
||||
elif st == "avg":
|
||||
val = float(sum(nums) / len(nums)) if nums else 0
|
||||
elif st == "max":
|
||||
val = float(max(nums)) if nums else 0
|
||||
elif st == "min":
|
||||
val = float(min(nums)) if nums else 0
|
||||
else:
|
||||
val = float(sum(nums)) if nums else 0
|
||||
return BindData(value=val, data_list=rows)
|
||||
|
||||
|
||||
def expand_span(bind: BindData, cell: Dict[str, Any]) -> int:
|
||||
"""该 bind 在向下/向右扩展时占用的行/列数。"""
|
||||
poly = _poly_type(cell)
|
||||
if poly == "3":
|
||||
return 1
|
||||
if poly == "2":
|
||||
return 1
|
||||
return max(1, len(bind.data_list))
|
||||
Reference in New Issue
Block a user