sera-2 1.25.0__tar.gz → 1.26.1__tar.gz
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_2-1.25.0 → sera_2-1.26.1}/PKG-INFO +1 -1
- {sera_2-1.25.0 → sera_2-1.26.1}/pyproject.toml +1 -1
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_python_model.py +36 -40
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_typescript_model.py +6 -45
- sera_2-1.26.1/sera/make/py_backend/misc.py +13 -0
- sera_2-1.26.1/sera/make/ts_frontend/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/ts_frontend/make_enums.py +1 -20
- sera_2-1.26.1/sera/make/ts_frontend/make_query.py +146 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/ts_frontend/misc.py +2 -10
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_collection.py +5 -1
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_module.py +0 -1
- {sera_2-1.25.0 → sera_2-1.26.1}/README.md +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/constants.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/exports/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/exports/schema.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/exports/test.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/api_helper.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/api_test_helper.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/base_orm.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/base_service.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_dcg.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_edge.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_flow.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_fn_signature.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_node.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_runtime.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_type_conversion.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/middlewares/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/middlewares/auth.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/middlewares/uscp.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/search_helper.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/__main__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_app.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_python_api.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_python_services.py +0 -0
- {sera_2-1.25.0/sera/make/ts_frontend → sera_2-1.26.1/sera/make/py_backend}/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/ts_frontend/make_class_schema.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/misc/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/misc/_formatter.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/misc/_utils.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/__init__.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_class.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_constraints.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_datatype.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_default.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_enum.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_multi_lingual_string.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_parse.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_property.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_schema.py +0 -0
- {sera_2-1.25.0 → sera_2-1.26.1}/sera/typing.py +0 -0
@@ -12,6 +12,7 @@ from codegen.models import (
|
|
12
12
|
stmt,
|
13
13
|
)
|
14
14
|
|
15
|
+
from sera.make.py_backend.misc import get_python_property_name
|
15
16
|
from sera.misc import (
|
16
17
|
assert_isinstance,
|
17
18
|
assert_not_null,
|
@@ -110,21 +111,21 @@ def make_python_data_model(
|
|
110
111
|
), "If value_pass_as_args is provided, record should not be used as a dummy value should be passed instead."
|
111
112
|
else:
|
112
113
|
value = PredefinedFn.attr_getter(record, expr.ExprIdent(prop.name))
|
114
|
+
|
115
|
+
propname = get_python_property_name(prop)
|
113
116
|
if isinstance(prop, ObjectProperty) and prop.target.db is not None:
|
114
117
|
if prop.cardinality.is_star_to_many():
|
115
118
|
value = PredefinedFn.map_list(
|
116
119
|
value,
|
117
120
|
lambda item: PredefinedFn.attr_getter(
|
118
|
-
item, expr.ExprIdent(
|
121
|
+
item, expr.ExprIdent(propname)
|
119
122
|
),
|
120
123
|
)
|
121
124
|
else:
|
122
125
|
assert (
|
123
126
|
value_pass_as_args is None
|
124
127
|
), "Cannot use value_pass_as_args for a single object property."
|
125
|
-
value = PredefinedFn.attr_getter(
|
126
|
-
record, expr.ExprIdent(prop.name + "_id")
|
127
|
-
)
|
128
|
+
value = PredefinedFn.attr_getter(record, expr.ExprIdent(propname))
|
128
129
|
|
129
130
|
target_idprop = assert_not_null(prop.target.get_id_property())
|
130
131
|
conversion_fn = get_data_conversion(
|
@@ -147,10 +148,8 @@ def make_python_data_model(
|
|
147
148
|
mode: Literal["create", "update"],
|
148
149
|
prop: DataProperty | ObjectProperty,
|
149
150
|
):
|
150
|
-
|
151
|
-
|
152
|
-
else:
|
153
|
-
propname = prop.name
|
151
|
+
propname = get_python_property_name(prop)
|
152
|
+
|
154
153
|
value = PredefinedFn.attr_getter(slf, expr.ExprIdent(propname))
|
155
154
|
if isinstance(prop, ObjectProperty):
|
156
155
|
if (
|
@@ -178,7 +177,7 @@ def make_python_data_model(
|
|
178
177
|
expr.ExprIdent(AssociationTable),
|
179
178
|
[
|
180
179
|
PredefinedFn.keyword_assignment(
|
181
|
-
|
180
|
+
propname, conversion_fn(item)
|
182
181
|
)
|
183
182
|
],
|
184
183
|
),
|
@@ -236,6 +235,8 @@ def make_python_data_model(
|
|
236
235
|
if prop.data.system_controlled is None:
|
237
236
|
continue
|
238
237
|
|
238
|
+
propname = get_python_property_name(prop)
|
239
|
+
|
239
240
|
update_func = None
|
240
241
|
if mode == "create":
|
241
242
|
if prop.data.system_controlled.on_create_bypass is not None:
|
@@ -294,7 +295,7 @@ def make_python_data_model(
|
|
294
295
|
|
295
296
|
smt = stmt.AssignStatement(
|
296
297
|
PredefinedFn.attr_getter(
|
297
|
-
expr.ExprIdent("self"), expr.ExprIdent(
|
298
|
+
expr.ExprIdent("self"), expr.ExprIdent(propname)
|
298
299
|
),
|
299
300
|
epr,
|
300
301
|
)
|
@@ -374,6 +375,7 @@ def make_python_data_model(
|
|
374
375
|
):
|
375
376
|
continue
|
376
377
|
|
378
|
+
propname = get_python_property_name(prop)
|
377
379
|
if isinstance(prop, DataProperty):
|
378
380
|
pytype = prop.get_data_model_datatype().get_python_type().clone()
|
379
381
|
if len(prop.data.constraints) > 0:
|
@@ -419,21 +421,17 @@ def make_python_data_model(
|
|
419
421
|
)
|
420
422
|
|
421
423
|
cls_ast(
|
422
|
-
stmt.DefClassVarStatement(
|
423
|
-
prop.name, pytype.type, prop_default_value
|
424
|
-
)
|
424
|
+
stmt.DefClassVarStatement(propname, pytype.type, prop_default_value)
|
425
425
|
)
|
426
426
|
elif isinstance(prop, ObjectProperty):
|
427
427
|
if prop.target.db is not None:
|
428
428
|
# if the target class is in the database, we expect the user to pass the foreign key for it.
|
429
|
-
propname = prop.name + "_id"
|
430
429
|
pytype = (
|
431
430
|
assert_not_null(prop.target.get_id_property())
|
432
431
|
.get_data_model_datatype()
|
433
432
|
.get_python_type()
|
434
433
|
)
|
435
434
|
else:
|
436
|
-
propname = prop.name
|
437
435
|
pytype = PyTypeWithDep(
|
438
436
|
f"Create{prop.target.name}",
|
439
437
|
[
|
@@ -561,6 +559,8 @@ def make_python_data_model(
|
|
561
559
|
):
|
562
560
|
continue
|
563
561
|
|
562
|
+
propname = get_python_property_name(prop)
|
563
|
+
|
564
564
|
if isinstance(prop, DataProperty):
|
565
565
|
pytype = prop.get_data_model_datatype().get_python_type().clone()
|
566
566
|
|
@@ -605,13 +605,10 @@ def make_python_data_model(
|
|
605
605
|
)
|
606
606
|
|
607
607
|
cls_ast(
|
608
|
-
stmt.DefClassVarStatement(
|
609
|
-
prop.name, pytype.type, prop_default_value
|
610
|
-
)
|
608
|
+
stmt.DefClassVarStatement(propname, pytype.type, prop_default_value)
|
611
609
|
)
|
612
610
|
elif isinstance(prop, ObjectProperty):
|
613
611
|
if prop.target.db is not None:
|
614
|
-
propname = prop.name + "_id"
|
615
612
|
# if the target class is in the database, we expect the user to pass the foreign key for it.
|
616
613
|
pytype = (
|
617
614
|
assert_not_null(prop.target.get_id_property())
|
@@ -620,7 +617,6 @@ def make_python_data_model(
|
|
620
617
|
)
|
621
618
|
|
622
619
|
else:
|
623
|
-
propname = prop.name
|
624
620
|
pytype = PyTypeWithDep(
|
625
621
|
f"Update{prop.target.name}",
|
626
622
|
[
|
@@ -686,7 +682,9 @@ def make_python_data_model(
|
|
686
682
|
[
|
687
683
|
PredefinedFn.attr_getter(
|
688
684
|
expr.ExprIdent("self"),
|
689
|
-
expr.ExprIdent(
|
685
|
+
expr.ExprIdent(
|
686
|
+
get_python_property_name(prop)
|
687
|
+
),
|
690
688
|
)
|
691
689
|
],
|
692
690
|
)
|
@@ -750,7 +748,7 @@ def make_python_data_model(
|
|
750
748
|
# skip private fields as this is for APIs exchange
|
751
749
|
continue
|
752
750
|
|
753
|
-
propname = prop
|
751
|
+
propname = get_python_property_name(prop)
|
754
752
|
if isinstance(prop, DataProperty):
|
755
753
|
pytype = prop.get_data_model_datatype().get_python_type()
|
756
754
|
if prop.is_optional:
|
@@ -759,10 +757,9 @@ def make_python_data_model(
|
|
759
757
|
for dep in pytype.deps:
|
760
758
|
program.import_(dep, True)
|
761
759
|
|
762
|
-
cls_ast(stmt.DefClassVarStatement(
|
760
|
+
cls_ast(stmt.DefClassVarStatement(propname, pytype.type))
|
763
761
|
elif isinstance(prop, ObjectProperty):
|
764
762
|
if prop.target.db is not None:
|
765
|
-
propname = prop.name + "_id"
|
766
763
|
pytype = (
|
767
764
|
assert_not_null(prop.target.get_id_property())
|
768
765
|
.get_data_model_datatype()
|
@@ -823,12 +820,14 @@ def make_python_data_model(
|
|
823
820
|
as_composite_args = [DeferredVar.simple("cls")]
|
824
821
|
as_composite_null_condition = []
|
825
822
|
for prop in cls.properties.values():
|
823
|
+
propname = get_python_property_name(prop)
|
824
|
+
|
826
825
|
assert (
|
827
826
|
not prop.data.is_private
|
828
|
-
), f"Embedded classes should not have private properties: {cls.name}.{
|
829
|
-
as_composite_args.append(DeferredVar.simple(
|
827
|
+
), f"Embedded classes should not have private properties: {cls.name}.{propname}"
|
828
|
+
as_composite_args.append(DeferredVar.simple(propname))
|
830
829
|
as_composite_null_condition.append(
|
831
|
-
expr.ExprIs(expr.ExprIdent(
|
830
|
+
expr.ExprIs(expr.ExprIdent(propname), expr.ExprConstant(None))
|
832
831
|
)
|
833
832
|
|
834
833
|
# For simplicity, we assume that this embedded class can be used in a nullable field (check if all properties are None and return None).
|
@@ -855,7 +854,9 @@ def make_python_data_model(
|
|
855
854
|
expr.ExprIdent("cls"),
|
856
855
|
[
|
857
856
|
from_db_type_conversion(
|
858
|
-
expr.ExprIdent(""),
|
857
|
+
expr.ExprIdent(""),
|
858
|
+
prop,
|
859
|
+
expr.ExprIdent(get_python_property_name(prop)),
|
859
860
|
)
|
860
861
|
for prop in cls.properties.values()
|
861
862
|
],
|
@@ -946,12 +947,6 @@ def make_python_relational_model(
|
|
946
947
|
"""
|
947
948
|
app = target_pkg.app
|
948
949
|
|
949
|
-
def get_property_name(prop: DataProperty | ObjectProperty):
|
950
|
-
if isinstance(prop, ObjectProperty):
|
951
|
-
if prop.target.db is not None:
|
952
|
-
return f"{prop.name}_id"
|
953
|
-
return prop.name
|
954
|
-
|
955
950
|
def make_base(custom_types: Sequence[ObjectProperty]):
|
956
951
|
"""Make a base class for our database."""
|
957
952
|
program = Program()
|
@@ -1229,19 +1224,20 @@ def make_python_relational_model(
|
|
1229
1224
|
or not prop.db.is_indexed
|
1230
1225
|
):
|
1231
1226
|
continue
|
1227
|
+
propname = get_python_property_name(prop)
|
1232
1228
|
if prop.db.index_type == IndexType.POSTGRES_FTS_SEVI:
|
1233
1229
|
fts_index.append(
|
1234
1230
|
expr.ExprFuncCall(
|
1235
1231
|
expr.ExprIdent("Index"),
|
1236
1232
|
[
|
1237
1233
|
expr.ExprConstant(
|
1238
|
-
f"ix_{cls.db.table_name}_{
|
1234
|
+
f"ix_{cls.db.table_name}_{propname}_gin"
|
1239
1235
|
),
|
1240
1236
|
expr.ExprFuncCall(
|
1241
1237
|
ident_manager.use("text"),
|
1242
1238
|
[
|
1243
1239
|
expr.ExprConstant(
|
1244
|
-
f"to_tsvector('sevi', {
|
1240
|
+
f"to_tsvector('sevi', {propname})"
|
1245
1241
|
)
|
1246
1242
|
],
|
1247
1243
|
),
|
@@ -1257,13 +1253,13 @@ def make_python_relational_model(
|
|
1257
1253
|
expr.ExprIdent("Index"),
|
1258
1254
|
[
|
1259
1255
|
expr.ExprConstant(
|
1260
|
-
f"ix_{cls.db.table_name}_{
|
1256
|
+
f"ix_{cls.db.table_name}_{propname}_gist"
|
1261
1257
|
),
|
1262
1258
|
expr.ExprFuncCall(
|
1263
1259
|
expr.ExprIdent("text"),
|
1264
1260
|
[
|
1265
1261
|
expr.ExprConstant(
|
1266
|
-
f"f_unaccent({
|
1262
|
+
f"f_unaccent({propname}) gist_trgm_ops(siglen=256)"
|
1267
1263
|
)
|
1268
1264
|
],
|
1269
1265
|
),
|
@@ -1286,7 +1282,7 @@ def make_python_relational_model(
|
|
1286
1282
|
[expr.ExprConstant(index.name)]
|
1287
1283
|
+ [
|
1288
1284
|
expr.ExprConstant(
|
1289
|
-
|
1285
|
+
get_python_property_name(cls.properties[prop])
|
1290
1286
|
)
|
1291
1287
|
for prop in index.columns
|
1292
1288
|
]
|
@@ -1496,7 +1492,7 @@ def make_python_relational_object_property(
|
|
1496
1492
|
)
|
1497
1493
|
|
1498
1494
|
# we store this class in the database
|
1499
|
-
propname =
|
1495
|
+
propname = get_python_property_name(prop)
|
1500
1496
|
idprop = prop.target.get_id_property()
|
1501
1497
|
assert idprop is not None
|
1502
1498
|
idprop_pytype = idprop.datatype.get_sqlalchemy_type()
|
@@ -8,6 +8,7 @@ from codegen.models.var import DeferredVar
|
|
8
8
|
from loguru import logger
|
9
9
|
|
10
10
|
from sera.make.ts_frontend.make_class_schema import make_class_schema
|
11
|
+
from sera.make.ts_frontend.make_query import make_query
|
11
12
|
from sera.make.ts_frontend.misc import TS_GLOBAL_IDENTS, get_normalizer
|
12
13
|
from sera.misc import (
|
13
14
|
assert_isinstance,
|
@@ -1144,50 +1145,6 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
1144
1145
|
|
1145
1146
|
outmod.write(program)
|
1146
1147
|
|
1147
|
-
def make_query_processor(cls: Class, pkg: Package):
|
1148
|
-
if not cls.is_public:
|
1149
|
-
# skip classes that are not public
|
1150
|
-
return
|
1151
|
-
|
1152
|
-
outmod = pkg.module(cls.name + "Query")
|
1153
|
-
|
1154
|
-
program = Program()
|
1155
|
-
program.import_(f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}", True)
|
1156
|
-
program.import_(f"sera-db.QueryProcessor", True)
|
1157
|
-
|
1158
|
-
query_args = []
|
1159
|
-
for prop in cls.properties.values():
|
1160
|
-
pypropname = prop.name
|
1161
|
-
tspropname = to_camel_case(prop.name)
|
1162
|
-
|
1163
|
-
if isinstance(prop, ObjectProperty) and prop.target.db is not None:
|
1164
|
-
tspropname = tspropname + "Id"
|
1165
|
-
pypropname = prop.name + "_id"
|
1166
|
-
|
1167
|
-
if tspropname != pypropname:
|
1168
|
-
query_args.append(
|
1169
|
-
(
|
1170
|
-
expr.ExprIdent(tspropname),
|
1171
|
-
expr.ExprConstant(pypropname),
|
1172
|
-
)
|
1173
|
-
)
|
1174
|
-
|
1175
|
-
program.root(
|
1176
|
-
stmt.LineBreak(),
|
1177
|
-
stmt.TypescriptStatement(
|
1178
|
-
f"export const query = "
|
1179
|
-
+ expr.ExprNewInstance(
|
1180
|
-
expr.ExprIdent(f"QueryProcessor<{cls.name}>"),
|
1181
|
-
[
|
1182
|
-
PredefinedFn.dict(query_args),
|
1183
|
-
],
|
1184
|
-
).to_typescript()
|
1185
|
-
+ ";",
|
1186
|
-
),
|
1187
|
-
)
|
1188
|
-
|
1189
|
-
outmod.write(program)
|
1190
|
-
|
1191
1148
|
def make_index(pkg: Package):
|
1192
1149
|
outmod = pkg.module("index")
|
1193
1150
|
if outmod.exists():
|
@@ -1209,7 +1166,11 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
1209
1166
|
program.import_(
|
1210
1167
|
f"@.models.{pkg.dir.name}.{cls.name}Schema.{cls.name}Schema", True
|
1211
1168
|
)
|
1169
|
+
program.import_(
|
1170
|
+
f"@.models.{pkg.dir.name}.{cls.name}Query.{cls.name}Query", True
|
1171
|
+
)
|
1212
1172
|
export_types.append(f"{cls.name}Schema")
|
1173
|
+
export_iso_types.append(f"{cls.name}Query")
|
1213
1174
|
program.import_(
|
1214
1175
|
f"@.models.{pkg.dir.name}.{cls.name}Schema.{cls.name}SchemaType", True
|
1215
1176
|
)
|
@@ -1241,7 +1202,7 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
1241
1202
|
pkg = target_pkg.pkg(cls.get_tsmodule_name())
|
1242
1203
|
make_normal(cls, pkg)
|
1243
1204
|
make_draft(cls, pkg)
|
1244
|
-
|
1205
|
+
make_query(schema, cls, pkg)
|
1245
1206
|
make_table(cls, pkg)
|
1246
1207
|
make_class_schema(schema, cls, pkg)
|
1247
1208
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from sera.models import DataProperty, ObjectProperty
|
4
|
+
|
5
|
+
|
6
|
+
def get_python_property_name(prop: DataProperty | ObjectProperty) -> str:
|
7
|
+
"""Get property name of a property in Python model"""
|
8
|
+
if isinstance(prop, ObjectProperty) and prop.target.db is not None:
|
9
|
+
# the property value is a foreign key to another table, we should add _id to
|
10
|
+
# the end of the property name
|
11
|
+
return prop.name + "_id"
|
12
|
+
else:
|
13
|
+
return prop.name
|
File without changes
|
@@ -1,32 +1,13 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import re
|
4
|
-
from typing import Any, Callable
|
5
3
|
|
6
|
-
from codegen.models import
|
7
|
-
from codegen.models.var import DeferredVar
|
8
|
-
from loguru import logger
|
4
|
+
from codegen.models import PredefinedFn, Program, expr, stmt
|
9
5
|
|
10
|
-
from sera.make.ts_frontend.make_class_schema import make_class_schema
|
11
|
-
from sera.make.ts_frontend.misc import TS_GLOBAL_IDENTS, get_normalizer
|
12
|
-
from sera.misc import (
|
13
|
-
assert_isinstance,
|
14
|
-
assert_not_null,
|
15
|
-
identity,
|
16
|
-
to_camel_case,
|
17
|
-
to_pascal_case,
|
18
|
-
to_snake_case,
|
19
|
-
)
|
20
6
|
from sera.models import (
|
21
|
-
Class,
|
22
|
-
DataProperty,
|
23
7
|
Enum,
|
24
|
-
ObjectProperty,
|
25
8
|
Package,
|
26
9
|
Schema,
|
27
|
-
TsTypeWithDep,
|
28
10
|
)
|
29
|
-
from sera.typing import is_set
|
30
11
|
|
31
12
|
|
32
13
|
def make_typescript_enums(schema: Schema, target_pkg: Package):
|
@@ -0,0 +1,146 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from codegen.models import AST, ImportHelper, PredefinedFn, Program, expr, stmt
|
4
|
+
from codegen.models.var import DeferredVar
|
5
|
+
from loguru import logger
|
6
|
+
|
7
|
+
from sera.misc import (
|
8
|
+
assert_isinstance,
|
9
|
+
assert_not_null,
|
10
|
+
identity,
|
11
|
+
to_camel_case,
|
12
|
+
to_pascal_case,
|
13
|
+
to_snake_case,
|
14
|
+
)
|
15
|
+
from sera.models import (
|
16
|
+
Class,
|
17
|
+
DataProperty,
|
18
|
+
Enum,
|
19
|
+
ObjectProperty,
|
20
|
+
Package,
|
21
|
+
Schema,
|
22
|
+
TsTypeWithDep,
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
def make_query(schema: Schema, cls: Class, pkg: Package):
|
27
|
+
"""Make query processor and query schema.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
schema: The overall schema of the application, which contains all classes & enums
|
31
|
+
cls: The class that we want to generate the query processor and schema
|
32
|
+
pkg: The output package (directory) for the class in the `@.models` package. For example, if the
|
33
|
+
class is `User`, then the package would be `src/models/user`.
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
This function do not return anything as it writes the query helper directly to a file.
|
37
|
+
"""
|
38
|
+
if not cls.is_public:
|
39
|
+
# skip classes that are not public
|
40
|
+
return
|
41
|
+
|
42
|
+
outmod = pkg.module(cls.name + "Query")
|
43
|
+
|
44
|
+
program = Program()
|
45
|
+
program.import_(f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}", True)
|
46
|
+
program.import_(f"sera-db.QueryProcessor", True)
|
47
|
+
program.import_(f"sera-db.Query", True)
|
48
|
+
|
49
|
+
query_args = []
|
50
|
+
for prop in cls.properties.values():
|
51
|
+
pypropname = prop.name
|
52
|
+
tspropname = to_camel_case(prop.name)
|
53
|
+
|
54
|
+
if isinstance(prop, ObjectProperty) and prop.target.db is not None:
|
55
|
+
tspropname = tspropname + "Id"
|
56
|
+
pypropname = prop.name + "_id"
|
57
|
+
|
58
|
+
if tspropname != pypropname:
|
59
|
+
query_args.append(
|
60
|
+
(
|
61
|
+
expr.ExprIdent(tspropname),
|
62
|
+
expr.ExprConstant(pypropname),
|
63
|
+
)
|
64
|
+
)
|
65
|
+
|
66
|
+
query_condition_args = []
|
67
|
+
for prop in cls.properties.values():
|
68
|
+
if prop.db is None or prop.data.is_private:
|
69
|
+
# This property is not stored in the database or it's private, so we skip it
|
70
|
+
continue
|
71
|
+
if (
|
72
|
+
isinstance(prop, DataProperty)
|
73
|
+
and prop.db is not None
|
74
|
+
and not prop.db.is_indexed
|
75
|
+
):
|
76
|
+
# This property is not indexed, so we skip it
|
77
|
+
continue
|
78
|
+
if isinstance(prop, ObjectProperty) and prop.target.db is None:
|
79
|
+
# TODO: Implement this! This property is an embedded object property, we need to figure out
|
80
|
+
# which necessary properties are queryable and add them to the field names
|
81
|
+
continue
|
82
|
+
|
83
|
+
tspropname = to_camel_case(prop.name)
|
84
|
+
if isinstance(prop, ObjectProperty) and prop.target.db is not None:
|
85
|
+
# This property is an object property stored in the database, "Id" is added to the property name
|
86
|
+
tspropname = tspropname + "Id"
|
87
|
+
|
88
|
+
if isinstance(prop, DataProperty):
|
89
|
+
tstype = prop.datatype.get_typescript_type()
|
90
|
+
else:
|
91
|
+
assert isinstance(prop, ObjectProperty)
|
92
|
+
tstype = assert_not_null(
|
93
|
+
prop.target.get_id_property()
|
94
|
+
).datatype.get_typescript_type()
|
95
|
+
|
96
|
+
for dep in tstype.deps:
|
97
|
+
program.import_(dep, is_import_attr=True)
|
98
|
+
|
99
|
+
if tstype.type == "string":
|
100
|
+
op = '"fuzzy"'
|
101
|
+
elif tstype.type in "number" or tstype.type == "Date":
|
102
|
+
op = '"eq" | "ne" | "lt" | "lte" | "gt" | "gte"'
|
103
|
+
elif tstype.is_enum_type():
|
104
|
+
op = '"eq" | "ne"'
|
105
|
+
else:
|
106
|
+
raise NotImplementedError(tstype.type)
|
107
|
+
|
108
|
+
if tstype.type == "Date":
|
109
|
+
# for date type, we use timestamp (number) for comparison
|
110
|
+
value = "number"
|
111
|
+
else:
|
112
|
+
value = tstype.type
|
113
|
+
|
114
|
+
query_condition_args.append(
|
115
|
+
(
|
116
|
+
expr.ExprIdent(tspropname + "?"),
|
117
|
+
PredefinedFn.dict(
|
118
|
+
[
|
119
|
+
(expr.ExprIdent("op"), expr.ExprIdent(op)),
|
120
|
+
(expr.ExprIdent("value"), expr.ExprIdent(value)),
|
121
|
+
]
|
122
|
+
),
|
123
|
+
)
|
124
|
+
)
|
125
|
+
|
126
|
+
program.root(
|
127
|
+
stmt.LineBreak(),
|
128
|
+
stmt.TypescriptStatement(
|
129
|
+
f"export const query = "
|
130
|
+
+ expr.ExprNewInstance(
|
131
|
+
expr.ExprIdent(f"QueryProcessor<{cls.name}>"),
|
132
|
+
[
|
133
|
+
PredefinedFn.dict(query_args),
|
134
|
+
],
|
135
|
+
).to_typescript()
|
136
|
+
+ ";",
|
137
|
+
),
|
138
|
+
stmt.LineBreak(),
|
139
|
+
stmt.TypescriptStatement(
|
140
|
+
f"export type {cls.name}Query = Query<{cls.name}, "
|
141
|
+
+ PredefinedFn.dict(query_condition_args).to_typescript()
|
142
|
+
+ ">;"
|
143
|
+
),
|
144
|
+
)
|
145
|
+
|
146
|
+
outmod.write(program)
|
@@ -2,17 +2,9 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from typing import Optional
|
4
4
|
|
5
|
-
from codegen.models import
|
5
|
+
from codegen.models import ImportHelper, expr
|
6
6
|
|
7
|
-
from sera.models import
|
8
|
-
Class,
|
9
|
-
DataProperty,
|
10
|
-
Enum,
|
11
|
-
ObjectProperty,
|
12
|
-
Package,
|
13
|
-
Schema,
|
14
|
-
TsTypeWithDep,
|
15
|
-
)
|
7
|
+
from sera.models import TsTypeWithDep
|
16
8
|
|
17
9
|
TS_GLOBAL_IDENTS = {
|
18
10
|
"normalizers.normalizeNumber": "sera-db.normalizers",
|
@@ -55,7 +55,11 @@ class DataCollection:
|
|
55
55
|
"""Get the fields of this collection that can be used in join queries."""
|
56
56
|
output = {}
|
57
57
|
for prop in self.cls.properties.values():
|
58
|
-
if
|
58
|
+
if (
|
59
|
+
isinstance(prop, DataProperty)
|
60
|
+
and prop.db is not None
|
61
|
+
and prop.db.foreign_key is not None
|
62
|
+
):
|
59
63
|
# This property is a foreign key, so we add it to the output
|
60
64
|
output[prop.name] = DataCollection(
|
61
65
|
prop.db.foreign_key
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|