deepfos 1.1.60__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deepfos/__init__.py +6 -0
- deepfos/_version.py +21 -0
- deepfos/algo/__init__.py +0 -0
- deepfos/algo/graph.py +171 -0
- deepfos/algo/segtree.py +31 -0
- deepfos/api/V1_1/__init__.py +0 -0
- deepfos/api/V1_1/business_model.py +119 -0
- deepfos/api/V1_1/dimension.py +599 -0
- deepfos/api/V1_1/models/__init__.py +0 -0
- deepfos/api/V1_1/models/business_model.py +1033 -0
- deepfos/api/V1_1/models/dimension.py +2768 -0
- deepfos/api/V1_2/__init__.py +0 -0
- deepfos/api/V1_2/dimension.py +285 -0
- deepfos/api/V1_2/models/__init__.py +0 -0
- deepfos/api/V1_2/models/dimension.py +2923 -0
- deepfos/api/__init__.py +0 -0
- deepfos/api/account.py +167 -0
- deepfos/api/accounting_engines.py +147 -0
- deepfos/api/app.py +626 -0
- deepfos/api/approval_process.py +198 -0
- deepfos/api/base.py +983 -0
- deepfos/api/business_model.py +160 -0
- deepfos/api/consolidation.py +129 -0
- deepfos/api/consolidation_process.py +106 -0
- deepfos/api/datatable.py +341 -0
- deepfos/api/deep_pipeline.py +61 -0
- deepfos/api/deepconnector.py +36 -0
- deepfos/api/deepfos_task.py +92 -0
- deepfos/api/deepmodel.py +188 -0
- deepfos/api/dimension.py +486 -0
- deepfos/api/financial_model.py +319 -0
- deepfos/api/journal_model.py +119 -0
- deepfos/api/journal_template.py +132 -0
- deepfos/api/memory_financial_model.py +98 -0
- deepfos/api/models/__init__.py +3 -0
- deepfos/api/models/account.py +483 -0
- deepfos/api/models/accounting_engines.py +756 -0
- deepfos/api/models/app.py +1338 -0
- deepfos/api/models/approval_process.py +1043 -0
- deepfos/api/models/base.py +234 -0
- deepfos/api/models/business_model.py +805 -0
- deepfos/api/models/consolidation.py +711 -0
- deepfos/api/models/consolidation_process.py +248 -0
- deepfos/api/models/datatable_mysql.py +427 -0
- deepfos/api/models/deep_pipeline.py +55 -0
- deepfos/api/models/deepconnector.py +28 -0
- deepfos/api/models/deepfos_task.py +386 -0
- deepfos/api/models/deepmodel.py +308 -0
- deepfos/api/models/dimension.py +1576 -0
- deepfos/api/models/financial_model.py +1796 -0
- deepfos/api/models/journal_model.py +341 -0
- deepfos/api/models/journal_template.py +854 -0
- deepfos/api/models/memory_financial_model.py +478 -0
- deepfos/api/models/platform.py +178 -0
- deepfos/api/models/python.py +221 -0
- deepfos/api/models/reconciliation_engine.py +411 -0
- deepfos/api/models/reconciliation_report.py +161 -0
- deepfos/api/models/role_strategy.py +884 -0
- deepfos/api/models/smartlist.py +237 -0
- deepfos/api/models/space.py +1137 -0
- deepfos/api/models/system.py +1065 -0
- deepfos/api/models/variable.py +463 -0
- deepfos/api/models/workflow.py +946 -0
- deepfos/api/platform.py +199 -0
- deepfos/api/python.py +90 -0
- deepfos/api/reconciliation_engine.py +181 -0
- deepfos/api/reconciliation_report.py +64 -0
- deepfos/api/role_strategy.py +234 -0
- deepfos/api/smartlist.py +69 -0
- deepfos/api/space.py +582 -0
- deepfos/api/system.py +372 -0
- deepfos/api/variable.py +154 -0
- deepfos/api/workflow.py +264 -0
- deepfos/boost/__init__.py +6 -0
- deepfos/boost/py_jstream.py +89 -0
- deepfos/boost/py_pandas.py +20 -0
- deepfos/cache.py +121 -0
- deepfos/config.py +6 -0
- deepfos/core/__init__.py +27 -0
- deepfos/core/cube/__init__.py +10 -0
- deepfos/core/cube/_base.py +462 -0
- deepfos/core/cube/constants.py +21 -0
- deepfos/core/cube/cube.py +408 -0
- deepfos/core/cube/formula.py +707 -0
- deepfos/core/cube/syscube.py +532 -0
- deepfos/core/cube/typing.py +7 -0
- deepfos/core/cube/utils.py +238 -0
- deepfos/core/dimension/__init__.py +11 -0
- deepfos/core/dimension/_base.py +506 -0
- deepfos/core/dimension/dimcreator.py +184 -0
- deepfos/core/dimension/dimension.py +472 -0
- deepfos/core/dimension/dimexpr.py +271 -0
- deepfos/core/dimension/dimmember.py +155 -0
- deepfos/core/dimension/eledimension.py +22 -0
- deepfos/core/dimension/filters.py +99 -0
- deepfos/core/dimension/sysdimension.py +168 -0
- deepfos/core/logictable/__init__.py +5 -0
- deepfos/core/logictable/_cache.py +141 -0
- deepfos/core/logictable/_operator.py +663 -0
- deepfos/core/logictable/nodemixin.py +673 -0
- deepfos/core/logictable/sqlcondition.py +609 -0
- deepfos/core/logictable/tablemodel.py +497 -0
- deepfos/db/__init__.py +36 -0
- deepfos/db/cipher.py +660 -0
- deepfos/db/clickhouse.py +191 -0
- deepfos/db/connector.py +195 -0
- deepfos/db/daclickhouse.py +171 -0
- deepfos/db/dameng.py +101 -0
- deepfos/db/damysql.py +189 -0
- deepfos/db/dbkits.py +358 -0
- deepfos/db/deepengine.py +99 -0
- deepfos/db/deepmodel.py +82 -0
- deepfos/db/deepmodel_kingbase.py +83 -0
- deepfos/db/edb.py +214 -0
- deepfos/db/gauss.py +83 -0
- deepfos/db/kingbase.py +83 -0
- deepfos/db/mysql.py +184 -0
- deepfos/db/oracle.py +131 -0
- deepfos/db/postgresql.py +192 -0
- deepfos/db/sqlserver.py +99 -0
- deepfos/db/utils.py +135 -0
- deepfos/element/__init__.py +89 -0
- deepfos/element/accounting.py +348 -0
- deepfos/element/apvlprocess.py +215 -0
- deepfos/element/base.py +398 -0
- deepfos/element/bizmodel.py +1269 -0
- deepfos/element/datatable.py +2467 -0
- deepfos/element/deep_pipeline.py +186 -0
- deepfos/element/deepconnector.py +59 -0
- deepfos/element/deepmodel.py +1806 -0
- deepfos/element/dimension.py +1254 -0
- deepfos/element/fact_table.py +427 -0
- deepfos/element/finmodel.py +1485 -0
- deepfos/element/journal.py +840 -0
- deepfos/element/journal_template.py +943 -0
- deepfos/element/pyscript.py +412 -0
- deepfos/element/reconciliation.py +553 -0
- deepfos/element/rolestrategy.py +243 -0
- deepfos/element/smartlist.py +457 -0
- deepfos/element/variable.py +756 -0
- deepfos/element/workflow.py +560 -0
- deepfos/exceptions/__init__.py +239 -0
- deepfos/exceptions/hook.py +86 -0
- deepfos/lazy.py +104 -0
- deepfos/lazy_import.py +84 -0
- deepfos/lib/__init__.py +0 -0
- deepfos/lib/_javaobj.py +366 -0
- deepfos/lib/asynchronous.py +879 -0
- deepfos/lib/concurrency.py +107 -0
- deepfos/lib/constant.py +39 -0
- deepfos/lib/decorator.py +310 -0
- deepfos/lib/deepchart.py +778 -0
- deepfos/lib/deepux.py +477 -0
- deepfos/lib/discovery.py +273 -0
- deepfos/lib/edb_lexer.py +789 -0
- deepfos/lib/eureka.py +156 -0
- deepfos/lib/filterparser.py +751 -0
- deepfos/lib/httpcli.py +106 -0
- deepfos/lib/jsonstreamer.py +80 -0
- deepfos/lib/msg.py +394 -0
- deepfos/lib/nacos.py +225 -0
- deepfos/lib/patch.py +92 -0
- deepfos/lib/redis.py +241 -0
- deepfos/lib/serutils.py +181 -0
- deepfos/lib/stopwatch.py +99 -0
- deepfos/lib/subtask.py +572 -0
- deepfos/lib/sysutils.py +703 -0
- deepfos/lib/utils.py +1003 -0
- deepfos/local.py +160 -0
- deepfos/options.py +670 -0
- deepfos/translation.py +237 -0
- deepfos-1.1.60.dist-info/METADATA +33 -0
- deepfos-1.1.60.dist-info/RECORD +175 -0
- deepfos-1.1.60.dist-info/WHEEL +5 -0
- deepfos-1.1.60.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,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)
|