#!/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))