sera-2 1.24.1__py3-none-any.whl → 1.25.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.
- sera/libs/base_service.py +85 -21
- sera/libs/search_helper.py +4 -2
- sera/make/make_python_model.py +70 -3
- sera/models/__init__.py +8 -1
- sera/models/_class.py +2 -1
- sera/models/_parse.py +10 -1
- sera/models/_property.py +8 -0
- sera/typing.py +1 -0
- {sera_2-1.24.1.dist-info → sera_2-1.25.0.dist-info}/METADATA +4 -3
- {sera_2-1.24.1.dist-info → sera_2-1.25.0.dist-info}/RECORD +11 -11
- {sera_2-1.24.1.dist-info → sera_2-1.25.0.dist-info}/WHEEL +1 -1
sera/libs/base_service.py
CHANGED
@@ -10,8 +10,8 @@ from sqlalchemy.orm import contains_eager, load_only
|
|
10
10
|
|
11
11
|
from sera.libs.base_orm import BaseORM
|
12
12
|
from sera.libs.search_helper import Query, QueryOp
|
13
|
-
from sera.misc import assert_not_null, to_snake_case
|
14
|
-
from sera.models import Cardinality, Class, DataProperty, ObjectProperty
|
13
|
+
from sera.misc import assert_isinstance, assert_not_null, to_snake_case
|
14
|
+
from sera.models import Cardinality, Class, DataProperty, IndexType, ObjectProperty
|
15
15
|
|
16
16
|
R = TypeVar("R", bound=BaseORM)
|
17
17
|
ID = TypeVar("ID") # ID of a class
|
@@ -20,6 +20,7 @@ SqlResult = TypeVar("SqlResult", bound=Result)
|
|
20
20
|
|
21
21
|
class QueryResult(NamedTuple, Generic[R]):
|
22
22
|
records: Sequence[R]
|
23
|
+
extra_columns: Mapping[str, Sequence]
|
23
24
|
total: Optional[int]
|
24
25
|
|
25
26
|
|
@@ -36,6 +37,7 @@ class BaseAsyncService(Generic[ID, R]):
|
|
36
37
|
self._cls_id_prop = getattr(self.orm_cls, self.id_prop.name)
|
37
38
|
self.is_id_auto_increment = assert_not_null(self.id_prop.db).is_auto_increment
|
38
39
|
|
40
|
+
# mapping from property name to ORM class for object properties
|
39
41
|
self.prop2orm: dict[str, type] = {
|
40
42
|
prop.name: orm_classes[prop.target.name]
|
41
43
|
for prop in cls.properties.values()
|
@@ -103,15 +105,15 @@ class BaseAsyncService(Generic[ID, R]):
|
|
103
105
|
# A -> B is either 1:1 or N:1, we will store the foreign key is in A
|
104
106
|
# .join(B, A.<foreign_key> == B.id)
|
105
107
|
self.join_clauses[prop.name] = [
|
106
|
-
{
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
},
|
108
|
+
# {
|
109
|
+
# "class": target_tbl,
|
110
|
+
# "condition": getattr(
|
111
|
+
# target_tbl,
|
112
|
+
# assert_not_null(target_cls.get_id_property()).name,
|
113
|
+
# )
|
114
|
+
# == getattr(self.orm_cls, source_fk),
|
115
|
+
# "contains_eager": getattr(self.orm_cls, source_relprop),
|
116
|
+
# },
|
115
117
|
]
|
116
118
|
|
117
119
|
@classmethod
|
@@ -135,6 +137,7 @@ class BaseAsyncService(Generic[ID, R]):
|
|
135
137
|
session: The database session
|
136
138
|
"""
|
137
139
|
q = self._select()
|
140
|
+
extra_cols = []
|
138
141
|
|
139
142
|
if len(query.fields) > 0:
|
140
143
|
q = q.options(
|
@@ -195,12 +198,60 @@ class BaseAsyncService(Generic[ID, R]):
|
|
195
198
|
q = q.where(~getattr(self.orm_cls, clause.field).in_(clause.value))
|
196
199
|
else:
|
197
200
|
assert clause.op == QueryOp.fuzzy
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
clause.value
|
202
|
-
)
|
201
|
+
clause_prop = self.cls.properties[clause.field]
|
202
|
+
assert (
|
203
|
+
isinstance(clause_prop, DataProperty) and clause_prop.db is not None
|
203
204
|
)
|
205
|
+
clause_orm_field = getattr(self.orm_cls, clause.field)
|
206
|
+
extra_cols.append(f"{clause.field}_score")
|
207
|
+
|
208
|
+
if clause_prop.db.index_type == IndexType.POSTGRES_FTS_SEVI:
|
209
|
+
# fuzzy search is implemented using Postgres Full-Text Search
|
210
|
+
# sevi is a custom text search configuration that we defined in `configs/postgres-fts.sql`
|
211
|
+
q = q.where(
|
212
|
+
func.to_tsvector("sevi", clause_orm_field).bool_op("@@")(
|
213
|
+
func.plainto_tsquery("sevi", clause.value)
|
214
|
+
)
|
215
|
+
)
|
216
|
+
# TODO: figure out which rank function is better
|
217
|
+
# https://www.postgresql.org/docs/current/textsearch-controls.html#TEXTSEARCH-RANKING
|
218
|
+
q = q.order_by(
|
219
|
+
func.ts_rank_cd(
|
220
|
+
func.to_tsvector("sevi", clause_orm_field),
|
221
|
+
func.plainto_tsquery("sevi", clause.value),
|
222
|
+
).desc()
|
223
|
+
)
|
224
|
+
q = q.add_columns(
|
225
|
+
func.ts_rank_cd(
|
226
|
+
func.to_tsvector("sevi", clause_orm_field),
|
227
|
+
func.plainto_tsquery("sevi", clause.value),
|
228
|
+
).label(f"{clause.field}_score")
|
229
|
+
)
|
230
|
+
elif clause_prop.db.index_type == IndexType.POSTGRES_TRIGRAM:
|
231
|
+
# fuzzy search is implemented using Postgres trigram index
|
232
|
+
# using a custom function f_unaccent to ignore accents -- see `configs/postgres-fts.sql`
|
233
|
+
q = q.where(
|
234
|
+
func.f_unaccent(clause_orm_field).bool_op("%>")(
|
235
|
+
func.f_unaccent(clause.value)
|
236
|
+
)
|
237
|
+
)
|
238
|
+
q = q.order_by(
|
239
|
+
func.f_unaccent(clause_orm_field).op("<->>")(
|
240
|
+
func.f_unaccent(clause.value)
|
241
|
+
)
|
242
|
+
)
|
243
|
+
q = q.add_columns(
|
244
|
+
(
|
245
|
+
1
|
246
|
+
- func.f_unaccent(clause_orm_field).op("<->>")(
|
247
|
+
func.f_unaccent(clause.value)
|
248
|
+
)
|
249
|
+
).label(f"{clause.field}_score")
|
250
|
+
)
|
251
|
+
else:
|
252
|
+
raise NotImplementedError(
|
253
|
+
f"Fuzzy search is not implemented for index type {clause_prop.db.index_type}"
|
254
|
+
)
|
204
255
|
|
205
256
|
for join_condition in query.join_conditions:
|
206
257
|
for join_clause in self.join_clauses[join_condition.prop]:
|
@@ -211,16 +262,29 @@ class BaseAsyncService(Generic[ID, R]):
|
|
211
262
|
full=join_condition.join_type == "full",
|
212
263
|
).options(contains_eager(join_clause["contains_eager"]))
|
213
264
|
|
214
|
-
|
215
|
-
|
216
|
-
cq = select(func.count()).select_from(q.subquery())
|
265
|
+
# Create count query without order_by clause to improve performance
|
266
|
+
cq = select(func.count()).select_from(q.order_by(None).subquery())
|
217
267
|
rq = q.limit(query.limit).offset(query.offset)
|
218
|
-
|
268
|
+
|
269
|
+
if len(extra_cols) == 0:
|
270
|
+
records = self._process_result(await session.execute(rq)).scalars().all()
|
271
|
+
extra_columns = {}
|
272
|
+
else:
|
273
|
+
records = []
|
274
|
+
raw_extra_columns = [[] for col in extra_cols]
|
275
|
+
for row in self._process_result(await session.execute(rq)):
|
276
|
+
records.append(row[0])
|
277
|
+
for i in range(len(extra_cols)):
|
278
|
+
raw_extra_columns[i].append(row[i + 1])
|
279
|
+
extra_columns = {
|
280
|
+
col: raw_extra_columns[i] for i, col in enumerate(extra_cols)
|
281
|
+
}
|
282
|
+
|
219
283
|
if query.return_total:
|
220
284
|
total = (await session.execute(cq)).scalar_one()
|
221
285
|
else:
|
222
286
|
total = None
|
223
|
-
return QueryResult(records, total)
|
287
|
+
return QueryResult(records, extra_columns, total)
|
224
288
|
|
225
289
|
async def get_by_id(self, id: ID, session: AsyncSession) -> Optional[R]:
|
226
290
|
"""Retrieving a record by ID."""
|
sera/libs/search_helper.py
CHANGED
@@ -253,9 +253,9 @@ class Query(msgspec.Struct):
|
|
253
253
|
else:
|
254
254
|
if join_clause.join_type != "inner":
|
255
255
|
output[target_name].extend(
|
256
|
-
deser_func(
|
256
|
+
deser_func(val)
|
257
257
|
for record in result.records
|
258
|
-
if val is not None
|
258
|
+
if (val := getattr(record, source_relprop)) is not None
|
259
259
|
)
|
260
260
|
else:
|
261
261
|
output[target_name].extend(
|
@@ -265,6 +265,8 @@ class Query(msgspec.Struct):
|
|
265
265
|
|
266
266
|
deser_func = dataschema[cls.name].from_db
|
267
267
|
output[cls.name] = [deser_func(record) for record in result.records]
|
268
|
+
# include extra columns such as fuzzy search scores
|
269
|
+
output.update(result.extra_columns)
|
268
270
|
|
269
271
|
return output
|
270
272
|
|
sera/make/make_python_model.py
CHANGED
@@ -22,6 +22,7 @@ from sera.models import (
|
|
22
22
|
Cardinality,
|
23
23
|
Class,
|
24
24
|
DataProperty,
|
25
|
+
IndexType,
|
25
26
|
ObjectProperty,
|
26
27
|
Package,
|
27
28
|
PyTypeWithDep,
|
@@ -1207,14 +1208,79 @@ def make_python_relational_model(
|
|
1207
1208
|
)
|
1208
1209
|
|
1209
1210
|
index_stmts = []
|
1210
|
-
|
1211
|
+
|
1212
|
+
if len(cls.db.indices) > 0 or any(
|
1213
|
+
isinstance(prop, DataProperty)
|
1214
|
+
and prop.db is not None
|
1215
|
+
and prop.db.is_indexed
|
1216
|
+
and (
|
1217
|
+
prop.db.index_type == IndexType.POSTGRES_FTS_SEVI
|
1218
|
+
or prop.db.index_type == IndexType.POSTGRES_TRIGRAM
|
1219
|
+
)
|
1220
|
+
for prop in cls.properties.values()
|
1221
|
+
):
|
1211
1222
|
program.import_("sqlalchemy.Index", True)
|
1223
|
+
|
1224
|
+
fts_index = []
|
1225
|
+
for prop in cls.properties.values():
|
1226
|
+
if (
|
1227
|
+
not isinstance(prop, DataProperty)
|
1228
|
+
or prop.db is None
|
1229
|
+
or not prop.db.is_indexed
|
1230
|
+
):
|
1231
|
+
continue
|
1232
|
+
if prop.db.index_type == IndexType.POSTGRES_FTS_SEVI:
|
1233
|
+
fts_index.append(
|
1234
|
+
expr.ExprFuncCall(
|
1235
|
+
expr.ExprIdent("Index"),
|
1236
|
+
[
|
1237
|
+
expr.ExprConstant(
|
1238
|
+
f"ix_{cls.db.table_name}_{get_property_name(prop)}_gin"
|
1239
|
+
),
|
1240
|
+
expr.ExprFuncCall(
|
1241
|
+
ident_manager.use("text"),
|
1242
|
+
[
|
1243
|
+
expr.ExprConstant(
|
1244
|
+
f"to_tsvector('sevi', {get_property_name(prop)})"
|
1245
|
+
)
|
1246
|
+
],
|
1247
|
+
),
|
1248
|
+
PredefinedFn.keyword_assignment(
|
1249
|
+
"postgresql_using", expr.ExprConstant("gin")
|
1250
|
+
),
|
1251
|
+
],
|
1252
|
+
)
|
1253
|
+
)
|
1254
|
+
if prop.db.index_type == IndexType.POSTGRES_TRIGRAM:
|
1255
|
+
fts_index.append(
|
1256
|
+
expr.ExprFuncCall(
|
1257
|
+
expr.ExprIdent("Index"),
|
1258
|
+
[
|
1259
|
+
expr.ExprConstant(
|
1260
|
+
f"ix_{cls.db.table_name}_{get_property_name(prop)}_gist"
|
1261
|
+
),
|
1262
|
+
expr.ExprFuncCall(
|
1263
|
+
expr.ExprIdent("text"),
|
1264
|
+
[
|
1265
|
+
expr.ExprConstant(
|
1266
|
+
f"f_unaccent({get_property_name(prop)}) gist_trgm_ops(siglen=256)"
|
1267
|
+
)
|
1268
|
+
],
|
1269
|
+
),
|
1270
|
+
PredefinedFn.keyword_assignment(
|
1271
|
+
"postgresql_using", expr.ExprConstant("gist")
|
1272
|
+
),
|
1273
|
+
],
|
1274
|
+
)
|
1275
|
+
)
|
1276
|
+
|
1212
1277
|
index_stmts.append(
|
1213
1278
|
stmt.DefClassVarStatement(
|
1214
1279
|
"_table_args__",
|
1215
1280
|
None,
|
1216
1281
|
PredefinedFn.tuple(
|
1217
|
-
|
1282
|
+
fts_index
|
1283
|
+
+ [
|
1218
1284
|
expr.ExprFuncCall(
|
1219
1285
|
expr.ExprIdent("Index"),
|
1220
1286
|
[expr.ExprConstant(index.name)]
|
@@ -1317,7 +1383,8 @@ def make_python_relational_model(
|
|
1317
1383
|
"unique", expr.ExprConstant(True)
|
1318
1384
|
)
|
1319
1385
|
)
|
1320
|
-
elif prop.db.is_indexed:
|
1386
|
+
elif prop.db.is_indexed and prop.db.index_type == IndexType.DEFAULT:
|
1387
|
+
# only add index=True for default index type
|
1321
1388
|
propvalargs.append(
|
1322
1389
|
PredefinedFn.keyword_assignment(
|
1323
1390
|
"index", expr.ExprConstant(True)
|
sera/models/__init__.py
CHANGED
@@ -5,7 +5,13 @@ from sera.models._enum import Enum
|
|
5
5
|
from sera.models._module import App, Module, Package
|
6
6
|
from sera.models._multi_lingual_string import MultiLingualString
|
7
7
|
from sera.models._parse import parse_schema
|
8
|
-
from sera.models._property import
|
8
|
+
from sera.models._property import (
|
9
|
+
Cardinality,
|
10
|
+
DataProperty,
|
11
|
+
IndexType,
|
12
|
+
ObjectProperty,
|
13
|
+
Property,
|
14
|
+
)
|
9
15
|
from sera.models._schema import Schema
|
10
16
|
|
11
17
|
__all__ = [
|
@@ -14,6 +20,7 @@ __all__ = [
|
|
14
20
|
"Property",
|
15
21
|
"DataProperty",
|
16
22
|
"ObjectProperty",
|
23
|
+
"IndexType",
|
17
24
|
"Class",
|
18
25
|
"Cardinality",
|
19
26
|
"DataType",
|
sera/models/_class.py
CHANGED
@@ -5,7 +5,7 @@ from typing import Optional
|
|
5
5
|
|
6
6
|
from sera.misc import to_kebab_case, to_snake_case
|
7
7
|
from sera.models._multi_lingual_string import MultiLingualString
|
8
|
-
from sera.models._property import DataProperty, ObjectProperty
|
8
|
+
from sera.models._property import DataProperty, IndexType, ObjectProperty
|
9
9
|
|
10
10
|
|
11
11
|
@dataclass(kw_only=True)
|
@@ -13,6 +13,7 @@ class Index:
|
|
13
13
|
name: str
|
14
14
|
columns: list[str]
|
15
15
|
unique: bool = False
|
16
|
+
index_type: IndexType = IndexType.DEFAULT
|
16
17
|
|
17
18
|
|
18
19
|
@dataclass(kw_only=True)
|
sera/models/_parse.py
CHANGED
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import re
|
4
4
|
from copy import deepcopy
|
5
|
+
from operator import index
|
5
6
|
from pathlib import Path
|
6
7
|
from typing import Sequence
|
7
8
|
|
@@ -30,6 +31,7 @@ from sera.models._property import (
|
|
30
31
|
ForeignKeyOnDelete,
|
31
32
|
ForeignKeyOnUpdate,
|
32
33
|
GetSCPropValueFunc,
|
34
|
+
IndexType,
|
33
35
|
ObjectPropDBInfo,
|
34
36
|
ObjectProperty,
|
35
37
|
PropDataAttrs,
|
@@ -150,7 +152,7 @@ def _parse_property(
|
|
150
152
|
|
151
153
|
assert isinstance(prop, dict), prop
|
152
154
|
if "datatype" in prop:
|
153
|
-
|
155
|
+
return_prop = DataProperty(
|
154
156
|
name=prop_name,
|
155
157
|
label=_parse_multi_lingual_string(prop.get("label", prop_name)),
|
156
158
|
description=_parse_multi_lingual_string(prop.get("desc", "")),
|
@@ -164,6 +166,9 @@ def _parse_property(
|
|
164
166
|
is_indexed=db.get("is_indexed", False)
|
165
167
|
or db.get("is_unique", False)
|
166
168
|
or db.get("is_primary_key", False),
|
169
|
+
index_type=(
|
170
|
+
IndexType(db["index_type"]) if "index_type" in db else None
|
171
|
+
),
|
167
172
|
foreign_key=schema.classes.get(db.get("foreign_key")),
|
168
173
|
)
|
169
174
|
if "db" in prop
|
@@ -173,6 +178,10 @@ def _parse_property(
|
|
173
178
|
default_value=_parse_default_value(prop.get("default_value", None)),
|
174
179
|
default_factory=_parse_default_factory(prop.get("default_factory", None)),
|
175
180
|
)
|
181
|
+
if return_prop.db is not None and return_prop.db.is_indexed:
|
182
|
+
if return_prop.db.index_type is None:
|
183
|
+
return_prop.db.index_type = IndexType.DEFAULT
|
184
|
+
return return_prop
|
176
185
|
|
177
186
|
assert "target" in prop, prop
|
178
187
|
return ObjectProperty(
|
sera/models/_property.py
CHANGED
@@ -16,6 +16,12 @@ if TYPE_CHECKING:
|
|
16
16
|
from sera.models._class import Class
|
17
17
|
|
18
18
|
|
19
|
+
class IndexType(str, Enum):
|
20
|
+
DEFAULT = "default"
|
21
|
+
POSTGRES_FTS_SEVI = "postgres_fts_sevi"
|
22
|
+
POSTGRES_TRIGRAM = "postgres_trigram"
|
23
|
+
|
24
|
+
|
19
25
|
class ForeignKeyOnDelete(str, Enum):
|
20
26
|
CASCADE = "cascade"
|
21
27
|
SET_NULL = "set null"
|
@@ -150,6 +156,8 @@ class DataPropDBInfo:
|
|
150
156
|
is_unique: bool = False
|
151
157
|
# whether this property is indexed or not
|
152
158
|
is_indexed: bool = False
|
159
|
+
# type of the index if it is indexed --- if is_indexed is True, this must be not None
|
160
|
+
index_type: Optional[IndexType] = None
|
153
161
|
# this is used in conjunction with is_primary_key = True for the case of
|
154
162
|
# extending a table with frequently updated properties. The value for the `foreign_key`
|
155
163
|
# will be a target class. The cardinality is one-to-one, on target class deletion,
|
sera/typing.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: sera-2
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.25.0
|
4
4
|
Summary:
|
5
5
|
Author: Binh Vu
|
6
6
|
Author-email: bvu687@gmail.com
|
@@ -8,8 +8,9 @@ Requires-Python: >=3.12,<4.0
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
9
|
Classifier: Programming Language :: Python :: 3.12
|
10
10
|
Classifier: Programming Language :: Python :: 3.13
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
11
12
|
Requires-Dist: black (==25.1.0)
|
12
|
-
Requires-Dist: codegen-2 (>=2.
|
13
|
+
Requires-Dist: codegen-2 (>=2.15.1,<3.0.0)
|
13
14
|
Requires-Dist: graph-wrapper (>=1.7.3,<2.0.0)
|
14
15
|
Requires-Dist: isort (==6.0.1)
|
15
16
|
Requires-Dist: litestar (>=2.15.1,<3.0.0)
|
@@ -7,7 +7,7 @@ sera/libs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
sera/libs/api_helper.py,sha256=SkdgZkoYlX6HCyD_D6EiBvE0dWH0QW40xh4opIy737M,3237
|
8
8
|
sera/libs/api_test_helper.py,sha256=SVGhl9UXPyCOw3IRK2znV6_c5tl1lTYYsb9qXJnJHoE,1344
|
9
9
|
sera/libs/base_orm.py,sha256=5hOH_diUeaABm3cpE2-9u50VRqG1QW2osPQnvVHIhIA,3365
|
10
|
-
sera/libs/base_service.py,sha256=
|
10
|
+
sera/libs/base_service.py,sha256=I3mPufl0A0HKKkmYbs_9X0651bkedks4dRQgNnSa7ns,14114
|
11
11
|
sera/libs/directed_computing_graph/__init__.py,sha256=xiF5_I1y9HtQ-cyq02iwkRYgEZvxBB8YIvysCHCLBco,1290
|
12
12
|
sera/libs/directed_computing_graph/_dcg.py,sha256=nQf9MhnTkFU2-dxhv_PFThz9L61Nn8cvYIOoMq0OVb8,15352
|
13
13
|
sera/libs/directed_computing_graph/_edge.py,sha256=iBq6cpLWWyuD99QWTHVEh8naWUJrR4WJJuq5iuCrwHo,1026
|
@@ -19,12 +19,12 @@ sera/libs/directed_computing_graph/_type_conversion.py,sha256=_XGvDidOJVmHS4gqdP
|
|
19
19
|
sera/libs/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
20
|
sera/libs/middlewares/auth.py,sha256=r6aix1ZBwxMd1Jv5hMCTB8a_gFOJQ6egvxIrf3DWEOs,2323
|
21
21
|
sera/libs/middlewares/uscp.py,sha256=DRy99nmS3qS5HLjRMIGP0oNUtQIci_a4hL5xQh-lXNY,2322
|
22
|
-
sera/libs/search_helper.py,sha256=
|
22
|
+
sera/libs/search_helper.py,sha256=3ADhO6s6iIL3TWuH2zicAm_NmJfIm7wPY0cRQjESx2Y,13779
|
23
23
|
sera/make/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
24
|
sera/make/__main__.py,sha256=HRfOR53p351h6KblVvYm3DLhDIfEtk6R0kjl78_S_S8,1453
|
25
25
|
sera/make/make_app.py,sha256=dTzpJRPGoCojCdJr1TAzwbaquctwwitrGPxkRm9skpo,6033
|
26
26
|
sera/make/make_python_api.py,sha256=sfaYnX6Vwj0xWPpvpWbBhq8GyNHgNkvwlyZr7NDCO0k,24770
|
27
|
-
sera/make/make_python_model.py,sha256=
|
27
|
+
sera/make/make_python_model.py,sha256=FYqtzfVMfJxRPuEVhFuYCG5-DzVOS2aW_5RUtgiQY7Y,72685
|
28
28
|
sera/make/make_python_services.py,sha256=SvdU--QF23FN_d-Ao07CeXIK9d5eL_-mdXYwXx69dRE,2102
|
29
29
|
sera/make/make_typescript_model.py,sha256=Ax6xVrZ-x3xaKCsv6X4NKLdaMsAOau_GDrZK1TJ8NL8,57543
|
30
30
|
sera/make/ts_frontend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -34,8 +34,8 @@ sera/make/ts_frontend/misc.py,sha256=R96Tm4Sz05YPVk-gJ8vGW_GbMoUM7sLStar1LVnEn-I
|
|
34
34
|
sera/misc/__init__.py,sha256=ee7OODdWPKP8pCDxsfg1EOdxxKFMJCoNMljgV0NCxBw,767
|
35
35
|
sera/misc/_formatter.py,sha256=aCGYL08l8f3aLODHxSocxBBwkRYEo3K1QzCDEn3suj0,1685
|
36
36
|
sera/misc/_utils.py,sha256=_zWGjihxTEo3N1gzdxxB5CPCBBHxsviKWhQSCEonzVQ,13420
|
37
|
-
sera/models/__init__.py,sha256=
|
38
|
-
sera/models/_class.py,sha256=
|
37
|
+
sera/models/__init__.py,sha256=ZCnwzgUXhmHEcx5a7nYLopsBXJmSnfSkjt4kUH_vPnM,833
|
38
|
+
sera/models/_class.py,sha256=495nP6j4pZIX1j0ZElUHNxe9doUX7vzSIwVrp_lAOHE,2922
|
39
39
|
sera/models/_collection.py,sha256=72ZBdVa3UE8UyghJ8V-BPimO0f68zFRJvXdzfNaKDMo,2874
|
40
40
|
sera/models/_constraints.py,sha256=SwUkvV8sESfuO3En6keA_r8GxKiarXYxMb5biml63lU,2021
|
41
41
|
sera/models/_datatype.py,sha256=OlCnQlEpB4w9doz0hPhaZk5d4sF664CdGznSBvx0X9g,10293
|
@@ -43,10 +43,10 @@ sera/models/_default.py,sha256=ABggW6qdPR4ZDqIPJdJ0GCGQ-7kfsfZmQ_DchgZEa-I,137
|
|
43
43
|
sera/models/_enum.py,sha256=FRtEfto2httDB308W8OAuHh2LSazV3v16DHixlz1IZA,2088
|
44
44
|
sera/models/_module.py,sha256=r9_3nEbLaZKO-jvbZuFYy9DkM6E9jY8hBsBhQBULpVU,5428
|
45
45
|
sera/models/_multi_lingual_string.py,sha256=JETN6k00VH4wrA4w5vAHMEJV8fp3SY9bJebskFTjQLA,1186
|
46
|
-
sera/models/_parse.py,sha256=
|
47
|
-
sera/models/_property.py,sha256=
|
46
|
+
sera/models/_parse.py,sha256=nihBEDni-jKwecSBagd6bupDOuCzGviuoD9u6-rT64Q,13203
|
47
|
+
sera/models/_property.py,sha256=Qo23KZl5OQNbuceygicgC3_Yv5ve1KxiFiMoWq6Ljj0,7758
|
48
48
|
sera/models/_schema.py,sha256=VxJEiqgVvbXgcSUK4UW6JnRcggk4nsooVSE6MyXmfNY,1636
|
49
|
-
sera/typing.py,sha256=
|
50
|
-
sera_2-1.
|
51
|
-
sera_2-1.
|
52
|
-
sera_2-1.
|
49
|
+
sera/typing.py,sha256=7S6Ah-Yfcl8a0xAJ4lxEcguq3rYVM7SBeOiuxyJhC04,1089
|
50
|
+
sera_2-1.25.0.dist-info/METADATA,sha256=7NpIOo2Jk7y1K6hK81XLFOJ0oJiP765h1M5Jch841V0,987
|
51
|
+
sera_2-1.25.0.dist-info/WHEEL,sha256=M5asmiAlL6HEcOq52Yi5mmk9KmTVjY2RDPtO4p9DMrc,88
|
52
|
+
sera_2-1.25.0.dist-info/RECORD,,
|