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/deepux.py
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
"""DeepUX组件数据源"""
|
|
2
|
+
import asyncio
|
|
3
|
+
import functools
|
|
4
|
+
from typing import Dict, Type, List, Union, Tuple
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from pydantic import Field
|
|
9
|
+
|
|
10
|
+
from deepfos import OPTION
|
|
11
|
+
from deepfos.api.models import BaseModel
|
|
12
|
+
from deepfos.api.models.dimension import DimensionMemberBean
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
'BaseField',
|
|
16
|
+
'String',
|
|
17
|
+
'Integer',
|
|
18
|
+
'Boolean',
|
|
19
|
+
'Json',
|
|
20
|
+
'Float',
|
|
21
|
+
'DateTime',
|
|
22
|
+
'UUID',
|
|
23
|
+
'as_datasource',
|
|
24
|
+
'Struct',
|
|
25
|
+
'NodeStruct',
|
|
26
|
+
'EdgeStruct',
|
|
27
|
+
'to_desc'
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
FLAG_FOR_META = "describe"
|
|
31
|
+
FIELDS = "fields"
|
|
32
|
+
STRUCT_FIELD = "objectInfos"
|
|
33
|
+
ELE_INFO_FIELD = "elementInfo"
|
|
34
|
+
DATA_FIELD = "json"
|
|
35
|
+
DESC_FIELD = "description"
|
|
36
|
+
|
|
37
|
+
NODES = "nodes"
|
|
38
|
+
EDGES = "edges"
|
|
39
|
+
OBJECT_TYPE = "object[]"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BaseField(BaseModel):
|
|
43
|
+
name: str = None
|
|
44
|
+
type: str = None
|
|
45
|
+
|
|
46
|
+
def __init__(self, name: str = None, **data):
|
|
47
|
+
super().__init__(name=name, **data)
|
|
48
|
+
|
|
49
|
+
def to_dict(self) -> Dict:
|
|
50
|
+
return self.dict()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ScalarField(BaseField):
|
|
54
|
+
def fit(self, df: pd.DataFrame, field_name: str):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class String(ScalarField):
|
|
59
|
+
"""文本"""
|
|
60
|
+
type = 'str'
|
|
61
|
+
|
|
62
|
+
def fit(self, df: pd.DataFrame, field_name: str):
|
|
63
|
+
df[field_name] = np.where(
|
|
64
|
+
df[field_name].isna(),
|
|
65
|
+
df[field_name],
|
|
66
|
+
df[field_name].astype(str, errors='ignore')
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Integer(ScalarField):
|
|
71
|
+
"""整数"""
|
|
72
|
+
type = 'int'
|
|
73
|
+
|
|
74
|
+
def fit(self, df: pd.DataFrame, field_name: str):
|
|
75
|
+
df[field_name] = np.where(
|
|
76
|
+
df[field_name].isna(),
|
|
77
|
+
df[field_name],
|
|
78
|
+
pd.to_numeric(df[field_name], errors='ignore', downcast='signed')
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Boolean(ScalarField):
|
|
83
|
+
"""布尔"""
|
|
84
|
+
type = 'bool'
|
|
85
|
+
|
|
86
|
+
def fit(self, df: pd.DataFrame, field_name: str):
|
|
87
|
+
df[field_name] = np.where(
|
|
88
|
+
df[field_name].isna(),
|
|
89
|
+
df[field_name],
|
|
90
|
+
df[field_name].astype(bool, errors='ignore')
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class Json(ScalarField):
|
|
95
|
+
"""多语言文本(json)"""
|
|
96
|
+
type = 'json'
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Float(ScalarField):
|
|
100
|
+
"""小数"""
|
|
101
|
+
type = 'float'
|
|
102
|
+
|
|
103
|
+
def fit(self, df: pd.DataFrame, field_name: str):
|
|
104
|
+
df[field_name] = np.where(
|
|
105
|
+
df[field_name].isna(),
|
|
106
|
+
df[field_name],
|
|
107
|
+
pd.to_numeric(df[field_name], errors='ignore')
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class DateTime(ScalarField):
|
|
112
|
+
"""日期时间"""
|
|
113
|
+
type = 'datetime'
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class UUID(ScalarField):
|
|
117
|
+
"""uuid"""
|
|
118
|
+
type = 'uuid'
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class ObjectField(BaseField):
|
|
122
|
+
"""表示对象类型的结构信息, 图格式数据源专用"""
|
|
123
|
+
type = OBJECT_TYPE
|
|
124
|
+
fields: List[ScalarField] = Field(default_factory=list)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class StructMeta(type):
|
|
128
|
+
def __new__(mcs, cls_name, bases, namespace: dict):
|
|
129
|
+
namespace['fields'] = mcs.collect_fields(bases, namespace)
|
|
130
|
+
return type.__new__(mcs, cls_name, bases, namespace)
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def collect_fields(bases, ns):
|
|
134
|
+
fields = {}
|
|
135
|
+
if len(bases) > 0:
|
|
136
|
+
for parent in bases:
|
|
137
|
+
if (
|
|
138
|
+
issubclass(parent, Struct)
|
|
139
|
+
and hasattr(parent, 'fields')
|
|
140
|
+
):
|
|
141
|
+
fields.update(parent.fields)
|
|
142
|
+
|
|
143
|
+
for field_name, anno in ns.get('__annotations__', {}).items():
|
|
144
|
+
if (
|
|
145
|
+
isinstance(anno, type)
|
|
146
|
+
and issubclass(anno, ScalarField)
|
|
147
|
+
and field_name not in ns
|
|
148
|
+
):
|
|
149
|
+
fields.update({field_name: anno(name=field_name)})
|
|
150
|
+
|
|
151
|
+
for k, v in dict(ns).items():
|
|
152
|
+
if isinstance(v, ScalarField):
|
|
153
|
+
ns.pop(k)
|
|
154
|
+
if v.name is None:
|
|
155
|
+
v.name = k
|
|
156
|
+
fields.update({v.name: v})
|
|
157
|
+
|
|
158
|
+
return fields
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class Struct(metaclass=StructMeta):
|
|
162
|
+
"""数据源字段信息结构
|
|
163
|
+
|
|
164
|
+
.. admonition:: 示例
|
|
165
|
+
|
|
166
|
+
.. code-block:: python
|
|
167
|
+
|
|
168
|
+
from deepfos.lib.deepux import Struct
|
|
169
|
+
|
|
170
|
+
class Data(Struct):
|
|
171
|
+
# 声明字段 text 为str类型
|
|
172
|
+
text = String()
|
|
173
|
+
# 声明字段 int1 为int类型
|
|
174
|
+
integer = Integer('int1')
|
|
175
|
+
# 声明字段 float_ 为float类型
|
|
176
|
+
float_: Float
|
|
177
|
+
# 声明字段 dt 为datetime类型
|
|
178
|
+
datetime = DateTime(name='dt')
|
|
179
|
+
|
|
180
|
+
See Also:
|
|
181
|
+
:meth:`as_datasource`
|
|
182
|
+
:class:`NodeStruct`
|
|
183
|
+
:class:`EdgeStruct`
|
|
184
|
+
|
|
185
|
+
"""
|
|
186
|
+
@classmethod
|
|
187
|
+
def structure(cls):
|
|
188
|
+
return [field.to_dict() for field in cls.fields.values()] # noqa
|
|
189
|
+
|
|
190
|
+
@classmethod
|
|
191
|
+
def to_dict(cls):
|
|
192
|
+
return {
|
|
193
|
+
ELE_INFO_FIELD: {
|
|
194
|
+
DESC_FIELD: OPTION.general.task_info.get('element_desc', {})
|
|
195
|
+
},
|
|
196
|
+
STRUCT_FIELD: [
|
|
197
|
+
{FIELDS: cls.structure()}
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class NodeStruct(Struct):
|
|
203
|
+
"""图格式的数据源Node字段信息结构
|
|
204
|
+
|
|
205
|
+
默认的_id(节点主键)和label(节点描述)字段已设置,
|
|
206
|
+
继承后即可声明其他节点键信息
|
|
207
|
+
|
|
208
|
+
.. admonition:: 示例
|
|
209
|
+
|
|
210
|
+
.. code-block:: python
|
|
211
|
+
|
|
212
|
+
from deepfos.lib.deepux import NodeStruct
|
|
213
|
+
|
|
214
|
+
class NodeData(NodeStruct):
|
|
215
|
+
# 声明节点键 text 为str类型
|
|
216
|
+
text = String()
|
|
217
|
+
# 声明节点键 int1 为int类型
|
|
218
|
+
integer = Integer('int1')
|
|
219
|
+
# 声明节点键 float_ 为float类型
|
|
220
|
+
float_: Float
|
|
221
|
+
# 声明节点键 dt 为datetime类型
|
|
222
|
+
datetime = DateTime(name='dt')
|
|
223
|
+
|
|
224
|
+
See Also:
|
|
225
|
+
:meth:`as_datasource`
|
|
226
|
+
:class:`Struct`
|
|
227
|
+
:class:`EdgeStruct`
|
|
228
|
+
|
|
229
|
+
"""
|
|
230
|
+
_id: String
|
|
231
|
+
label: String
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class EdgeStruct(Struct):
|
|
235
|
+
"""图格式的数据源Edge字段信息结构
|
|
236
|
+
|
|
237
|
+
默认的source(关系的来源)和target(关系的目标)字段已设置,
|
|
238
|
+
继承后即可声明其他节点关系信息
|
|
239
|
+
|
|
240
|
+
.. admonition:: 示例
|
|
241
|
+
|
|
242
|
+
.. code-block:: python
|
|
243
|
+
|
|
244
|
+
from deepfos.lib.deepux import EdgeStruct
|
|
245
|
+
|
|
246
|
+
class EdgeData(EdgeStruct):
|
|
247
|
+
# 声明节点关系 color 字段为str类型
|
|
248
|
+
color = String()
|
|
249
|
+
# 声明节点关系 size 字段为int类型
|
|
250
|
+
size = Integer('size')
|
|
251
|
+
|
|
252
|
+
See Also:
|
|
253
|
+
:meth:`as_datasource`
|
|
254
|
+
:class:`Struct`
|
|
255
|
+
:class:`NodeStruct`
|
|
256
|
+
|
|
257
|
+
"""
|
|
258
|
+
source: String
|
|
259
|
+
target: String
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class GraphStruct(Struct):
|
|
263
|
+
node: NodeStruct
|
|
264
|
+
edge: EdgeStruct
|
|
265
|
+
|
|
266
|
+
@classmethod
|
|
267
|
+
def structure(cls):
|
|
268
|
+
return [
|
|
269
|
+
ObjectField(
|
|
270
|
+
name=NODES,
|
|
271
|
+
fields=list(cls.node.fields.values()) # noqa
|
|
272
|
+
).to_dict(),
|
|
273
|
+
ObjectField(
|
|
274
|
+
name=EDGES,
|
|
275
|
+
fields=list(cls.edge.fields.values()) # noqa
|
|
276
|
+
).to_dict(),
|
|
277
|
+
]
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def to_desc(data: List[DimensionMemberBean]):
|
|
281
|
+
desc = {}
|
|
282
|
+
for dim in data:
|
|
283
|
+
desc[dim.name] = dim.multilingual.get(
|
|
284
|
+
OPTION.api.header.get('language', 'en'),
|
|
285
|
+
dim.name
|
|
286
|
+
)
|
|
287
|
+
return desc
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _resolve_param(args: tuple):
|
|
291
|
+
if len(args) == 2:
|
|
292
|
+
return args[1]
|
|
293
|
+
if len(args) == 1:
|
|
294
|
+
return args[0]
|
|
295
|
+
raise ValueError("main函数入参数非法")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _check_str_dict(data: Dict[str, Union[Dict, str]], value_type):
|
|
299
|
+
if any(not isinstance(k, str) for k in data):
|
|
300
|
+
raise ValueError("字段描述格式非法")
|
|
301
|
+
|
|
302
|
+
if any(not isinstance(v, value_type) for v in data.values()):
|
|
303
|
+
raise ValueError("字段描述格式非法")
|
|
304
|
+
|
|
305
|
+
if value_type == str:
|
|
306
|
+
return
|
|
307
|
+
|
|
308
|
+
for v in data.values():
|
|
309
|
+
_check_str_dict(v, str)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def valid_df(df, struct: Dict[str, ScalarField]):
|
|
313
|
+
if lacked := (set(struct).difference(df.columns)):
|
|
314
|
+
raise ValueError(f'字段: {lacked} 缺失')
|
|
315
|
+
|
|
316
|
+
df = df[list(struct.keys())]
|
|
317
|
+
df = df.replace({None: np.nan})
|
|
318
|
+
df = df.replace({np.nan: None})
|
|
319
|
+
for field_name, field_type in struct.items():
|
|
320
|
+
field_type.fit(df, field_name)
|
|
321
|
+
return df
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _resolve_return_value(
|
|
325
|
+
return_value,
|
|
326
|
+
struct: Union[Type[Struct], Tuple[Type[Struct], Type[Struct]]],
|
|
327
|
+
graph_struct: bool,
|
|
328
|
+
node_edge: bool
|
|
329
|
+
) -> Tuple[Dict, Dict]:
|
|
330
|
+
is_two_tuples = isinstance(return_value, tuple) and len(return_value) == 2
|
|
331
|
+
desc = {}
|
|
332
|
+
if graph_struct:
|
|
333
|
+
if (
|
|
334
|
+
not is_two_tuples
|
|
335
|
+
and any([not isinstance(each, pd.DataFrame) for each in return_value])
|
|
336
|
+
):
|
|
337
|
+
raise ValueError(
|
|
338
|
+
"图结构的数据源预期main函数返回值为"
|
|
339
|
+
"nodes和edges内容组成的pandas DataFrame"
|
|
340
|
+
)
|
|
341
|
+
node_struct, edge_struct = struct
|
|
342
|
+
if node_edge:
|
|
343
|
+
nodes, edges = return_value
|
|
344
|
+
else:
|
|
345
|
+
edges, nodes = return_value
|
|
346
|
+
nodes = valid_df(nodes, node_struct.fields)
|
|
347
|
+
edges = valid_df(edges, edge_struct.fields)
|
|
348
|
+
data = [{NODES: nodes.to_dict('records'), EDGES: edges.to_dict('records')}]
|
|
349
|
+
elif is_two_tuples:
|
|
350
|
+
df, desc = return_value
|
|
351
|
+
if not isinstance(df, pd.DataFrame):
|
|
352
|
+
raise TypeError('通用结构的数据源预期main函数返回的第一个值为pandas DataFrame')
|
|
353
|
+
|
|
354
|
+
if not isinstance(desc, dict):
|
|
355
|
+
raise TypeError("通用结构的数据源预期main函数返回的第二个值为字段描述,类型为字典")
|
|
356
|
+
|
|
357
|
+
_check_str_dict(desc, dict)
|
|
358
|
+
df = valid_df(df, struct.fields) # noqa
|
|
359
|
+
data = df.to_dict('records')
|
|
360
|
+
elif isinstance(return_value, pd.DataFrame):
|
|
361
|
+
df = valid_df(return_value, struct.fields) # noqa
|
|
362
|
+
data = df.to_dict('records')
|
|
363
|
+
else:
|
|
364
|
+
raise ValueError(
|
|
365
|
+
"通用结构的数据源预期main函数返回值为pandas DataFrame"
|
|
366
|
+
"或pandas DataFrame与字段描述字典组成的元组"
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
return data, desc
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _resolve_struct_desc(
|
|
373
|
+
struct: Union[Type[Struct], Tuple[Type[Struct], Type[Struct]]],
|
|
374
|
+
graph_struct: bool
|
|
375
|
+
) -> Dict:
|
|
376
|
+
if graph_struct:
|
|
377
|
+
# order preprared outside
|
|
378
|
+
node_struct, edge_struct = struct
|
|
379
|
+
helper_struct = GraphStruct
|
|
380
|
+
helper_struct.node, helper_struct.edge = node_struct, edge_struct
|
|
381
|
+
return helper_struct.to_dict()
|
|
382
|
+
else:
|
|
383
|
+
return struct.to_dict()
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def as_datasource(
|
|
387
|
+
func=None,
|
|
388
|
+
struct: Union[Type[Struct], Tuple[Type[Struct], Type[Struct]]] = None,
|
|
389
|
+
):
|
|
390
|
+
"""用作DeepUX数据源的main函数装饰器
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
func: 返回pandas DataFrame和字段描述(可选)的main方法
|
|
394
|
+
struct: 定义字段及其字段类型的类名称,必填
|
|
395
|
+
|
|
396
|
+
.. admonition:: 用法示例
|
|
397
|
+
|
|
398
|
+
.. code-block:: python
|
|
399
|
+
|
|
400
|
+
from deepfos.lib.deepux import as_datasource, Struct
|
|
401
|
+
|
|
402
|
+
# 声明结构信息
|
|
403
|
+
class Data(Struct):
|
|
404
|
+
...
|
|
405
|
+
|
|
406
|
+
@as_datasource(struct=Data)
|
|
407
|
+
def main(p2):
|
|
408
|
+
...
|
|
409
|
+
|
|
410
|
+
See Also:
|
|
411
|
+
:class:`Struct`
|
|
412
|
+
:class:`NodeStruct`
|
|
413
|
+
:class:`EdgeStruct`
|
|
414
|
+
|
|
415
|
+
"""
|
|
416
|
+
if func is None:
|
|
417
|
+
return functools.partial(as_datasource, struct=struct)
|
|
418
|
+
|
|
419
|
+
if struct is None:
|
|
420
|
+
raise ValueError("需定义DeepUX数据源的字段信息")
|
|
421
|
+
|
|
422
|
+
struct_is_tuple = isinstance(struct, tuple)
|
|
423
|
+
graph_struct = (
|
|
424
|
+
struct_is_tuple and len(struct) == 2
|
|
425
|
+
and (
|
|
426
|
+
(issubclass(struct[0], NodeStruct) and issubclass(struct[1], EdgeStruct))
|
|
427
|
+
or
|
|
428
|
+
(issubclass(struct[0], EdgeStruct) and issubclass(struct[1], NodeStruct))
|
|
429
|
+
)
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
if not struct_is_tuple and not issubclass(struct, Struct):
|
|
433
|
+
raise ValueError(
|
|
434
|
+
"DeepUX数据源的字段信息需为Struct的子类"
|
|
435
|
+
"或EdgeStruct和NodeStruct的子类组成的元组"
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
if struct_is_tuple and not graph_struct:
|
|
439
|
+
raise ValueError(
|
|
440
|
+
"DeepUX图结构数据源的字段信息需为EdgeStruct和NodeStruct的子类组成的元组"
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
node_edge = True
|
|
444
|
+
if graph_struct and issubclass(struct[1], NodeStruct): # noqa
|
|
445
|
+
node_edge = False
|
|
446
|
+
struct = struct[1], struct[0] # noqa
|
|
447
|
+
|
|
448
|
+
if asyncio.iscoroutinefunction(func):
|
|
449
|
+
async def wrapper(*args):
|
|
450
|
+
param = _resolve_param(args)
|
|
451
|
+
|
|
452
|
+
if param == FLAG_FOR_META:
|
|
453
|
+
return _resolve_struct_desc(struct, graph_struct)
|
|
454
|
+
|
|
455
|
+
maybe_df_desc = await func(*args)
|
|
456
|
+
|
|
457
|
+
data, desc = _resolve_return_value(
|
|
458
|
+
maybe_df_desc, struct, graph_struct, node_edge
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
return {DATA_FIELD: data, DESC_FIELD: desc}
|
|
462
|
+
else:
|
|
463
|
+
def wrapper(*args):
|
|
464
|
+
param = _resolve_param(args)
|
|
465
|
+
|
|
466
|
+
if param == FLAG_FOR_META:
|
|
467
|
+
return _resolve_struct_desc(struct, graph_struct)
|
|
468
|
+
|
|
469
|
+
maybe_df_desc = func(*args)
|
|
470
|
+
|
|
471
|
+
data, desc = _resolve_return_value(
|
|
472
|
+
maybe_df_desc, struct, graph_struct, node_edge
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
return {DATA_FIELD: data, DESC_FIELD: desc}
|
|
476
|
+
|
|
477
|
+
return functools.wraps(func)(wrapper)
|