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.
Files changed (175) hide show
  1. deepfos/__init__.py +6 -0
  2. deepfos/_version.py +21 -0
  3. deepfos/algo/__init__.py +0 -0
  4. deepfos/algo/graph.py +171 -0
  5. deepfos/algo/segtree.py +31 -0
  6. deepfos/api/V1_1/__init__.py +0 -0
  7. deepfos/api/V1_1/business_model.py +119 -0
  8. deepfos/api/V1_1/dimension.py +599 -0
  9. deepfos/api/V1_1/models/__init__.py +0 -0
  10. deepfos/api/V1_1/models/business_model.py +1033 -0
  11. deepfos/api/V1_1/models/dimension.py +2768 -0
  12. deepfos/api/V1_2/__init__.py +0 -0
  13. deepfos/api/V1_2/dimension.py +285 -0
  14. deepfos/api/V1_2/models/__init__.py +0 -0
  15. deepfos/api/V1_2/models/dimension.py +2923 -0
  16. deepfos/api/__init__.py +0 -0
  17. deepfos/api/account.py +167 -0
  18. deepfos/api/accounting_engines.py +147 -0
  19. deepfos/api/app.py +626 -0
  20. deepfos/api/approval_process.py +198 -0
  21. deepfos/api/base.py +983 -0
  22. deepfos/api/business_model.py +160 -0
  23. deepfos/api/consolidation.py +129 -0
  24. deepfos/api/consolidation_process.py +106 -0
  25. deepfos/api/datatable.py +341 -0
  26. deepfos/api/deep_pipeline.py +61 -0
  27. deepfos/api/deepconnector.py +36 -0
  28. deepfos/api/deepfos_task.py +92 -0
  29. deepfos/api/deepmodel.py +188 -0
  30. deepfos/api/dimension.py +486 -0
  31. deepfos/api/financial_model.py +319 -0
  32. deepfos/api/journal_model.py +119 -0
  33. deepfos/api/journal_template.py +132 -0
  34. deepfos/api/memory_financial_model.py +98 -0
  35. deepfos/api/models/__init__.py +3 -0
  36. deepfos/api/models/account.py +483 -0
  37. deepfos/api/models/accounting_engines.py +756 -0
  38. deepfos/api/models/app.py +1338 -0
  39. deepfos/api/models/approval_process.py +1043 -0
  40. deepfos/api/models/base.py +234 -0
  41. deepfos/api/models/business_model.py +805 -0
  42. deepfos/api/models/consolidation.py +711 -0
  43. deepfos/api/models/consolidation_process.py +248 -0
  44. deepfos/api/models/datatable_mysql.py +427 -0
  45. deepfos/api/models/deep_pipeline.py +55 -0
  46. deepfos/api/models/deepconnector.py +28 -0
  47. deepfos/api/models/deepfos_task.py +386 -0
  48. deepfos/api/models/deepmodel.py +308 -0
  49. deepfos/api/models/dimension.py +1576 -0
  50. deepfos/api/models/financial_model.py +1796 -0
  51. deepfos/api/models/journal_model.py +341 -0
  52. deepfos/api/models/journal_template.py +854 -0
  53. deepfos/api/models/memory_financial_model.py +478 -0
  54. deepfos/api/models/platform.py +178 -0
  55. deepfos/api/models/python.py +221 -0
  56. deepfos/api/models/reconciliation_engine.py +411 -0
  57. deepfos/api/models/reconciliation_report.py +161 -0
  58. deepfos/api/models/role_strategy.py +884 -0
  59. deepfos/api/models/smartlist.py +237 -0
  60. deepfos/api/models/space.py +1137 -0
  61. deepfos/api/models/system.py +1065 -0
  62. deepfos/api/models/variable.py +463 -0
  63. deepfos/api/models/workflow.py +946 -0
  64. deepfos/api/platform.py +199 -0
  65. deepfos/api/python.py +90 -0
  66. deepfos/api/reconciliation_engine.py +181 -0
  67. deepfos/api/reconciliation_report.py +64 -0
  68. deepfos/api/role_strategy.py +234 -0
  69. deepfos/api/smartlist.py +69 -0
  70. deepfos/api/space.py +582 -0
  71. deepfos/api/system.py +372 -0
  72. deepfos/api/variable.py +154 -0
  73. deepfos/api/workflow.py +264 -0
  74. deepfos/boost/__init__.py +6 -0
  75. deepfos/boost/py_jstream.py +89 -0
  76. deepfos/boost/py_pandas.py +20 -0
  77. deepfos/cache.py +121 -0
  78. deepfos/config.py +6 -0
  79. deepfos/core/__init__.py +27 -0
  80. deepfos/core/cube/__init__.py +10 -0
  81. deepfos/core/cube/_base.py +462 -0
  82. deepfos/core/cube/constants.py +21 -0
  83. deepfos/core/cube/cube.py +408 -0
  84. deepfos/core/cube/formula.py +707 -0
  85. deepfos/core/cube/syscube.py +532 -0
  86. deepfos/core/cube/typing.py +7 -0
  87. deepfos/core/cube/utils.py +238 -0
  88. deepfos/core/dimension/__init__.py +11 -0
  89. deepfos/core/dimension/_base.py +506 -0
  90. deepfos/core/dimension/dimcreator.py +184 -0
  91. deepfos/core/dimension/dimension.py +472 -0
  92. deepfos/core/dimension/dimexpr.py +271 -0
  93. deepfos/core/dimension/dimmember.py +155 -0
  94. deepfos/core/dimension/eledimension.py +22 -0
  95. deepfos/core/dimension/filters.py +99 -0
  96. deepfos/core/dimension/sysdimension.py +168 -0
  97. deepfos/core/logictable/__init__.py +5 -0
  98. deepfos/core/logictable/_cache.py +141 -0
  99. deepfos/core/logictable/_operator.py +663 -0
  100. deepfos/core/logictable/nodemixin.py +673 -0
  101. deepfos/core/logictable/sqlcondition.py +609 -0
  102. deepfos/core/logictable/tablemodel.py +497 -0
  103. deepfos/db/__init__.py +36 -0
  104. deepfos/db/cipher.py +660 -0
  105. deepfos/db/clickhouse.py +191 -0
  106. deepfos/db/connector.py +195 -0
  107. deepfos/db/daclickhouse.py +171 -0
  108. deepfos/db/dameng.py +101 -0
  109. deepfos/db/damysql.py +189 -0
  110. deepfos/db/dbkits.py +358 -0
  111. deepfos/db/deepengine.py +99 -0
  112. deepfos/db/deepmodel.py +82 -0
  113. deepfos/db/deepmodel_kingbase.py +83 -0
  114. deepfos/db/edb.py +214 -0
  115. deepfos/db/gauss.py +83 -0
  116. deepfos/db/kingbase.py +83 -0
  117. deepfos/db/mysql.py +184 -0
  118. deepfos/db/oracle.py +131 -0
  119. deepfos/db/postgresql.py +192 -0
  120. deepfos/db/sqlserver.py +99 -0
  121. deepfos/db/utils.py +135 -0
  122. deepfos/element/__init__.py +89 -0
  123. deepfos/element/accounting.py +348 -0
  124. deepfos/element/apvlprocess.py +215 -0
  125. deepfos/element/base.py +398 -0
  126. deepfos/element/bizmodel.py +1269 -0
  127. deepfos/element/datatable.py +2467 -0
  128. deepfos/element/deep_pipeline.py +186 -0
  129. deepfos/element/deepconnector.py +59 -0
  130. deepfos/element/deepmodel.py +1806 -0
  131. deepfos/element/dimension.py +1254 -0
  132. deepfos/element/fact_table.py +427 -0
  133. deepfos/element/finmodel.py +1485 -0
  134. deepfos/element/journal.py +840 -0
  135. deepfos/element/journal_template.py +943 -0
  136. deepfos/element/pyscript.py +412 -0
  137. deepfos/element/reconciliation.py +553 -0
  138. deepfos/element/rolestrategy.py +243 -0
  139. deepfos/element/smartlist.py +457 -0
  140. deepfos/element/variable.py +756 -0
  141. deepfos/element/workflow.py +560 -0
  142. deepfos/exceptions/__init__.py +239 -0
  143. deepfos/exceptions/hook.py +86 -0
  144. deepfos/lazy.py +104 -0
  145. deepfos/lazy_import.py +84 -0
  146. deepfos/lib/__init__.py +0 -0
  147. deepfos/lib/_javaobj.py +366 -0
  148. deepfos/lib/asynchronous.py +879 -0
  149. deepfos/lib/concurrency.py +107 -0
  150. deepfos/lib/constant.py +39 -0
  151. deepfos/lib/decorator.py +310 -0
  152. deepfos/lib/deepchart.py +778 -0
  153. deepfos/lib/deepux.py +477 -0
  154. deepfos/lib/discovery.py +273 -0
  155. deepfos/lib/edb_lexer.py +789 -0
  156. deepfos/lib/eureka.py +156 -0
  157. deepfos/lib/filterparser.py +751 -0
  158. deepfos/lib/httpcli.py +106 -0
  159. deepfos/lib/jsonstreamer.py +80 -0
  160. deepfos/lib/msg.py +394 -0
  161. deepfos/lib/nacos.py +225 -0
  162. deepfos/lib/patch.py +92 -0
  163. deepfos/lib/redis.py +241 -0
  164. deepfos/lib/serutils.py +181 -0
  165. deepfos/lib/stopwatch.py +99 -0
  166. deepfos/lib/subtask.py +572 -0
  167. deepfos/lib/sysutils.py +703 -0
  168. deepfos/lib/utils.py +1003 -0
  169. deepfos/local.py +160 -0
  170. deepfos/options.py +670 -0
  171. deepfos/translation.py +237 -0
  172. deepfos-1.1.60.dist-info/METADATA +33 -0
  173. deepfos-1.1.60.dist-info/RECORD +175 -0
  174. deepfos-1.1.60.dist-info/WHEEL +5 -0
  175. deepfos-1.1.60.dist-info/top_level.txt +1 -0
@@ -0,0 +1,191 @@
1
+ """数据库连接类"""
2
+ import asyncio
3
+ from typing import Union, List, Iterable, TYPE_CHECKING
4
+
5
+ import pandas as pd
6
+ from cachetools import TTLCache
7
+
8
+ from deepfos.api.datatable import ClickHouseAPI
9
+ from deepfos.cache import Manager
10
+ from deepfos.lib.utils import cachedclass, split_dataframe
11
+ from deepfos.lib.decorator import singleton, cached_property
12
+ from deepfos.options import OPTION
13
+ from .dbkits import BaseSqlParser, SyncMeta, T_DataInfo
14
+ from .connector import ClickHouseAPIConnector, ClickHouseDirectAccess
15
+ from .mysql import _AbsAsyncMySQLClient, MySQLConvertor # noqa
16
+
17
+
18
+ __all__ = [
19
+ 'ClickHouseClient',
20
+ 'AsyncClickHouseClient',
21
+ 'ClickHouseConvertor',
22
+ ]
23
+
24
+
25
+ @singleton
26
+ class SqlParser(BaseSqlParser):
27
+ api_cls = ClickHouseAPI
28
+
29
+ @cached_property
30
+ def datatable_cls(self):
31
+ from deepfos.element.datatable import AsyncDataTableClickHouse
32
+ return AsyncDataTableClickHouse
33
+
34
+
35
+ class ClickHouseConvertor(MySQLConvertor):
36
+ def build_sql(
37
+ self,
38
+ columns: str,
39
+ values_in_line: Iterable[str],
40
+ tablename: str,
41
+ updatecol: Iterable[str] = None,
42
+ **opts
43
+ ):
44
+ if updatecol is not None:
45
+ raise NotImplementedError("`updatecol` is not yet implemented for ClickHouseDB.")
46
+
47
+ return super().build_sql(columns, values_in_line, tablename, updatecol, **opts)
48
+
49
+
50
+ # -----------------------------------------------------------------------------
51
+ # core
52
+ class APIBasedClient(_AbsAsyncMySQLClient):
53
+ convertor = ClickHouseConvertor(quote_char='`')
54
+
55
+ def __init__(self, version: Union[float, str] = None): # noqa
56
+ self.parser = SqlParser()
57
+ self.connector = ClickHouseAPIConnector(version)
58
+ self.connector.trxn_execute = self.connector.execute_many
59
+
60
+
61
+ class DirectAccessClient:
62
+ def __init__(self, version: Union[float, str] = None):
63
+ self.parser = SqlParser()
64
+ self.connector = ClickHouseDirectAccess(version)
65
+
66
+ async def exec_sqls(
67
+ self,
68
+ sqls: Union[str, Iterable[str]],
69
+ table_info: T_DataInfo = None
70
+ ):
71
+ """执行多句sql
72
+
73
+ Args:
74
+ sqls: 要执行的sql语句,str或多句sql的list
75
+ table_info: sql中表名占位符对应的数据表元素信息。
76
+
77
+ """
78
+
79
+ if isinstance(sqls, str):
80
+ sqls = [sqls]
81
+
82
+ parsed_sql = await self.parser.parse(sqls, table_info)
83
+ return await self.connector.execute_many(parsed_sql)
84
+
85
+ async def query_dfs(
86
+ self,
87
+ sqls: Union[str, Iterable[str]],
88
+ table_info: T_DataInfo = None
89
+ ) -> Union[pd.DataFrame, List[pd.DataFrame]]:
90
+ """执行sql查询语句
91
+
92
+ 获取DataFrame格式的二维表
93
+
94
+ Args:
95
+ sqls: 执行的sql,表名可以通过${table_name}占位
96
+ table_info: sql中表名占位符对应的数据表元素信息。
97
+
98
+ Notes:
99
+ 如果执行的sql中没有任何表名占位符,sql将直接执行。
100
+ 如果有占位符, 例如 ``${table1}``,那么要求 ``table_info`` 有
101
+ key值为 ``table1`` , 对应键值为包含
102
+ ``elementName, elementType, folderId/path`` 的字典,
103
+ 或者 :class:`DataTableMySQL` 类型,或者 :class:`ElementModel` 类型
104
+
105
+ Returns:
106
+ :class:`DataFrame` 格式的二维数据表
107
+
108
+ """
109
+ if isinstance(sqls, str):
110
+ sql_list = await self.parser.parse([sqls], table_info)
111
+ return await self.connector.query_dataframe(sql_list[0])
112
+ else:
113
+ sqls = await self.parser.parse(sqls, table_info)
114
+ return await asyncio.gather(*[
115
+ self.connector.query_dataframe(sql) for sql in sqls
116
+ ])
117
+
118
+ async def insert_df(
119
+ self,
120
+ dataframe: pd.DataFrame,
121
+ element_name: str = None,
122
+ table_name: str = None,
123
+ table_info: T_DataInfo = None,
124
+ chunksize: int = None,
125
+ ):
126
+ """将 :class:`DataFrame` 的插入数据表
127
+
128
+ Args:
129
+ dataframe: 入库数据
130
+ element_name: 数据表元素名
131
+ table_name: 数据表的 **实际表名**
132
+ table_info: 数据表元素信息,使用table
133
+ chunksize: 单次插库的数据行数
134
+
135
+ """
136
+ if table_name is not None:
137
+ tbl_name = table_name
138
+ elif element_name is not None:
139
+ tbl_name = (await self.parser.parse(["${%s}" % element_name], table_info))[0]
140
+ else:
141
+ raise ValueError("Either 'element_name' or 'table_name' must be presented.")
142
+
143
+ return await asyncio.gather(*[
144
+ self.connector.insert_dataframe(tbl_name, df)
145
+ for df in split_dataframe(dataframe, chunksize)
146
+ ])
147
+
148
+
149
+ if OPTION.general.db_direct_access:
150
+ ClientBase = DirectAccessClient
151
+ else:
152
+ ClientBase = APIBasedClient
153
+
154
+
155
+ @cachedclass(Manager.create_cache(TTLCache, maxsize=5, ttl=3600))
156
+ class AsyncClickHouseClient(ClientBase):
157
+ pass
158
+
159
+
160
+ @cachedclass(Manager.create_cache(TTLCache, maxsize=5, ttl=3600))
161
+ class ClickHouseClient(ClientBase, metaclass=SyncMeta):
162
+ synchronize = (
163
+ 'exec_sqls',
164
+ 'query_dfs',
165
+ 'insert_df',
166
+ )
167
+
168
+ if TYPE_CHECKING: # pragma: no cover
169
+ def exec_sqls(
170
+ self,
171
+ sqls: Union[str, Iterable[str]],
172
+ table_info: T_DataInfo = None
173
+ ):
174
+ ...
175
+
176
+ def query_dfs(
177
+ self,
178
+ sqls: Union[str, Iterable[str]],
179
+ table_info: T_DataInfo = None
180
+ ) -> Union[pd.DataFrame, List[pd.DataFrame]]:
181
+ ...
182
+
183
+ def insert_df(
184
+ self,
185
+ dataframe: pd.DataFrame,
186
+ element_name: str = None,
187
+ table_name: str = None,
188
+ table_info: T_DataInfo = None,
189
+ chunksize: int = None,
190
+ ):
191
+ ...
@@ -0,0 +1,195 @@
1
+ import asyncio
2
+ from abc import ABC, abstractmethod
3
+ from typing import Union, List
4
+
5
+ import pandas as pd
6
+
7
+ from deepfos.api.datatable import MySQLAPI, ClickHouseAPI, OracleAPI, SQLServerAPI, KingBaseAPI, GaussAPI, DaMengAPI, \
8
+ PostgreSQLAPI, DeepEngineAPI, DeepModelAPI, DeepModelKingBaseAPI
9
+ from deepfos.lib.asynchronous import cache_async
10
+ from .dbkits import APIFinder
11
+ from . import daclickhouse
12
+ from . import damysql
13
+
14
+ __all__ = [
15
+ 'AbstractConnector',
16
+ 'MySQLAPIConnector',
17
+ 'MySQLDirectAccess',
18
+ 'ClickHouseAPIConnector',
19
+ 'ClickHouseDirectAccess',
20
+ 'SQLServerAPIConnector',
21
+ 'OracleAPIConnector',
22
+ 'GaussAPIConnector',
23
+ 'KingBaseAPIConnector',
24
+ 'DaMengAPIConnector',
25
+ 'PostgreSQLAPIConnector',
26
+ 'DeepEngineAPIConnector',
27
+ 'DeepModelAPIConnector',
28
+ 'DeepModelKingBaseAPIConnector',
29
+ ]
30
+
31
+
32
+ class AbstractConnector(ABC): # pragma: no cover
33
+ def __init__(self, *args, **kwargs):
34
+ pass
35
+
36
+ @abstractmethod
37
+ async def execute(self, sql: str):
38
+ pass
39
+
40
+ @abstractmethod
41
+ async def execute_many(self, sqls: List[str]):
42
+ pass
43
+
44
+ @abstractmethod
45
+ async def trxn_execute(self, sqls: List[str]):
46
+ pass
47
+
48
+ @abstractmethod
49
+ async def select(self, sql: str):
50
+ pass
51
+
52
+ @abstractmethod
53
+ async def query_dataframe(self, sql: str) -> pd.DataFrame:
54
+ pass
55
+
56
+
57
+ class MySQLAPIConnector(AbstractConnector):
58
+ def __init__( # noqa
59
+ self,
60
+ version: Union[float, str] = None,
61
+ ):
62
+ self._v = version
63
+
64
+ @cache_async
65
+ async def build_api(self) -> MySQLAPI:
66
+ return await APIFinder().find_api(MySQLAPI, version=self._v)
67
+
68
+ async def execute(self, sql: str):
69
+ api = await self.build_api()
70
+ return await api.dml.run_sql(sql)
71
+
72
+ async def execute_many(self, sqls: List[str]):
73
+ api = await self.build_api()
74
+ return await asyncio.gather(
75
+ *(api.dml.run_sql(sql) for sql in sqls)
76
+ )
77
+
78
+ async def trxn_execute(self, sqls: List[str]):
79
+ api = await self.build_api()
80
+ return await api.dml.execute_batch_sql(sqls)
81
+
82
+ select = execute
83
+
84
+ async def query_dataframe(self, sql: str) -> pd.DataFrame:
85
+ api = await self.build_api()
86
+ resp = await api.dml.run_sql(sql)
87
+ select_data = [item or {} for item in resp.selectResult]
88
+ return pd.DataFrame.from_records(select_data)
89
+
90
+
91
+ class ClickHouseAPIConnector(MySQLAPIConnector):
92
+ async def trxn_execute(self, sqls: List[str]):
93
+ raise NotImplementedError('ClickHouse does not support transaction')
94
+
95
+ @cache_async
96
+ async def build_api(self) -> ClickHouseAPI:
97
+ return await APIFinder().find_api(ClickHouseAPI, version=self._v)
98
+
99
+
100
+ class MySQLDirectAccess(AbstractConnector):
101
+ async def execute(self, sql: str):
102
+ return await damysql.execute(sql)
103
+
104
+ async def execute_many(self, sqls: List[str]):
105
+ return await asyncio.gather(
106
+ *(damysql.execute(sql) for sql in sqls)
107
+ )
108
+
109
+ async def trxn_execute(self, sqls: List[str]):
110
+ return await damysql.trxn_execute(sqls)
111
+
112
+ async def select(self, sql: str):
113
+ return await damysql.select(sql)
114
+
115
+ async def query_dataframe(self, sql: str) -> pd.DataFrame:
116
+ return await damysql.query_dataframe(sql)
117
+
118
+
119
+ class ClickHouseDirectAccess(AbstractConnector):
120
+ async def execute(self, sql: str):
121
+ return await daclickhouse.execute(sql)
122
+
123
+ async def execute_many(self, sqls: List[str]):
124
+ return await asyncio.gather(
125
+ *(daclickhouse.execute(sql) for sql in sqls)
126
+ )
127
+
128
+ async def trxn_execute(self, sqls: List[str]):
129
+ raise NotImplementedError('ClickHouse does not support transaction')
130
+
131
+ async def select(self, sql: str):
132
+ return await daclickhouse.select(sql)
133
+
134
+ async def query_dataframe(self, sql: str) -> pd.DataFrame:
135
+ return await daclickhouse.query_dataframe(sql)
136
+
137
+ async def insert_dataframe(self, table: str, dataframe: pd.DataFrame): # noqa
138
+ return await daclickhouse.insert_dataframe(table, dataframe)
139
+
140
+
141
+ class SQLServerAPIConnector(MySQLAPIConnector):
142
+ @cache_async
143
+ async def build_api(self) -> SQLServerAPI:
144
+ return await APIFinder().find_api(SQLServerAPI, version=self._v)
145
+
146
+
147
+ class OracleAPIConnector(MySQLAPIConnector):
148
+ @cache_async
149
+ async def build_api(self) -> OracleAPI:
150
+ return await APIFinder().find_api(OracleAPI, version=self._v)
151
+
152
+
153
+ class KingBaseAPIConnector(MySQLAPIConnector):
154
+ @cache_async
155
+ async def build_api(self) -> KingBaseAPI:
156
+ return await APIFinder().find_api(KingBaseAPI, version=self._v)
157
+
158
+
159
+ class GaussAPIConnector(MySQLAPIConnector):
160
+ @cache_async
161
+ async def build_api(self) -> GaussAPI:
162
+ return await APIFinder().find_api(GaussAPI, version=self._v)
163
+
164
+
165
+ class DaMengAPIConnector(MySQLAPIConnector):
166
+ @cache_async
167
+ async def build_api(self) -> DaMengAPI:
168
+ return await APIFinder().find_api(DaMengAPI, version=self._v)
169
+
170
+
171
+ class PostgreSQLAPIConnector(MySQLAPIConnector):
172
+ @cache_async
173
+ async def build_api(self) -> PostgreSQLAPI:
174
+ return await APIFinder().find_api(PostgreSQLAPI, version=self._v)
175
+
176
+
177
+ class DeepEngineAPIConnector(ClickHouseAPIConnector):
178
+ @cache_async
179
+ async def build_api(self) -> DeepEngineAPI:
180
+ return await APIFinder().find_api(DeepEngineAPI, version=self._v)
181
+
182
+ async def trxn_execute(self, sqls: List[str]):
183
+ raise NotImplementedError('DeepEngine does not support transaction')
184
+
185
+
186
+ class DeepModelAPIConnector(MySQLAPIConnector):
187
+ @cache_async
188
+ async def build_api(self) -> DeepModelAPI:
189
+ return await APIFinder().find_api(DeepModelAPI, version=self._v)
190
+
191
+
192
+ class DeepModelKingBaseAPIConnector(KingBaseAPIConnector):
193
+ @cache_async
194
+ async def build_api(self) -> DeepModelKingBaseAPI:
195
+ return await APIFinder().find_api(DeepModelKingBaseAPI, version=self._v)
@@ -0,0 +1,171 @@
1
+ import time
2
+ from reprlib import aRepr
3
+ import warnings
4
+
5
+ from loguru import logger
6
+ from cachetools import TTLCache
7
+ import clickhouse_driver
8
+ import threading
9
+
10
+ from deepfos import OPTION
11
+ from deepfos.api.datatable import ClickHouseAPI
12
+ from deepfos.lib.constant import UNSET
13
+ from deepfos.lib.asynchronous import register_on_loop_shutdown
14
+ from .utils import (
15
+ decrypt, AbsLeaseManager, ACCOUNT_EXPIRE,
16
+ PENDING, INITIALIZED, INITIALIZING, DBConnecetionError
17
+ )
18
+
19
+ aRepr.maxstring = 200
20
+
21
+
22
+ # cdef class ClientCH:
23
+ # cdef:
24
+ # object conn
25
+ # int status
26
+ # object inited
27
+ # object lease
28
+ # bytes secret
29
+
30
+ class ClientCH:
31
+ def __init__(self):
32
+ self.conn = UNSET
33
+ self.status = PENDING
34
+ self.inited = threading.Event()
35
+ self.lease = LeaseManagerCH(ACCOUNT_EXPIRE / 3)
36
+ self.secret = "!ABCD-EFGH-IJKL@".encode()
37
+
38
+ async def establish_conn(self):
39
+ if self.status == INITIALIZED:
40
+ return self.conn
41
+
42
+ if self.status == PENDING:
43
+ self.status = INITIALIZING
44
+ else:
45
+ self.inited.wait()
46
+ if self.status != INITIALIZED:
47
+ raise RuntimeError("Failed to initialze connection pool.")
48
+ return self.conn
49
+
50
+ conf = await self.lease.renew()
51
+ self.lease.schedule(slow_start=True)
52
+ register_on_loop_shutdown(self.close)
53
+
54
+ conn_info = {
55
+ 'host': conf.host,
56
+ 'database': conf.dbName,
57
+ 'user': conf.name,
58
+ 'password': decrypt(self.secret, conf.password),
59
+ 'port': conf.port,
60
+ 'settings': {'use_numpy': True}
61
+ }
62
+
63
+ try:
64
+ self.conn = clickhouse_driver.Client(**conn_info)
65
+ self.conn.connection.force_connect()
66
+ except Exception as e:
67
+ self.status = PENDING
68
+ raise DBConnecetionError(e) from None
69
+ if not self.conn.connection.connected:
70
+ self.status = PENDING
71
+ raise DBConnecetionError("Not a valid connection")
72
+
73
+ self.status = INITIALIZED
74
+ self.inited.set()
75
+ return self.conn
76
+
77
+ async def ensure_connected(self):
78
+ max_retry = 3
79
+ retries = 0
80
+ interval = 0.5
81
+
82
+ while self.conn is UNSET or not self.conn.connection.connected:
83
+ try:
84
+ await self.establish_conn()
85
+ except DBConnecetionError:
86
+ retries += 1
87
+ if retries > max_retry:
88
+ self.inited.set()
89
+ raise
90
+ logger.exception(f'Failed to establish connection, '
91
+ f'starting {retries} times retry.')
92
+ time.sleep(interval)
93
+ interval *= 2
94
+ except Exception:
95
+ self.inited.set()
96
+ self.status = PENDING
97
+ raise
98
+
99
+ return self.conn
100
+
101
+ async def execute(self, sql):
102
+ conn = await self.ensure_connected()
103
+ logger.opt(lazy=True).debug("Run sql: {sql}", sql=lambda: aRepr.repr(sql))
104
+ return conn.execute(sql)
105
+
106
+ async def select(self, sql):
107
+ conn = await self.ensure_connected()
108
+ logger.opt(lazy=True).debug("Run sql: {sql}", sql=lambda: aRepr.repr(sql))
109
+ return conn.execute(sql)
110
+
111
+ async def query_dataframe(self, sql):
112
+ conn = await self.ensure_connected()
113
+ logger.opt(lazy=True).debug("Run sql: {sql}", sql=lambda: aRepr.repr(sql))
114
+ with warnings.catch_warnings():
115
+ warnings.simplefilter("ignore", category=RuntimeWarning)
116
+ return conn.query_dataframe(sql)
117
+
118
+ async def insert_dataframe(self, table, dataframe):
119
+ query = f"INSERT INTO `{table}` VALUES"
120
+ conn = await self.ensure_connected()
121
+ logger.debug(f"Run sql : {query} ...")
122
+ with warnings.catch_warnings():
123
+ warnings.simplefilter("ignore", category=RuntimeWarning)
124
+ return conn.insert_dataframe(query, dataframe)
125
+
126
+ def close(self):
127
+ self.lease.cancel()
128
+ self.status = PENDING
129
+
130
+ if self.conn is not UNSET:
131
+ self.conn.disconnect()
132
+ self.conn = UNSET
133
+
134
+
135
+ # cdef class ClientManager:
136
+ class ClientManager:
137
+ cache = TTLCache(maxsize=10, ttl=1800)
138
+
139
+ def new_client(self):
140
+ key = (
141
+ OPTION.api.header.get('space'),
142
+ OPTION.api.header.get('app'),
143
+ )
144
+
145
+ if key not in self.cache:
146
+ self.cache[key] = ClientCH()
147
+
148
+ return self.cache[key]
149
+
150
+
151
+ class LeaseManagerCH(AbsLeaseManager):
152
+ DB = 'clickhouse'
153
+
154
+ async def new_api(self):
155
+ return await ClickHouseAPI(version=1.0, sync=False)
156
+
157
+
158
+ async def select(sql):
159
+ return await ClientManager().new_client().select(sql)
160
+
161
+
162
+ async def execute(sql):
163
+ return await ClientManager().new_client().execute(sql)
164
+
165
+
166
+ async def query_dataframe(sql):
167
+ return await ClientManager().new_client().query_dataframe(sql)
168
+
169
+
170
+ async def insert_dataframe(table, dataframe):
171
+ return await ClientManager().new_client().insert_dataframe(table, dataframe)
deepfos/db/dameng.py ADDED
@@ -0,0 +1,101 @@
1
+ """Dameng客户端"""
2
+ import asyncio
3
+ from typing import Union, List, Iterable, TYPE_CHECKING
4
+
5
+ import pandas as pd
6
+
7
+ from deepfos.api.datatable import DaMengAPI
8
+ from deepfos.cache import Manager, SpaceSeperatedTTLCache
9
+ from deepfos.lib.utils import cachedclass
10
+ from deepfos.lib.decorator import singleton, cached_property
11
+ from deepfos.db.oracle import OracleDFSQLConvertor
12
+ from .dbkits import SyncMeta, T_DataInfo, BaseSqlParser
13
+ from .connector import DaMengAPIConnector
14
+ from .mysql import _AbsAsyncMySQLClient # noqa
15
+
16
+
17
+ __all__ = [
18
+ 'DaMengClient',
19
+ 'AsyncDaMengClient',
20
+ ]
21
+
22
+
23
+ @singleton
24
+ class SqlParser(BaseSqlParser):
25
+ api_cls = DaMengAPI
26
+
27
+ @cached_property
28
+ def datatable_cls(self):
29
+ from deepfos.element.datatable import AsyncDataTableDaMeng
30
+ return AsyncDataTableDaMeng
31
+
32
+ @staticmethod
33
+ async def query_table_names(api: DaMengAPI, query_table): # pragma: no cover
34
+ async def query_single(tbl_ele):
35
+ return tbl_ele.elementName, await api.dml.get_tablename(tbl_ele)
36
+
37
+ tablenames = await asyncio.gather(*(
38
+ query_single(table)
39
+ for table in query_table
40
+ ))
41
+
42
+ if len(tablenames) != len(query_table):
43
+ missing = set(t.elementName for t in query_table).difference(
44
+ set(t[0] for t in tablenames))
45
+
46
+ raise ValueError(f"Cannot resolve actual table names for element: {missing}")
47
+ return dict(tablenames)
48
+
49
+
50
+ # -----------------------------------------------------------------------------
51
+ # core
52
+ class _AsyncDaMengClient(_AbsAsyncMySQLClient):
53
+ convertor = OracleDFSQLConvertor(quote_char='"')
54
+
55
+ def __init__(self, version: Union[float, str] = None): # noqa
56
+ self.parser = SqlParser()
57
+ self.connector = DaMengAPIConnector(version)
58
+
59
+
60
+ class _DaMengClient(_AsyncDaMengClient, metaclass=SyncMeta):
61
+ synchronize = (
62
+ 'exec_sqls',
63
+ 'query_dfs',
64
+ 'insert_df',
65
+ )
66
+
67
+ if TYPE_CHECKING: # pragma: no cover
68
+ def exec_sqls(
69
+ self,
70
+ sqls: Union[str, Iterable[str]],
71
+ table_info: T_DataInfo = None
72
+ ):
73
+ ...
74
+
75
+ def query_dfs(
76
+ self,
77
+ sqls: Union[str, Iterable[str]],
78
+ table_info: T_DataInfo = None
79
+ ) -> Union[pd.DataFrame, List[pd.DataFrame]]:
80
+ ...
81
+
82
+ def insert_df(
83
+ self,
84
+ dataframe: pd.DataFrame,
85
+ element_name: str = None,
86
+ table_name: str = None,
87
+ updatecol: Iterable[str] = None,
88
+ table_info: T_DataInfo = None,
89
+ chunksize: int = None,
90
+ ):
91
+ ...
92
+
93
+
94
+ @cachedclass(Manager.create_cache(SpaceSeperatedTTLCache, maxsize=5, ttl=3600))
95
+ class AsyncDaMengClient(_AsyncDaMengClient):
96
+ pass
97
+
98
+
99
+ @cachedclass(Manager.create_cache(SpaceSeperatedTTLCache, maxsize=5, ttl=3600))
100
+ class DaMengClient(_DaMengClient):
101
+ pass