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,663 @@
1
+ """
2
+ sql操作符相关类
3
+ OpStr开头的类属于字符类型支持的操作符
4
+ 其余Op开头的用于数字,日期等支持比较运算符的对象。
5
+
6
+ 同类操作符在__and__函数下是完整的域,
7
+ 即任意两个操作符进行&运算后,产生的结果仍属于这类操作符。
8
+ """
9
+ import operator
10
+ import datetime
11
+
12
+ from collections.abc import Sequence, Set
13
+
14
+ import pandas as pd
15
+
16
+ IN = "in"
17
+ NOT_IN = "ni"
18
+ EQUAL = "eq"
19
+ NOT_EQ = "ne"
20
+ GREAT_THAN = "gt"
21
+ GREAT_EQ = "ge"
22
+ LESS_THAN = "lt"
23
+ LESS_EQ = "le"
24
+
25
+ TYPE_TIME = (datetime.datetime, pd.Timestamp)
26
+
27
+
28
+ class _Largest(object): # pragma: no cover
29
+ def __eq__(self, other):
30
+ return isinstance(other, self.__class__)
31
+
32
+ def __le__(self, other):
33
+ return self == other
34
+
35
+ def __lt__(self, other):
36
+ return False
37
+
38
+ def __gt__(self, other):
39
+ return True
40
+
41
+ def __ge__(self, other):
42
+ return True
43
+
44
+ def __repr__(self):
45
+ return "<PINF>"
46
+
47
+
48
+ class _Smallest(object): # pragma: no cover
49
+ def __eq__(self, other):
50
+ return isinstance(other, self.__class__)
51
+
52
+ def __le__(self, other):
53
+ return True
54
+
55
+ def __lt__(self, other):
56
+ return True
57
+
58
+ def __gt__(self, other):
59
+ return False
60
+
61
+ def __ge__(self, other):
62
+ return self == other
63
+
64
+ def __repr__(self):
65
+ return "<NINF>"
66
+
67
+
68
+ PINF = _Largest()
69
+ NINF = _Smallest()
70
+
71
+
72
+ class OpCombineError(Exception):
73
+ def __init__(self, op1, op2):
74
+ self.err = f"Cannot combine {op1!r} with {op2!r}."
75
+ super().__init__(self.err)
76
+
77
+
78
+ def _str_init(op, value):
79
+ if op == EQUAL:
80
+ return OpStrEqual(value)
81
+ if op == NOT_EQ:
82
+ return OpStrNotEqual(value)
83
+ if op == IN:
84
+ return OpStrIn(value)
85
+ if op == NOT_IN:
86
+ return OpStrNotIn(value)
87
+ raise TypeError(f"Unknown operator type for string: {op!r}.")
88
+
89
+
90
+ def _common_init(op, value):
91
+ if op == EQUAL:
92
+ return OpEqual(value)
93
+ if op == NOT_EQ:
94
+ return OpMultiInterval(
95
+ Interval(NINF, value, False, False),
96
+ Interval(value, PINF, False, False)
97
+ )
98
+ if op == IN:
99
+ return OpIn(value)
100
+
101
+ if op == NOT_IN:
102
+ vals = [NINF, *sorted(value), PINF]
103
+ return OpMultiInterval(*[
104
+ Interval(lb, ub, False, False) for
105
+ lb, ub in zip(vals[:-1], vals[1:])
106
+ ])
107
+
108
+ if op == GREAT_THAN:
109
+ return OpMultiInterval(
110
+ Interval(value, PINF, False, False)
111
+ )
112
+ if op == GREAT_EQ:
113
+ return OpMultiInterval(
114
+ Interval(value, PINF, True, False)
115
+ )
116
+
117
+ if op == LESS_THAN:
118
+ return OpMultiInterval(
119
+ Interval(NINF, value, False, False)
120
+ )
121
+ if op == LESS_EQ:
122
+ return OpMultiInterval(
123
+ Interval(NINF, value, False, True)
124
+ )
125
+ raise TypeError(f"Unknown operator type: {op!r}.")
126
+
127
+
128
+ class OpFactory:
129
+ def __new__(cls, op, value):
130
+ if isinstance(value, str):
131
+ return _str_init(op, value)
132
+ elif isinstance(value, (Sequence, Set)):
133
+ if len(value) == 0:
134
+ raise ValueError("Value must not be an empty [sequence|set].")
135
+ if isinstance(value, Set):
136
+ value = list(value)
137
+ if isinstance(value[0], str):
138
+ return _str_init(op, value)
139
+ return _common_init(op, value)
140
+
141
+
142
+ def _tuple_repr(tup, tail_comma=False):
143
+ if isinstance(tup[0], TYPE_TIME):
144
+ tup = tuple(dt.strftime('%Y-%m-%d %H:%M:%S') for dt in tup)
145
+ if len(tup) == 1 and not tail_comma:
146
+ return f"({tup[0]!r})"
147
+ else:
148
+ return repr(tup)
149
+
150
+
151
+ ###########################################
152
+ # Operators For Object Support Comparison #
153
+ ###########################################
154
+ class BaseOperator:
155
+ op = None
156
+ op_pandas = None
157
+
158
+ def __init__(self, value):
159
+ # 对于class:In,NotIn,在基类已经处理为set类。
160
+ if not isinstance(value, str) and \
161
+ isinstance(value, Sequence): # pragma: no cover
162
+ self.value = set(value)
163
+ elif isinstance(value, pd.Timestamp):
164
+ self.value = value.to_pydatetime()
165
+ else:
166
+ self.value = value
167
+ if self.op_pandas is None:
168
+ self.op_pandas = self.op
169
+
170
+ def __and__(self, other): # pragma: no cover
171
+ return other
172
+
173
+ def __iand__(self, other): # pragma: no cover
174
+ return other
175
+
176
+ def _as_string(self, sort=False):
177
+ if isinstance(self.value, set):
178
+ if sort:
179
+ value = _tuple_repr(tuple(sorted(self.value)))
180
+ else:
181
+ value = _tuple_repr(tuple(self.value))
182
+ elif isinstance(self.value, TYPE_TIME):
183
+ value = repr(self.value.strftime('%Y-%m-%d %H:%M:%S'))
184
+ else:
185
+ value = repr(self.value)
186
+ return f"{self.op}{value}"
187
+
188
+ def __repr__(self):
189
+ return self._as_string(sort=False)
190
+
191
+ def __str__(self):
192
+ return self._as_string(sort=True)
193
+
194
+ def to_sql_template(self, quote_char='`'):
195
+ return f"{quote_char}{{0}}{quote_char}{repr(self)}"
196
+
197
+ def to_pandasql(self):
198
+ if isinstance(self.value, set):
199
+ value = tuple(self.value)
200
+ else:
201
+ value = self.value
202
+ return f"{{0}}{self.op_pandas.lower()}{value!r}"
203
+
204
+ def __eq__(self, other):
205
+ return isinstance(other, self.__class__) and self.value == other.value
206
+
207
+ def __lt__(self, other):
208
+ return self <= other and not self == other
209
+
210
+ def __le__(self, other):
211
+ if self == other:
212
+ return True
213
+ try:
214
+ return (self & other) == self
215
+ except OpCombineError:
216
+ return False
217
+
218
+
219
+ class BaseOpInNotIn(BaseOperator):
220
+ fallback = None
221
+
222
+ def __new__(cls, value):
223
+ if isinstance(value, str):
224
+ raise TypeError("value must be [list|tuple|set].")
225
+ try:
226
+ val = list(value)
227
+ except Exception:
228
+ raise TypeError("value must be [list|tuple|set].") from None
229
+ else:
230
+ if len(val) == 1:
231
+ return cls.fallback(val[0])
232
+ else:
233
+ return super().__new__(cls)
234
+
235
+ def __init__(self, value):
236
+ if len(value) == 0:
237
+ raise ValueError("value must not be empty.")
238
+ value = set(v.to_pydatetime() if isinstance(v, pd.Timestamp) else v for v in value)
239
+ super(BaseOpInNotIn, self).__init__(value)
240
+
241
+
242
+ class OpEqual(BaseOperator):
243
+ op = '='
244
+ op_pandas = '=='
245
+
246
+ def __and__(self, other):
247
+ if isinstance(other, OpEqual):
248
+ if self.value != other.value:
249
+ raise OpCombineError(self, other)
250
+ elif isinstance(other, OpIn):
251
+ if self.value not in other.value:
252
+ raise OpCombineError(self, other)
253
+ elif isinstance(other, OpMultiInterval):
254
+ if self.value not in other:
255
+ raise OpCombineError(self, other)
256
+ else:
257
+ raise OpCombineError(self, other)
258
+ return OpEqual(self.value)
259
+
260
+ def __eq__(self, other):
261
+ if isinstance(other, (Interval, OpMultiInterval)):
262
+ return other == self
263
+ return super().__eq__(other)
264
+
265
+
266
+ class OpIn(BaseOpInNotIn):
267
+ fallback = OpEqual
268
+ op = ' IN '
269
+
270
+ def __and__(self, other):
271
+ if isinstance(other, OpEqual):
272
+ return other & self
273
+ elif isinstance(other, OpIn):
274
+ new_set = self.value.intersection(other.value)
275
+ if not new_set:
276
+ raise OpCombineError(self, other)
277
+ return OpIn(new_set)
278
+ elif isinstance(other, OpMultiInterval):
279
+ new_val = [
280
+ val for val in self.value if
281
+ val in other
282
+ ]
283
+ if not new_val:
284
+ raise OpCombineError(self, other)
285
+ else:
286
+ return OpIn(new_val)
287
+ else:
288
+ raise OpCombineError(self, other)
289
+
290
+ def __eq__(self, other):
291
+ if isinstance(other, OpMultiInterval):
292
+ return other == self
293
+ return super().__eq__(other)
294
+
295
+
296
+ class Interval:
297
+ def __init__(self, lower_bound, upper_bound, close_lower, close_upper):
298
+ if not isinstance(lower_bound, pd.Timestamp):
299
+ self.lower = lower_bound
300
+ else:
301
+ self.lower = lower_bound.to_pydatetime()
302
+
303
+ if not isinstance(upper_bound, pd.Timestamp):
304
+ self.upper = upper_bound
305
+ else:
306
+ self.upper = upper_bound.to_pydatetime()
307
+
308
+ if self.lower > self.upper:
309
+ raise ValueError("Upper bound must be greater or equal to lower bound!")
310
+
311
+ self.cl = close_lower
312
+ self.cu = close_upper
313
+
314
+ def __repr__(self):
315
+ if self.is_point:
316
+ value = self.lower
317
+ if isinstance(value, TYPE_TIME):
318
+ return f"={value.strftime('%Y-%m-%d %H:%M:%S')!r}"
319
+ else:
320
+ return f"={value!r}"
321
+ return f"{'[' if self.cl else '('}{self._as_str(self.lower)}, " \
322
+ f"{self._as_str(self.upper)}{']' if self.cu else ')'}"
323
+
324
+ def __contains__(self, item):
325
+ if self.is_empty:
326
+ return False
327
+ ge_or_gt = operator.ge if self.cl else operator.gt
328
+ le_or_lt = operator.le if self.cu else operator.lt
329
+ return ge_or_gt(item, self.lower) and le_or_lt(item, self.upper)
330
+
331
+ def overlap(self, other: 'Interval'):
332
+ if self.is_empty or other.is_empty:
333
+ return False
334
+ left, right = sorted([self, other], key=operator.attrgetter('lower', 'upper'))
335
+
336
+ if left.upper > right.lower:
337
+ return True
338
+ elif left.upper == right.lower:
339
+ return left.cu and right.cl
340
+ else:
341
+ return False
342
+
343
+ @property
344
+ def is_empty(self):
345
+ return (self.lower == self.upper) and (not self.cl or not self.cu)
346
+
347
+ def __or__(self, other: 'Interval'):
348
+ if self.is_empty:
349
+ return other
350
+ if other.is_empty:
351
+ return self
352
+
353
+ left, right = sorted([self, other], key=operator.attrgetter('lower', 'upper'))
354
+
355
+ if not self.overlap(other):
356
+ if left.upper == right.lower and (left.cu or right.cl):
357
+ return Interval(left.lower, right.upper, left.cl, right.cu)
358
+ return [self, other]
359
+
360
+ if left.lower == right.lower:
361
+ close_lower = left.cl or right.cl
362
+ else:
363
+ close_lower = left.cl
364
+
365
+ if left.upper == right.upper:
366
+ close_upper = left.cu or right.cu
367
+ else:
368
+ close_upper = right.cu
369
+
370
+ return Interval(left.lower, right.upper, close_lower, close_upper)
371
+
372
+ def __and__(self, other: 'Interval'):
373
+ if not self.overlap(other):
374
+ return Interval.make_empty()
375
+
376
+ if self.lower > other.lower:
377
+ lower_bound = self.lower
378
+ close_lower = self.cl
379
+ elif self.lower == other.lower:
380
+ lower_bound = self.lower
381
+ close_lower = self.cl and other.cl
382
+ else:
383
+ lower_bound = other.lower
384
+ close_lower = other.cl
385
+
386
+ if self.upper < other.upper:
387
+ upper_bound = self.upper
388
+ close_upper = self.cu
389
+ elif self.upper == other.upper:
390
+ upper_bound = self.upper
391
+ close_upper = self.cu and other.cu
392
+ else:
393
+ upper_bound = other.upper
394
+ close_upper = other.cu
395
+
396
+ return Interval(lower_bound, upper_bound, close_lower, close_upper)
397
+
398
+ @classmethod
399
+ def make_empty(cls):
400
+ return cls(0, 0, False, False)
401
+
402
+ @staticmethod
403
+ def _as_str(obj, force_repr=False):
404
+ if not force_repr and isinstance(obj, TYPE_TIME):
405
+ return repr(obj.strftime('%Y-%m-%d %H:%M:%S'))
406
+ else:
407
+ return repr(obj)
408
+
409
+ def __to_sql(self, quote_field=True, force_repr=False, quote_char='`'):
410
+ ge_or_gt = '>=' if self.cl else '>'
411
+ le_or_lt = '<=' if self.cu else '<'
412
+ field = f"{quote_char}{{0}}{quote_char}" if quote_field else "{0}"
413
+ if self.upper == PINF:
414
+ return f"{field}{ge_or_gt}{self._as_str(self.lower, force_repr)}"
415
+ if self.lower == NINF:
416
+ return f"{field}{le_or_lt}{self._as_str(self.upper, force_repr)}"
417
+ else:
418
+ return f"({field}{le_or_lt}{self._as_str(self.upper, force_repr)} " \
419
+ f"and {field}{ge_or_gt}{self._as_str(self.lower, force_repr)})"
420
+
421
+ def to_sql_template(self, quote_char='`'):
422
+ if self.is_point:
423
+ return OpEqual(self.lower).to_sql_template(quote_char=quote_char)
424
+ return self.__to_sql(quote_field=True, force_repr=False, quote_char=quote_char)
425
+
426
+ def to_pandasql(self):
427
+ if self.is_point:
428
+ return OpEqual(self.lower).to_pandasql()
429
+ return self.__to_sql(quote_field=False, force_repr=True)
430
+
431
+ def __eq__(self, other):
432
+ if isinstance(other, OpEqual):
433
+ return self.is_point and self.lower == other.value
434
+ elif not isinstance(other, self.__class__):
435
+ return False
436
+
437
+ if self.is_empty and other.is_empty:
438
+ return True
439
+
440
+ if not (self.upper == other.upper and self.lower == other.lower):
441
+ return False
442
+ lower_match = self.cl == other.cl
443
+ upper_match = self.cu == other.cu
444
+ if self.upper == PINF:
445
+ upper_match = True
446
+ if self.lower == NINF:
447
+ lower_match = True
448
+ return upper_match and lower_match
449
+
450
+ def __lt__(self, other):
451
+ return self <= other and (not self == other)
452
+
453
+ def __le__(self, other):
454
+ return self & other == self
455
+
456
+ @property
457
+ def is_point(self):
458
+ return self.lower == self.upper and self.cl and self.upper
459
+
460
+
461
+ class OpMultiInterval:
462
+ def __init__(self, *interval: Interval):
463
+ self.intervals = list(interval)
464
+ self.merge()
465
+
466
+ def sort(self):
467
+ self.intervals.sort(key=operator.attrgetter('lower', 'upper'))
468
+ return self
469
+
470
+ def merge(self):
471
+ self.sort()
472
+ rtn = [Interval.make_empty()]
473
+ for it in self.intervals:
474
+ cur_inv = rtn.pop()
475
+ cur_inv |= it
476
+ if isinstance(cur_inv, list):
477
+ rtn.extend(cur_inv)
478
+ else:
479
+ rtn.append(cur_inv)
480
+ self.intervals = rtn
481
+ return self
482
+
483
+ def __repr__(self):
484
+ return '|'.join(repr(it) for it in self.intervals)
485
+
486
+ def __contains__(self, item):
487
+ return any(item in rg for rg in self.intervals)
488
+
489
+ def __and__(self, other):
490
+ if isinstance(other, (OpEqual, OpIn)):
491
+ return other & self
492
+
493
+ elif isinstance(other, OpMultiInterval):
494
+ all_invs = []
495
+ for sinv in self.intervals:
496
+ inv_list = []
497
+ for oinv in other.intervals:
498
+ intersxn = sinv & oinv
499
+ if not intersxn.is_empty:
500
+ inv_list.append(intersxn)
501
+ all_invs.extend(inv_list)
502
+
503
+ rtn = OpMultiInterval(*all_invs)
504
+ if any(it.is_empty for it in rtn.intervals):
505
+ raise OpCombineError(self, other)
506
+ return rtn
507
+
508
+ def __to_sql(self, engine='mysql', quote_char='`'):
509
+ if engine == 'mysql':
510
+ conctr = ' OR '
511
+ caller = operator.methodcaller('to_sql_template', quote_char=quote_char)
512
+ elif engine == 'pandas':
513
+ conctr = ' or '
514
+ caller = operator.methodcaller('to_pandasql')
515
+ else: # pragma no cover
516
+ raise ValueError(f"Unsupported engine type: {engine}")
517
+
518
+ if len(self.intervals) == 1:
519
+ return caller(self.intervals[0])
520
+ elif all(it.is_point for it in self.intervals):
521
+ return caller(OpIn(list(it.lower for it in self.intervals)))
522
+ return '(' + conctr.join(caller(it) for it in self.intervals) + ')'
523
+
524
+ def to_sql_template(self, quote_char='`'):
525
+ return self.__to_sql(engine='mysql', quote_char=quote_char)
526
+
527
+ def to_pandasql(self):
528
+ return self.__to_sql(engine='pandas')
529
+
530
+ def __eq__(self, other):
531
+ its = self.intervals
532
+ if isinstance(other, OpEqual):
533
+ return len(its) == 1 and \
534
+ its[0].is_point and \
535
+ its[0].lower == other.value
536
+ elif isinstance(other, OpIn):
537
+ return len(its) == len(other.value) and \
538
+ all(it.is_point for it in its) and \
539
+ set(it.lower for it in its) == other.value
540
+ elif not isinstance(other, OpMultiInterval):
541
+ return False
542
+ if not len(its) == len(other.intervals):
543
+ return False
544
+ return all(its == ito for its, ito in zip(its, other.intervals))
545
+
546
+ def __le__(self, other):
547
+ if self.is_empty:
548
+ return True
549
+
550
+ if isinstance(other, (OpEqual, OpIn)):
551
+ try:
552
+ return other & self == self
553
+ except OpCombineError:
554
+ return False
555
+
556
+ o_its = other.intervals
557
+ o_its_len = len(o_its)
558
+
559
+ idx = 0 # other.intervals的当前下标
560
+ le_count = 0 # 已经在other找到其所属区间的self.interval的数量
561
+ for s_it in self.intervals:
562
+ while idx < o_its_len:
563
+ if s_it <= o_its[idx]:
564
+ le_count += 1
565
+ break
566
+ else:
567
+ idx += 1
568
+
569
+ # 必须全部都找到所属区间
570
+ return le_count == len(self.intervals)
571
+
572
+ def __lt__(self, other): # pragma: no cover
573
+ return self <= other and not (self == other)
574
+
575
+ @property
576
+ def is_empty(self):
577
+ return all(it.is_empty for it in self.intervals)
578
+
579
+
580
+ ########################
581
+ # Operators For String #
582
+ ########################
583
+ class OpStrEqual(BaseOperator):
584
+ op = '='
585
+ op_pandas = '=='
586
+
587
+ def __and__(self, other):
588
+ if isinstance(other, OpStrEqual):
589
+ if self.value != other.value:
590
+ raise OpCombineError(self, other)
591
+ elif isinstance(other, OpStrNotEqual):
592
+ if self.value == other.value:
593
+ raise OpCombineError(self, other)
594
+ elif isinstance(other, OpStrIn):
595
+ if self.value not in other.value:
596
+ raise OpCombineError(self, other)
597
+ elif isinstance(other, OpStrNotIn):
598
+ if self.value in other.value:
599
+ raise OpCombineError(self, other)
600
+ else:
601
+ raise OpCombineError(self, other)
602
+ return OpStrEqual(self.value)
603
+
604
+
605
+ class OpStrIn(BaseOpInNotIn):
606
+ op = ' IN '
607
+ fallback = OpStrEqual
608
+
609
+ def __and__(self, other):
610
+ if isinstance(other, OpStrEqual):
611
+ return other & self
612
+ elif isinstance(other, OpStrNotEqual):
613
+ if other.value in self.value:
614
+ new_set = self.value.copy()
615
+ new_set.remove(other.value)
616
+ # 由于value只有一个时会退化成OpStrEqual,此处new_set不可能为空
617
+ if not new_set: # pragma: no cover
618
+ raise OpCombineError(self, other)
619
+ return OpStrIn(new_set)
620
+ else:
621
+ return OpStrIn(self.value)
622
+ elif isinstance(other, OpStrIn):
623
+ new_set = self.value.intersection(other.value)
624
+ if not new_set:
625
+ raise OpCombineError(self, other)
626
+ return OpStrIn(new_set)
627
+ elif isinstance(other, OpStrNotIn):
628
+ remain = self.value - other.value
629
+ if not remain:
630
+ raise OpCombineError(self, other)
631
+ return OpStrIn(remain)
632
+ else:
633
+ raise OpCombineError(self, other)
634
+
635
+
636
+ class OpStrNotEqual(BaseOperator):
637
+ op = '!='
638
+
639
+ def __and__(self, other):
640
+ if isinstance(other, (OpStrEqual, OpStrIn)):
641
+ return other & self
642
+ elif isinstance(other, OpStrNotEqual):
643
+ if self.value == other.value:
644
+ return OpStrNotEqual(self.value)
645
+ else:
646
+ return OpStrNotIn((self.value, other.value))
647
+ elif isinstance(other, OpStrNotIn):
648
+ return OpStrNotIn(other.value.union({self.value}))
649
+ else:
650
+ raise OpCombineError(self, other)
651
+
652
+
653
+ class OpStrNotIn(BaseOpInNotIn):
654
+ op = ' NOT IN '
655
+ fallback = OpStrNotEqual
656
+
657
+ def __and__(self, other):
658
+ if isinstance(other, (OpStrEqual, OpStrIn, OpStrNotEqual)):
659
+ return other & self
660
+ elif isinstance(other, OpStrNotIn):
661
+ return OpStrNotIn(self.value.union(other.value))
662
+ else:
663
+ raise OpCombineError(self, other)