teaql 0.1.0__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 (46) hide show
  1. teaql/__init__.py +0 -0
  2. teaql/core/__init__.py +63 -0
  3. teaql/core/entity.py +35 -0
  4. teaql/core/eval.py +84 -0
  5. teaql/core/expr.py +345 -0
  6. teaql/core/graph.py +94 -0
  7. teaql/core/list.py +80 -0
  8. teaql/core/meta.py +68 -0
  9. teaql/core/mutation.py +149 -0
  10. teaql/core/query.py +424 -0
  11. teaql/core/safe_expression.py +86 -0
  12. teaql/core/value.py +259 -0
  13. teaql/core/xls.py +142 -0
  14. teaql/data_service/__init__.py +187 -0
  15. teaql/provider/__init__.py +0 -0
  16. teaql/provider/mysql/__init__.py +8 -0
  17. teaql/provider/mysql/dialect.py +31 -0
  18. teaql/provider/mysql/transport.py +101 -0
  19. teaql/provider/postgres/__init__.py +8 -0
  20. teaql/provider/postgres/dialect.py +42 -0
  21. teaql/provider/postgres/transport.py +90 -0
  22. teaql/provider/redis/__init__.py +3 -0
  23. teaql/provider/redis/remote.py +57 -0
  24. teaql/provider/redis/store.py +50 -0
  25. teaql/provider/sqlite/__init__.py +26 -0
  26. teaql/provider/sqlite/dialect.py +47 -0
  27. teaql/provider/sqlite/transport.py +89 -0
  28. teaql/provider/tfp_client/__init__.py +127 -0
  29. teaql/py.typed +1 -0
  30. teaql/runtime/__init__.py +7 -0
  31. teaql/runtime/audit.py +70 -0
  32. teaql/runtime/context.py +657 -0
  33. teaql/runtime/env.py +29 -0
  34. teaql/runtime/module.py +67 -0
  35. teaql/runtime/store.py +13 -0
  36. teaql/sql/__init__.py +25 -0
  37. teaql/sql/dialect.py +471 -0
  38. teaql/sql/executor.py +377 -0
  39. teaql/sql/types.py +78 -0
  40. teaql/web_integration/__init__.py +1 -0
  41. teaql/web_integration/fastapi/__init__.py +93 -0
  42. teaql-0.1.0.dist-info/METADATA +71 -0
  43. teaql-0.1.0.dist-info/RECORD +46 -0
  44. teaql-0.1.0.dist-info/WHEEL +5 -0
  45. teaql-0.1.0.dist-info/licenses/LICENSE +201 -0
  46. teaql-0.1.0.dist-info/top_level.txt +1 -0
teaql/__init__.py ADDED
File without changes
teaql/core/__init__.py ADDED
@@ -0,0 +1,63 @@
1
+ from .value import Value, DataType, Timestamp
2
+ from .entity import BaseEntityData
3
+ from .expr import (
4
+ Expr, ExprBuilder, BinaryOp, ExprFunction,
5
+ ColumnExpr, ValueExpr, FunctionExpr, BinaryExpr,
6
+ SubQueryExpr, BetweenExpr, IsNullExpr, IsNotNullExpr,
7
+ AndExpr, OrExpr, NotExpr
8
+ )
9
+ from .query import (
10
+ SelectQuery, SortDirection, OrderBy, Aggregate, AggregateFunction,
11
+ Slice, RelationLoad, RawSqlProjection, ObjectGroupBy,
12
+ AggregationCacheOptions, StreamConfig, NamedExpr
13
+ )
14
+ from .mutation import (
15
+ InsertCommand, UpdateCommand, DeleteCommand, RecoverCommand,
16
+ BatchInsertCommand, BatchUpdateCommand, TraceNode,
17
+ InsertCommand, UpdateCommand, DeleteCommand, RecoverCommand,
18
+ BatchInsertCommand, BatchUpdateCommand, MutationRequest
19
+ )
20
+ from .graph import GraphNode
21
+
22
+ from .meta import EntityDescriptor, PropertyDescriptor
23
+ from .list import SmartList
24
+ from .eval import LoadState, EvalResult
25
+ from .safe_expression import SafeExpression
26
+ from .xls import XlsWorkbook, XlsPage, XlsBlock, XlsBlockBuildContext
27
+
28
+ __all__ = [
29
+ "Value", "DataType", "Timestamp",
30
+ "BaseEntityData",
31
+ "Expr", "ExprBuilder", "BinaryOp", "ExprFunction",
32
+ "SelectQuery", "SortDirection", "OrderBy", "Aggregate", "AggregateFunction",
33
+ "Slice", "RelationLoad", "RawSqlProjection", "ObjectGroupBy",
34
+ "AggregationCacheOptions", "StreamConfig", "NamedExpr",
35
+ "InsertCommand", "UpdateCommand", "DeleteCommand", "RecoverCommand",
36
+ "BatchInsertCommand", "BatchUpdateCommand", "TraceNode",
37
+ "InsertCommand", "UpdateCommand", "DeleteCommand", "RecoverCommand",
38
+ "BatchInsertCommand", "BatchUpdateCommand", "MutationRequest",
39
+ "GraphNode",
40
+ "EntityDescriptor", "PropertyDescriptor", "SmartList",
41
+ "LoadState", "EvalResult", "SafeExpression",
42
+ "XlsWorkbook", "XlsPage", "XlsBlock", "XlsBlockBuildContext"
43
+ ]
44
+ import builtins
45
+ builtins.uint64 = int
46
+ builtins.int64 = int
47
+ builtins.string = str
48
+ builtins.time = type('time', (), {'Time': str})
49
+ builtins.typing = __import__('typing')
50
+ builtins.decimal = __import__('decimal')
51
+ TypeU64 = "U64"
52
+ TypeI64 = "I64"
53
+ TypeText = "Text"
54
+ TypeTimestamp = "Timestamp"
55
+ TypeDecimal = "Decimal"
56
+ TypeJson = "Json"
57
+ TypeBool = "Bool"
58
+ TypeDate = "Date"
59
+ def NewInsertCommand(entity: str):
60
+ return InsertCommand(entity=entity)
61
+
62
+ def NewUpdateCommand(entity: str, id_val):
63
+ return UpdateCommand(entity=entity, id=Value.from_any(id_val))
teaql/core/entity.py ADDED
@@ -0,0 +1,35 @@
1
+ from typing import Dict, Optional, Any
2
+ from .value import Value
3
+
4
+ class BaseEntityData:
5
+ def __init__(self, id: int = 0, version: int = 0, dynamic: Optional[Dict[str, Value]] = None):
6
+ self.id = id
7
+ self.version = version
8
+ self.dynamic = dynamic or {}
9
+
10
+ @classmethod
11
+ def new(cls) -> 'BaseEntityData':
12
+ return cls()
13
+
14
+ def with_id(self, id: int) -> 'BaseEntityData':
15
+ self.id = id
16
+ return self
17
+
18
+ def with_version(self, version: int) -> 'BaseEntityData':
19
+ self.version = version
20
+ return self
21
+
22
+ def with_dynamic(self, key: str, value: Any) -> 'BaseEntityData':
23
+ self.dynamic[key] = Value.from_any(value)
24
+ return self
25
+
26
+ def to_record(self) -> Dict[str, Any]:
27
+ rec = {"id": self.id, "version": self.version}
28
+ if self.dynamic:
29
+ for k, v in self.dynamic.items():
30
+ rec[k] = v.to_json_value()
31
+ return rec
32
+ def get_dynamic(self, key: str) -> Optional[Value]:
33
+ return self.dynamic.get(key)
34
+ def put_dynamic(self, key, value):
35
+ self.dynamic[key] = value
teaql/core/eval.py ADDED
@@ -0,0 +1,84 @@
1
+ from enum import Enum, auto
2
+ from typing import Set, Any, Generic, TypeVar, Callable
3
+
4
+ class LoadStateType(Enum):
5
+ NotLoaded = auto()
6
+ Partial = auto()
7
+ FullyLoaded = auto()
8
+
9
+ class LoadState:
10
+ def __init__(self, state_type: LoadStateType = LoadStateType.NotLoaded, fields: Set[str] = None):
11
+ self.state_type = state_type
12
+ self.fields = fields or set()
13
+
14
+ @classmethod
15
+ def NotLoaded(cls) -> 'LoadState':
16
+ return cls(LoadStateType.NotLoaded)
17
+
18
+ @classmethod
19
+ def Partial(cls, fields: Set[str]) -> 'LoadState':
20
+ return cls(LoadStateType.Partial, fields)
21
+
22
+ @classmethod
23
+ def FullyLoaded(cls) -> 'LoadState':
24
+ return cls(LoadStateType.FullyLoaded)
25
+
26
+ def is_loaded(self, field_or_relation: str) -> bool:
27
+ if self.state_type == LoadStateType.NotLoaded:
28
+ return False
29
+ if self.state_type == LoadStateType.FullyLoaded:
30
+ return True
31
+ return field_or_relation in self.fields
32
+
33
+ T = TypeVar('T')
34
+ U = TypeVar('U')
35
+
36
+ class EvalResultType(Enum):
37
+ Value = auto()
38
+ Null = auto()
39
+ NotLoaded = auto()
40
+
41
+ class EvalResult(Generic[T]):
42
+ def __init__(self, result_type: EvalResultType, value: T = None, failed_node: str = None, attempted_path: str = None):
43
+ self.result_type = result_type
44
+ self.value = value
45
+ self.failed_node = failed_node
46
+ self.attempted_path = attempted_path
47
+
48
+ @classmethod
49
+ def Value(cls, value: T) -> 'EvalResult[T]':
50
+ return cls(EvalResultType.Value, value=value)
51
+
52
+ @classmethod
53
+ def Null(cls) -> 'EvalResult[T]':
54
+ return cls(EvalResultType.Null)
55
+
56
+ @classmethod
57
+ def NotLoaded(cls, failed_node: str, attempted_path: str) -> 'EvalResult[T]':
58
+ return cls(EvalResultType.NotLoaded, failed_node=failed_node, attempted_path=attempted_path)
59
+
60
+ def and_then(self, field_name: str, f: Callable[[T], 'EvalResult[U]']) -> 'EvalResult[U]':
61
+ if self.result_type == EvalResultType.Value:
62
+ res = f(self.value)
63
+ if res.result_type == EvalResultType.NotLoaded:
64
+ new_path = res.attempted_path
65
+ if new_path == field_name:
66
+ pass
67
+ elif not new_path:
68
+ new_path = field_name
69
+ else:
70
+ new_path = f"{field_name}.{new_path}"
71
+ return EvalResult.NotLoaded(res.failed_node, new_path)
72
+ return res
73
+ elif self.result_type == EvalResultType.Null:
74
+ return EvalResult.Null()
75
+ else:
76
+ return EvalResult.NotLoaded(self.failed_node, self.attempted_path)
77
+
78
+ def map(self, f: Callable[[T], U]) -> 'EvalResult[U]':
79
+ if self.result_type == EvalResultType.Value:
80
+ return EvalResult.Value(f(self.value))
81
+ elif self.result_type == EvalResultType.Null:
82
+ return EvalResult.Null()
83
+ else:
84
+ return EvalResult.NotLoaded(self.failed_node, self.attempted_path)
teaql/core/expr.py ADDED
@@ -0,0 +1,345 @@
1
+ from enum import Enum, auto
2
+ from typing import List, Optional, Any, Union
3
+ from dataclasses import dataclass
4
+ from .value import Value
5
+
6
+ class BinaryOp(Enum):
7
+ Eq = auto()
8
+ Ne = auto()
9
+ Gt = auto()
10
+ Gte = auto()
11
+ Lt = auto()
12
+ Lte = auto()
13
+ Like = auto()
14
+ NotLike = auto()
15
+ In = auto()
16
+ NotIn = auto()
17
+ InLarge = auto()
18
+ NotInLarge = auto()
19
+
20
+ class ExprFunction(Enum):
21
+ Soundex = auto()
22
+ Gbk = auto()
23
+ Count = auto()
24
+ Sum = auto()
25
+ Avg = auto()
26
+ Min = auto()
27
+ Max = auto()
28
+ Stddev = auto()
29
+ StddevPop = auto()
30
+ VarSamp = auto()
31
+ VarPop = auto()
32
+ BitAnd = auto()
33
+ BitOr = auto()
34
+ BitXor = auto()
35
+
36
+ class Expr:
37
+ @staticmethod
38
+ def eq(field: str, value: Any) -> 'Expr':
39
+ return BinaryExpr(ExprBuilder.column(field), BinaryOp.Eq, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
40
+ @staticmethod
41
+ def ne(field: str, value: Any) -> 'Expr':
42
+ return BinaryExpr(ExprBuilder.column(field), BinaryOp.Ne, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
43
+ @staticmethod
44
+ def gt(field: str, value: Any) -> 'Expr':
45
+ return BinaryExpr(ExprBuilder.column(field), BinaryOp.Gt, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
46
+ @staticmethod
47
+ def gte(field: str, value: Any) -> 'Expr':
48
+ return BinaryExpr(ExprBuilder.column(field), BinaryOp.Gte, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
49
+ @staticmethod
50
+ def lt(field: str, value: Any) -> 'Expr':
51
+ return BinaryExpr(ExprBuilder.column(field), BinaryOp.Lt, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
52
+ @staticmethod
53
+ def lte(field: str, value: Any) -> 'Expr':
54
+ return BinaryExpr(ExprBuilder.column(field), BinaryOp.Lte, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
55
+ @staticmethod
56
+ def like(field: str, value: Any) -> 'Expr':
57
+ return BinaryExpr(ExprBuilder.column(field), BinaryOp.Like, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
58
+ @staticmethod
59
+ def not_like(field: str, value: Any) -> 'Expr':
60
+ return BinaryExpr(ExprBuilder.column(field), BinaryOp.NotLike, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
61
+
62
+ @staticmethod
63
+ def new_and(left: 'Expr', right: 'Expr') -> 'Expr':
64
+ return AndExpr([left, right])
65
+
66
+ @staticmethod
67
+ def new_or(left: 'Expr', right: 'Expr') -> 'Expr':
68
+ return OrExpr([left, right])
69
+
70
+
71
+ @dataclass
72
+ class ColumnExpr(Expr):
73
+ name: str
74
+
75
+ @dataclass
76
+ class ValueExpr(Expr):
77
+ value: Value
78
+
79
+ @dataclass
80
+ class FunctionExpr(Expr):
81
+ function: ExprFunction
82
+ args: List[Expr]
83
+
84
+ @dataclass
85
+ class BinaryExpr(Expr):
86
+ left: Expr
87
+ op: BinaryOp
88
+ right: Expr
89
+
90
+ @dataclass
91
+ class SubQueryExpr(Expr):
92
+ left: Expr
93
+ op: BinaryOp
94
+ entity: Any # EntityDescriptor
95
+ query: Any # SelectQuery
96
+
97
+ @dataclass
98
+ class BetweenExpr(Expr):
99
+ expr: Expr
100
+ lower: Expr
101
+ upper: Expr
102
+
103
+ @dataclass
104
+ class IsNullExpr(Expr):
105
+ expr: Expr
106
+
107
+ @dataclass
108
+ class IsNotNullExpr(Expr):
109
+ expr: Expr
110
+
111
+ @dataclass
112
+ class AndExpr(Expr):
113
+ exprs: List[Expr]
114
+
115
+ @dataclass
116
+ class OrExpr(Expr):
117
+ exprs: List[Expr]
118
+
119
+ @dataclass
120
+ class NotExpr(Expr):
121
+ expr: Expr
122
+
123
+ class ExprBuilder:
124
+ @staticmethod
125
+ def column(name: str) -> Expr:
126
+ return ColumnExpr(name)
127
+
128
+ @staticmethod
129
+ def value(value: Any) -> Expr:
130
+ return ValueExpr(Value.from_any(value))
131
+
132
+ @staticmethod
133
+ def function(function: ExprFunction, args: List[Expr]) -> Expr:
134
+ return FunctionExpr(function, args)
135
+
136
+ @staticmethod
137
+ def soundex(expr: Expr) -> Expr:
138
+ return ExprBuilder.function(ExprFunction.Soundex, [expr])
139
+
140
+ @staticmethod
141
+ def gbk(expr: Expr) -> Expr:
142
+ return ExprBuilder.function(ExprFunction.Gbk, [expr])
143
+
144
+ @staticmethod
145
+ def count_all() -> Expr:
146
+ return ExprBuilder.function(ExprFunction.Count, [])
147
+
148
+ @staticmethod
149
+ def count_expr(expr: Expr) -> Expr:
150
+ return ExprBuilder.function(ExprFunction.Count, [expr])
151
+
152
+ @staticmethod
153
+ def sum_expr(expr: Expr) -> Expr:
154
+ return ExprBuilder.function(ExprFunction.Sum, [expr])
155
+
156
+ @staticmethod
157
+ def avg_expr(expr: Expr) -> Expr:
158
+ return ExprBuilder.function(ExprFunction.Avg, [expr])
159
+
160
+ @staticmethod
161
+ def min_expr(expr: Expr) -> Expr:
162
+ return ExprBuilder.function(ExprFunction.Min, [expr])
163
+
164
+ @staticmethod
165
+ def max_expr(expr: Expr) -> Expr:
166
+ return ExprBuilder.function(ExprFunction.Max, [expr])
167
+
168
+ @staticmethod
169
+ def stddev_expr(expr: Expr) -> Expr:
170
+ return ExprBuilder.function(ExprFunction.Stddev, [expr])
171
+
172
+ @staticmethod
173
+ def stddev_pop_expr(expr: Expr) -> Expr:
174
+ return ExprBuilder.function(ExprFunction.StddevPop, [expr])
175
+
176
+ @staticmethod
177
+ def var_samp_expr(expr: Expr) -> Expr:
178
+ return ExprBuilder.function(ExprFunction.VarSamp, [expr])
179
+
180
+ @staticmethod
181
+ def var_pop_expr(expr: Expr) -> Expr:
182
+ return ExprBuilder.function(ExprFunction.VarPop, [expr])
183
+
184
+ @staticmethod
185
+ def bit_and_expr(expr: Expr) -> Expr:
186
+ return ExprBuilder.function(ExprFunction.BitAnd, [expr])
187
+
188
+ @staticmethod
189
+ def bit_or_expr(expr: Expr) -> Expr:
190
+ return ExprBuilder.function(ExprFunction.BitOr, [expr])
191
+
192
+ @staticmethod
193
+ def bit_xor_expr(expr: Expr) -> Expr:
194
+ return ExprBuilder.function(ExprFunction.BitXor, [expr])
195
+
196
+
197
+ def column(name: str) -> Expr:
198
+ return ExprBuilder.column(name)
199
+
200
+ def value(val: Any) -> Expr:
201
+ return ExprBuilder.value(val)
202
+
203
+ def function(func: ExprFunction, args: List[Expr]) -> Expr:
204
+ return ExprBuilder.function(func, args)
205
+
206
+ def soundex(expr: Expr) -> Expr:
207
+ return ExprBuilder.soundex(expr)
208
+
209
+ def gbk(expr: Expr) -> Expr:
210
+ return ExprBuilder.gbk(expr)
211
+
212
+ def count_all() -> Expr:
213
+ return ExprBuilder.count_all()
214
+
215
+ def count_expr(expr: Expr) -> Expr:
216
+ return ExprBuilder.count_expr(expr)
217
+
218
+ def sum_expr(expr: Expr) -> Expr:
219
+ return ExprBuilder.sum_expr(expr)
220
+
221
+ def avg_expr(expr: Expr) -> Expr:
222
+ return ExprBuilder.avg_expr(expr)
223
+
224
+ def min_expr(expr: Expr) -> Expr:
225
+ return ExprBuilder.min_expr(expr)
226
+
227
+ def max_expr(expr: Expr) -> Expr:
228
+ return ExprBuilder.max_expr(expr)
229
+
230
+ def stddev_expr(expr: Expr) -> Expr:
231
+ return ExprBuilder.stddev_expr(expr)
232
+
233
+ def stddev_pop_expr(expr: Expr) -> Expr:
234
+ return ExprBuilder.stddev_pop_expr(expr)
235
+
236
+ def var_samp_expr(expr: Expr) -> Expr:
237
+ return ExprBuilder.var_samp_expr(expr)
238
+
239
+ def var_pop_expr(expr: Expr) -> Expr:
240
+ return ExprBuilder.var_pop_expr(expr)
241
+
242
+ def bit_and_expr(expr: Expr) -> Expr:
243
+ return ExprBuilder.bit_and_expr(expr)
244
+
245
+ def bit_or_expr(expr: Expr) -> Expr:
246
+ return ExprBuilder.bit_or_expr(expr)
247
+
248
+ def bit_xor_expr(expr: Expr) -> Expr:
249
+ return ExprBuilder.bit_xor_expr(expr)
250
+
251
+ def binary(left: Expr, op: BinaryOp, right: Expr) -> Expr:
252
+ return BinaryExpr(left, op, right)
253
+
254
+ def eq(field: str, val: Any) -> Expr:
255
+ return Expr.eq(field, val)
256
+
257
+ def ne(field: str, val: Any) -> Expr:
258
+ return Expr.ne(field, val)
259
+
260
+ def gt(field: str, val: Any) -> Expr:
261
+ return Expr.gt(field, val)
262
+
263
+ def gte(field: str, val: Any) -> Expr:
264
+ return Expr.gte(field, val)
265
+
266
+ def lt(field: str, val: Any) -> Expr:
267
+ return Expr.lt(field, val)
268
+
269
+ def lte(field: str, val: Any) -> Expr:
270
+ return Expr.lte(field, val)
271
+
272
+ def like(field: str, val: Any) -> Expr:
273
+ return Expr.like(field, val)
274
+
275
+ def not_like(field: str, val: Any) -> Expr:
276
+ return Expr.not_like(field, val)
277
+
278
+ def contain(field: str, val: Any) -> Expr:
279
+ return Expr.like(field, f"%{val}%")
280
+
281
+ def not_contain(field: str, val: Any) -> Expr:
282
+ return Expr.not_like(field, f"%{val}%")
283
+
284
+ def begin_with(field: str, val: Any) -> Expr:
285
+ return Expr.like(field, f"{val}%")
286
+
287
+ def not_begin_with(field: str, val: Any) -> Expr:
288
+ return Expr.not_like(field, f"{val}%")
289
+
290
+ def end_with(field: str, val: Any) -> Expr:
291
+ return Expr.like(field, f"%{val}")
292
+
293
+ def not_end_with(field: str, val: Any) -> Expr:
294
+ return Expr.not_like(field, f"%{val}")
295
+
296
+ def sound_like(field: str, val: Any) -> Expr:
297
+ return eq(soundex(column(field)), soundex(value(val)))
298
+
299
+ def in_list(field: str, vals: List[Any]) -> Expr:
300
+ return BinaryExpr(column(field), BinaryOp.In, value(vals))
301
+
302
+ def not_in_list(field: str, vals: List[Any]) -> Expr:
303
+ return BinaryExpr(column(field), BinaryOp.NotIn, value(vals))
304
+
305
+ def in_large(field: str, vals: List[Any]) -> Expr:
306
+ return BinaryExpr(column(field), BinaryOp.InLarge, value(vals))
307
+
308
+ def not_in_large(field: str, vals: List[Any]) -> Expr:
309
+ return BinaryExpr(column(field), BinaryOp.NotInLarge, value(vals))
310
+
311
+ def is_null(expr: Expr) -> Expr:
312
+ return IsNullExpr(expr)
313
+
314
+ def is_not_null(expr: Expr) -> Expr:
315
+ return IsNotNullExpr(expr)
316
+
317
+ def between(expr: Expr, lower: Expr, upper: Expr) -> Expr:
318
+ return BetweenExpr(expr, lower, upper)
319
+
320
+ def compare_columns(left: str, op: BinaryOp, right: str) -> Expr:
321
+ return BinaryExpr(column(left), op, column(right))
322
+
323
+ def subquery(left: Expr, op: BinaryOp, entity: Any, query: Any) -> Expr:
324
+ return SubQueryExpr(left, op, entity, query)
325
+
326
+ def in_subquery(left: Expr, entity: Any, query: Any) -> Expr:
327
+ return subquery(left, BinaryOp.In, entity, query)
328
+
329
+ def not_in_subquery(left: Expr, entity: Any, query: Any) -> Expr:
330
+ return subquery(left, BinaryOp.NotIn, entity, query)
331
+
332
+ def negate(expr: Expr) -> Expr:
333
+ return NotExpr(expr)
334
+
335
+ def and_expr(left: Expr, right: Expr) -> Expr:
336
+ return Expr.new_and(left, right)
337
+
338
+ def or_expr(left: Expr, right: Expr) -> Expr:
339
+ return OrExpr([left, right])
340
+
341
+ def and_(*exprs: Expr) -> Expr:
342
+ return AndExpr(list(exprs))
343
+
344
+ def or_(*exprs: Expr) -> Expr:
345
+ return OrExpr(list(exprs))
teaql/core/graph.py ADDED
@@ -0,0 +1,94 @@
1
+ from typing import Any, Dict, List, Optional
2
+ from enum import Enum
3
+
4
+ class EntityGraphOperation(Enum):
5
+ SAVE = 1
6
+ DELETE = 2
7
+
8
+ class GraphNode:
9
+ def __init__(self, entity: str):
10
+ self.entity = entity
11
+ self.fields: Dict[str, Any] = {}
12
+ self.is_deleted: bool = False
13
+ self.comment_text: Optional[str] = None
14
+ self.children: Dict[str, List['GraphNode']] = {}
15
+
16
+ def child(self, rel: str) -> 'GraphNode':
17
+ if rel not in self.children:
18
+ self.children[rel] = []
19
+ node = GraphNode(rel)
20
+ self.children[rel].append(node)
21
+ return node
22
+
23
+ def relation(self, rel: str) -> 'GraphNode':
24
+ return self.child(rel)
25
+
26
+ def relations(self) -> Dict[str, List['GraphNode']]:
27
+ return self.children
28
+
29
+ def remove(self, rel: str):
30
+ if rel in self.children:
31
+ del self.children[rel]
32
+
33
+ def set(self, field: str, val: Any) -> 'GraphNode':
34
+ self.fields[field] = val
35
+ return self
36
+
37
+ def value(self, field: str) -> Optional[Any]:
38
+ return self.fields.get(field)
39
+
40
+ def delete(self) -> 'GraphNode':
41
+ self.is_deleted = True
42
+ return self
43
+
44
+ def comment(self, text: str) -> 'GraphNode':
45
+ self.comment_text = text
46
+ return self
47
+
48
+ def set_comment(self, text: str):
49
+ self.comment_text = text
50
+
51
+ def id(self) -> Optional[Any]:
52
+ return self.fields.get('id')
53
+
54
+ def operation(self) -> Optional['EntityGraphOperation']:
55
+ if self.is_deleted:
56
+ return EntityGraphOperation.DELETE
57
+ return EntityGraphOperation.SAVE
58
+
59
+ def reference(self, rel: str, ref_id: Any) -> 'GraphNode':
60
+ node = self.child(rel)
61
+ node.set('id', ref_id)
62
+ return node
63
+
64
+ class EntityGraphBuilder:
65
+ def __init__(self, entity: str):
66
+ self.node = GraphNode(entity)
67
+
68
+ def set(self, key: str, value: Any) -> 'EntityGraphBuilder':
69
+ self.node.set(key, value)
70
+ return self
71
+
72
+ def delete(self) -> 'EntityGraphBuilder':
73
+ self.node.delete()
74
+ return self
75
+
76
+ def comment(self, text: str) -> 'EntityGraphBuilder':
77
+ self.node.comment(text)
78
+ return self
79
+
80
+ def child(self, child_node: GraphNode) -> 'EntityGraphBuilder':
81
+ self.node.children.append(child_node)
82
+ return self
83
+
84
+ def save(self) -> 'EntityGraphBuilder':
85
+ self.node.operation = EntityGraphOperation.SAVE
86
+ return self
87
+
88
+ def build(self) -> GraphNode:
89
+ return self.node
90
+
91
+ class EntityGraph:
92
+ @staticmethod
93
+ def new(entity: str) -> EntityGraphBuilder:
94
+ return EntityGraphBuilder(entity)
teaql/core/list.py ADDED
@@ -0,0 +1,80 @@
1
+ from typing import Any, List, Dict, Iterator
2
+
3
+ class SmartList:
4
+ def __init__(self, data: List[Any], facets: Dict[str, Any] = None, total_count: int = None):
5
+ self.data = data
6
+ self.facets = facets or {}
7
+ self.total_count = total_count if total_count is not None else len(data)
8
+
9
+ def facet(self, name: str) -> Any:
10
+ return self.facets.get(name)
11
+
12
+ def __iter__(self) -> Iterator[Any]:
13
+ return iter(self.data)
14
+
15
+ def __len__(self) -> int:
16
+ return len(self.data)
17
+
18
+ def map(self, f) -> 'SmartList':
19
+ return SmartList([f(x) for x in self.data], self.facets, self.total_count)
20
+
21
+ def filter(self, f) -> 'SmartList':
22
+ return SmartList([x for x in self.data if f(x)], self.facets, self.total_count)
23
+
24
+ def flat_map(self, f) -> 'SmartList':
25
+ result = []
26
+ for x in self.data:
27
+ result.extend(f(x))
28
+ return SmartList(result, self.facets, self.total_count)
29
+
30
+ def first(self) -> Any:
31
+ return self.data[0] if self.data else None
32
+
33
+ def last(self) -> Any:
34
+ return self.data[-1] if self.data else None
35
+
36
+ def is_empty(self) -> bool:
37
+ return len(self.data) == 0
38
+
39
+ def into_vec(self) -> List[Any]:
40
+ return self.data
41
+
42
+ def get(self, index: int) -> Any:
43
+ if 0 <= index < len(self.data):
44
+ return self.data[index]
45
+ return None
46
+
47
+ def retain(self, f):
48
+ self.data = [x for x in self.data if f(x)]
49
+
50
+ def to_list(lst: SmartList) -> List[Any]:
51
+ return lst.data
52
+
53
+ def to_set(lst: SmartList) -> set:
54
+ return set(lst.data)
55
+
56
+ def identity_map(lst: SmartList) -> Dict[Any, Any]:
57
+ return {x: x for x in lst.data}
58
+
59
+ def group_by(lst: SmartList, key_func) -> Dict[Any, List[Any]]:
60
+ result = {}
61
+ for item in lst.data:
62
+ key = key_func(item)
63
+ if key not in result:
64
+ result[key] = []
65
+ result[key].append(item)
66
+ return result
67
+
68
+ def into_records(lst: SmartList) -> List[Dict[str, Any]]:
69
+ # Assuming the elements are entities with a .values dict or similar, or they are just dicts
70
+ return [x.values if hasattr(x, 'values') else dict(x) for x in lst.data]
71
+
72
+ def ids(lst: SmartList) -> List[Any]:
73
+ # Assuming elements have a .get('id') or similar
74
+ return [x.get('id') if isinstance(x, dict) else getattr(x, 'id', None) for x in lst.data]
75
+
76
+ def map_by_id(lst: SmartList) -> Dict[Any, Any]:
77
+ return { (x.get('id') if isinstance(x, dict) else getattr(x, 'id', None)): x for x in lst.data }
78
+
79
+ def versions(lst: SmartList) -> List[Any]:
80
+ return [x.get('version') if isinstance(x, dict) else getattr(x, 'version', None) for x in lst.data]