deepfos 1.1.23__tar.gz → 1.1.26__tar.gz

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 (181) hide show
  1. {deepfos-1.1.23 → deepfos-1.1.26}/PKG-INFO +1 -1
  2. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/_version.py +3 -3
  3. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/journal_model.py +10 -1
  4. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/financial_model.py +8 -4
  5. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/finmodel.py +235 -30
  6. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/journal.py +250 -101
  7. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/exceptions/__init__.py +4 -0
  8. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/asynchronous.py +50 -23
  9. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/discovery.py +4 -1
  10. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/serutils.py +4 -20
  11. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos.egg-info/PKG-INFO +1 -1
  12. {deepfos-1.1.23 → deepfos-1.1.26}/MANIFEST.in +0 -0
  13. {deepfos-1.1.23 → deepfos-1.1.26}/README.md +0 -0
  14. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/__init__.py +0 -0
  15. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/algo/__init__.py +0 -0
  16. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/algo/graph.py +0 -0
  17. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_1/__init__.py +0 -0
  18. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_1/business_model.py +0 -0
  19. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_1/dimension.py +0 -0
  20. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_1/models/__init__.py +0 -0
  21. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_1/models/business_model.py +0 -0
  22. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_1/models/dimension.py +0 -0
  23. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_2/__init__.py +0 -0
  24. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_2/dimension.py +0 -0
  25. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_2/models/__init__.py +0 -0
  26. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/V1_2/models/dimension.py +0 -0
  27. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/__init__.py +0 -0
  28. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/account.py +0 -0
  29. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/accounting_engines.py +0 -0
  30. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/app.py +0 -0
  31. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/approval_process.py +0 -0
  32. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/base.py +0 -0
  33. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/business_model.py +0 -0
  34. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/consolidation.py +0 -0
  35. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/consolidation_process.py +0 -0
  36. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/datatable.py +0 -0
  37. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/deepconnector.py +0 -0
  38. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/deepfos_task.py +0 -0
  39. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/deepmodel.py +0 -0
  40. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/dimension.py +0 -0
  41. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/financial_model.py +0 -0
  42. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/journal_template.py +0 -0
  43. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/memory_financial_model.py +0 -0
  44. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/__init__.py +0 -0
  45. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/account.py +0 -0
  46. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/accounting_engines.py +0 -0
  47. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/app.py +0 -0
  48. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/approval_process.py +0 -0
  49. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/base.py +0 -0
  50. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/business_model.py +0 -0
  51. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/consolidation.py +0 -0
  52. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/consolidation_process.py +0 -0
  53. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/datatable_mysql.py +0 -0
  54. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/deepconnector.py +0 -0
  55. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/deepfos_task.py +0 -0
  56. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/deepmodel.py +0 -0
  57. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/dimension.py +0 -0
  58. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/journal_model.py +0 -0
  59. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/journal_template.py +0 -0
  60. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/memory_financial_model.py +0 -0
  61. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/platform.py +0 -0
  62. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/python.py +0 -0
  63. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/reconciliation_engine.py +0 -0
  64. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/reconciliation_report.py +0 -0
  65. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/role_strategy.py +0 -0
  66. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/smartlist.py +0 -0
  67. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/space.py +0 -0
  68. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/system.py +0 -0
  69. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/variable.py +0 -0
  70. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/models/workflow.py +0 -0
  71. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/platform.py +0 -0
  72. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/python.py +0 -0
  73. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/reconciliation_engine.py +0 -0
  74. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/reconciliation_report.py +0 -0
  75. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/role_strategy.py +0 -0
  76. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/smartlist.py +0 -0
  77. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/space.py +0 -0
  78. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/system.py +0 -0
  79. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/variable.py +0 -0
  80. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/api/workflow.py +0 -0
  81. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/boost/__init__.py +0 -0
  82. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/boost/jstream.c +0 -0
  83. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/boost/jstream.pyx +0 -0
  84. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/boost/pandas.c +0 -0
  85. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/boost/pandas.pyx +0 -0
  86. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/boost/py_jstream.py +0 -0
  87. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/boost/py_pandas.py +0 -0
  88. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/cache.py +0 -0
  89. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/config.py +0 -0
  90. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/__init__.py +0 -0
  91. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/cube/__init__.py +0 -0
  92. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/cube/_base.py +0 -0
  93. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/cube/constants.py +0 -0
  94. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/cube/cube.py +0 -0
  95. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/cube/formula.py +0 -0
  96. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/cube/syscube.py +0 -0
  97. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/cube/typing.py +0 -0
  98. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/cube/utils.py +0 -0
  99. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/dimension/__init__.py +0 -0
  100. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/dimension/_base.py +0 -0
  101. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/dimension/dimcreator.py +0 -0
  102. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/dimension/dimension.py +0 -0
  103. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/dimension/dimexpr.py +0 -0
  104. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/dimension/dimmember.py +0 -0
  105. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/dimension/eledimension.py +0 -0
  106. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/dimension/filters.py +0 -0
  107. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/dimension/sysdimension.py +0 -0
  108. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/logictable/__init__.py +0 -0
  109. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/logictable/_cache.py +0 -0
  110. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/logictable/_operator.py +0 -0
  111. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/logictable/nodemixin.py +0 -0
  112. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/logictable/sqlcondition.py +0 -0
  113. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/core/logictable/tablemodel.py +0 -0
  114. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/__init__.py +0 -0
  115. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/cipher.py +0 -0
  116. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/clickhouse.py +0 -0
  117. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/connector.py +0 -0
  118. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/daclickhouse.py +0 -0
  119. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/dameng.py +0 -0
  120. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/damysql.py +0 -0
  121. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/dbkits.py +0 -0
  122. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/deepengine.py +0 -0
  123. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/deepmodel.py +0 -0
  124. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/deepmodel_kingbase.py +0 -0
  125. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/edb.py +0 -0
  126. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/gauss.py +0 -0
  127. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/kingbase.py +0 -0
  128. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/mysql.py +0 -0
  129. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/oracle.py +0 -0
  130. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/postgresql.py +0 -0
  131. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/sqlserver.py +0 -0
  132. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/db/utils.py +0 -0
  133. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/__init__.py +0 -0
  134. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/accounting.py +0 -0
  135. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/apvlprocess.py +0 -0
  136. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/base.py +0 -0
  137. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/bizmodel.py +0 -0
  138. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/datatable.py +0 -0
  139. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/deepconnector.py +0 -0
  140. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/deepmodel.py +0 -0
  141. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/dimension.py +0 -0
  142. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/fact_table.py +0 -0
  143. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/journal_template.py +0 -0
  144. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/pyscript.py +0 -0
  145. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/reconciliation.py +0 -0
  146. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/rolestrategy.py +0 -0
  147. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/smartlist.py +0 -0
  148. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/variable.py +0 -0
  149. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/element/workflow.py +0 -0
  150. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/exceptions/hook.py +0 -0
  151. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lazy.py +0 -0
  152. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/__init__.py +0 -0
  153. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/_javaobj.py +0 -0
  154. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/concurrency.py +0 -0
  155. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/constant.py +0 -0
  156. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/decorator.py +0 -0
  157. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/deepchart.py +0 -0
  158. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/deepux.py +0 -0
  159. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/eureka.py +0 -0
  160. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/filterparser.py +0 -0
  161. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/httpcli.py +0 -0
  162. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/jsonstreamer.py +0 -0
  163. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/nacos.py +0 -0
  164. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/patch.py +0 -0
  165. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/redis.py +0 -0
  166. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/stopwatch.py +0 -0
  167. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/subtask.py +0 -0
  168. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/sysutils.py +0 -0
  169. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/lib/utils.py +0 -0
  170. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/local.py +0 -0
  171. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/options.py +0 -0
  172. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos/translation.py +0 -0
  173. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos.egg-info/SOURCES.txt +0 -0
  174. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos.egg-info/dependency_links.txt +0 -0
  175. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos.egg-info/not-zip-safe +0 -0
  176. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos.egg-info/requires.txt +0 -0
  177. {deepfos-1.1.23 → deepfos-1.1.26}/deepfos.egg-info/top_level.txt +0 -0
  178. {deepfos-1.1.23 → deepfos-1.1.26}/setup.cfg +0 -0
  179. {deepfos-1.1.23 → deepfos-1.1.26}/setup.py +0 -0
  180. {deepfos-1.1.23 → deepfos-1.1.26}/tests/__init__.py +0 -0
  181. {deepfos-1.1.23 → deepfos-1.1.26}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deepfos
3
- Version: 1.1.23
3
+ Version: 1.1.26
4
4
  Summary: Collecions of useful and handy tools for deepfos platform
5
5
  Home-page: http://py.deepfos.com
6
6
  Author: deepfos-python-team
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2023-11-15T06:38:21+0000",
11
+ "date": "2023-12-28T02:24:37+0000",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "8db1e866488ddeb1ede8a951c6ee035b0eb4e37e",
15
- "version": "1.1.23"
14
+ "full-revisionid": "7b2975015e7105274b4054e1136a0dfc22361966",
15
+ "version": "1.1.26"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -30,7 +30,16 @@ class JournalModelData(ChildAPI):
30
30
  基于凭证模型的数据批量保存
31
31
  """
32
32
  return {'body': batch}
33
-
33
+
34
+ @post('/data/update')
35
+ def update(self, batch: ModelDataBatchDTO) -> Union[CommonResultDTO, Awaitable[CommonResultDTO]]:
36
+ """
37
+ 【★】凭证模型-数据-单条更新-只支持头行更新及行插入和删除
38
+
39
+ 基于凭证模型的数据更新
40
+ """
41
+ return {'body': batch}
42
+
34
43
  @post('data/calc/net/amount')
35
44
  def calc_net_amount(self, param: JmPostParamVO) -> Union[JmPostResultVO, Awaitable[JmPostResultVO]]:
36
45
  """
@@ -187,14 +187,16 @@ class TaskExecutionResult(BaseModel):
187
187
 
188
188
  - **POST** ``/mdx/task/execution`` (Response: 200)
189
189
  """
190
- #: businessId
190
+ #: 业务id
191
191
  businessId: Optional[str]
192
- #: failReason
192
+ #: 失败原因
193
193
  failReason: Optional[str]
194
- #: result
194
+ #: 处理是否正常,true 正常, false 异常
195
195
  result: Optional[bool]
196
- #: status
196
+ #: 状态,默认为0(正常返回),超时为1(超时返回),判断超时逻辑:result = true & status = 1
197
197
  status: Optional[int]
198
+ #: 计算脚本影响单元格数,模型编辑器入口时返回具体值,其他时候返回null
199
+ cell: Optional[int]
198
200
 
199
201
 
200
202
  class CubeRightElementDTO(BaseModel):
@@ -1157,6 +1159,8 @@ class ReactSpreadsheetSaveForm(BaseModel):
1157
1159
  wbName: Optional[str]
1158
1160
  #: 电子表格path
1159
1161
  wbPath: Optional[str]
1162
+ #: 是否回调
1163
+ callback: Optional[bool]
1160
1164
 
1161
1165
 
1162
1166
  class SheetColumnDto(BaseModel):
@@ -1,10 +1,13 @@
1
1
  import asyncio
2
2
  import re
3
3
  from enum import Enum
4
+ from itertools import chain
4
5
  from typing import (
5
- List, Dict, Optional, Union,
6
- Tuple, Iterable, TYPE_CHECKING, Any
6
+ List, Dict, Optional, Union,
7
+ Tuple, Iterable, TYPE_CHECKING, Any, Set
7
8
  )
9
+
10
+ import numpy as np
8
11
  import pandas as pd
9
12
  from pydantic import Field, parse_obj_as
10
13
  from loguru import logger
@@ -24,7 +27,7 @@ from deepfos.lib.utils import (
24
27
  dict_to_sql, split_dataframe, find_str, concat_url, CIEnumMeta
25
28
  )
26
29
  from deepfos.lib.constant import (
27
- DFLT_DATA_COLUMN, VIEW, VIEW_DICT,
30
+ DFLT_DATA_COLUMN, VIEW, VIEW_DICT,
28
31
  HIERARCHY, DECIMAL_COL, STRING_COL
29
32
  )
30
33
  from deepfos.boost import pandas as bp
@@ -41,8 +44,7 @@ from deepfos.api.models.financial_model import (
41
44
  from deepfos.api.models.base import BaseModel
42
45
  from deepfos.options import OPTION
43
46
  from deepfos.lib.decorator import cached_property
44
- from deepfos.exceptions import MDXExecuteTimeout
45
-
47
+ from deepfos.exceptions import MDXExecuteTimeout, MDXExecuteFail
46
48
 
47
49
  __all__ = [
48
50
  'AsyncFinancialCube',
@@ -110,6 +112,16 @@ class RoundType(int, Enum, metaclass=CIEnumMeta):
110
112
 
111
113
  _RE_USE_SECTION = re.compile(r'.*USE\s+\w+;', re.I | re.S)
112
114
 
115
+ # 可直接转为mdx member或简单集合函数方法的维度表达式
116
+ # 维度名由字母数字下划线和中划线组成,或者等于#root
117
+ _RE_SIMPLE_EXPR = re.compile(
118
+ r'(?P<hierarchy>i?(base|descendant|children))'
119
+ r'\s*\((?P<mbr>(\x23root)|([\w\.\-\[\]]+))\s*,'
120
+ r'\s*[01]\s*(,\s*(?P<with_parent>[01])\s*)?\)',
121
+ re.I
122
+ )
123
+ RE_NAME_WITH_PARENT = re.compile(r'\[(.+)]\.\[(.+)]')
124
+
113
125
 
114
126
  # -----------------------------------------------------------------------------
115
127
  # core classes
@@ -195,6 +207,12 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
195
207
 
196
208
  return dim_memo
197
209
 
210
+ @cached_property
211
+ def account_col(self) -> str:
212
+ for dim in self._meta.cubeDimensionList:
213
+ if dim.dimensionUsage == 4:
214
+ return dim.datatableColumn
215
+
198
216
  @cached_property
199
217
  def dim_elements(self) -> LazyDict[str, AsyncDimension]:
200
218
  """财务Cube的维度元素
@@ -355,7 +373,8 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
355
373
  pivot_dim: Optional[str] = None,
356
374
  validate_expr: bool = True,
357
375
  verify_access: bool = False,
358
- include_ignored: bool = False
376
+ include_ignored: bool = False,
377
+ normalize_view: bool = False,
359
378
  ) -> Union[pd.DataFrame, Tuple[pd.DataFrame, Dict[str, str]]]:
360
379
  """
361
380
  根据维度表达式以及pov获取cube数据
@@ -368,6 +387,7 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
368
387
  validate_expr: 是否需要python校验/修改表达式,开启可能会导致额外的接口请求
369
388
  verify_access: 是否带权限查询
370
389
  include_ignored: 包含多版本实体维时,是否在结果中包含无效数据(即i列为1的数据)
390
+ normalize_view: 是否把大小写View统一成"View"
371
391
 
372
392
  .. admonition:: 示例
373
393
 
@@ -425,6 +445,13 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
425
445
  data = pd.DataFrame(columns=columns)
426
446
  data[DFLT_DATA_COLUMN] = data[DFLT_DATA_COLUMN].astype(float)
427
447
 
448
+ if normalize_view:
449
+ if (view := VIEW.lower()) in data.columns:
450
+ if VIEW in data.columns:
451
+ data[view] = np.where(data[VIEW].isnull(), data[view], data[VIEW])
452
+ data = data.drop(columns=[VIEW])
453
+ data = data.rename(columns=VIEW_DICT)
454
+
428
455
  if pivot_dim is not None:
429
456
  pivot_col = self._get_column_from_dim(pivot_dim)
430
457
 
@@ -459,6 +486,7 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
459
486
  need_check: bool = True,
460
487
  data_audit: bool = True,
461
488
  chunksize: Optional[int] = None,
489
+ callback: bool = True
462
490
  ):
463
491
  """
464
492
  将DataFrame的数据保存至cube。
@@ -471,6 +499,7 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
471
499
  data_audit: 是否需要记录到数据审计
472
500
  chunksize: 单次调用保存接口时最大的dataframe行数。
473
501
  当data的行数超过此值时,将会分多次进行保存。
502
+ callback: 是否回调
474
503
 
475
504
  Note:
476
505
  此方法会对落库数据做以下处理:
@@ -511,7 +540,10 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
511
540
  if missing_dims := required_cols - set(data.columns):
512
541
  raise ValueError(
513
542
  f"Cannot save data because following columns are missing: {missing_dims}")
514
- return await self._save_impl(data[list(required_cols)], pov, need_check, data_audit, chunksize)
543
+ return await self._save_impl(
544
+ data[list(required_cols)],
545
+ pov, need_check, data_audit, chunksize, callback
546
+ )
515
547
 
516
548
  async def save_unpivot(
517
549
  self,
@@ -522,6 +554,7 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
522
554
  data_audit: bool = True,
523
555
  chunksize: Optional[int] = None,
524
556
  save_nan: bool = False,
557
+ callback: bool = True
525
558
  ):
526
559
  """保存有某个维度所有成员在列上的 ``DataFrame``
527
560
 
@@ -539,6 +572,7 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
539
572
  chunksize: 单次调用保存接口时最大的dataframe行数。
540
573
  当data的行数超过此值时,将会分多次进行保存。
541
574
  save_nan: 当把数据列成员转换到行上时,data为空的数据是否保存
575
+ callback: 是否回调
542
576
 
543
577
  Warnings:
544
578
  由于数据完整性等原因,此方法接收的dataframe数据列经常会有一些额外的空值。
@@ -582,7 +616,9 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
582
616
  logger.info("Will not save to cube because dataframe is empty.")
583
617
  return
584
618
 
585
- return await self._save_impl(data, pov, need_check, data_audit, chunksize)
619
+ return await self._save_impl(
620
+ data, pov, need_check, data_audit, chunksize, callback
621
+ )
586
622
 
587
623
  async def _save_impl(
588
624
  self,
@@ -591,6 +627,7 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
591
627
  need_check: bool = True,
592
628
  data_audit: bool = True,
593
629
  chunksize: Optional[int] = None,
630
+ callback: bool = True
594
631
  ):
595
632
  # replace NaN to standard None
596
633
  data = data.mask(data.isna(), None)
@@ -615,16 +652,144 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
615
652
  needCheck=need_check,
616
653
  dataAuditSwitch=data_audit,
617
654
  entryMode=1,
618
- validateDimensionMember=need_check
655
+ validateDimensionMember=need_check,
656
+ callback=callback
657
+ )
658
+ r = await self.async_api.reactspreadsheet.save(
659
+ payload.dict(exclude_unset=True)
619
660
  )
620
- r = await self.async_api.reactspreadsheet.save(payload.dict(exclude_unset=True))
621
661
  resp.append(r)
622
662
  return resp
623
663
 
664
+ async def delete_with_mdx(
665
+ self,
666
+ expression: Union[str, Dict[str, Union[List[str], str]]]
667
+ ):
668
+ """通过MDX脚本删除数据
669
+
670
+ 根据维度表达式删除Cube数据
671
+
672
+ Warnings:
673
+ 此方法将根据维度表达式生成对应的MDX脚本并执行MDX的Cleardata
674
+ 对于只有成员和单集合方法的表达式,可以直接转换为MDX的成员集合或集合函数表达式
675
+ 如为复杂表达式(例如包含聚合方法),则会查询实际对应的成员后,再组成MDX的成员集合
676
+
677
+ Args:
678
+ expression: 维度表达式
679
+
680
+ .. admonition:: 示例
681
+
682
+ 两种调用方式等价:
683
+
684
+ .. code-block:: python
685
+ :emphasize-lines: 3,8
686
+
687
+ cube = FinancialCube('example')
688
+ expr = 'Year{2021;2022}->Entiy{Base(TotalEntity,0)}'
689
+ r = cube.delete_with_mdx(expr)
690
+ expr_dict = {
691
+ "Year": ['2021', '2022'],
692
+ "Entity": "Base(TotalEntity,0)"
693
+ }
694
+ r = cube.delete_with_mdx(expr_dict)
695
+
696
+ Returns:
697
+ MDX执行结果
698
+
699
+ See Also:
700
+ :meth:`insert_null` :meth:`delete`
701
+
702
+ """
703
+ if isinstance(expression, dict):
704
+ expression = dict_to_expr(expression)
705
+
706
+ query_dims = []
707
+ all_cols = set(self.dim_col_map.values())
708
+ dimexprs = {}
709
+ cols_appeared = set()
710
+
711
+ def normalize_name(name_: str) -> str:
712
+ if RE_NAME_WITH_PARENT.match(name_):
713
+ return name_
714
+ return f'[{name_}]'
715
+
716
+ async def query_dim(
717
+ col_: str,
718
+ part_: str,
719
+ dim_name_: str
720
+ ) -> Tuple[str, Set[str]]:
721
+ result: List[Dict[str, Any]] = await self.dim_elements[dim_name_].query(
722
+ part_, fields=['name'], as_model=False
723
+ )
724
+ mbrs_ = set()
725
+ for item in result:
726
+ if (name := item.get('expectedName')) is not None:
727
+ mbrs_.add(f"[{col_}].{normalize_name(name)}")
728
+ else:
729
+ mbrs_.add(f"[{col_}].{normalize_name(item['name'])}")
730
+ return col_, mbrs_
731
+
732
+ for expr in expression.split('->'):
733
+ dim, body = unpack_expr(expr)
734
+ col = self._get_column_from_dim(dim)
735
+
736
+ for part in body.split(';'):
737
+ part = part.replace(' ', '')
738
+ if not part:
739
+ continue
740
+
741
+ cols_appeared.add(col)
742
+
743
+ if is_valid_pov(part):
744
+ dimexprs.setdefault(col, set()).add(
745
+ f"[{col}].{normalize_name(part)}"
746
+ )
747
+ elif match := _RE_SIMPLE_EXPR.match(part):
748
+ mbr = match.group('mbr')
749
+ hier = match.group('hierarchy').capitalize()
750
+ with_parent = match.group('with_parent')
751
+
752
+ if with_parent == '1':
753
+ dimexprs.setdefault(col, set()).add(
754
+ f"{hier}([{col}].{normalize_name(mbr)},WITH_PARENT)"
755
+ )
756
+ else:
757
+ dimexprs.setdefault(col, set()).add(
758
+ f"{hier}([{col}].{normalize_name(mbr)})"
759
+ )
760
+ else:
761
+ dim_name = self.col_dim_map.get(col, col)
762
+ query_dims.append(query_dim(col, part, dim_name))
763
+
764
+ for col in all_cols - cols_appeared:
765
+ dimexprs[col] = {f"Base([{col}].[#root])"}
766
+
767
+ dim_mbrs = await asyncio.gather(*query_dims)
768
+
769
+ for col, mbrs in dim_mbrs:
770
+ dimexprs.setdefault(col, set())
771
+ dimexprs[col] = dimexprs[col].union(mbrs)
772
+
773
+ # ClearData方法的维度范围优先使用科目维度
774
+ if account_col := self.account_col:
775
+ clear_data_expr = dimexprs.pop(account_col)
776
+ scope_expr = chain(*dimexprs.values())
777
+ else:
778
+ expr_list = list(dimexprs.values())
779
+ clear_data_expr = expr_list[0]
780
+ scope_expr = chain(*expr_list[1::])
781
+
782
+ script = """Scope(%s);\nCleardata(%s);\nEnd Scope;
783
+ """ % (','.join(scope_expr), ','.join(clear_data_expr))
784
+
785
+ return await self.mdx_execution(script)
786
+
624
787
  async def delete(
625
788
  self,
626
789
  expression: Union[str, Dict[str, Union[List[str], str]]],
627
- chunksize: Optional[int] = None
790
+ chunksize: Optional[int] = None,
791
+ use_mdx: bool = False,
792
+ callback: bool = True
628
793
  ):
629
794
  """删除数据
630
795
 
@@ -639,6 +804,8 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
639
804
  expression: 维度表达式
640
805
  chunksize: 单次调用保存接口时最大的dataframe行数。
641
806
  当data的行数超过此值时,将会分多次进行保存。
807
+ use_mdx: 是否使用MDX脚本实现,默认为否,等效于调用 :meth:`delete_with_mdx`
808
+ callback: 是否回调
642
809
 
643
810
  .. admonition:: 示例
644
811
 
@@ -660,26 +827,31 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
660
827
  删除结果
661
828
 
662
829
  See Also:
663
- :meth:`insert_null`
830
+ :meth:`insert_null` :meth:`delete_with_mdx`
664
831
 
665
832
  """
833
+ if use_mdx:
834
+ return await self.delete_with_mdx(expression)
835
+
666
836
  if self._meta.autoCalculation:
667
837
  if isinstance(expression, str):
668
838
  expression = expr_to_dict(expression)
669
839
  expression = {**expression}
670
- # expression.pop(VIEW, None)
671
840
 
672
841
  if isinstance(expression, dict):
673
842
  expression = dict_to_expr(expression)
674
843
 
675
844
  data, pov = await self.query(expression)
676
845
  data[DFLT_DATA_COLUMN] = None
677
- return await self.save(data, pov, data_audit=True, chunksize=chunksize)
846
+ return await self.save(
847
+ data, pov, data_audit=True, chunksize=chunksize, callback=callback
848
+ )
678
849
 
679
850
  async def queries(
680
851
  self,
681
852
  expressions: Iterable[str],
682
- drop_duplicates: bool = True
853
+ drop_duplicates: bool = True,
854
+ normalize_view: bool = False,
683
855
  ) -> pd.DataFrame:
684
856
  """查询多个表达式
685
857
 
@@ -689,20 +861,23 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
689
861
  Args:
690
862
  expressions: 待查询的维度表达式列表
691
863
  drop_duplicates: 是否需要去重
864
+ normalize_view: 是否把大小写View统一成"View"
692
865
 
693
866
  Returns:
694
867
  查询结果
695
868
 
696
869
  """
697
870
  if isinstance(expressions, str):
698
- return await self.query(expressions, compact=False)
871
+ return await self.query(
872
+ expressions, compact=False, normalize_view=normalize_view)
699
873
 
700
874
  expressions = list(expressions)
701
875
  if len(expressions) == 1:
702
- return await self.query(expressions[0], compact=False)
876
+ return await self.query(
877
+ expressions[0], compact=False, normalize_view=normalize_view)
703
878
 
704
879
  df_list = await asyncio.gather(*(
705
- self.query(expr, compact=False)
880
+ self.query(expr, compact=False, normalize_view=True)
706
881
  for expr in expressions
707
882
  ))
708
883
 
@@ -711,13 +886,12 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
711
886
  data = pd.DataFrame(columns=dflt_cols)
712
887
  data[DFLT_DATA_COLUMN] = data[DFLT_DATA_COLUMN].astype(float)
713
888
  else:
714
- if self._meta.autoCalculation:
715
- for idx in range(len(df_list)):
716
- df_list[idx] = df_list[idx].rename(columns={VIEW: 'view'})
717
889
  data = pd.concat(df_list, sort=False)
718
890
  if drop_duplicates:
719
891
  dim_cols = data.columns.difference([DFLT_DATA_COLUMN])
720
892
  data = data.drop_duplicates(dim_cols)
893
+ if not normalize_view:
894
+ data = data.rename(columns={VIEW: "view"})
721
895
  return data
722
896
 
723
897
  async def pc_init(
@@ -995,7 +1169,7 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
995
1169
  script: str,
996
1170
  parameters: Optional[Dict[str, str]] = None,
997
1171
  precision: Optional[int] = None,
998
- timeout: Optional[int] = OPTION.api.timeout * 1000,
1172
+ timeout: Optional[int] = None,
999
1173
  round_type: Union[RoundType, str] = RoundType.floor,
1000
1174
  ):
1001
1175
  """执行MDX计算语句
@@ -1047,10 +1221,18 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
1047
1221
 
1048
1222
  """
1049
1223
  if _RE_USE_SECTION.match(script.upper()):
1050
- raise ValueError('MDX语句中发现use section,在FinancialCube中使用时,固定为当前Cube,不支持指定其他Cube')
1224
+ raise ValueError(
1225
+ 'MDX语句中发现use section,在FinancialCube中使用时,'
1226
+ '固定为当前Cube,不支持指定其他Cube'
1227
+ )
1228
+
1229
+ if timeout is None:
1230
+ timeout = OPTION.api.timeout * 1000
1051
1231
 
1052
- business_id = (f"PythonScript_{OPTION.general.task_info.get('script_name', '')}_MDX"
1053
- f"-{datetime.datetime.now().strftime('%Y%m%d_%H%M%S_%f')}")
1232
+ business_id = (
1233
+ f"PythonScript_{OPTION.general.task_info.get('script_name', '')}_MDX"
1234
+ f"-{datetime.datetime.now().strftime('%Y%m%d_%H%M%S_%f')}"
1235
+ )
1054
1236
  params = []
1055
1237
 
1056
1238
  if parameters is not None:
@@ -1063,12 +1245,17 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
1063
1245
  path = self._path
1064
1246
 
1065
1247
  if path is None:
1066
- path = await AppAPI(sync=False).folder.get_folder_full(self.element_info.folderId)
1248
+ path = await AppAPI(sync=False).folder.get_folder_full(
1249
+ self.element_info.folderId
1250
+ )
1067
1251
 
1068
1252
  path = path.replace('\\', '/')
1069
1253
 
1070
1254
  params.append(
1071
- MDXCubeParameter(key=self.element_name, value=concat_url(path, f"{self.element_name}.cub"))
1255
+ MDXCubeParameter(
1256
+ key=self.element_name,
1257
+ value=concat_url(path, f"{self.element_name}.cub")
1258
+ )
1072
1259
  )
1073
1260
 
1074
1261
  res = await self.async_api.mdxtask.execution(
@@ -1085,6 +1272,11 @@ class AsyncFinancialCube(ElementBase[FinancialModelAPI]):
1085
1272
  if res.status == 1:
1086
1273
  raise MDXExecuteTimeout(f'MDX执行超时,具体响应:\n{res}')
1087
1274
 
1275
+ if res.result is False:
1276
+ raise MDXExecuteFail(
1277
+ f'MDX执行失败,失败原因:\n{res.failReason}'
1278
+ )
1279
+
1088
1280
  return res
1089
1281
 
1090
1282
 
@@ -1095,6 +1287,7 @@ class FinancialCube(AsyncFinancialCube, metaclass=SyncMeta):
1095
1287
  'save',
1096
1288
  'save_unpivot',
1097
1289
  'delete',
1290
+ 'delete_with_mdx',
1098
1291
  'pc_init',
1099
1292
  'pc_update',
1100
1293
  'pc_upsert',
@@ -1107,7 +1300,8 @@ class FinancialCube(AsyncFinancialCube, metaclass=SyncMeta):
1107
1300
  def queries(
1108
1301
  self,
1109
1302
  expressions: Iterable[str],
1110
- drop_duplicates: bool = True
1303
+ drop_duplicates: bool = True,
1304
+ normalize_view: bool = False,
1111
1305
  ) -> pd.DataFrame:
1112
1306
  ...
1113
1307
 
@@ -1119,7 +1313,8 @@ class FinancialCube(AsyncFinancialCube, metaclass=SyncMeta):
1119
1313
  pivot_dim: Optional[str] = None,
1120
1314
  validate_expr: bool = True,
1121
1315
  verify_access: bool = False,
1122
- include_ignored: bool = False
1316
+ include_ignored: bool = False,
1317
+ normalize_view: bool = False,
1123
1318
  ) -> Union[pd.DataFrame, Tuple[pd.DataFrame, Dict[str, str]]]:
1124
1319
  ...
1125
1320
 
@@ -1131,6 +1326,7 @@ class FinancialCube(AsyncFinancialCube, metaclass=SyncMeta):
1131
1326
  need_check: bool = True,
1132
1327
  data_audit: bool = True,
1133
1328
  chunksize: Optional[int] = None,
1329
+ callback: bool = True
1134
1330
  ):
1135
1331
  ...
1136
1332
 
@@ -1143,13 +1339,22 @@ class FinancialCube(AsyncFinancialCube, metaclass=SyncMeta):
1143
1339
  data_audit: bool = True,
1144
1340
  chunksize: Optional[int] = None,
1145
1341
  save_nan: bool = False,
1342
+ callback: bool = True
1146
1343
  ):
1147
1344
  ...
1148
1345
 
1149
1346
  def delete(
1150
1347
  self,
1151
1348
  expression: Union[str, Dict[str, Union[List[str], str]]],
1152
- chunksize: Optional[int] = None
1349
+ chunksize: Optional[int] = None,
1350
+ use_mdx: bool = False,
1351
+ callback: bool = True
1352
+ ):
1353
+ ...
1354
+
1355
+ def delete_with_mdx(
1356
+ self,
1357
+ expression: Union[str, Dict[str, Union[List[str], str]]]
1153
1358
  ):
1154
1359
  ...
1155
1360