Files
ai-agent-admin/backend-fastapi/online_dev/report_manager/engine/polymerize.py
T

150 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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))