deepfos 1.1.60__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deepfos/__init__.py +6 -0
- deepfos/_version.py +21 -0
- deepfos/algo/__init__.py +0 -0
- deepfos/algo/graph.py +171 -0
- deepfos/algo/segtree.py +31 -0
- deepfos/api/V1_1/__init__.py +0 -0
- deepfos/api/V1_1/business_model.py +119 -0
- deepfos/api/V1_1/dimension.py +599 -0
- deepfos/api/V1_1/models/__init__.py +0 -0
- deepfos/api/V1_1/models/business_model.py +1033 -0
- deepfos/api/V1_1/models/dimension.py +2768 -0
- deepfos/api/V1_2/__init__.py +0 -0
- deepfos/api/V1_2/dimension.py +285 -0
- deepfos/api/V1_2/models/__init__.py +0 -0
- deepfos/api/V1_2/models/dimension.py +2923 -0
- deepfos/api/__init__.py +0 -0
- deepfos/api/account.py +167 -0
- deepfos/api/accounting_engines.py +147 -0
- deepfos/api/app.py +626 -0
- deepfos/api/approval_process.py +198 -0
- deepfos/api/base.py +983 -0
- deepfos/api/business_model.py +160 -0
- deepfos/api/consolidation.py +129 -0
- deepfos/api/consolidation_process.py +106 -0
- deepfos/api/datatable.py +341 -0
- deepfos/api/deep_pipeline.py +61 -0
- deepfos/api/deepconnector.py +36 -0
- deepfos/api/deepfos_task.py +92 -0
- deepfos/api/deepmodel.py +188 -0
- deepfos/api/dimension.py +486 -0
- deepfos/api/financial_model.py +319 -0
- deepfos/api/journal_model.py +119 -0
- deepfos/api/journal_template.py +132 -0
- deepfos/api/memory_financial_model.py +98 -0
- deepfos/api/models/__init__.py +3 -0
- deepfos/api/models/account.py +483 -0
- deepfos/api/models/accounting_engines.py +756 -0
- deepfos/api/models/app.py +1338 -0
- deepfos/api/models/approval_process.py +1043 -0
- deepfos/api/models/base.py +234 -0
- deepfos/api/models/business_model.py +805 -0
- deepfos/api/models/consolidation.py +711 -0
- deepfos/api/models/consolidation_process.py +248 -0
- deepfos/api/models/datatable_mysql.py +427 -0
- deepfos/api/models/deep_pipeline.py +55 -0
- deepfos/api/models/deepconnector.py +28 -0
- deepfos/api/models/deepfos_task.py +386 -0
- deepfos/api/models/deepmodel.py +308 -0
- deepfos/api/models/dimension.py +1576 -0
- deepfos/api/models/financial_model.py +1796 -0
- deepfos/api/models/journal_model.py +341 -0
- deepfos/api/models/journal_template.py +854 -0
- deepfos/api/models/memory_financial_model.py +478 -0
- deepfos/api/models/platform.py +178 -0
- deepfos/api/models/python.py +221 -0
- deepfos/api/models/reconciliation_engine.py +411 -0
- deepfos/api/models/reconciliation_report.py +161 -0
- deepfos/api/models/role_strategy.py +884 -0
- deepfos/api/models/smartlist.py +237 -0
- deepfos/api/models/space.py +1137 -0
- deepfos/api/models/system.py +1065 -0
- deepfos/api/models/variable.py +463 -0
- deepfos/api/models/workflow.py +946 -0
- deepfos/api/platform.py +199 -0
- deepfos/api/python.py +90 -0
- deepfos/api/reconciliation_engine.py +181 -0
- deepfos/api/reconciliation_report.py +64 -0
- deepfos/api/role_strategy.py +234 -0
- deepfos/api/smartlist.py +69 -0
- deepfos/api/space.py +582 -0
- deepfos/api/system.py +372 -0
- deepfos/api/variable.py +154 -0
- deepfos/api/workflow.py +264 -0
- deepfos/boost/__init__.py +6 -0
- deepfos/boost/py_jstream.py +89 -0
- deepfos/boost/py_pandas.py +20 -0
- deepfos/cache.py +121 -0
- deepfos/config.py +6 -0
- deepfos/core/__init__.py +27 -0
- deepfos/core/cube/__init__.py +10 -0
- deepfos/core/cube/_base.py +462 -0
- deepfos/core/cube/constants.py +21 -0
- deepfos/core/cube/cube.py +408 -0
- deepfos/core/cube/formula.py +707 -0
- deepfos/core/cube/syscube.py +532 -0
- deepfos/core/cube/typing.py +7 -0
- deepfos/core/cube/utils.py +238 -0
- deepfos/core/dimension/__init__.py +11 -0
- deepfos/core/dimension/_base.py +506 -0
- deepfos/core/dimension/dimcreator.py +184 -0
- deepfos/core/dimension/dimension.py +472 -0
- deepfos/core/dimension/dimexpr.py +271 -0
- deepfos/core/dimension/dimmember.py +155 -0
- deepfos/core/dimension/eledimension.py +22 -0
- deepfos/core/dimension/filters.py +99 -0
- deepfos/core/dimension/sysdimension.py +168 -0
- deepfos/core/logictable/__init__.py +5 -0
- deepfos/core/logictable/_cache.py +141 -0
- deepfos/core/logictable/_operator.py +663 -0
- deepfos/core/logictable/nodemixin.py +673 -0
- deepfos/core/logictable/sqlcondition.py +609 -0
- deepfos/core/logictable/tablemodel.py +497 -0
- deepfos/db/__init__.py +36 -0
- deepfos/db/cipher.py +660 -0
- deepfos/db/clickhouse.py +191 -0
- deepfos/db/connector.py +195 -0
- deepfos/db/daclickhouse.py +171 -0
- deepfos/db/dameng.py +101 -0
- deepfos/db/damysql.py +189 -0
- deepfos/db/dbkits.py +358 -0
- deepfos/db/deepengine.py +99 -0
- deepfos/db/deepmodel.py +82 -0
- deepfos/db/deepmodel_kingbase.py +83 -0
- deepfos/db/edb.py +214 -0
- deepfos/db/gauss.py +83 -0
- deepfos/db/kingbase.py +83 -0
- deepfos/db/mysql.py +184 -0
- deepfos/db/oracle.py +131 -0
- deepfos/db/postgresql.py +192 -0
- deepfos/db/sqlserver.py +99 -0
- deepfos/db/utils.py +135 -0
- deepfos/element/__init__.py +89 -0
- deepfos/element/accounting.py +348 -0
- deepfos/element/apvlprocess.py +215 -0
- deepfos/element/base.py +398 -0
- deepfos/element/bizmodel.py +1269 -0
- deepfos/element/datatable.py +2467 -0
- deepfos/element/deep_pipeline.py +186 -0
- deepfos/element/deepconnector.py +59 -0
- deepfos/element/deepmodel.py +1806 -0
- deepfos/element/dimension.py +1254 -0
- deepfos/element/fact_table.py +427 -0
- deepfos/element/finmodel.py +1485 -0
- deepfos/element/journal.py +840 -0
- deepfos/element/journal_template.py +943 -0
- deepfos/element/pyscript.py +412 -0
- deepfos/element/reconciliation.py +553 -0
- deepfos/element/rolestrategy.py +243 -0
- deepfos/element/smartlist.py +457 -0
- deepfos/element/variable.py +756 -0
- deepfos/element/workflow.py +560 -0
- deepfos/exceptions/__init__.py +239 -0
- deepfos/exceptions/hook.py +86 -0
- deepfos/lazy.py +104 -0
- deepfos/lazy_import.py +84 -0
- deepfos/lib/__init__.py +0 -0
- deepfos/lib/_javaobj.py +366 -0
- deepfos/lib/asynchronous.py +879 -0
- deepfos/lib/concurrency.py +107 -0
- deepfos/lib/constant.py +39 -0
- deepfos/lib/decorator.py +310 -0
- deepfos/lib/deepchart.py +778 -0
- deepfos/lib/deepux.py +477 -0
- deepfos/lib/discovery.py +273 -0
- deepfos/lib/edb_lexer.py +789 -0
- deepfos/lib/eureka.py +156 -0
- deepfos/lib/filterparser.py +751 -0
- deepfos/lib/httpcli.py +106 -0
- deepfos/lib/jsonstreamer.py +80 -0
- deepfos/lib/msg.py +394 -0
- deepfos/lib/nacos.py +225 -0
- deepfos/lib/patch.py +92 -0
- deepfos/lib/redis.py +241 -0
- deepfos/lib/serutils.py +181 -0
- deepfos/lib/stopwatch.py +99 -0
- deepfos/lib/subtask.py +572 -0
- deepfos/lib/sysutils.py +703 -0
- deepfos/lib/utils.py +1003 -0
- deepfos/local.py +160 -0
- deepfos/options.py +670 -0
- deepfos/translation.py +237 -0
- deepfos-1.1.60.dist-info/METADATA +33 -0
- deepfos-1.1.60.dist-info/RECORD +175 -0
- deepfos-1.1.60.dist-info/WHEEL +5 -0
- deepfos-1.1.60.dist-info/top_level.txt +1 -0
deepfos/lib/deepchart.py
ADDED
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
"""python作为图表组件数据源"""
|
|
2
|
+
import collections
|
|
3
|
+
import functools
|
|
4
|
+
import asyncio
|
|
5
|
+
from inspect import isfunction
|
|
6
|
+
from pandas import DataFrame
|
|
7
|
+
from typing import *
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from pydantic import Field, parse_obj_as
|
|
10
|
+
|
|
11
|
+
from deepfos.lib.asynchronous import evloop
|
|
12
|
+
from deepfos.api.models import BaseModel
|
|
13
|
+
from deepfos.api.dimension import DimensionAPI
|
|
14
|
+
from deepfos.api.smartlist import SmartListAPI
|
|
15
|
+
from deepfos.element.base import ElementBase
|
|
16
|
+
from deepfos.lib.decorator import cached_property
|
|
17
|
+
from deepfos.lib.filterparser import set_date_type_fields, set_dt_precision, Parameter, TimeType, FilterParser, \
|
|
18
|
+
FieldType, Sql
|
|
19
|
+
from deepfos.lib.utils import unpack_expr, dict_to_expr
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
'BaseField',
|
|
23
|
+
'BoundField',
|
|
24
|
+
'Text',
|
|
25
|
+
'Number',
|
|
26
|
+
'Date',
|
|
27
|
+
'Dimension',
|
|
28
|
+
'SmartList',
|
|
29
|
+
'Person',
|
|
30
|
+
'Struct',
|
|
31
|
+
'as_datasource',
|
|
32
|
+
'StructMeta',
|
|
33
|
+
'ChartEngine',
|
|
34
|
+
'HandleExpr',
|
|
35
|
+
'Filter'
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
FLAG_FOR_META = 'describe'
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DimElement(ElementBase):
|
|
42
|
+
api_class = DimensionAPI
|
|
43
|
+
api: DimensionAPI
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SmlElement(ElementBase):
|
|
47
|
+
api_class = SmartListAPI
|
|
48
|
+
api: SmartListAPI
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def as_dict(self=None, exclude_none: bool = True) -> Dict:
|
|
52
|
+
# will be bound to NamedTuple, self is actually positional
|
|
53
|
+
result = {}
|
|
54
|
+
for k, v in self._asdict().items():
|
|
55
|
+
if v is not None or (v is None and not exclude_none):
|
|
56
|
+
try:
|
|
57
|
+
result[k] = v.as_dict(exclude_none) # noqa
|
|
58
|
+
except AttributeError:
|
|
59
|
+
result[k] = v
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# 字段名
|
|
64
|
+
COLUMN_NAME = 'columnName'
|
|
65
|
+
# 字段描述
|
|
66
|
+
DESC = 'description'
|
|
67
|
+
# 字段逻辑信息
|
|
68
|
+
LOGIC_INFO = 'logicInfo'
|
|
69
|
+
# 表示元素字段的类型
|
|
70
|
+
VALUE_KEY = 'valueKey'
|
|
71
|
+
# 表示值列表的选中值
|
|
72
|
+
VALUE_FIELD = 'valueField'
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# -----------------------------------------------------------------------------
|
|
76
|
+
# Data Source Struct
|
|
77
|
+
class ElementDetail(NamedTuple):
|
|
78
|
+
elementName: str
|
|
79
|
+
elementType: str
|
|
80
|
+
folderId: str = None
|
|
81
|
+
path: str = None
|
|
82
|
+
serverName: str = None
|
|
83
|
+
absoluteTag: bool = True
|
|
84
|
+
|
|
85
|
+
as_dict = as_dict
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class LogicInfo(NamedTuple):
|
|
89
|
+
valueType: int
|
|
90
|
+
valueKey: str = None
|
|
91
|
+
elementDetail: ElementDetail = None
|
|
92
|
+
|
|
93
|
+
as_dict = as_dict
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class BaseField(BaseModel):
|
|
97
|
+
code: int = None
|
|
98
|
+
columnName: str = None
|
|
99
|
+
description: str = None
|
|
100
|
+
value_key: str = None
|
|
101
|
+
|
|
102
|
+
def __init__(self, name: str = None, description: str = None, **data):
|
|
103
|
+
super().__init__(columnName=name, description=description, **data)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def logic_info(self) -> LogicInfo:
|
|
107
|
+
return LogicInfo(valueType=self.code)
|
|
108
|
+
|
|
109
|
+
def to_dict(self) -> Dict:
|
|
110
|
+
if self.description is None:
|
|
111
|
+
self.description = self.columnName
|
|
112
|
+
result = self.dict(include={COLUMN_NAME, DESC})
|
|
113
|
+
result.update({LOGIC_INFO: self.logic_info.as_dict()})
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def name(self):
|
|
118
|
+
return self.columnName
|
|
119
|
+
|
|
120
|
+
def __setattr__(self, name, value):
|
|
121
|
+
if name == 'name':
|
|
122
|
+
self.columnName = value
|
|
123
|
+
else:
|
|
124
|
+
super().__setattr__(name, value)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class Text(BaseField):
|
|
128
|
+
code = 1
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class Date(BaseField):
|
|
132
|
+
code = 11
|
|
133
|
+
# 前端时间筛选器颗粒度字段;4:最宽泛的颗粒度
|
|
134
|
+
value_key = '4'
|
|
135
|
+
precision = TimeType.day
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def logic_info(self) -> LogicInfo:
|
|
139
|
+
return LogicInfo(valueType=self.code, valueKey=self.value_key)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class Number(BaseField):
|
|
143
|
+
code = 15
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class Person(BaseField):
|
|
147
|
+
code = 12
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class BoundField(BaseField):
|
|
151
|
+
code: int = None
|
|
152
|
+
element_name: str = None
|
|
153
|
+
folder_id: str = None
|
|
154
|
+
path: str = None
|
|
155
|
+
bound_class: Type[ElementBase] = None
|
|
156
|
+
value_field: List[Any] = Field(default_factory=list)
|
|
157
|
+
|
|
158
|
+
def __init__(
|
|
159
|
+
self,
|
|
160
|
+
element_name: str,
|
|
161
|
+
folder_id: str = None,
|
|
162
|
+
path: str = None,
|
|
163
|
+
value_key: str = None,
|
|
164
|
+
value_field: list = None,
|
|
165
|
+
name: str = None,
|
|
166
|
+
description: str = None,
|
|
167
|
+
**data
|
|
168
|
+
):
|
|
169
|
+
super().__init__(name=name, description=description, **data)
|
|
170
|
+
self.element_name = element_name
|
|
171
|
+
self.folder_id = folder_id
|
|
172
|
+
self.path = path
|
|
173
|
+
self.value_field = value_field or []
|
|
174
|
+
|
|
175
|
+
async def get_element_detail(self) -> ElementDetail:
|
|
176
|
+
ele = await self.bound_class( # noqa
|
|
177
|
+
element_name=self.element_name,
|
|
178
|
+
folder_id=self.folder_id,
|
|
179
|
+
path=self.path
|
|
180
|
+
)._get_element_info()
|
|
181
|
+
return ElementDetail(
|
|
182
|
+
elementName=self.element_name,
|
|
183
|
+
elementType=ele.elementType,
|
|
184
|
+
folderId=ele.folderId,
|
|
185
|
+
path=self.path,
|
|
186
|
+
serverName=ele.serverName
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
async def async_to_dict(self) -> Dict:
|
|
190
|
+
result = super().to_dict()
|
|
191
|
+
element_detail = await self.get_element_detail()
|
|
192
|
+
logic_info = self.logic_info._replace(elementDetail=element_detail)
|
|
193
|
+
result.update({LOGIC_INFO: logic_info.as_dict()})
|
|
194
|
+
return result
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class SmartList(BoundField):
|
|
198
|
+
code = 3
|
|
199
|
+
bound_class: Type[ElementBase] = SmlElement
|
|
200
|
+
|
|
201
|
+
async def async_to_dict(self) -> Dict:
|
|
202
|
+
result = super().to_dict()
|
|
203
|
+
element_detail = await self.get_element_detail()
|
|
204
|
+
logic_info = self.logic_info._replace(elementDetail=element_detail)
|
|
205
|
+
result.update({LOGIC_INFO: logic_info.as_dict()})
|
|
206
|
+
result[VALUE_KEY] = logic_info.elementDetail.elementName
|
|
207
|
+
result[VALUE_FIELD] = self.value_field
|
|
208
|
+
return result
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class Dimension(BoundField):
|
|
212
|
+
code = 8
|
|
213
|
+
bound_class: Type[ElementBase] = DimElement
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class StructMeta(type):
|
|
217
|
+
def __new__(mcs, cls_name, bases, namespace: dict):
|
|
218
|
+
fields = []
|
|
219
|
+
|
|
220
|
+
for field_name, anno in namespace.get('__annotations__', {}).items():
|
|
221
|
+
if issubclass(anno, BoundField):
|
|
222
|
+
if field_name not in namespace:
|
|
223
|
+
raise ValueError(f"Bound field: <{field_name}> must have a default value.")
|
|
224
|
+
|
|
225
|
+
field = namespace.pop(field_name)
|
|
226
|
+
if field.columnName is None:
|
|
227
|
+
field.columnName = field_name
|
|
228
|
+
if not isinstance(field, anno):
|
|
229
|
+
raise TypeError(
|
|
230
|
+
f"Expect type {anno} for field: <{field_name}>, but got {type(field)}")
|
|
231
|
+
fields.append(field)
|
|
232
|
+
|
|
233
|
+
elif issubclass(anno, BaseField):
|
|
234
|
+
if field_name not in namespace:
|
|
235
|
+
fields.append(anno(name=field_name))
|
|
236
|
+
|
|
237
|
+
collected = []
|
|
238
|
+
|
|
239
|
+
for k, v in namespace.items():
|
|
240
|
+
if isinstance(v, BaseField):
|
|
241
|
+
collected.append(k)
|
|
242
|
+
if v.columnName is None: # 如果name字段赋值了,就取name字段的值
|
|
243
|
+
v.columnName = k
|
|
244
|
+
fields.append(v)
|
|
245
|
+
|
|
246
|
+
for k in collected:
|
|
247
|
+
namespace.pop(k)
|
|
248
|
+
|
|
249
|
+
namespace['fields'] = fields
|
|
250
|
+
|
|
251
|
+
return super().__new__(mcs, cls_name, bases, namespace)
|
|
252
|
+
|
|
253
|
+
def to_dict(cls):
|
|
254
|
+
columns_meta = []
|
|
255
|
+
sml_or_dim = []
|
|
256
|
+
futures = []
|
|
257
|
+
for field in cls.fields:
|
|
258
|
+
if isinstance(field, BoundField):
|
|
259
|
+
sml_or_dim.append(field)
|
|
260
|
+
elif isinstance(field, BaseField):
|
|
261
|
+
columns_meta.append(field.to_dict())
|
|
262
|
+
|
|
263
|
+
for field in sml_or_dim:
|
|
264
|
+
future = evloop.apply(field.async_to_dict())
|
|
265
|
+
futures.append(future)
|
|
266
|
+
|
|
267
|
+
for future in futures:
|
|
268
|
+
columns_meta.append(future.result())
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
"__AsSource__": True,
|
|
272
|
+
"columns": columns_meta
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
def get_dim_fields(cls):
|
|
276
|
+
result = {}
|
|
277
|
+
for field in cls.fields:
|
|
278
|
+
if isinstance(field, Dimension):
|
|
279
|
+
result[field.columnName] = (field.path, field.folder_id)
|
|
280
|
+
return result
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class Struct(metaclass=StructMeta):
|
|
284
|
+
"""help class"""
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _agg_to_dict(agg_func_col, df, column_name_to_alias):
|
|
288
|
+
"""针对只有聚合操作但是没有分组的情况做处理:
|
|
289
|
+
1.先求聚合
|
|
290
|
+
2.将结果按照'分组且聚合的方式'转为dict
|
|
291
|
+
3.支持返回字段为alias的形式
|
|
292
|
+
"""
|
|
293
|
+
df = df.agg(agg_func_col)
|
|
294
|
+
res = dict()
|
|
295
|
+
for col in df.columns:
|
|
296
|
+
for ind in df.index:
|
|
297
|
+
if str(df.loc[ind, col]) == 'nan':
|
|
298
|
+
continue
|
|
299
|
+
res[f'{col}#{ind}'] = df.loc[ind, col]
|
|
300
|
+
|
|
301
|
+
for k, v in column_name_to_alias.items():
|
|
302
|
+
if v in res:
|
|
303
|
+
value = res.pop(v)
|
|
304
|
+
res[k] = value
|
|
305
|
+
return [res]
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _batch_get_mbrs_by_expr(
|
|
309
|
+
fix: Dict[str, Union[str, list]],
|
|
310
|
+
paths: Union[str, Dict[str, Tuple[str, str]]] = None,
|
|
311
|
+
) -> dict:
|
|
312
|
+
"""根据批量维度表达式异步调用获取解析结果"""
|
|
313
|
+
from deepfos.element.dimension import AsyncDimension
|
|
314
|
+
|
|
315
|
+
if isinstance(paths, dict):
|
|
316
|
+
path_getter = paths.__getitem__
|
|
317
|
+
else:
|
|
318
|
+
path_getter = lambda _: (paths, None)
|
|
319
|
+
|
|
320
|
+
# 遍历fix,如果fix的值为str,则认为是维度表达式,将表达式转换为成员list
|
|
321
|
+
mbrs = {}
|
|
322
|
+
futures = []
|
|
323
|
+
|
|
324
|
+
for dim, exp in fix.items():
|
|
325
|
+
if isinstance(exp, str):
|
|
326
|
+
if "(" not in exp:
|
|
327
|
+
exp = exp.strip(dim)
|
|
328
|
+
exp = exp.strip('{').strip('}')
|
|
329
|
+
mbrs[dim] = exp.split(';')
|
|
330
|
+
else:
|
|
331
|
+
path, folder_id = path_getter(dim)
|
|
332
|
+
future = evloop.apply(AsyncDimension(element_name=dim, path=path, folder_id=folder_id).query(
|
|
333
|
+
expression=exp, fields=['name'], as_model=False
|
|
334
|
+
))
|
|
335
|
+
|
|
336
|
+
futures.append((dim, future))
|
|
337
|
+
else:
|
|
338
|
+
mbrs[dim] = exp
|
|
339
|
+
|
|
340
|
+
for dim, future in futures:
|
|
341
|
+
mbrs[dim] = [item['name'] for item in future.result()]
|
|
342
|
+
return mbrs
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class Filter:
|
|
346
|
+
def __init__(
|
|
347
|
+
self,
|
|
348
|
+
params: Union[Dict, Parameter],
|
|
349
|
+
struct: Type[Struct] = None,
|
|
350
|
+
all_enable_dim_columns=None,
|
|
351
|
+
date_round=False
|
|
352
|
+
):
|
|
353
|
+
if isinstance(params, dict):
|
|
354
|
+
params = parse_obj_as(Parameter, params)
|
|
355
|
+
self.params = params
|
|
356
|
+
self.struct = struct
|
|
357
|
+
self._all_enable_dim_columns = all_enable_dim_columns
|
|
358
|
+
self.date_round = date_round
|
|
359
|
+
|
|
360
|
+
def apply(
|
|
361
|
+
self,
|
|
362
|
+
df: pd.DataFrame,
|
|
363
|
+
exclude_dim_expr: bool = True
|
|
364
|
+
) -> pd.DataFrame:
|
|
365
|
+
"""根据筛选条件对数据源进行过滤处理
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
df: 需要过滤的数据源
|
|
369
|
+
exclude_dim_expr: 是否排除维度表达式,默认为True,只对维度类型之外的字段进行条件过滤
|
|
370
|
+
"""
|
|
371
|
+
where = self.as_pandas_sql(exclude_dim_expr=exclude_dim_expr)
|
|
372
|
+
if not where:
|
|
373
|
+
return df
|
|
374
|
+
|
|
375
|
+
return df.query(where)
|
|
376
|
+
|
|
377
|
+
def _axis_where(self, sql_type, exclude_dim_expr: bool = True):
|
|
378
|
+
if len(self.params.queryInfo.axisRanges) == 0:
|
|
379
|
+
return ''
|
|
380
|
+
|
|
381
|
+
parser = FilterParser(
|
|
382
|
+
self.params.queryInfo.axisRanges[0],
|
|
383
|
+
self._all_enable_dim_columns,
|
|
384
|
+
exclude_dim_expr=exclude_dim_expr,
|
|
385
|
+
date_round=self.date_round
|
|
386
|
+
)
|
|
387
|
+
parser.parse()
|
|
388
|
+
dim_mbrs = {}
|
|
389
|
+
|
|
390
|
+
if not exclude_dim_expr and parser.axis_dim_fields_to_expr:
|
|
391
|
+
# 如果不排除维度,则需要在获取where表达式之前解析所有维度表达式
|
|
392
|
+
# 异步请求获取批量维度表达式的解析结果
|
|
393
|
+
# 多组合中可能出现多个相同的维度,将list中 多个维度处理为或(;)的关系
|
|
394
|
+
_axis_dim_fields_to_expr = collections.defaultdict(list)
|
|
395
|
+
dim_expr = {} # 存储维度元素的元素名和表达式的映射
|
|
396
|
+
column_name_to_dim_name = {} # 存储字段列名和对应的维度元素名称的映射
|
|
397
|
+
|
|
398
|
+
for column_name, expr_list in parser.axis_dim_fields_to_expr.items():
|
|
399
|
+
# 同column_name对应的表达式为同一个维度的,dim_name不变,以第一个为准即可
|
|
400
|
+
dim_name = None
|
|
401
|
+
for expr in expr_list:
|
|
402
|
+
_dim_name, v = unpack_expr(expr)
|
|
403
|
+
|
|
404
|
+
if dim_name is None:
|
|
405
|
+
dim_name = _dim_name
|
|
406
|
+
if dim_name != column_name:
|
|
407
|
+
column_name_to_dim_name[column_name] = dim_name
|
|
408
|
+
self._all_enable_dim_columns[dim_name] = self._all_enable_dim_columns[column_name]
|
|
409
|
+
|
|
410
|
+
_axis_dim_fields_to_expr[dim_name].append(v)
|
|
411
|
+
|
|
412
|
+
single_dim_expr = dict_to_expr(_axis_dim_fields_to_expr)
|
|
413
|
+
dim_expr[dim_name] = single_dim_expr
|
|
414
|
+
_axis_dim_fields_to_expr.clear()
|
|
415
|
+
|
|
416
|
+
dim_mbrs = _batch_get_mbrs_by_expr(fix=dim_expr, paths=self._all_enable_dim_columns)
|
|
417
|
+
empty_dim_mbrs = [dim for dim, member in dim_mbrs.items() if not member]
|
|
418
|
+
|
|
419
|
+
if empty_dim_mbrs:
|
|
420
|
+
raise ValueError(f"({','.join(empty_dim_mbrs)})解析维度成员结果为空!")
|
|
421
|
+
|
|
422
|
+
# 将键为维度名,值为维度成员的字典替换为键为列名,值为维度成员的字典
|
|
423
|
+
for c, d in column_name_to_dim_name.items():
|
|
424
|
+
if d in dim_mbrs:
|
|
425
|
+
dim_mbrs[c] = dim_mbrs.pop(d)
|
|
426
|
+
|
|
427
|
+
return parser.make_query(dim_mbrs=dim_mbrs, sql=sql_type)
|
|
428
|
+
|
|
429
|
+
def _pov_where(self, sql_type=Sql.PANDAS_SQL):
|
|
430
|
+
parser = FilterParser(self.params.queryInfo.selected.selectedFilter, date_round=self.date_round)
|
|
431
|
+
parser.parse()
|
|
432
|
+
# 兼容2023-02-20迭代前,前置format date
|
|
433
|
+
# 导致SelectGroup和filterGroup内时间信息取自timeColumns的逻辑
|
|
434
|
+
date_fields = {group.columnName: group.formatType for group in self.params.queryInfo.timeColumns}
|
|
435
|
+
return parser.make_query(date_fields=date_fields, sql=sql_type)
|
|
436
|
+
|
|
437
|
+
def _generate_where(self, sql_type=Sql.PANDAS_SQL, exclude_dim_expr: bool = True):
|
|
438
|
+
# pov:where条件
|
|
439
|
+
pov_where = self._pov_where(sql_type)
|
|
440
|
+
# axis:where条件
|
|
441
|
+
axis_where = self._axis_where(sql_type, exclude_dim_expr=exclude_dim_expr)
|
|
442
|
+
|
|
443
|
+
if pov_where and axis_where:
|
|
444
|
+
return f'{axis_where} and ({pov_where})'
|
|
445
|
+
if axis_where:
|
|
446
|
+
return axis_where
|
|
447
|
+
if pov_where:
|
|
448
|
+
return f'({pov_where})'
|
|
449
|
+
|
|
450
|
+
def as_sql(self, exclude_dim_expr: bool = True) -> str:
|
|
451
|
+
"""获取MySql格式的过滤条件"""
|
|
452
|
+
return self._generate_where(Sql.SQL, exclude_dim_expr)
|
|
453
|
+
|
|
454
|
+
def as_pandas_sql(self, exclude_dim_expr: bool = True) -> str:
|
|
455
|
+
"""组装符合pandas格式的sql"""
|
|
456
|
+
return self._generate_where(exclude_dim_expr=exclude_dim_expr)
|
|
457
|
+
|
|
458
|
+
def agg(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
459
|
+
"""对df 进行分组,聚合,重命名处理"""
|
|
460
|
+
# 分组,聚合,排序,重命名alias
|
|
461
|
+
group_by_col = []
|
|
462
|
+
agg_func_col = {}
|
|
463
|
+
column_name_to_alias = {}
|
|
464
|
+
|
|
465
|
+
# 分组普通字段
|
|
466
|
+
for group in self.params.queryInfo.columns:
|
|
467
|
+
group_by_col.append(group.columnName)
|
|
468
|
+
column_name_to_alias[group.alias] = group.columnName
|
|
469
|
+
|
|
470
|
+
# 分组时间类型字段
|
|
471
|
+
for group in self.params.queryInfo.timeColumns:
|
|
472
|
+
column_name = group.columnName
|
|
473
|
+
group_by_col.append(column_name)
|
|
474
|
+
column_name_to_alias[group.alias] = group.columnName
|
|
475
|
+
|
|
476
|
+
# 聚合字段
|
|
477
|
+
for column in self.params.queryInfo.measures:
|
|
478
|
+
column_name = column.columnName
|
|
479
|
+
alias = column.alias
|
|
480
|
+
collect_method = column.collectMethod
|
|
481
|
+
# 聚合操作中:会有一个字段多个不同类型的聚合,会有一个字段多次同种聚合类型的运算
|
|
482
|
+
if collect_method:
|
|
483
|
+
column_alias = f'{column_name}#{collect_method.lower()}'
|
|
484
|
+
# alias设为key,因为column_alias可能存在重复
|
|
485
|
+
column_name_to_alias[alias] = column_alias
|
|
486
|
+
if column_name in agg_func_col:
|
|
487
|
+
agg_func_col[column_name].append(collect_method.lower())
|
|
488
|
+
else:
|
|
489
|
+
agg_func_col[column_name] = [collect_method.lower()]
|
|
490
|
+
|
|
491
|
+
# 分组字段:处理存在为空的列重新赋值为 '',防止记录丢失
|
|
492
|
+
for item in group_by_col:
|
|
493
|
+
if item in df:
|
|
494
|
+
df[item] = df[item].fillna('')
|
|
495
|
+
|
|
496
|
+
if group_by_col and agg_func_col:
|
|
497
|
+
df = df.groupby(group_by_col, as_index=False, sort=False).agg(agg_func_col)
|
|
498
|
+
elif agg_func_col:
|
|
499
|
+
data = _agg_to_dict(agg_func_col, df, column_name_to_alias)
|
|
500
|
+
return pd.DataFrame(data)
|
|
501
|
+
elif group_by_col:
|
|
502
|
+
raise ValueError('数据分组后,缺少聚合函数')
|
|
503
|
+
|
|
504
|
+
# 将df的MultiIndex转为Index,并且调整列名格式
|
|
505
|
+
columns = ['#'.join(col).strip().rstrip('#') for col in df.columns.values]
|
|
506
|
+
for k, v in column_name_to_alias.items():
|
|
507
|
+
if v in columns:
|
|
508
|
+
columns[columns.index(v)] = k
|
|
509
|
+
|
|
510
|
+
df.columns = columns
|
|
511
|
+
|
|
512
|
+
return df
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
class HandleExpr:
|
|
516
|
+
def __init__(self, params: Union[Dict, Parameter], struct: Type[Struct] = None, all_enable_dim_columns=None):
|
|
517
|
+
if isinstance(params, dict):
|
|
518
|
+
params = parse_obj_as(Parameter, params)
|
|
519
|
+
self.params = params
|
|
520
|
+
self.struct = struct
|
|
521
|
+
self._all_enable_dim_columns = all_enable_dim_columns
|
|
522
|
+
self.parser = None
|
|
523
|
+
self._load_axis_expr()
|
|
524
|
+
|
|
525
|
+
def _load_axis_expr(self):
|
|
526
|
+
"""将组合(axisRanges)中的字段进行解析"""
|
|
527
|
+
if self.params.queryInfo.axisRanges:
|
|
528
|
+
parser = FilterParser(self.params.queryInfo.axisRanges[0], self._all_enable_dim_columns)
|
|
529
|
+
parser.parse() # 此解析过程会将维度类型字段和对应的维度表达式进行映射
|
|
530
|
+
self.parser = parser
|
|
531
|
+
|
|
532
|
+
def as_string(self) -> str:
|
|
533
|
+
"""将维度表达式作为字符串返回"""
|
|
534
|
+
result = collections.defaultdict(list)
|
|
535
|
+
expr_pov = self.get_pov()
|
|
536
|
+
expr_axis = self.get_axis()
|
|
537
|
+
|
|
538
|
+
for k, v in expr_pov.items():
|
|
539
|
+
result[k].extend(v)
|
|
540
|
+
|
|
541
|
+
for column_name, values in expr_axis.items():
|
|
542
|
+
# 同一个维度可能会同时出现在多个行列组合中,故在axis中也会存在一个字段多个维度表达式的情况
|
|
543
|
+
for value in values:
|
|
544
|
+
k, v = unpack_expr(value)
|
|
545
|
+
# 维度表达式拼接是的字段列名+表达式(筛选器和行列都是都是如此)
|
|
546
|
+
result[column_name].append(v)
|
|
547
|
+
|
|
548
|
+
result = dict_to_expr(result)
|
|
549
|
+
return result
|
|
550
|
+
|
|
551
|
+
def get_dim_expr_dict(self) -> Dict[str, Dict[str, Union[str, List[str]]]]:
|
|
552
|
+
"""将维度表达式以字典形式返回"""
|
|
553
|
+
result = {
|
|
554
|
+
'selectedFilter': self.get_pov(),
|
|
555
|
+
'axisRanges': self.get_axis()
|
|
556
|
+
}
|
|
557
|
+
return result
|
|
558
|
+
|
|
559
|
+
def get_pov(self, include_variable: bool = False) -> Dict[str, Union[List[str]]]:
|
|
560
|
+
"""只获取筛选器上的维度表达式
|
|
561
|
+
|
|
562
|
+
在组合行列中出现的字段如果是被@cur替换过了,
|
|
563
|
+
那么筛选器上的该相同字段的值就失效了,
|
|
564
|
+
此处根据参数决定是否仍然获取失效的字段值
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
include_variable: 是否获取@cur替换后的失效的字段值,默认False不获取
|
|
568
|
+
"""
|
|
569
|
+
expr_dict = collections.defaultdict(list)
|
|
570
|
+
selected = self.params.queryInfo.selected
|
|
571
|
+
|
|
572
|
+
if param := selected.selectedFilter:
|
|
573
|
+
for item in param.selectGroups:
|
|
574
|
+
if item.columnName in self._all_enable_dim_columns:
|
|
575
|
+
expr_dict[item.columnName].extend(item.real_conditions[0].value)
|
|
576
|
+
|
|
577
|
+
if not include_variable:
|
|
578
|
+
return expr_dict
|
|
579
|
+
|
|
580
|
+
if param := selected.selectedParam:
|
|
581
|
+
for item in param.selectGroups:
|
|
582
|
+
if item.columnName in self._all_enable_dim_columns:
|
|
583
|
+
expr_dict[item.columnName].extend(item.real_conditions[0].value)
|
|
584
|
+
|
|
585
|
+
return expr_dict
|
|
586
|
+
|
|
587
|
+
def get_axis(self) -> Dict[str, Union[List[str]]]:
|
|
588
|
+
"""只获取组合中的维度表达式"""
|
|
589
|
+
if self.parser: # 有组合类型(axisRanges)数据
|
|
590
|
+
expr_dict = self.parser.axis_dim_fields_to_expr
|
|
591
|
+
else:
|
|
592
|
+
expr_dict = collections.defaultdict(list)
|
|
593
|
+
return expr_dict
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
class ChartEngine:
|
|
597
|
+
def __init__(self, params: Dict, struct: Type[Struct] = None, before_return=None, date_round=False):
|
|
598
|
+
self.raw_params = params
|
|
599
|
+
self.params = parse_obj_as(Parameter, params)
|
|
600
|
+
self.struct = struct
|
|
601
|
+
self.all_enable_dim_columns = struct.get_dim_fields()
|
|
602
|
+
self.before_return = before_return
|
|
603
|
+
self.date_round = date_round
|
|
604
|
+
self.init_parser_env()
|
|
605
|
+
|
|
606
|
+
def init_parser_env(self):
|
|
607
|
+
set_date_type_fields(
|
|
608
|
+
{col.columnName: col.formatType
|
|
609
|
+
for col in self.params.queryInfo.timeColumns}
|
|
610
|
+
)
|
|
611
|
+
set_dt_precision(
|
|
612
|
+
{col.columnName: col.precision
|
|
613
|
+
for col in self.struct.fields if isinstance(col, Date)}
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
@cached_property
|
|
617
|
+
def filter(self) -> Filter:
|
|
618
|
+
return Filter(self.params, self.struct, self.all_enable_dim_columns, self.date_round)
|
|
619
|
+
|
|
620
|
+
@cached_property
|
|
621
|
+
def expr(self) -> HandleExpr:
|
|
622
|
+
return HandleExpr(self.params, self.struct, self.all_enable_dim_columns)
|
|
623
|
+
|
|
624
|
+
def get_dim_expr_dict(self) -> Dict[str, Dict[str, Union[str, List[str]]]]:
|
|
625
|
+
"""以字典格式获取维度表达式结果"""
|
|
626
|
+
return self.expr.get_dim_expr_dict()
|
|
627
|
+
|
|
628
|
+
def get_dim_expr_str(self) -> str:
|
|
629
|
+
"""以字符串格式获取维度表达式结果"""
|
|
630
|
+
return self.expr.as_string()
|
|
631
|
+
|
|
632
|
+
def get_pov(self, include_variable: bool = False) -> Dict[str, Union[List[str]]]:
|
|
633
|
+
"""只获取筛选器上的维度表达式
|
|
634
|
+
|
|
635
|
+
在组合行列中出现的字段如果是被@cur替换过了,
|
|
636
|
+
那么筛选器上的该相同字段的值就失效了,
|
|
637
|
+
此处根据参数决定是否仍然获取失效的字段值
|
|
638
|
+
|
|
639
|
+
Args:
|
|
640
|
+
include_variable: 是否获取@cur替换后的失效的字段值,默认False不获取
|
|
641
|
+
"""
|
|
642
|
+
return self.expr.get_pov(include_variable)
|
|
643
|
+
|
|
644
|
+
def get_axis(self) -> Dict[str, Union[List[str]]]:
|
|
645
|
+
"""获取行列组合上的维度表达式"""
|
|
646
|
+
return self.expr.get_axis()
|
|
647
|
+
|
|
648
|
+
def get_sql(self, exclude_dim_expr: bool = True) -> str:
|
|
649
|
+
"""获取MySql格式的where条件表达式
|
|
650
|
+
|
|
651
|
+
Args:
|
|
652
|
+
exclude_dim_expr: 过滤时是否去除维度表达式,默认True:去除
|
|
653
|
+
"""
|
|
654
|
+
return self.filter.as_sql(exclude_dim_expr=exclude_dim_expr)
|
|
655
|
+
|
|
656
|
+
def apply_filter(
|
|
657
|
+
self,
|
|
658
|
+
df: pd.DataFrame,
|
|
659
|
+
exclude_dim_expr: bool = True
|
|
660
|
+
) -> pd.DataFrame:
|
|
661
|
+
"""根据筛选条件对数据源进行过滤处理
|
|
662
|
+
|
|
663
|
+
Args:
|
|
664
|
+
df: 要进行过滤的数据源
|
|
665
|
+
exclude_dim_expr: 过滤时是否去除维度表达式,默认True:去除
|
|
666
|
+
"""
|
|
667
|
+
df = self.filter.apply(df, exclude_dim_expr=exclude_dim_expr)
|
|
668
|
+
return self._format_date(df)
|
|
669
|
+
|
|
670
|
+
def apply_agg(self, df: pd.DataFrame, date_formated: bool = False) -> pd.DataFrame:
|
|
671
|
+
"""根据分组和聚合函数进行聚合处理"""
|
|
672
|
+
if not date_formated:
|
|
673
|
+
df = self._format_date(df)
|
|
674
|
+
return self.filter.agg(df)
|
|
675
|
+
|
|
676
|
+
def _format_date(self, df: pd.DataFrame):
|
|
677
|
+
"""对日期类型进行格式化处理"""
|
|
678
|
+
for column in self.params.queryInfo.timeColumns:
|
|
679
|
+
column_name = column.columnName
|
|
680
|
+
if column_name in df.columns:
|
|
681
|
+
df[column_name] = pd.to_datetime(df[column_name])
|
|
682
|
+
|
|
683
|
+
if column.formatType == FieldType.year:
|
|
684
|
+
df[column_name] = df[column_name].dt.strftime('%Y')
|
|
685
|
+
elif column.formatType == FieldType.month:
|
|
686
|
+
df[column_name] = df[column_name].dt.strftime('%Y-%m')
|
|
687
|
+
elif column.formatType == FieldType.day:
|
|
688
|
+
df[column_name] = df[column_name].dt.strftime('%Y-%m-%d')
|
|
689
|
+
elif column.formatType == FieldType.quarter:
|
|
690
|
+
year_col = df[column_name].dt.strftime('%Y-')
|
|
691
|
+
quarter_col = df[column_name].dt.quarter.astype('str')
|
|
692
|
+
df[column_name] = year_col + 'Q' + quarter_col
|
|
693
|
+
|
|
694
|
+
df[column_name] = df[column_name].fillna('')
|
|
695
|
+
return df
|
|
696
|
+
|
|
697
|
+
def _main(self, df: DataFrame):
|
|
698
|
+
"""入口函数"""
|
|
699
|
+
after_where_df = self.apply_filter(df, exclude_dim_expr=False)
|
|
700
|
+
df = self.apply_agg(after_where_df, True)
|
|
701
|
+
if self.before_return:
|
|
702
|
+
df = self.before_return(df, self.raw_params)
|
|
703
|
+
return df
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
# -----------------------------------------------------------------------------
|
|
707
|
+
# main decorator
|
|
708
|
+
def _resolve_param(args: tuple):
|
|
709
|
+
if len(args) == 2:
|
|
710
|
+
return args[1]
|
|
711
|
+
if len(args) == 1:
|
|
712
|
+
return args[0]
|
|
713
|
+
raise ValueError("Bad signature for main function.")
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
def as_datasource(
|
|
717
|
+
func=None,
|
|
718
|
+
struct: Type[Struct] = None,
|
|
719
|
+
engine: Optional[Type[ChartEngine]] = ChartEngine,
|
|
720
|
+
before_return: Optional[Callable[[pd.DataFrame, Dict], pd.DataFrame]] = None,
|
|
721
|
+
date_round: bool = False
|
|
722
|
+
):
|
|
723
|
+
"""用作图表数据源的main函数装饰器
|
|
724
|
+
|
|
725
|
+
Args:
|
|
726
|
+
func: main方法
|
|
727
|
+
struct: 定义字段及其字段类型的类名称,必填
|
|
728
|
+
engine: 用于处理结果DataFrame的engine, 默认为ChartEngine;
|
|
729
|
+
如需自定义, 需继承ChartEngine;
|
|
730
|
+
为None时, 不对结果DataFrame作处理
|
|
731
|
+
before_return: 自定义同步function,作为ChartEngine处理的后置逻辑
|
|
732
|
+
接受处理后的DataFrame和来自图表原始参数为入参
|
|
733
|
+
date_round: 是否允许低精度日期值与筛选条件内高精度日期值进行比较,默认不允许;
|
|
734
|
+
允许后则精度缺失(例如2012与2012-10,缺失了月份部分,而2011与2012-10本身在年份可比,不属于精度缺失)时,
|
|
735
|
+
除了相等以外,统一判定为不符合条件
|
|
736
|
+
|
|
737
|
+
"""
|
|
738
|
+
if func is None:
|
|
739
|
+
return functools.partial(as_datasource,
|
|
740
|
+
before_return=before_return,
|
|
741
|
+
engine=engine,
|
|
742
|
+
struct=struct)
|
|
743
|
+
|
|
744
|
+
if struct is None:
|
|
745
|
+
raise ValueError("需定义图表数据源的字段信息")
|
|
746
|
+
|
|
747
|
+
if engine and not issubclass(engine, ChartEngine):
|
|
748
|
+
raise TypeError(f"engine参数应为ChartEngine子类")
|
|
749
|
+
|
|
750
|
+
if before_return is not None and not isfunction(before_return):
|
|
751
|
+
raise TypeError(f"before_return参数应为函数")
|
|
752
|
+
|
|
753
|
+
if asyncio.iscoroutinefunction(func):
|
|
754
|
+
async def wrapper(*args):
|
|
755
|
+
param = _resolve_param(args)
|
|
756
|
+
if param == FLAG_FOR_META:
|
|
757
|
+
return struct.to_dict()
|
|
758
|
+
|
|
759
|
+
df = await func(*args)
|
|
760
|
+
if engine is not None:
|
|
761
|
+
handler = engine(param, struct=struct, before_return=before_return, date_round=date_round)
|
|
762
|
+
df = handler._main(df) # noqa
|
|
763
|
+
|
|
764
|
+
return df.to_dict('records')
|
|
765
|
+
else:
|
|
766
|
+
def wrapper(*args):
|
|
767
|
+
param = _resolve_param(args)
|
|
768
|
+
if param == FLAG_FOR_META:
|
|
769
|
+
return struct.to_dict()
|
|
770
|
+
|
|
771
|
+
df = func(*args)
|
|
772
|
+
if engine is not None:
|
|
773
|
+
handler = engine(param, struct=struct, before_return=before_return, date_round=date_round)
|
|
774
|
+
df = handler._main(df) # noqa
|
|
775
|
+
|
|
776
|
+
return df.to_dict('records')
|
|
777
|
+
|
|
778
|
+
return functools.wraps(func)(wrapper)
|