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,271 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from deepfos.core.logictable.nodemixin import NodeMixin, TreeRenderer
|
|
3
|
+
from deepfos.lib.decorator import cached_property
|
|
4
|
+
|
|
5
|
+
_RE_ESCAPE_QUOTE = re.compile("^'(?P<body>.*)'")
|
|
6
|
+
_RE_ESCAPE_DQUOTE = re.compile('^"(?P<body>.*)"')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ASTNode(NodeMixin):
|
|
10
|
+
def __init__(self, value: str):
|
|
11
|
+
self.value = value
|
|
12
|
+
self.is_quoted = False
|
|
13
|
+
|
|
14
|
+
def __str__(self):
|
|
15
|
+
return self.value
|
|
16
|
+
|
|
17
|
+
@cached_property
|
|
18
|
+
def eval(self):
|
|
19
|
+
"""解析维度表达式,如果为数字,直接返回,否则提取维度操作返回"""
|
|
20
|
+
val = self.value
|
|
21
|
+
if val.isdigit():
|
|
22
|
+
return int(val)
|
|
23
|
+
for patt in (_RE_ESCAPE_QUOTE, _RE_ESCAPE_DQUOTE):
|
|
24
|
+
rslt = patt.match(val)
|
|
25
|
+
if rslt is not None:
|
|
26
|
+
self.is_quoted = True
|
|
27
|
+
return rslt.group('body')
|
|
28
|
+
|
|
29
|
+
return self.value
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ASTOpBase(NodeMixin):
|
|
33
|
+
"""维度表达式语法树中的维度操作节点"""
|
|
34
|
+
def __init__(self, dim_obj, op_name):
|
|
35
|
+
"""
|
|
36
|
+
Args:
|
|
37
|
+
dim_obj(Dimension): 维度表达式操作的维度树
|
|
38
|
+
op_name: 维度操作名
|
|
39
|
+
"""
|
|
40
|
+
self._op_name = op_name
|
|
41
|
+
self._dim_obj = dim_obj
|
|
42
|
+
|
|
43
|
+
def solve(self):
|
|
44
|
+
"""执行该节点的维度操作"""
|
|
45
|
+
args = self.collect_args()
|
|
46
|
+
return self.calc(args)
|
|
47
|
+
|
|
48
|
+
def calc(self, args):
|
|
49
|
+
raise NotImplementedError()
|
|
50
|
+
|
|
51
|
+
def eval(self, node: ASTNode):
|
|
52
|
+
return node.eval
|
|
53
|
+
|
|
54
|
+
def collect_args(self):
|
|
55
|
+
"""收集维度操作所需的参数"""
|
|
56
|
+
args = []
|
|
57
|
+
for child in self.children:
|
|
58
|
+
if isinstance(child, ASTNode):
|
|
59
|
+
args.append(self.eval(child))
|
|
60
|
+
elif isinstance(child, ASTOpBase):
|
|
61
|
+
args.append(child.solve())
|
|
62
|
+
else:
|
|
63
|
+
raise TypeError(f"Expect type {ASTOpBase} or {ASTNode}, got {type(child)}")
|
|
64
|
+
|
|
65
|
+
return args
|
|
66
|
+
|
|
67
|
+
def _get_member(self, mbr):
|
|
68
|
+
"""获取语法树中指定节点"""
|
|
69
|
+
if isinstance(mbr, str):
|
|
70
|
+
return self._dim_obj[mbr]
|
|
71
|
+
if isinstance(mbr, int):
|
|
72
|
+
return self._dim_obj[str(mbr)]
|
|
73
|
+
return mbr
|
|
74
|
+
|
|
75
|
+
def __str__(self):
|
|
76
|
+
"""直接打印当前维度操作节点的操作名"""
|
|
77
|
+
return self._op_name
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ASTOpHierachy(ASTOpBase):
|
|
81
|
+
"""
|
|
82
|
+
Operator class for getting dimension hierachy
|
|
83
|
+
维度表达式语法树中的层级操作节点
|
|
84
|
+
"""
|
|
85
|
+
def calc(self, args):
|
|
86
|
+
"""
|
|
87
|
+
计算当前节点的层级结果
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
args: 层级操作所需的参数
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
返回当前节点的层级结果列表
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
hierachy = self._op_name
|
|
97
|
+
member, reverse, *remain = args
|
|
98
|
+
order = -1 if reverse != 0 else 1
|
|
99
|
+
|
|
100
|
+
dim_container = self._dim_obj[str(member)]
|
|
101
|
+
if hierachy in {'Level', 'ILevel'}:
|
|
102
|
+
start, stop = remain
|
|
103
|
+
return getattr(dim_container, hierachy)[start:stop:order]
|
|
104
|
+
else:
|
|
105
|
+
return getattr(dim_container, hierachy)[::order]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ASTOpAttr(ASTOpBase):
|
|
109
|
+
def eval(self, node: ASTNode):
|
|
110
|
+
val = node.eval
|
|
111
|
+
if node.is_quoted:
|
|
112
|
+
return {'N': False, 'Y': True}.get(val, val)
|
|
113
|
+
else:
|
|
114
|
+
return val
|
|
115
|
+
|
|
116
|
+
def calc(self, args):
|
|
117
|
+
"""构建 ``filter`` 过滤字典"""
|
|
118
|
+
key, val = args
|
|
119
|
+
return {key: val}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ASTOpFilter(ASTOpBase):
|
|
123
|
+
"""过滤操作语法树节点"""
|
|
124
|
+
filter_map = {
|
|
125
|
+
"OrFilter": "or",
|
|
126
|
+
"AndFilter": "and",
|
|
127
|
+
"NorFilter": "nor",
|
|
128
|
+
"NAndFilter": "nand",
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
def calc(self, args):
|
|
132
|
+
_filter = self.filter_map[self._op_name]
|
|
133
|
+
dim_container, *conditions = args
|
|
134
|
+
dim_container = self._get_member(dim_container)
|
|
135
|
+
|
|
136
|
+
all_cond = {}
|
|
137
|
+
for cond in conditions:
|
|
138
|
+
all_cond.update(cond)
|
|
139
|
+
|
|
140
|
+
return dim_container.where(_filter, **all_cond)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class ASTOpRemove(ASTOpBase):
|
|
144
|
+
"""删除操作语法树节点"""
|
|
145
|
+
def calc(self, args):
|
|
146
|
+
dim_container, *remain = args
|
|
147
|
+
dim_container = self._get_member(dim_container)
|
|
148
|
+
|
|
149
|
+
to_remove = []
|
|
150
|
+
for item in remain:
|
|
151
|
+
to_remove.append(self._get_member(item))
|
|
152
|
+
|
|
153
|
+
return dim_container.remove(*to_remove)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class ASTOpRoot(ASTOpBase):
|
|
157
|
+
"""语法树根节点,将维度表达式最终结果收集,并存储到 ``Dimension`` 中的查询集"""
|
|
158
|
+
def __init__(self, dim_obj):
|
|
159
|
+
super().__init__(dim_obj, 'Root')
|
|
160
|
+
self._dim_obj = dim_obj
|
|
161
|
+
|
|
162
|
+
def calc(self, args):
|
|
163
|
+
to_select = (self._get_member(mbr) for mbr in args)
|
|
164
|
+
return self._dim_obj.select(*to_select)
|
|
165
|
+
|
|
166
|
+
def __str__(self):
|
|
167
|
+
return 'DummyRoot'
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class ASTOpFactory:
|
|
171
|
+
"""语法树维度操作节点工程,可以根据 ``cls_map`` 创建不同的维度操作节点"""
|
|
172
|
+
cls_map = {
|
|
173
|
+
'Remove': ASTOpRemove,
|
|
174
|
+
"OrFilter": ASTOpFilter,
|
|
175
|
+
"AndFilter": ASTOpFilter,
|
|
176
|
+
"NorFilter": ASTOpFilter,
|
|
177
|
+
"NAndFilter": ASTOpFilter,
|
|
178
|
+
"Attr": ASTOpAttr,
|
|
179
|
+
"Base": ASTOpHierachy,
|
|
180
|
+
"IBase": ASTOpHierachy,
|
|
181
|
+
"Children": ASTOpHierachy,
|
|
182
|
+
"IChildren": ASTOpHierachy,
|
|
183
|
+
"Descendant": ASTOpHierachy,
|
|
184
|
+
"IDescendant": ASTOpHierachy,
|
|
185
|
+
"Level": ASTOpHierachy,
|
|
186
|
+
"ILevel": ASTOpHierachy,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
def __new__(cls, dim, value):
|
|
190
|
+
target_cls = cls.cls_map.get(value)
|
|
191
|
+
if target_cls is None:
|
|
192
|
+
raise ValueError(f"Unknow Expression: {value}")
|
|
193
|
+
return target_cls(dim, value)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class DimExprAnalysor:
|
|
197
|
+
"""基于语法树的维度表达式分析器"""
|
|
198
|
+
def __init__(self, dimension, dim_expr):
|
|
199
|
+
self.dim = dimension
|
|
200
|
+
self.dummy_root = ASTOpRoot(dimension)
|
|
201
|
+
for expr in dim_expr.split(";"):
|
|
202
|
+
self._gen_ast(expr)
|
|
203
|
+
|
|
204
|
+
def _gen_ast(self, expr):
|
|
205
|
+
"""创建语法树"""
|
|
206
|
+
root_stack = [self.dummy_root]
|
|
207
|
+
dim = self.dim
|
|
208
|
+
end_bracket = False
|
|
209
|
+
|
|
210
|
+
tmp_string = []
|
|
211
|
+
last_char = ')'
|
|
212
|
+
|
|
213
|
+
for char in expr:
|
|
214
|
+
if char == '(':
|
|
215
|
+
node = ASTOpFactory(dim, ''.join(tmp_string))
|
|
216
|
+
tmp_string.clear()
|
|
217
|
+
node.set_parent(root_stack[-1])
|
|
218
|
+
root_stack.append(node)
|
|
219
|
+
elif char == ',':
|
|
220
|
+
if not end_bracket:
|
|
221
|
+
node = ASTNode(''.join(tmp_string))
|
|
222
|
+
tmp_string.clear()
|
|
223
|
+
node.set_parent(root_stack[-1])
|
|
224
|
+
else:
|
|
225
|
+
end_bracket = False
|
|
226
|
+
elif char == ')':
|
|
227
|
+
if not end_bracket:
|
|
228
|
+
node = ASTNode(''.join(tmp_string))
|
|
229
|
+
tmp_string.clear()
|
|
230
|
+
root = root_stack.pop(-1)
|
|
231
|
+
node.set_parent(root)
|
|
232
|
+
end_bracket = True
|
|
233
|
+
else:
|
|
234
|
+
root_stack.pop(-1)
|
|
235
|
+
else:
|
|
236
|
+
if char == ' ' and last_char in {')', ','}:
|
|
237
|
+
char = last_char # 去除连续空格
|
|
238
|
+
else:
|
|
239
|
+
tmp_string.append(char)
|
|
240
|
+
|
|
241
|
+
last_char = char
|
|
242
|
+
|
|
243
|
+
if tmp_string:
|
|
244
|
+
node = ASTNode(''.join(tmp_string))
|
|
245
|
+
node.set_parent(root_stack[-1])
|
|
246
|
+
|
|
247
|
+
def show_ast(self):
|
|
248
|
+
"""
|
|
249
|
+
打印语法树
|
|
250
|
+
|
|
251
|
+
Example:
|
|
252
|
+
.. code-block:: python
|
|
253
|
+
|
|
254
|
+
# dim 是加载维度表达式的维度树
|
|
255
|
+
dim_expr = DimExprAnalysor(dim, 'Base(node, 0)')
|
|
256
|
+
dim_expr.show_ast()
|
|
257
|
+
# 执行上述代码会打印如下所示的语法树:
|
|
258
|
+
'''
|
|
259
|
+
DummyRoot
|
|
260
|
+
└── Base
|
|
261
|
+
├── node
|
|
262
|
+
└── 0
|
|
263
|
+
'''
|
|
264
|
+
|
|
265
|
+
"""
|
|
266
|
+
print(TreeRenderer().render(self.dummy_root))
|
|
267
|
+
return self
|
|
268
|
+
|
|
269
|
+
def solve(self):
|
|
270
|
+
"""计算维度表达式结果,返回已加载执行维度表达式的维度,结果存储在 `selected` 集合中"""
|
|
271
|
+
return self.dummy_root.solve()
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from deepfos.core.logictable.nodemixin import ShareableNodeMixin
|
|
2
|
+
from ._base import MemberContainer, MemberBase, pack_filter, flatten
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class GeneralMemberProxy(MemberContainer):
|
|
6
|
+
def __init__(self, *anchor_mbrs, hierarchy=None, node_attr=None):
|
|
7
|
+
super().__init__(*anchor_mbrs, hierarchy=hierarchy)
|
|
8
|
+
self.node_attr = node_attr
|
|
9
|
+
|
|
10
|
+
def _get_all_member(self):
|
|
11
|
+
super()._get_all_member()
|
|
12
|
+
return flatten(getattr(mbr, self.node_attr) for mbr in self.anchor_mbrs)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LevelMemberProxy(GeneralMemberProxy):
|
|
16
|
+
@property
|
|
17
|
+
def members(self):
|
|
18
|
+
include = self.node_attr.startswith('i')
|
|
19
|
+
levels = flatten(list(mbr.iter_level(self.start, self.stop, include)) for mbr in self.anchor_mbrs)
|
|
20
|
+
return self.apply_filter(levels)
|
|
21
|
+
|
|
22
|
+
def __getitem__(self, item):
|
|
23
|
+
super().__getitem__(item)
|
|
24
|
+
|
|
25
|
+
start, stop = item.start, item.stop
|
|
26
|
+
if start is None:
|
|
27
|
+
raise ValueError("Start level is not specified.")
|
|
28
|
+
|
|
29
|
+
if stop is None:
|
|
30
|
+
raise ValueError("Stop level is not specified.")
|
|
31
|
+
|
|
32
|
+
if stop <= start:
|
|
33
|
+
raise ValueError("Stop level should be greater than start.")
|
|
34
|
+
|
|
35
|
+
self.start, self.stop = start, stop
|
|
36
|
+
return self
|
|
37
|
+
|
|
38
|
+
def __str__(self):
|
|
39
|
+
order = 1 if self._reversed else 0
|
|
40
|
+
|
|
41
|
+
if self.start is None or self.stop is None:
|
|
42
|
+
raise RuntimeError(
|
|
43
|
+
"start|stop level is not specified. "
|
|
44
|
+
"Use slice [] syntax to indicated level detail.")
|
|
45
|
+
raw_expr = ';'.join(
|
|
46
|
+
f"{self.hierarchy}({anchor},{order},{self.start},{self.stop})"
|
|
47
|
+
for anchor in self.anchor_mbrs)
|
|
48
|
+
|
|
49
|
+
return pack_filter(raw_expr, self._filters)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AsMbrProxy:
|
|
53
|
+
def __init__(self, data_mapping, proxy_cls=GeneralMemberProxy):
|
|
54
|
+
self._dmap = data_mapping
|
|
55
|
+
self.proxy_cls = proxy_cls
|
|
56
|
+
|
|
57
|
+
def __get__(self, instance, owner=None):
|
|
58
|
+
proxy = self.proxy_cls(instance, hierarchy=self.name, node_attr=self._dmap)
|
|
59
|
+
return proxy
|
|
60
|
+
|
|
61
|
+
def __set_name__(self, owner, name):
|
|
62
|
+
self.name = name
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class DimMember(MemberBase, ShareableNodeMixin):
|
|
66
|
+
def __init__(self, name):
|
|
67
|
+
super(DimMember, self).__init__(name)
|
|
68
|
+
self.extra_attrs = ()
|
|
69
|
+
|
|
70
|
+
#: 寻找以当前节点为根的维度树的叶子节点。
|
|
71
|
+
Base = AsMbrProxy("base")
|
|
72
|
+
#: 寻找以当前节点为根的维度树的叶子节点,包含自身。
|
|
73
|
+
IBase = AsMbrProxy("ibase")
|
|
74
|
+
#: 寻找当前节点的直接孩子节点。
|
|
75
|
+
Children = AsMbrProxy("children")
|
|
76
|
+
#: 寻找当前节点的直接孩子节点, 包含自身。
|
|
77
|
+
IChildren = AsMbrProxy("ichildren")
|
|
78
|
+
#: 寻找当前节点的后继节点。
|
|
79
|
+
Descendant = AsMbrProxy("descendant")
|
|
80
|
+
#: 寻找当前节点的后继节点,包含自身。
|
|
81
|
+
IDescendant = AsMbrProxy("idescendant")
|
|
82
|
+
#: level节点。
|
|
83
|
+
Level = AsMbrProxy("level", LevelMemberProxy)
|
|
84
|
+
#: level节点,包含自身。
|
|
85
|
+
ILevel = AsMbrProxy("ilevel", LevelMemberProxy)
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def data(self):
|
|
89
|
+
"""返回当前维度成员的成员名, ``list`` 类型。"""
|
|
90
|
+
return [self.name]
|
|
91
|
+
|
|
92
|
+
def to_dict(self, *attrs, name, parent_name, show_all=False, exclude=None):
|
|
93
|
+
"""
|
|
94
|
+
将当前维度成员及其指定属性存储为字典。
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
*attrs(dict): 字典需要包含的维度成员指定属性
|
|
98
|
+
name(str): 维度成员名
|
|
99
|
+
parent_name(str): 维度成员的父节点名
|
|
100
|
+
show_all(bool): 显示成员的所有属性
|
|
101
|
+
exclude: 需要排除(不输出)的属性
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
包含当前维度成员指定信息的字典
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
if show_all:
|
|
108
|
+
attrs = self.extra_attrs
|
|
109
|
+
|
|
110
|
+
if isinstance(exclude, str):
|
|
111
|
+
exclude = {exclude}
|
|
112
|
+
else:
|
|
113
|
+
exclude = exclude or set()
|
|
114
|
+
|
|
115
|
+
rtn = {attr: getattr(self, attr) for attr in attrs if attr not in exclude}
|
|
116
|
+
rtn[name] = self.name
|
|
117
|
+
parent = self.parent
|
|
118
|
+
rtn[parent_name] = None if not parent else [p.name for p in parent]
|
|
119
|
+
return rtn
|
|
120
|
+
|
|
121
|
+
def where(self, method, **kwargs):
|
|
122
|
+
"""
|
|
123
|
+
判断当前维度成员的属性按照 ``method`` 是否符合指定属性。
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
method(str): 判断方法,包含 ``and` , ``nand`` , ``or`` , ``nor``
|
|
127
|
+
**kwargs: 要求的属性
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
成员对象容器
|
|
131
|
+
|
|
132
|
+
Warnings:
|
|
133
|
+
返回值是成员对象容器,需要调用容器的 ``members`` 或 ``data`` 才能执行操作。
|
|
134
|
+
此函数一般用于和 ``Dimension`` 的 ``select`` 搭配使用,不推荐单独使用。
|
|
135
|
+
|
|
136
|
+
"""
|
|
137
|
+
return MemberContainer(self).where(method, **kwargs)
|
|
138
|
+
|
|
139
|
+
def remove(self, *to_remove):
|
|
140
|
+
"""
|
|
141
|
+
移除当前成员对象
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
*to_remove(DimMember): 需要移除的成员对象
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
成员对象容器
|
|
148
|
+
|
|
149
|
+
Warnings:
|
|
150
|
+
返回值是成员对象容器,需要调用容器的 ``members`` 才能执行操作。
|
|
151
|
+
如果当前成员对象存在于 ``*to_remove`` 中,则移除当前成员对象,否则,直接返回。
|
|
152
|
+
此函数一般用于和 ``Dimension`` 的 ``select`` 搭配使用,不推荐单独使用。
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
return MemberContainer(self).remove(*to_remove)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from ._base import DimensionBase, MemberBase
|
|
2
|
+
from deepfos.element.dimension import Dimension as EleDimension
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ElementDimension(DimensionBase):
|
|
6
|
+
def __init__(self, dimension: EleDimension):
|
|
7
|
+
self.ele = dimension
|
|
8
|
+
super(ElementDimension, self).__init__(dimension.element_name)
|
|
9
|
+
|
|
10
|
+
def load_expr(self, expr) -> 'ElementDimension':
|
|
11
|
+
self.selected = selected = []
|
|
12
|
+
for record in self.ele.query(
|
|
13
|
+
expr, fields=['name', 'aggweight'],
|
|
14
|
+
as_model=False
|
|
15
|
+
):
|
|
16
|
+
mbr = MemberBase(record['name'])
|
|
17
|
+
mbr.weight = record['aggweight']
|
|
18
|
+
selected.append(mbr)
|
|
19
|
+
return self
|
|
20
|
+
|
|
21
|
+
def __getitem__(self, item):
|
|
22
|
+
return item
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
|
|
2
|
+
__all__ = ['AndFilter', 'NAndFilter', 'OrFilter', 'NorFilter', 'RemoveFilter', 'BaseFilter']
|
|
3
|
+
|
|
4
|
+
import functools
|
|
5
|
+
|
|
6
|
+
_NOTSET = object()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseFilter:
|
|
10
|
+
def __init__(self, attr_kv_pairs, objs=None):
|
|
11
|
+
"""
|
|
12
|
+
Args:
|
|
13
|
+
attr_kv_pairs(dict): 属性键值对
|
|
14
|
+
objs: 被过滤的对象
|
|
15
|
+
"""
|
|
16
|
+
self.objs = objs
|
|
17
|
+
self.attr_kv_pairs = attr_kv_pairs
|
|
18
|
+
|
|
19
|
+
def iter_single(self, obj):
|
|
20
|
+
for attr, val in self.attr_kv_pairs.items():
|
|
21
|
+
actual = getattr(obj, attr, _NOTSET)
|
|
22
|
+
if actual is _NOTSET:
|
|
23
|
+
yield False
|
|
24
|
+
else:
|
|
25
|
+
yield val == actual
|
|
26
|
+
|
|
27
|
+
def filter_strategy(self, obj):
|
|
28
|
+
"""过滤策略"""
|
|
29
|
+
raise NotImplementedError()
|
|
30
|
+
|
|
31
|
+
def apply(self):
|
|
32
|
+
"""执行过滤策略"""
|
|
33
|
+
return self.apply_to(self.objs)
|
|
34
|
+
|
|
35
|
+
def apply_to(self, objs):
|
|
36
|
+
"""返回符合过滤策略的对象列表"""
|
|
37
|
+
return [obj for obj in objs if self.filter_strategy(obj)]
|
|
38
|
+
|
|
39
|
+
def __str__(self):
|
|
40
|
+
return self.__class__.__name__
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AndFilter(BaseFilter):
|
|
44
|
+
"""筛选符合全部属性要求的节点"""
|
|
45
|
+
def filter_strategy(self, obj):
|
|
46
|
+
return all(self.iter_single(obj))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class NAndFilter(AndFilter):
|
|
50
|
+
"""筛选除了符合全部属性要求节点的节点"""
|
|
51
|
+
def filter_strategy(self, obj):
|
|
52
|
+
return not super().filter_strategy(obj)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class OrFilter(BaseFilter):
|
|
56
|
+
"""筛选符合任意一项属性要求的节点"""
|
|
57
|
+
def filter_strategy(self, obj):
|
|
58
|
+
return any(self.iter_single(obj))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class NorFilter(OrFilter):
|
|
62
|
+
"""筛选不符合全部属性要求的节点"""
|
|
63
|
+
def filter_strategy(self, obj):
|
|
64
|
+
return not super().filter_strategy(obj)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class RemoveFilter:
|
|
68
|
+
def __init__(self, to_remove, mbr_getter=None):
|
|
69
|
+
"""
|
|
70
|
+
Args:
|
|
71
|
+
to_remove(list): 列表元素为 ``DimMember`` 类型,需要移除的节点
|
|
72
|
+
mbr_getter:
|
|
73
|
+
"""
|
|
74
|
+
self.to_remove = to_remove
|
|
75
|
+
self.getter = mbr_getter or (lambda x: x)
|
|
76
|
+
|
|
77
|
+
def apply_to(self, total):
|
|
78
|
+
"""
|
|
79
|
+
Args:
|
|
80
|
+
total(list): ``DimMember`` 对象列表,从中移除 ``to_remove`` 中的对象
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
返回 ``DimMember`` 对象列表
|
|
84
|
+
|
|
85
|
+
Tips:
|
|
86
|
+
如果需要被移除的对象不存在于 ``total`` 中,则不做任何事
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
if not self.to_remove:
|
|
90
|
+
return total
|
|
91
|
+
to_remove = functools.reduce(
|
|
92
|
+
lambda x, y: x | y,
|
|
93
|
+
(set(self.getter(obj)) for obj in self.to_remove),
|
|
94
|
+
set()
|
|
95
|
+
)
|
|
96
|
+
return [obj for obj in total if obj not in to_remove]
|
|
97
|
+
|
|
98
|
+
def __str__(self):
|
|
99
|
+
return 'Remove'
|