150 lines
4.2 KiB
Python
150 lines
4.2 KiB
Python
#!/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))
|