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
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from operator import attrgetter
|
|
6
|
+
from typing import List
|
|
7
|
+
from deepfos.lib.constant import SHAREDMEMBER
|
|
8
|
+
from .filters import *
|
|
9
|
+
from .dimexpr import DimExprAnalysor
|
|
10
|
+
|
|
11
|
+
filter_map = {
|
|
12
|
+
"or": OrFilter,
|
|
13
|
+
"and": AndFilter,
|
|
14
|
+
"nor": NorFilter,
|
|
15
|
+
"nand": NAndFilter,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
NAME_DFLT = "name"
|
|
19
|
+
PNAME_DFLT = "parent_name"
|
|
20
|
+
IS_SHARED_DFLT = SHAREDMEMBER
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def pack_filter(member_expr, filters):
|
|
24
|
+
"""给维度表达式增加 ``Attr`` , ``Remove`` ,以及各种 ``Filter`` """
|
|
25
|
+
if not filters:
|
|
26
|
+
return member_expr
|
|
27
|
+
|
|
28
|
+
rtn = member_expr
|
|
29
|
+
for fltr in filters:
|
|
30
|
+
if isinstance(fltr, BaseFilter):
|
|
31
|
+
attr_conds = (f"Attr({k},{v!r})" for k, v in fltr.attr_kv_pairs.items())
|
|
32
|
+
rtn = f"{fltr}({rtn},{','.join(attr_conds)})"
|
|
33
|
+
else:
|
|
34
|
+
rtn = f"{fltr}({rtn},{','.join(map(str, fltr.to_remove))})"
|
|
35
|
+
|
|
36
|
+
return rtn
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def flatten(nested_list):
|
|
40
|
+
"""
|
|
41
|
+
展开多级列表
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
nested_list: 多级列表
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
只含简单元素的列表
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
rtn = []
|
|
51
|
+
for item in nested_list:
|
|
52
|
+
if isinstance(item, list):
|
|
53
|
+
rtn.extend(flatten(item))
|
|
54
|
+
else:
|
|
55
|
+
rtn.append(item)
|
|
56
|
+
return rtn
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class AbstractMember(ABC):
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def members(self):
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def __str__(self):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def contribute(self, value):
|
|
69
|
+
weight = getattr(self, 'weight', 1)
|
|
70
|
+
if not weight:
|
|
71
|
+
weight = 1
|
|
72
|
+
return value * float(weight)
|
|
73
|
+
|
|
74
|
+
def calculate(self, **args):
|
|
75
|
+
return sum(args.values())
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class MemberBase(AbstractMember):
|
|
79
|
+
def __init__(self, name):
|
|
80
|
+
self.name = name
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def members(self):
|
|
84
|
+
"""
|
|
85
|
+
由 `DimMember`` 继承,返回只包含节点自身列表
|
|
86
|
+
"""
|
|
87
|
+
return [self]
|
|
88
|
+
|
|
89
|
+
def __str__(self):
|
|
90
|
+
return self.name
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class MemberContainer(AbstractMember):
|
|
94
|
+
"""维度成员容器,成员容器旨在一次性使用。"""
|
|
95
|
+
def __init__(self, *anchor_mbrs, hierarchy=None):
|
|
96
|
+
self.anchor_mbrs = list(anchor_mbrs)
|
|
97
|
+
# self.anchor_mbrs = anchor_mbr if isinstance(anchor_mbr, list) else [anchor_mbr]
|
|
98
|
+
self.hierarchy = hierarchy
|
|
99
|
+
self._reversed = False
|
|
100
|
+
self._filters = []
|
|
101
|
+
|
|
102
|
+
def _get_all_member(self):
|
|
103
|
+
"""返回容器中的所有维度成员,如果维度成员含有层级,则以层级结果代替原维度成员。"""
|
|
104
|
+
if self.hierarchy is None:
|
|
105
|
+
return self.anchor_mbrs
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def members(self):
|
|
109
|
+
"""
|
|
110
|
+
返回容器中所有维度成员, ``reverse`` 设置返回顺序, 默认正序。
|
|
111
|
+
如果有过滤操作,则先执行过滤,再返回结果。
|
|
112
|
+
"""
|
|
113
|
+
reverse_flag = -1 if self._reversed else 1
|
|
114
|
+
return self.apply_filter(self._get_all_member()[::reverse_flag])
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def data(self):
|
|
118
|
+
"""返回容器中所有维度成员的维度成员名。"""
|
|
119
|
+
return [member.name for member in self.members]
|
|
120
|
+
|
|
121
|
+
def apply_filter(self, total):
|
|
122
|
+
"""
|
|
123
|
+
执行过滤器列表中所有过滤器。
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
total(list): 需要过滤的所有维度成员
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
符合过滤条件的维度成员列表,存储在 ``selected`` 中。
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
remain = total
|
|
133
|
+
for fltr in self._filters:
|
|
134
|
+
remain = fltr.apply_to(remain)
|
|
135
|
+
return remain
|
|
136
|
+
|
|
137
|
+
def reverse(self):
|
|
138
|
+
"""逆序输出维度成员。"""
|
|
139
|
+
self._reversed = True
|
|
140
|
+
return self
|
|
141
|
+
|
|
142
|
+
def __getitem__(self, item):
|
|
143
|
+
"""
|
|
144
|
+
正序或逆序输出容器中的维度成员。
|
|
145
|
+
|
|
146
|
+
Examples:
|
|
147
|
+
literal blocks::
|
|
148
|
+
``MemberContainer``[::1]
|
|
149
|
+
|
|
150
|
+
``MemberContainer``[1:3:1]
|
|
151
|
+
|
|
152
|
+
Warnings:
|
|
153
|
+
只支持正序或者逆序输出全部维度成员,不支持只输出切片的部分成员。
|
|
154
|
+
上面两个例子的效果相同。
|
|
155
|
+
|
|
156
|
+
"""
|
|
157
|
+
if not isinstance(item, slice):
|
|
158
|
+
raise TypeError("Only slice type is supported")
|
|
159
|
+
if item.step == -1:
|
|
160
|
+
self._reversed = True
|
|
161
|
+
return self
|
|
162
|
+
|
|
163
|
+
def __str__(self):
|
|
164
|
+
"""
|
|
165
|
+
显示当前维度成员集对应的维度表达式。
|
|
166
|
+
|
|
167
|
+
Warning:
|
|
168
|
+
调用的 ``pack_filter`` 中会调用 ``str`` 方法,因此这里有递归。
|
|
169
|
+
|
|
170
|
+
"""
|
|
171
|
+
order = 1 if self._reversed else 0
|
|
172
|
+
|
|
173
|
+
total_expr = []
|
|
174
|
+
for mbr in self.anchor_mbrs:
|
|
175
|
+
if self.hierarchy is None:
|
|
176
|
+
expr = str(mbr)
|
|
177
|
+
else:
|
|
178
|
+
expr = f"{self.hierarchy}({mbr},{order})"
|
|
179
|
+
total_expr.append(pack_filter(expr, self._filters))
|
|
180
|
+
return ';'.join(total_expr)
|
|
181
|
+
|
|
182
|
+
def where(self, method, **kwargs):
|
|
183
|
+
"""
|
|
184
|
+
筛选维度成员。
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
method(str): 筛选维度成员的方法,共有 ``and`` , ``nand`` , ``or`` , ``nor`` 四种
|
|
188
|
+
**kwargs(dict): 筛选条件
|
|
189
|
+
|
|
190
|
+
Notes:
|
|
191
|
+
不返回结果,只有在显示容器中的维度成员时,才会执行所有过滤器,获得结果。
|
|
192
|
+
|
|
193
|
+
Raises:
|
|
194
|
+
ValueError: ``method`` 不在于四种方法中或者筛选条件 ``**kwargs`` 为空。
|
|
195
|
+
|
|
196
|
+
"""
|
|
197
|
+
filter_cls = filter_map.get(method.lower())
|
|
198
|
+
|
|
199
|
+
if filter_cls is None:
|
|
200
|
+
raise ValueError(f"Unsupported method: {method}.")
|
|
201
|
+
|
|
202
|
+
if not kwargs:
|
|
203
|
+
raise ValueError("Filter condition should not be empty.")
|
|
204
|
+
|
|
205
|
+
self._filters.append(filter_cls(kwargs))
|
|
206
|
+
return self
|
|
207
|
+
|
|
208
|
+
def remove(self, *to_remove: AbstractMember):
|
|
209
|
+
"""
|
|
210
|
+
从已选择的维度成员中移除维度成员。
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
*to_remove(DimMember): 需要从维度成员容器中移除的维度成员
|
|
214
|
+
|
|
215
|
+
Notes:
|
|
216
|
+
不返回任何结果,只将过滤器加入到过滤器列表中。
|
|
217
|
+
|
|
218
|
+
"""
|
|
219
|
+
to_remove = _as_abstract_member(to_remove)
|
|
220
|
+
self._filters.append(RemoveFilter(to_remove, attrgetter('members')))
|
|
221
|
+
return self
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def attr(self):
|
|
225
|
+
"""
|
|
226
|
+
属性映射,返回容器中维度成员对应的属性值,
|
|
227
|
+
属性为多个时,对每个维度成员都进行指定属性的返回。
|
|
228
|
+
"""
|
|
229
|
+
return AttrMapper(self.members)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def finalize(func):
|
|
233
|
+
@functools.wraps(func)
|
|
234
|
+
def wrapper(self, *args, **kwargs):
|
|
235
|
+
for idx, obj in enumerate(self.selected):
|
|
236
|
+
if isinstance(obj, Puppet):
|
|
237
|
+
self.selected[idx] = obj.puppet_apply()
|
|
238
|
+
return func(self, *args, **kwargs)
|
|
239
|
+
return wrapper
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class DimensionBase:
|
|
243
|
+
"""维度基类"""
|
|
244
|
+
def __init__(self, dim_name=None):
|
|
245
|
+
self.name = dim_name
|
|
246
|
+
self.selected = []
|
|
247
|
+
|
|
248
|
+
def select(self, *dim_members: AbstractMember):
|
|
249
|
+
"""选择维度成员
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
*dim_members: 需要加入到 ``selected`` 中的维度成员
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
不返回任何结果,只改变 ``selected`` 中的维度成员
|
|
256
|
+
|
|
257
|
+
Raises:
|
|
258
|
+
ValueError: 加入的维度成员 ``dim_members`` 为空
|
|
259
|
+
"""
|
|
260
|
+
if not dim_members:
|
|
261
|
+
raise ValueError("No dimension member to select.")
|
|
262
|
+
|
|
263
|
+
self.selected = _as_abstract_member(dim_members)
|
|
264
|
+
return self
|
|
265
|
+
|
|
266
|
+
def to_expr(self):
|
|
267
|
+
"""输出维度表达式
|
|
268
|
+
|
|
269
|
+
获取当前查询集下,等价的维度表达式
|
|
270
|
+
|
|
271
|
+
Example:
|
|
272
|
+
.. code-block:: python
|
|
273
|
+
|
|
274
|
+
# 向 selected 集中添加维度成员
|
|
275
|
+
dim.select(dim['Q1', 'Q2', 'Q3', 'Q4'])
|
|
276
|
+
print(dim.to_expr())
|
|
277
|
+
# 设维度树名为Dimension,则对应的维度表达式如下所示:
|
|
278
|
+
'''
|
|
279
|
+
Dimension{Q1;Q2;Q3;Q4}
|
|
280
|
+
'''
|
|
281
|
+
|
|
282
|
+
"""
|
|
283
|
+
return f"{self.name}{{{self.expr_body}}}"
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
@finalize
|
|
287
|
+
def expr_body(self) -> str:
|
|
288
|
+
"""维度表达式花括号内的部分"""
|
|
289
|
+
return ';'.join(map(str, self.selected))
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
@finalize
|
|
293
|
+
def members(self):
|
|
294
|
+
"""获取当前查询集下,所有维度成员对象"""
|
|
295
|
+
return sum((member.members for member in self.selected), [])
|
|
296
|
+
|
|
297
|
+
@property
|
|
298
|
+
def data(self) -> List[str]:
|
|
299
|
+
"""获取当前查询集下,所有维度成员名"""
|
|
300
|
+
return [member.name for member in self.members]
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def activated(self):
|
|
304
|
+
"""维度是否激活,等价于是否选择了维度成员"""
|
|
305
|
+
return len(self.selected) > 0
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def loc(self):
|
|
309
|
+
"""单次选择维度成员,在清空 ``selected`` 集后加入"""
|
|
310
|
+
return Locator(self)
|
|
311
|
+
|
|
312
|
+
@property
|
|
313
|
+
def exloc(self):
|
|
314
|
+
"""单次选择维度成员,增量加入到 ``selected`` 集"""
|
|
315
|
+
return Locator(self, append=True)
|
|
316
|
+
|
|
317
|
+
def __getitem__(self, item):
|
|
318
|
+
raise NotImplementedError()
|
|
319
|
+
|
|
320
|
+
@contextmanager
|
|
321
|
+
def multi_loc(self, do_remove=False):
|
|
322
|
+
"""在上下文中多次选择维度成员"""
|
|
323
|
+
self.selected = []
|
|
324
|
+
if do_remove:
|
|
325
|
+
yield Locator(self, append=True), Remover(self)
|
|
326
|
+
else:
|
|
327
|
+
yield Locator(self, append=True)
|
|
328
|
+
|
|
329
|
+
def load_expr(self, expr) -> 'DimensionBase':
|
|
330
|
+
"""
|
|
331
|
+
加载维度表达式
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
expr(str): 维度表达式
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
不返回任何结果,改变查询集
|
|
338
|
+
|
|
339
|
+
Notes:
|
|
340
|
+
维度表达式中的维度成员不需要带引号,使用 ``filter`` 时不能直接使用字典形式的键值对,
|
|
341
|
+
而是使用 ``Attr`` 获取属性作为键,而后跟上值,在传入的时候,会以字典的形式传入,
|
|
342
|
+
此时将传入的多参数转变成键值字典,所有调用的属性或者函数必须首字母大写。
|
|
343
|
+
|
|
344
|
+
Examples:
|
|
345
|
+
较为复杂的唯独表达式例,其中 ``uds`` 为额外属性, ``value`` 为指定的属性值:
|
|
346
|
+
|
|
347
|
+
.. code-block:: python
|
|
348
|
+
|
|
349
|
+
dim.load_expr("AndFilter(Base(#root, 0), Attr(uds, value))")
|
|
350
|
+
|
|
351
|
+
"""
|
|
352
|
+
return DimExprAnalysor(self, expr).solve()
|
|
353
|
+
|
|
354
|
+
@contextmanager
|
|
355
|
+
def load_expr_temporary(self, expr):
|
|
356
|
+
"""一次性加载维度表达式,存储结果"""
|
|
357
|
+
selected_bak = self.selected[:]
|
|
358
|
+
try:
|
|
359
|
+
self.load_expr(expr)
|
|
360
|
+
yield
|
|
361
|
+
finally:
|
|
362
|
+
self.selected = selected_bak
|
|
363
|
+
|
|
364
|
+
@property
|
|
365
|
+
def attr(self):
|
|
366
|
+
"""属性映射
|
|
367
|
+
|
|
368
|
+
返回查询集中维度成员对应的属性值,
|
|
369
|
+
属性为多个时,对每个维度成员都进行指定属性的返回
|
|
370
|
+
"""
|
|
371
|
+
return AttrMapper(self.members)
|
|
372
|
+
|
|
373
|
+
@finalize
|
|
374
|
+
def classify_selected(self):
|
|
375
|
+
"""分类查询集
|
|
376
|
+
|
|
377
|
+
将查询集按照 ``DimMember`` 类型和 ``MemberContainer`` 类型分成两类。
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
``DimMember`` 类型的对象列表和 ``MemberContainer`` 类型的对象列表
|
|
381
|
+
|
|
382
|
+
"""
|
|
383
|
+
members = []
|
|
384
|
+
mbr_containers = []
|
|
385
|
+
|
|
386
|
+
for mbr in self.selected:
|
|
387
|
+
if isinstance(mbr, MemberBase):
|
|
388
|
+
members.append(mbr)
|
|
389
|
+
elif isinstance(mbr, MemberContainer):
|
|
390
|
+
mbr_containers.append(mbr)
|
|
391
|
+
return members, mbr_containers
|
|
392
|
+
|
|
393
|
+
def clear(self):
|
|
394
|
+
self.selected.clear()
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class ATTR:
|
|
398
|
+
__slots__ = ('attr', 'callable', 'args', 'kwargs')
|
|
399
|
+
|
|
400
|
+
def __init__(self, attr):
|
|
401
|
+
self.attr = attr
|
|
402
|
+
self.callable = False
|
|
403
|
+
self.args = None
|
|
404
|
+
self.kwargs = None
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
class Puppet:
|
|
408
|
+
"""
|
|
409
|
+
链式调用代理
|
|
410
|
+
|
|
411
|
+
Examples:
|
|
412
|
+
简单的代理,此时不执行操作:
|
|
413
|
+
literal blocks::
|
|
414
|
+
dim.loc('Q1').iter_to_root()
|
|
415
|
+
|
|
416
|
+
"""
|
|
417
|
+
def __init__(self, bound):
|
|
418
|
+
"""bound的类型时DimMember"""
|
|
419
|
+
self.__bound = bound
|
|
420
|
+
self.__stack = []
|
|
421
|
+
|
|
422
|
+
def __getattr__(self, item):
|
|
423
|
+
"""成员属性"""
|
|
424
|
+
self.__stack.append(ATTR(item))
|
|
425
|
+
return self
|
|
426
|
+
|
|
427
|
+
def __call__(self, *args, **kwargs):
|
|
428
|
+
"""成员方法"""
|
|
429
|
+
last_attr = self.__stack[-1]
|
|
430
|
+
last_attr.callable = True
|
|
431
|
+
last_attr.args = args
|
|
432
|
+
last_attr.kwargs = kwargs
|
|
433
|
+
|
|
434
|
+
def puppet_apply(self):
|
|
435
|
+
rslt = self.__bound
|
|
436
|
+
for attr in self.__stack:
|
|
437
|
+
if attr.callable:
|
|
438
|
+
rslt = getattr(rslt, attr.attr)(*attr.args, **attr.kwargs)
|
|
439
|
+
else:
|
|
440
|
+
rslt = getattr(rslt, attr.attr)
|
|
441
|
+
return rslt
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class Locator:
|
|
445
|
+
"""选择维度成员加入 ``selected`` 集合中"""
|
|
446
|
+
def __init__(self, dim_obj: DimensionBase, append=False):
|
|
447
|
+
"""
|
|
448
|
+
Args:
|
|
449
|
+
dim_obj: 维度树
|
|
450
|
+
append: 选择增量还是全量,增量则会在原基础上加入,全量则清除后加入
|
|
451
|
+
|
|
452
|
+
Warnings:
|
|
453
|
+
默认将当前selected集合中的维度成员加入
|
|
454
|
+
"""
|
|
455
|
+
self._append = append
|
|
456
|
+
self._obj = dim_obj
|
|
457
|
+
self._puppets = dim_obj.selected
|
|
458
|
+
|
|
459
|
+
def __call__(self, *items):
|
|
460
|
+
"""items为维度成员名"""
|
|
461
|
+
if len(items) == 1:
|
|
462
|
+
item = items[0]
|
|
463
|
+
else:
|
|
464
|
+
item = items
|
|
465
|
+
puppet = Puppet(self._obj[item])
|
|
466
|
+
if not self._append:
|
|
467
|
+
self._puppets.clear()
|
|
468
|
+
self._puppets.append(puppet)
|
|
469
|
+
return puppet
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
class Remover:
|
|
473
|
+
def __init__(self, dim_obj: DimensionBase):
|
|
474
|
+
self._obj = dim_obj
|
|
475
|
+
|
|
476
|
+
def __call__(self, *items):
|
|
477
|
+
if len(items) == 1:
|
|
478
|
+
item = items[0]
|
|
479
|
+
else:
|
|
480
|
+
item = items
|
|
481
|
+
puppet = Puppet(self._obj[item])
|
|
482
|
+
return puppet
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
class AttrMapper:
|
|
486
|
+
"""属性映射"""
|
|
487
|
+
def __init__(self, obj_itor):
|
|
488
|
+
self._obj_itor = obj_itor
|
|
489
|
+
|
|
490
|
+
def __getitem__(self, item):
|
|
491
|
+
if isinstance(item, str):
|
|
492
|
+
return [getattr(obj, item) for obj in self._obj_itor]
|
|
493
|
+
|
|
494
|
+
elif isinstance(item, Iterable):
|
|
495
|
+
return [self[it] for it in item]
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def _as_abstract_member(members) -> List[AbstractMember]:
|
|
499
|
+
rtn = []
|
|
500
|
+
for mbr in members:
|
|
501
|
+
if isinstance(mbr, Puppet):
|
|
502
|
+
mbr = mbr.puppet_apply()
|
|
503
|
+
if not isinstance(mbr, AbstractMember):
|
|
504
|
+
raise TypeError(f"Expect type: {AbstractMember}, got {type(mbr)}.")
|
|
505
|
+
rtn.append(mbr)
|
|
506
|
+
return rtn
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from deepfos.options import OPTION
|
|
3
|
+
from .dimmember import DimMember
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
'TreeCreator',
|
|
7
|
+
'JsonTreeCreator', 'DBTreeCreator',
|
|
8
|
+
'DataFrameTreeCreator', 'ApiTreeCreator'
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TreeCreator:
|
|
13
|
+
def __init__(self, raw_data):
|
|
14
|
+
self.raw = raw_data
|
|
15
|
+
|
|
16
|
+
def iter_node(self):
|
|
17
|
+
"""迭代节点, 取出数据中的每条元数据"""
|
|
18
|
+
raise NotImplementedError()
|
|
19
|
+
|
|
20
|
+
def get_node_name(self, node):
|
|
21
|
+
"""获取节点名"""
|
|
22
|
+
raise NotImplementedError()
|
|
23
|
+
|
|
24
|
+
def get_node_parent_name(self, node):
|
|
25
|
+
"""获取父节点名"""
|
|
26
|
+
raise NotImplementedError()
|
|
27
|
+
|
|
28
|
+
def get_node_is_shared(self, node):
|
|
29
|
+
"""获取父节点名"""
|
|
30
|
+
raise NotImplementedError()
|
|
31
|
+
|
|
32
|
+
def set_extra_attrs(self, member, node):
|
|
33
|
+
"""设置节点额外属性"""
|
|
34
|
+
raise NotImplementedError()
|
|
35
|
+
|
|
36
|
+
def create_tree(self):
|
|
37
|
+
"""
|
|
38
|
+
建树
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: 节点名不为 ``str`` 或者多个根节点
|
|
42
|
+
ValueError: 无根节点
|
|
43
|
+
"""
|
|
44
|
+
dim_memo = {}
|
|
45
|
+
|
|
46
|
+
root = None
|
|
47
|
+
for node in self.iter_node():
|
|
48
|
+
name = self.get_node_name(node)
|
|
49
|
+
is_shared = self.get_node_is_shared(node)
|
|
50
|
+
|
|
51
|
+
if name not in dim_memo:
|
|
52
|
+
dim_memo[name] = DimMember(name)
|
|
53
|
+
member = dim_memo[name]
|
|
54
|
+
|
|
55
|
+
if is_shared:
|
|
56
|
+
shared_mbr = DimMember(name)
|
|
57
|
+
self.set_extra_attrs(shared_mbr, node)
|
|
58
|
+
member.add_shared(shared_mbr)
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
self.set_extra_attrs(member, node)
|
|
62
|
+
|
|
63
|
+
parent_name = self.get_node_parent_name(node)
|
|
64
|
+
|
|
65
|
+
if not isinstance(parent_name, str) or not parent_name:
|
|
66
|
+
if root is not None:
|
|
67
|
+
raise ValueError("Cannot create tree because there are more than one root in data.")
|
|
68
|
+
root = member
|
|
69
|
+
else:
|
|
70
|
+
if parent_name not in dim_memo:
|
|
71
|
+
parent = DimMember(parent_name)
|
|
72
|
+
dim_memo[parent_name] = parent
|
|
73
|
+
member.set_parent(dim_memo[parent_name])
|
|
74
|
+
|
|
75
|
+
if root is None:
|
|
76
|
+
raise ValueError("No root found from given data.")
|
|
77
|
+
|
|
78
|
+
return root, dim_memo
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class DataFrameTreeCreator(TreeCreator):
|
|
82
|
+
def __init__(self, raw_data, name_col, parent_name_col, is_shared_col, extra_attrs=None):
|
|
83
|
+
super().__init__(raw_data)
|
|
84
|
+
self.is_shared_col = is_shared_col
|
|
85
|
+
self.parent_name_col = parent_name_col
|
|
86
|
+
self.name_col = name_col
|
|
87
|
+
self.extra_attrs = extra_attrs
|
|
88
|
+
|
|
89
|
+
def iter_node(self):
|
|
90
|
+
for row in self.raw.itertuples(index=False):
|
|
91
|
+
yield row
|
|
92
|
+
|
|
93
|
+
def get_node_name(self, node):
|
|
94
|
+
return getattr(node, self.name_col)
|
|
95
|
+
|
|
96
|
+
def get_node_parent_name(self, node):
|
|
97
|
+
return getattr(node, self.parent_name_col)
|
|
98
|
+
|
|
99
|
+
def get_node_is_shared(self, node):
|
|
100
|
+
return getattr(node, self.is_shared_col)
|
|
101
|
+
|
|
102
|
+
def set_extra_attrs(self, member, node):
|
|
103
|
+
"""
|
|
104
|
+
设置节点额外属性
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
member(DimMember): 节点对象
|
|
108
|
+
node: 一条元数据
|
|
109
|
+
"""
|
|
110
|
+
extra_attrs = self.extra_attrs or self.raw.columns
|
|
111
|
+
|
|
112
|
+
for attr in extra_attrs:
|
|
113
|
+
if hasattr(node, attr):
|
|
114
|
+
val = getattr(node, attr)
|
|
115
|
+
else:
|
|
116
|
+
val = getattr(node, attr.lower(), None)
|
|
117
|
+
|
|
118
|
+
setattr(member, attr, val)
|
|
119
|
+
member.extra_attrs = tuple(extra_attrs)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class DBTreeCreator(DataFrameTreeCreator):
|
|
123
|
+
def __init__(self, dbconn, dimname, name_col, parent_name_col, is_shared_col, extra_attrs=None):
|
|
124
|
+
self.dimname = dimname
|
|
125
|
+
self.dbconn = dbconn
|
|
126
|
+
|
|
127
|
+
super().__init__(self.get_raw_data(), name_col, parent_name_col, is_shared_col, extra_attrs)
|
|
128
|
+
|
|
129
|
+
def get_raw_data(self):
|
|
130
|
+
"""从数据库中筛选出数据表建树"""
|
|
131
|
+
return self.dbconn.query_dfs(f"SELECT * FROM {self.get_dim_table()}")
|
|
132
|
+
|
|
133
|
+
def get_dim_table(self):
|
|
134
|
+
sql = f"SELECT table_dimension FROM app{OPTION.system.app_id}_dimension_info WHERE name='{self.dimname}'"
|
|
135
|
+
return self.dbconn.query_dfs(sql).iloc[0, 0]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class JsonTreeCreator(DataFrameTreeCreator):
|
|
139
|
+
def iter_node(self):
|
|
140
|
+
for node in self.raw:
|
|
141
|
+
yield node
|
|
142
|
+
|
|
143
|
+
def get_node_name(self, node):
|
|
144
|
+
return node.get(self.name_col)
|
|
145
|
+
|
|
146
|
+
def get_node_parent_name(self, node):
|
|
147
|
+
return node.get(self.parent_name_col)
|
|
148
|
+
|
|
149
|
+
def get_node_is_shared(self, node):
|
|
150
|
+
return node.get(self.is_shared_col)
|
|
151
|
+
|
|
152
|
+
def set_extra_attrs(self, member, node):
|
|
153
|
+
if self.extra_attrs is None:
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
member.extra_attrs = tuple(self.extra_attrs)
|
|
157
|
+
|
|
158
|
+
for attr in self.extra_attrs:
|
|
159
|
+
if attr in node:
|
|
160
|
+
val = node[attr]
|
|
161
|
+
else:
|
|
162
|
+
val = node.get(attr.lower(), None)
|
|
163
|
+
setattr(member, attr, val)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class ApiTreeCreator(DataFrameTreeCreator):
|
|
167
|
+
def __init__(
|
|
168
|
+
self, dimname, api,
|
|
169
|
+
name_col, parent_name_col, is_shared_col,
|
|
170
|
+
extra_attrs=None, fetch_all=False
|
|
171
|
+
):
|
|
172
|
+
super().__init__(None, name_col, parent_name_col, is_shared_col, extra_attrs)
|
|
173
|
+
self.dimname = dimname
|
|
174
|
+
self.api = api
|
|
175
|
+
self.fetch_all = fetch_all
|
|
176
|
+
self.raw = self.get_raw_data()
|
|
177
|
+
|
|
178
|
+
def get_raw_data(self):
|
|
179
|
+
if self.fetch_all:
|
|
180
|
+
cols = ''
|
|
181
|
+
else:
|
|
182
|
+
cols = ','.join((*self.extra_attrs, self.name_col, self.parent_name_col))
|
|
183
|
+
rslt = self.api.api_get_dimmbr(self.dimname + "{IDescendant(#root,0)}", cols, '1')["resultList"]
|
|
184
|
+
return pd.DataFrame(rslt)
|