sera-2 1.19.2__py3-none-any.whl → 1.20.4__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.
sera/libs/api_helper.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import re
4
- from typing import Collection, Generic, cast
4
+ from typing import Callable, Collection, Generic, TypeVar, cast
5
5
 
6
6
  from litestar import Request, status_codes
7
7
  from litestar.connection import ASGIConnection
@@ -19,12 +19,30 @@ from sera.libs.middlewares.uscp import STATE_SYSTEM_CONTROLLED_PROP_KEY
19
19
  from sera.typing import T
20
20
 
21
21
  # for parsing field names and operations from query string
22
- FIELD_REG = re.compile(r"(?P<name>[a-zA-Z_0-9]+)(?:\[(?P<op>[a-zA-Z0-9]+)\])?")
22
+ FIELD_REG = re.compile(r"(?P<name>[a-zA-Z_0-9]+)(?:\[(?P<op>[a-zA-Z_0-9]+)\])?")
23
23
  QUERY_OPS = {op.value for op in QueryOp}
24
24
  KEYWORDS = {"field", "limit", "offset", "unique", "sorted_by", "group_by"}
25
25
 
26
26
 
27
- def parse_query(request: Request, fields: set[str], debug: bool) -> Query:
27
+ class TypeConversion:
28
+
29
+ to_int = int
30
+ to_float = float
31
+
32
+ @staticmethod
33
+ def to_bool(value: str) -> bool:
34
+ if value == "1":
35
+ return True
36
+ elif value == "0":
37
+ return False
38
+ raise ValueError(f"Invalid boolean value: {value}")
39
+
40
+
41
+ def parse_query(
42
+ request: Request,
43
+ fields: dict[str, Callable[[str], str | int | bool | float]],
44
+ debug: bool,
45
+ ) -> Query:
28
46
  """Parse query for retrieving records that match a query.
29
47
 
30
48
  If a field name collides with a keyword, you can add `_` to the field name.
@@ -56,6 +74,7 @@ def parse_query(request: Request, fields: set[str], debug: bool) -> Query:
56
74
  continue
57
75
 
58
76
  # Process based on operation or default to equality check
77
+ # TODO: validate if the operation is allowed for the field
59
78
  if not operation:
60
79
  operation = QueryOp.eq
61
80
  else:
@@ -65,6 +84,21 @@ def parse_query(request: Request, fields: set[str], debug: bool) -> Query:
65
84
  detail=f"Invalid operation: {operation}",
66
85
  )
67
86
  operation = QueryOp(operation)
87
+
88
+ try:
89
+ norm_func = fields[field_name]
90
+ if isinstance(v, list):
91
+ v = [norm_func(x) for x in v]
92
+ else:
93
+ v = norm_func(v)
94
+ except ValueError:
95
+ if debug:
96
+ raise HTTPException(
97
+ status_code=status_codes.HTTP_400_BAD_REQUEST,
98
+ detail=f"Invalid value for field {field_name}: {v}",
99
+ )
100
+ continue
101
+
68
102
  query[field_name] = {operation: v}
69
103
  else:
70
104
  # Invalid field name format
@@ -78,7 +112,10 @@ def parse_query(request: Request, fields: set[str], debug: bool) -> Query:
78
112
  return query
79
113
 
80
114
 
81
- class SingleAutoUSCP(MsgspecDTO[T], Generic[T]):
115
+ S = TypeVar("S", bound=Struct)
116
+
117
+
118
+ class SingleAutoUSCP(MsgspecDTO[S], Generic[S]):
82
119
  """Auto Update System Controlled Property DTO"""
83
120
 
84
121
  @classmethod
sera/libs/base_service.py CHANGED
@@ -17,15 +17,15 @@ from sera.typing import FieldName, T, doc
17
17
 
18
18
 
19
19
  class QueryOp(str, Enum):
20
- lt = "<"
21
- lte = "<="
22
- gt = ">"
23
- gte = ">="
24
- eq = "="
25
- ne = "!="
20
+ lt = "lt"
21
+ lte = "lte"
22
+ gt = "gt"
23
+ gte = "gte"
24
+ eq = "eq"
25
+ ne = "ne"
26
26
  # select records where values are in the given list
27
27
  in_ = "in"
28
- not_in = "not in"
28
+ not_in = "not_in"
29
29
  # for full text search
30
30
  fuzzy = "fuzzy"
31
31
 
@@ -94,15 +94,41 @@ class BaseAsyncService(Generic[ID, R]):
94
94
  )
95
95
  if unique:
96
96
  q = q.distinct()
97
- if sorted_by:
98
- for field in sorted_by:
99
- if field.startswith("-"):
100
- q = q.order_by(getattr(self.orm_cls, field[1:]).desc())
97
+ for field in sorted_by:
98
+ if field.startswith("-"):
99
+ q = q.order_by(getattr(self.orm_cls, field[1:]).desc())
100
+ else:
101
+ q = q.order_by(getattr(self.orm_cls, field))
102
+ for field in group_by:
103
+ q = q.group_by(getattr(self.orm_cls, field))
104
+
105
+ for field, conditions in query.items():
106
+ for op, value in conditions.items():
107
+ # TODO: check if the operation is valid for the field.
108
+ if op == QueryOp.eq:
109
+ q = q.where(getattr(self.orm_cls, field) == value)
110
+ elif op == QueryOp.ne:
111
+ q = q.where(getattr(self.orm_cls, field) != value)
112
+ elif op == QueryOp.lt:
113
+ q = q.where(getattr(self.orm_cls, field) < value)
114
+ elif op == QueryOp.lte:
115
+ q = q.where(getattr(self.orm_cls, field) <= value)
116
+ elif op == QueryOp.gt:
117
+ q = q.where(getattr(self.orm_cls, field) > value)
118
+ elif op == QueryOp.gte:
119
+ q = q.where(getattr(self.orm_cls, field) >= value)
120
+ elif op == QueryOp.in_:
121
+ q = q.where(getattr(self.orm_cls, field).in_(value))
122
+ elif op == QueryOp.not_in:
123
+ q = q.where(~getattr(self.orm_cls, field).in_(value))
101
124
  else:
102
- q = q.order_by(getattr(self.orm_cls, field))
103
- if group_by:
104
- for field in group_by:
105
- q = q.group_by(getattr(self.orm_cls, field))
125
+ assert op == QueryOp.fuzzy
126
+ # Assuming fuzzy search is implemented as a full-text search
127
+ q = q.where(
128
+ func.to_tsvector(getattr(self.orm_cls, field)).match(value)
129
+ )
130
+
131
+ print(">>>", q)
106
132
 
107
133
  cq = select(func.count()).select_from(q.subquery())
108
134
  rq = q.limit(limit).offset(offset)
@@ -4,6 +4,7 @@ from typing import Sequence
4
4
 
5
5
  from codegen.models import DeferredVar, ImportHelper, PredefinedFn, Program, expr, stmt
6
6
  from loguru import logger
7
+ from msgspec import convert
7
8
 
8
9
  from sera.misc import assert_not_null, to_snake_case
9
10
  from sera.models import App, DataCollection, Module, Package
@@ -134,11 +135,21 @@ def make_python_get_api(
134
135
 
135
136
  func_name = "get_"
136
137
 
138
+ queryable_fields = []
139
+ for propname, (
140
+ convert_func,
141
+ convert_func_import,
142
+ ) in collection.get_queryable_fields():
143
+ program.import_(convert_func_import, True)
144
+ queryable_fields.append(
145
+ (expr.ExprConstant(propname), expr.ExprIdent(convert_func))
146
+ )
147
+
137
148
  program.root(
138
149
  stmt.LineBreak(),
139
150
  lambda ast00: ast00.assign(
140
151
  DeferredVar.simple("QUERYABLE_FIELDS"),
141
- expr.ExprConstant(collection.get_queryable_fields()),
152
+ PredefinedFn.dict(queryable_fields),
142
153
  ),
143
154
  stmt.PythonDecoratorStatement(
144
155
  expr.ExprFuncCall(
@@ -749,6 +749,7 @@ def make_python_data_model(
749
749
  # skip private fields as this is for APIs exchange
750
750
  continue
751
751
 
752
+ propname = prop.name
752
753
  if isinstance(prop, DataProperty):
753
754
  pytype = prop.get_data_model_datatype().get_python_type()
754
755
  if prop.is_optional:
@@ -760,6 +761,7 @@ def make_python_data_model(
760
761
  cls_ast(stmt.DefClassVarStatement(prop.name, pytype.type))
761
762
  elif isinstance(prop, ObjectProperty):
762
763
  if prop.target.db is not None:
764
+ propname = prop.name + "_id"
763
765
  pytype = (
764
766
  assert_not_null(prop.target.get_id_property())
765
767
  .get_data_model_datatype()
@@ -781,7 +783,7 @@ def make_python_data_model(
781
783
  for dep in pytype.deps:
782
784
  program.import_(dep, True)
783
785
 
784
- cls_ast(stmt.DefClassVarStatement(prop.name, pytype.type))
786
+ cls_ast(stmt.DefClassVarStatement(propname, pytype.type))
785
787
 
786
788
  cls_ast(
787
789
  stmt.LineBreak(),
@@ -1294,6 +1296,10 @@ def make_python_relational_object_property(
1294
1296
  idprop = prop.target.get_id_property()
1295
1297
  assert idprop is not None
1296
1298
  idprop_pytype = idprop.datatype.get_sqlalchemy_type()
1299
+
1300
+ if prop.is_optional:
1301
+ idprop_pytype = idprop_pytype.as_optional_type()
1302
+
1297
1303
  for dep in idprop_pytype.deps:
1298
1304
  program.import_(dep, True)
1299
1305
 
@@ -50,6 +50,66 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
50
50
  # the original object, then it's okay.
51
51
  return value
52
52
 
53
+ def get_normal_deser_args(
54
+ prop: DataProperty | ObjectProperty,
55
+ ) -> expr.Expr:
56
+ """Extract the value from the data record from the server response to set to the class property in the client."""
57
+ handle_optional = lambda value: expr.ExprTernary(
58
+ expr.ExprNotEqual(value, expr.ExprConstant(None)),
59
+ value,
60
+ expr.ExprConstant("undefined"),
61
+ )
62
+
63
+ if isinstance(prop, DataProperty):
64
+ value = PredefinedFn.attr_getter(
65
+ expr.ExprIdent("data"), expr.ExprIdent(prop.name)
66
+ )
67
+ if prop.is_optional:
68
+ value = handle_optional(value)
69
+ return value
70
+
71
+ assert isinstance(prop, ObjectProperty)
72
+ if prop.target.db is not None:
73
+ value = PredefinedFn.attr_getter(
74
+ expr.ExprIdent("data"), expr.ExprIdent(prop.name + "_id")
75
+ )
76
+ if prop.is_optional:
77
+ value = handle_optional(value)
78
+ return value
79
+ else:
80
+ if prop.cardinality.is_star_to_many():
81
+ # optional type for a list is simply an empty list, we don't need to check for None
82
+ value = PredefinedFn.map_list(
83
+ PredefinedFn.attr_getter(
84
+ expr.ExprIdent("data"),
85
+ expr.ExprIdent(prop.name),
86
+ ),
87
+ lambda item: expr.ExprMethodCall(
88
+ expr.ExprIdent(
89
+ assert_isinstance(prop, ObjectProperty).target.name
90
+ ),
91
+ "deser",
92
+ [item],
93
+ ),
94
+ )
95
+ return value
96
+ else:
97
+ value = expr.ExprFuncCall(
98
+ PredefinedFn.attr_getter(
99
+ expr.ExprIdent(prop.target.name),
100
+ expr.ExprIdent("deser"),
101
+ ),
102
+ [
103
+ PredefinedFn.attr_getter(
104
+ expr.ExprIdent("data"),
105
+ expr.ExprIdent(prop.name),
106
+ )
107
+ ],
108
+ )
109
+ if prop.is_optional:
110
+ value = handle_optional(value)
111
+ return value
112
+
53
113
  def make_normal(cls: Class, pkg: Package):
54
114
  """Make a data model for the normal Python data model"""
55
115
  if not cls.is_public:
@@ -89,9 +149,7 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
89
149
  deser_args.append(
90
150
  (
91
151
  expr.ExprIdent(propname),
92
- PredefinedFn.attr_getter(
93
- expr.ExprIdent("data"), expr.ExprIdent(prop.name)
94
- ),
152
+ get_normal_deser_args(prop),
95
153
  )
96
154
  )
97
155
  else:
@@ -101,9 +159,13 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
101
159
  propname = propname + "Id"
102
160
  tstype = TsTypeWithDep(
103
161
  f"{prop.target.name}Id",
104
- [
105
- f"@.models.{prop.target.get_tsmodule_name()}.{prop.target.name}.{prop.target.name}Id"
106
- ],
162
+ (
163
+ [
164
+ f"@.models.{prop.target.get_tsmodule_name()}.{prop.target.name}.{prop.target.name}Id"
165
+ ]
166
+ if prop.target.name != cls.name
167
+ else []
168
+ ),
107
169
  )
108
170
  if prop.cardinality.is_star_to_many():
109
171
  tstype = tstype.as_list_type()
@@ -113,10 +175,7 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
113
175
  deser_args.append(
114
176
  (
115
177
  expr.ExprIdent(propname),
116
- PredefinedFn.attr_getter(
117
- expr.ExprIdent("data"),
118
- expr.ExprIdent(prop.name + "_id"),
119
- ),
178
+ get_normal_deser_args(prop),
120
179
  )
121
180
  )
122
181
  else:
@@ -132,21 +191,7 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
132
191
  deser_args.append(
133
192
  (
134
193
  expr.ExprIdent(propname),
135
- PredefinedFn.map_list(
136
- PredefinedFn.attr_getter(
137
- expr.ExprIdent("data"),
138
- expr.ExprIdent(prop.name),
139
- ),
140
- lambda item: expr.ExprMethodCall(
141
- expr.ExprIdent(
142
- assert_isinstance(
143
- prop, ObjectProperty
144
- ).target.name
145
- ),
146
- "deser",
147
- [item],
148
- ),
149
- ),
194
+ get_normal_deser_args(prop),
150
195
  )
151
196
  )
152
197
  else:
@@ -156,18 +201,7 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
156
201
  deser_args.append(
157
202
  (
158
203
  expr.ExprIdent(propname),
159
- expr.ExprFuncCall(
160
- PredefinedFn.attr_getter(
161
- expr.ExprIdent(prop.target.name),
162
- expr.ExprIdent("deser"),
163
- ),
164
- [
165
- PredefinedFn.attr_getter(
166
- expr.ExprIdent("data"),
167
- expr.ExprIdent(prop.name),
168
- )
169
- ],
170
- ),
204
+ get_normal_deser_args(prop),
171
205
  )
172
206
  )
173
207
 
@@ -378,8 +412,13 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
378
412
  if tstype.type in idprop_aliases:
379
413
  create_propvalue = idprop_aliases[tstype.type].get_default()
380
414
  elif tstype.type in schema.enums:
381
- create_propvalue = expr.ExprConstant(
382
- next(iter(schema.enums[tstype.type].values.values())).value
415
+ enum_value = next(
416
+ iter(schema.enums[tstype.type].values.values())
417
+ ).value
418
+ # TODO: handle enum value integer
419
+ assert isinstance(enum_value, str)
420
+ create_propvalue = expr.ExprIdent(
421
+ tstype.type + "." + enum_value
383
422
  )
384
423
  else:
385
424
  create_propvalue = tstype.get_default()
@@ -1131,7 +1170,13 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
1131
1170
  # if prop.data.is_private:
1132
1171
  # # skip private fields as this is for APIs exchange
1133
1172
  # continue
1134
- propname = to_camel_case(prop.name)
1173
+ tspropname = to_camel_case(prop.name)
1174
+ pypropname = prop.name
1175
+ if isinstance(prop, ObjectProperty) and prop.target.db is not None:
1176
+ # this is a database object, we append id to the property name
1177
+ tspropname = tspropname + "Id"
1178
+ pypropname = prop.name + "_id"
1179
+
1135
1180
  tsprop = {}
1136
1181
 
1137
1182
  if isinstance(prop, DataProperty):
@@ -1169,7 +1214,7 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
1169
1214
  norm_func = get_normalizer(tstype, import_helper)
1170
1215
  if norm_func is not None:
1171
1216
  # we have a normalizer for this type
1172
- prop_normalizers.append((expr.ExprIdent(propname), norm_func))
1217
+ prop_normalizers.append((expr.ExprIdent(tspropname), norm_func))
1173
1218
  else:
1174
1219
  assert isinstance(prop, ObjectProperty)
1175
1220
  if prop.target.db is not None:
@@ -1224,11 +1269,11 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
1224
1269
  prop_defs.append(
1225
1270
  (
1226
1271
  prop,
1227
- expr.ExprIdent(propname),
1272
+ expr.ExprIdent(tspropname),
1228
1273
  PredefinedFn.dict(
1229
1274
  [
1230
- (expr.ExprIdent("name"), expr.ExprConstant(prop.name)),
1231
- (expr.ExprIdent("tsName"), expr.ExprConstant(propname)),
1275
+ (expr.ExprIdent("name"), expr.ExprConstant(pypropname)),
1276
+ (expr.ExprIdent("tsName"), expr.ExprConstant(tspropname)),
1232
1277
  (
1233
1278
  expr.ExprIdent("updateFuncName"),
1234
1279
  expr.ExprConstant(f"update{to_pascal_case(prop.name)}"),
@@ -1298,7 +1343,10 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
1298
1343
  " | ".join(
1299
1344
  [
1300
1345
  expr.ExprConstant(
1301
- to_camel_case(prop.name)
1346
+ to_camel_case(prop.name) + "Id"
1347
+ if isinstance(prop, ObjectProperty)
1348
+ and prop.target.db is not None
1349
+ else to_camel_case(prop.name)
1302
1350
  ).to_typescript()
1303
1351
  for prop in cls.properties.values()
1304
1352
  if not prop.data.is_private
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
4
 
5
+ from codegen.models import ImportHelper
6
+
5
7
  from sera.models._class import Class
6
8
  from sera.models._property import DataProperty, ObjectProperty
7
9
 
@@ -21,9 +23,9 @@ class DataCollection:
21
23
  """Get the python module name of this collection as if there is a python module created to store this collection only."""
22
24
  return self.cls.get_pymodule_name()
23
25
 
24
- def get_queryable_fields(self) -> set[str]:
26
+ def get_queryable_fields(self) -> list[tuple[str, tuple[str, str]]]:
25
27
  """Get the fields of this collection that can be used in a queries."""
26
- field_names = set()
28
+ output = []
27
29
  for prop in self.cls.properties.values():
28
30
  if prop.db is None or prop.data.is_private:
29
31
  # This property is not stored in the database or it's private, so we skip it
@@ -45,8 +47,19 @@ class DataCollection:
45
47
  else:
46
48
  # This property is a data property or an object property not stored in the database, so we use its name
47
49
  propname = prop.name
48
- field_names.add(propname)
49
- return field_names
50
+
51
+ if isinstance(prop, DataProperty):
52
+ convert_func = prop.datatype.pytype.get_string_conversion_func()
53
+ else:
54
+ assert isinstance(prop, ObjectProperty) and prop.target.db is not None
55
+ target_idprop = prop.target.get_id_property()
56
+ assert target_idprop is not None
57
+ convert_func = (
58
+ target_idprop.datatype.pytype.get_string_conversion_func()
59
+ )
60
+
61
+ output.append((propname, convert_func))
62
+ return output
50
63
 
51
64
  def get_service_name(self):
52
65
  return f"{self.name}Service"
sera/models/_datatype.py CHANGED
@@ -67,6 +67,18 @@ class PyTypeWithDep:
67
67
  """Clone the type with the same dependencies."""
68
68
  return PyTypeWithDep(type=self.type, deps=list(self.deps))
69
69
 
70
+ def get_string_conversion_func(self) -> tuple[str, str]:
71
+ if self.type == "str":
72
+ return ("identity", "sera.misc.identity")
73
+ if self.type == "int":
74
+ return ("TypeConversion.to_int", "sera.libs.api_helper.TypeConversion")
75
+ if self.type == "float":
76
+ return ("TypeConversion.to_float", "sera.libs.api_helper.TypeConversion")
77
+ if self.type == "bool":
78
+ return ("TypeConversion.to_bool", "sera.libs.api_helper.TypeConversion")
79
+ else:
80
+ raise NotImplementedError()
81
+
70
82
 
71
83
  @dataclass
72
84
  class TsTypeWithDep:
@@ -122,6 +134,23 @@ class SQLTypeWithDep:
122
134
  mapped_pytype=f"list[{self.mapped_pytype}]",
123
135
  )
124
136
 
137
+ def as_optional_type(self) -> SQLTypeWithDep:
138
+ """Convert the type to an optional type."""
139
+ if "typing.Optional" not in self.deps:
140
+ deps = self.deps + ["typing.Optional"]
141
+ else:
142
+ deps = self.deps
143
+
144
+ if "Optional[" in self.mapped_pytype:
145
+ raise NotImplementedError(
146
+ f"Have not handle nested optional yet: {self.mapped_pytype}"
147
+ )
148
+ return SQLTypeWithDep(
149
+ type=self.type,
150
+ mapped_pytype=f"Optional[{self.mapped_pytype}]",
151
+ deps=deps,
152
+ )
153
+
125
154
 
126
155
  @dataclass
127
156
  class DataType:
sera/models/_parse.py CHANGED
@@ -238,7 +238,12 @@ def _parse_datatype(schema: Schema, datatype: dict | str) -> DataType:
238
238
  ],
239
239
  ),
240
240
  sqltype=SQLTypeWithDep(
241
- type="String", mapped_pytype="str", deps=["sqlalchemy.String"]
241
+ type=f"Enum({enum.name})",
242
+ mapped_pytype=enum.name,
243
+ deps=[
244
+ "sqlalchemy.Enum",
245
+ f"{schema.name}.models.enums.{enum.get_pymodule_name()}.{enum.name}",
246
+ ],
242
247
  ),
243
248
  tstype=TsTypeWithDep(
244
249
  type=enum.name, deps=[f"@.models.enums.{enum.name}"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sera-2
3
- Version: 1.19.2
3
+ Version: 1.20.4
4
4
  Summary:
5
5
  Author: Binh Vu
6
6
  Author-email: bvu687@gmail.com
@@ -9,7 +9,7 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.12
10
10
  Classifier: Programming Language :: Python :: 3.13
11
11
  Requires-Dist: black (==25.1.0)
12
- Requires-Dist: codegen-2 (>=2.12.0,<3.0.0)
12
+ Requires-Dist: codegen-2 (>=2.12.2,<3.0.0)
13
13
  Requires-Dist: graph-wrapper (>=1.7.2,<2.0.0)
14
14
  Requires-Dist: isort (==6.0.1)
15
15
  Requires-Dist: litestar (>=2.15.1,<3.0.0)
@@ -4,10 +4,10 @@ sera/exports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  sera/exports/schema.py,sha256=wEBUrDOyuCoCJC8X4RlmoWpeqSugaboG-9Q1UQ8HEzk,7824
5
5
  sera/exports/test.py,sha256=jK1EJmLGiy7eREpnY_68IIVRH43uH8S_u5Z7STPbXOM,2002
6
6
  sera/libs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- sera/libs/api_helper.py,sha256=47y1kcwk3Xd2ZEMnUj_0OwCuUmgwOs5kYrE95BDVUn4,5411
7
+ sera/libs/api_helper.py,sha256=VZsYDeeMxzH4-7quM8W-1rl3zLFEEVShb8kLHS2UdHE,6382
8
8
  sera/libs/api_test_helper.py,sha256=3tRr8sLN4dBSrHgKAHMmyoENI0xh7K_JLel8AvujU7k,1323
9
9
  sera/libs/base_orm.py,sha256=5hOH_diUeaABm3cpE2-9u50VRqG1QW2osPQnvVHIhIA,3365
10
- sera/libs/base_service.py,sha256=AX1WoTHte6Z_birkkfagkNE6BrCLTlTjQE4jEsKEaAY,5152
10
+ sera/libs/base_service.py,sha256=V6wug1QA5KD0FsQU9r1XQmLUTB85E6oIR2e12Fi1IE0,6457
11
11
  sera/libs/directed_computing_graph/__init__.py,sha256=xiF5_I1y9HtQ-cyq02iwkRYgEZvxBB8YIvysCHCLBco,1290
12
12
  sera/libs/directed_computing_graph/_dcg.py,sha256=AGTzKVSl-EsSOJlNKPOA1Io7pIxfq0SMXuumq1IExl0,14902
13
13
  sera/libs/directed_computing_graph/_edge.py,sha256=iBq6cpLWWyuD99QWTHVEh8naWUJrR4WJJuq5iuCrwHo,1026
@@ -22,26 +22,26 @@ sera/libs/middlewares/uscp.py,sha256=H5umW8iEQSCdb_MJ5Im49kxg1E7TpxSg1p2_2A5zI1U
22
22
  sera/make/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  sera/make/__main__.py,sha256=HRfOR53p351h6KblVvYm3DLhDIfEtk6R0kjl78_S_S8,1453
24
24
  sera/make/make_app.py,sha256=n9NtW73O3s_5Q31VHIRmnd-jEIcpDO7ksAsOdovde2s,5999
25
- sera/make/make_python_api.py,sha256=iXGbKQ3IJvsY1ur_fhurr_THFNnH66E3Wl85o0emUbw,26853
26
- sera/make/make_python_model.py,sha256=3qz1OUg2n4QPt31mZHMncU5v9riXUAoQwIpXCa4Fyv4,63306
25
+ sera/make/make_python_api.py,sha256=aOm8QSiXNLe4akiOx_KKsDCwLVwetRzuxOgZaWqEj0w,27172
26
+ sera/make/make_python_model.py,sha256=t_2RUaO6WL4b5FtYj-Ly70njmcGGYwoddbL_oim9378,63479
27
27
  sera/make/make_python_services.py,sha256=0ZpWLwQ7Nwfn8BXAikAB4JRpNknpSJyJgY5b1cjtxV4,2073
28
- sera/make/make_typescript_model.py,sha256=laW27gFYGFhvLY5DfcVgh8tPlDqvmS824r-fbBnG7JI,67493
28
+ sera/make/make_typescript_model.py,sha256=u9pbe9JnSwiV1wLAS_H3p2mdOL_FKXjsZRCRuPu2QBY,69156
29
29
  sera/misc/__init__.py,sha256=rOmGMv7QNzpSKZSyxChbRmEnBr3O443UlLGS0FIs3AI,561
30
30
  sera/misc/_formatter.py,sha256=aCGYL08l8f3aLODHxSocxBBwkRYEo3K1QzCDEn3suj0,1685
31
31
  sera/misc/_utils.py,sha256=_-17XbK6qp3HobcI9iLF4xfaATvFg1ckUzgg7r7Ctmw,7135
32
32
  sera/models/__init__.py,sha256=vJC5Kzo_N7wd16ocNPy1VvAZDGNiWeiAhWJ4ihATKvA,780
33
33
  sera/models/_class.py,sha256=1J4Bd_LanzhhDWwZFHWGtFYD7lupe_alaB3D02ebNDI,2862
34
- sera/models/_collection.py,sha256=AmEy6SoTTaxqFU4mBFVfFMGyutJuNnUGBaIV7xzGxbc,2143
34
+ sera/models/_collection.py,sha256=nLlP85OfEhfj4pFAYxB5ZvtBN6EdPSWPJ5ZFh5SSEC4,2686
35
35
  sera/models/_constraints.py,sha256=RpWDU-TfCslXaMUaTG9utWbl5z8Z6nzvF_fhqlek6ew,1987
36
- sera/models/_datatype.py,sha256=cC6Wm0IvobDF5Tq9Jy_ncbjPWBRl3ECGBy0wJyDDMzU,7281
36
+ sera/models/_datatype.py,sha256=6s2TIhUX8Eb1w3XXKUcOoVybF_lrmKtNAriDgzv2JAE,8407
37
37
  sera/models/_default.py,sha256=ABggW6qdPR4ZDqIPJdJ0GCGQ-7kfsfZmQ_DchgZEa-I,137
38
38
  sera/models/_enum.py,sha256=sy0q7E646F-APsqrVQ52r1fAQ_DCAeaNq5YM5QN3zIk,2070
39
39
  sera/models/_module.py,sha256=I-GfnTgAa-5R87qTAvEzOt-VVEGeFBBwubGCgUkXVSw,5159
40
40
  sera/models/_multi_lingual_string.py,sha256=JETN6k00VH4wrA4w5vAHMEJV8fp3SY9bJebskFTjQLA,1186
41
- sera/models/_parse.py,sha256=ciTLzCkO0q6xA1R_rHbnYJYK3Duo2oh56WeuwxXwJaI,12392
41
+ sera/models/_parse.py,sha256=sm-TuhFuru4e-g8HYmPo694sGJooBtIFZeky24X7YYk,12588
42
42
  sera/models/_property.py,sha256=9yMDxrmbyuF6-29lQjiq163Xzwbk75TlmGBpu0NLpkI,7485
43
43
  sera/models/_schema.py,sha256=VxJEiqgVvbXgcSUK4UW6JnRcggk4nsooVSE6MyXmfNY,1636
44
44
  sera/typing.py,sha256=m4rir-fB6Cgcm7_ZSXXcNdla2LJgq96WXxtTTrDaJno,1058
45
- sera_2-1.19.2.dist-info/METADATA,sha256=yv6pv0XvFLzt1CqYP5u38CCVGHLInOCB1AflthDV5_g,936
46
- sera_2-1.19.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
47
- sera_2-1.19.2.dist-info/RECORD,,
45
+ sera_2-1.20.4.dist-info/METADATA,sha256=2HIbHZt_ScaEAzPZGlpNNX7jEmiv1sGmMVgmK-7nJU4,936
46
+ sera_2-1.20.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
47
+ sera_2-1.20.4.dist-info/RECORD,,