sera-2 1.9.1__py3-none-any.whl → 1.11.2__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 +81 -0
- sera/libs/base_service.py +19 -3
- sera/libs/middlewares/__init__.py +0 -0
- sera/libs/middlewares/auth.py +73 -0
- sera/libs/middlewares/uscp.py +68 -0
- sera/make/make_python_api.py +121 -221
- sera/make/make_python_model.py +140 -14
- sera/make/make_typescript_model.py +60 -25
- sera/models/_datatype.py +17 -15
- sera/models/_parse.py +20 -16
- sera/models/_property.py +3 -0
- {sera_2-1.9.1.dist-info → sera_2-1.11.2.dist-info}/METADATA +1 -1
- {sera_2-1.9.1.dist-info → sera_2-1.11.2.dist-info}/RECORD +14 -11
- {sera_2-1.9.1.dist-info → sera_2-1.11.2.dist-info}/WHEEL +0 -0
sera/make/make_python_model.py
CHANGED
@@ -190,6 +190,37 @@ def make_python_data_model(
|
|
190
190
|
True,
|
191
191
|
alias=f"{cls.name}DB",
|
192
192
|
)
|
193
|
+
|
194
|
+
has_system_controlled = any(
|
195
|
+
prop.data.is_system_controlled for prop in cls.properties.values()
|
196
|
+
)
|
197
|
+
if has_system_controlled:
|
198
|
+
program.import_("typing.TypedDict", True)
|
199
|
+
program.root(
|
200
|
+
stmt.LineBreak(),
|
201
|
+
lambda ast: ast.class_(
|
202
|
+
"SystemControlledProps",
|
203
|
+
[expr.ExprIdent("TypedDict")],
|
204
|
+
)(
|
205
|
+
*[
|
206
|
+
stmt.DefClassVarStatement(
|
207
|
+
prop.name,
|
208
|
+
(
|
209
|
+
prop.get_data_model_datatype().get_python_type().type
|
210
|
+
if isinstance(prop, DataProperty)
|
211
|
+
else assert_not_null(prop.target.get_id_property())
|
212
|
+
.get_data_model_datatype()
|
213
|
+
.get_python_type()
|
214
|
+
.type
|
215
|
+
),
|
216
|
+
)
|
217
|
+
for prop in cls.properties.values()
|
218
|
+
if prop.data.is_system_controlled
|
219
|
+
],
|
220
|
+
),
|
221
|
+
)
|
222
|
+
|
223
|
+
program.root.linebreak()
|
193
224
|
cls_ast = program.root.class_(
|
194
225
|
"Upsert" + cls.name,
|
195
226
|
[expr.ExprIdent("msgspec.Struct"), expr.ExprIdent("kw_only=True")],
|
@@ -203,8 +234,11 @@ def make_python_data_model(
|
|
203
234
|
|
204
235
|
if isinstance(prop, DataProperty):
|
205
236
|
pytype = prop.get_data_model_datatype().get_python_type()
|
206
|
-
if
|
207
|
-
|
237
|
+
if prop.is_optional:
|
238
|
+
pytype = pytype.as_optional_type()
|
239
|
+
|
240
|
+
for dep in pytype.deps:
|
241
|
+
program.import_(dep, True)
|
208
242
|
|
209
243
|
pytype_type = pytype.type
|
210
244
|
if len(prop.data.constraints) > 0:
|
@@ -257,11 +291,13 @@ def make_python_data_model(
|
|
257
291
|
else:
|
258
292
|
pytype = PyTypeWithDep(
|
259
293
|
f"Upsert{prop.target.name}",
|
260
|
-
|
294
|
+
[
|
295
|
+
f"{target_pkg.module(prop.target.get_pymodule_name()).path}.Upsert{prop.target.name}"
|
296
|
+
],
|
261
297
|
)
|
262
298
|
|
263
|
-
|
264
|
-
program.import_(
|
299
|
+
for dep in pytype.deps:
|
300
|
+
program.import_(dep, True)
|
265
301
|
|
266
302
|
if prop.cardinality.is_star_to_many():
|
267
303
|
pytype = pytype.as_list_type()
|
@@ -270,6 +306,69 @@ def make_python_data_model(
|
|
270
306
|
# has_to_db = True
|
271
307
|
# if any(prop for prop in cls.properties.values() if isinstance(prop, ObjectProperty) and prop.cardinality == Cardinality.MANY_TO_MANY):
|
272
308
|
# # if the class has many-to-many relationship, we need to
|
309
|
+
|
310
|
+
if has_system_controlled:
|
311
|
+
program.import_("typing.Optional", True)
|
312
|
+
cls_ast(
|
313
|
+
stmt.LineBreak(),
|
314
|
+
stmt.Comment(
|
315
|
+
"_verified is a special marker to indicate whether the data is updated by the system."
|
316
|
+
),
|
317
|
+
stmt.DefClassVarStatement(
|
318
|
+
"_verified", "bool", expr.ExprConstant(False)
|
319
|
+
),
|
320
|
+
stmt.LineBreak(),
|
321
|
+
lambda ast: ast.func(
|
322
|
+
"__post_init__",
|
323
|
+
[
|
324
|
+
DeferredVar.simple("self"),
|
325
|
+
],
|
326
|
+
)(
|
327
|
+
stmt.AssignStatement(
|
328
|
+
PredefinedFn.attr_getter(
|
329
|
+
expr.ExprIdent("self"), expr.ExprIdent("_verified")
|
330
|
+
),
|
331
|
+
expr.ExprConstant(False),
|
332
|
+
)
|
333
|
+
),
|
334
|
+
stmt.LineBreak(),
|
335
|
+
lambda ast: ast.func(
|
336
|
+
"update_system_controlled_props",
|
337
|
+
[
|
338
|
+
DeferredVar.simple("self"),
|
339
|
+
DeferredVar.simple(
|
340
|
+
"data",
|
341
|
+
expr.ExprIdent("Optional[SystemControlledProps]"),
|
342
|
+
),
|
343
|
+
],
|
344
|
+
)(
|
345
|
+
lambda ast00: ast00.if_(
|
346
|
+
expr.ExprNegation(
|
347
|
+
expr.ExprIs(expr.ExprIdent("data"), expr.ExprConstant(None))
|
348
|
+
),
|
349
|
+
)(
|
350
|
+
*[
|
351
|
+
stmt.AssignStatement(
|
352
|
+
PredefinedFn.attr_getter(
|
353
|
+
expr.ExprIdent("self"), expr.ExprIdent(prop.name)
|
354
|
+
),
|
355
|
+
PredefinedFn.item_getter(
|
356
|
+
expr.ExprIdent("data"), expr.ExprConstant(prop.name)
|
357
|
+
),
|
358
|
+
)
|
359
|
+
for prop in cls.properties.values()
|
360
|
+
if prop.data.is_system_controlled
|
361
|
+
]
|
362
|
+
),
|
363
|
+
stmt.AssignStatement(
|
364
|
+
PredefinedFn.attr_getter(
|
365
|
+
expr.ExprIdent("self"), expr.ExprIdent("_verified")
|
366
|
+
),
|
367
|
+
expr.ExprConstant(True),
|
368
|
+
),
|
369
|
+
),
|
370
|
+
)
|
371
|
+
|
273
372
|
cls_ast(
|
274
373
|
stmt.LineBreak(),
|
275
374
|
lambda ast00: ast00.func(
|
@@ -281,6 +380,18 @@ def make_python_data_model(
|
|
281
380
|
f"{cls.name}DB" if cls.db is not None else cls.name
|
282
381
|
),
|
283
382
|
)(
|
383
|
+
(
|
384
|
+
stmt.AssertionStatement(
|
385
|
+
PredefinedFn.attr_getter(
|
386
|
+
expr.ExprIdent("self"), expr.ExprIdent("_verified")
|
387
|
+
),
|
388
|
+
expr.ExprConstant(
|
389
|
+
"The model data must be verified before converting to db model"
|
390
|
+
),
|
391
|
+
)
|
392
|
+
if has_system_controlled
|
393
|
+
else None
|
394
|
+
),
|
284
395
|
lambda ast10: ast10.return_(
|
285
396
|
expr.ExprFuncCall(
|
286
397
|
expr.ExprIdent(
|
@@ -293,7 +404,7 @@ def make_python_data_model(
|
|
293
404
|
for prop in cls.properties.values()
|
294
405
|
],
|
295
406
|
)
|
296
|
-
)
|
407
|
+
),
|
297
408
|
),
|
298
409
|
)
|
299
410
|
|
@@ -320,8 +431,12 @@ def make_python_data_model(
|
|
320
431
|
|
321
432
|
if isinstance(prop, DataProperty):
|
322
433
|
pytype = prop.get_data_model_datatype().get_python_type()
|
323
|
-
if
|
324
|
-
|
434
|
+
if prop.is_optional:
|
435
|
+
pytype = pytype.as_optional_type()
|
436
|
+
|
437
|
+
for dep in pytype.deps:
|
438
|
+
program.import_(dep, True)
|
439
|
+
|
325
440
|
cls_ast(stmt.DefClassVarStatement(prop.name, pytype.type))
|
326
441
|
elif isinstance(prop, ObjectProperty):
|
327
442
|
if prop.target.db is not None:
|
@@ -333,11 +448,13 @@ def make_python_data_model(
|
|
333
448
|
else:
|
334
449
|
pytype = PyTypeWithDep(
|
335
450
|
prop.target.name,
|
336
|
-
|
451
|
+
[
|
452
|
+
f"{target_pkg.module(prop.target.get_pymodule_name()).path}.{prop.target.name}"
|
453
|
+
],
|
337
454
|
)
|
338
455
|
|
339
|
-
|
340
|
-
program.import_(
|
456
|
+
for dep in pytype.deps:
|
457
|
+
program.import_(dep, True)
|
341
458
|
|
342
459
|
if prop.cardinality.is_star_to_many():
|
343
460
|
pytype = pytype.as_list_type()
|
@@ -423,6 +540,7 @@ def make_python_relational_model(
|
|
423
540
|
# assume configuration for the app at the top level
|
424
541
|
program.import_(f"{app.config.path}.DB_CONNECTION", True)
|
425
542
|
program.import_(f"{app.config.path}.DB_DEBUG", True)
|
543
|
+
program.import_(f"contextlib.contextmanager", True)
|
426
544
|
|
427
545
|
program.root.linebreak()
|
428
546
|
|
@@ -484,7 +602,15 @@ def make_python_relational_model(
|
|
484
602
|
)
|
485
603
|
|
486
604
|
program.root.linebreak()
|
487
|
-
program.root.func("
|
605
|
+
program.root.func("async_get_session", [], is_async=True)(
|
606
|
+
lambda ast00: ast00.python_stmt("with Session(engine) as session:")(
|
607
|
+
lambda ast01: ast01.python_stmt("yield session")
|
608
|
+
)
|
609
|
+
)
|
610
|
+
|
611
|
+
program.root.linebreak()
|
612
|
+
program.root.python_stmt("@contextmanager")
|
613
|
+
program.root.func("get_session", [])(
|
488
614
|
lambda ast00: ast00.python_stmt("with Session(engine) as session:")(
|
489
615
|
lambda ast01: ast01.python_stmt("yield session")
|
490
616
|
)
|
@@ -701,7 +827,7 @@ def make_python_relational_object_property(
|
|
701
827
|
expr.ExprFuncCall(
|
702
828
|
expr.ExprIdent("mapped_column"),
|
703
829
|
[
|
704
|
-
expr.
|
830
|
+
expr.ExprConstant(f"{prop.name}_{p.name}"),
|
705
831
|
expr.ExprIdent(
|
706
832
|
assert_isinstance(p, DataProperty)
|
707
833
|
.datatype.get_sqlalchemy_type()
|
@@ -709,7 +835,7 @@ def make_python_relational_object_property(
|
|
709
835
|
),
|
710
836
|
PredefinedFn.keyword_assignment(
|
711
837
|
"nullable",
|
712
|
-
expr.ExprConstant(prop.is_optional),
|
838
|
+
expr.ExprConstant(prop.is_optional or p.is_optional),
|
713
839
|
),
|
714
840
|
],
|
715
841
|
)
|
@@ -68,8 +68,8 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
68
68
|
|
69
69
|
if isinstance(prop, DataProperty):
|
70
70
|
tstype = prop.get_data_model_datatype().get_typescript_type()
|
71
|
-
|
72
|
-
program.import_(
|
71
|
+
for dep in tstype.deps:
|
72
|
+
program.import_(dep, True)
|
73
73
|
|
74
74
|
if idprop is not None and prop.name == idprop.name:
|
75
75
|
# use id type alias
|
@@ -89,7 +89,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
89
89
|
# this class is stored in the database, we store the id instead
|
90
90
|
tstype = TsTypeWithDep(
|
91
91
|
f"{prop.target.name}Id",
|
92
|
-
|
92
|
+
[
|
93
|
+
f"@.models.{prop.target.get_tsmodule_name()}.{prop.target.name}.{prop.target.name}Id"
|
94
|
+
],
|
93
95
|
)
|
94
96
|
if prop.cardinality.is_star_to_many():
|
95
97
|
tstype = tstype.as_list_type()
|
@@ -105,7 +107,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
105
107
|
# we are going to store the whole object
|
106
108
|
tstype = TsTypeWithDep(
|
107
109
|
prop.target.name,
|
108
|
-
|
110
|
+
[
|
111
|
+
f"@.models.{prop.target.get_tsmodule_name()}.{prop.target.name}.{prop.target.name}"
|
112
|
+
],
|
109
113
|
)
|
110
114
|
if prop.cardinality.is_star_to_many():
|
111
115
|
tstype = tstype.as_list_type()
|
@@ -148,9 +152,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
148
152
|
)
|
149
153
|
)
|
150
154
|
|
151
|
-
|
155
|
+
for dep in tstype.deps:
|
152
156
|
program.import_(
|
153
|
-
|
157
|
+
dep,
|
154
158
|
True,
|
155
159
|
)
|
156
160
|
|
@@ -319,14 +323,14 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
319
323
|
# use id type alias
|
320
324
|
tstype = TsTypeWithDep(
|
321
325
|
f"{cls.name}Id",
|
322
|
-
|
326
|
+
deps=[f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}Id"],
|
323
327
|
)
|
324
328
|
else:
|
325
329
|
# for none id properties, we need to include a type for "invalid" value
|
326
330
|
tstype = _inject_type_for_invalid_value(tstype)
|
327
331
|
|
328
|
-
|
329
|
-
program.import_(
|
332
|
+
for dep in tstype.deps:
|
333
|
+
program.import_(dep, True)
|
330
334
|
|
331
335
|
# however, if this is a primary key and auto-increment, we set a different default value
|
332
336
|
# to be -1 to avoid start from 0
|
@@ -426,7 +430,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
426
430
|
# this class is stored in the database, we store the id instead
|
427
431
|
tstype = TsTypeWithDep(
|
428
432
|
f"{prop.target.name}Id",
|
429
|
-
|
433
|
+
[
|
434
|
+
f"@.models.{prop.target.get_tsmodule_name()}.{prop.target.name}.{prop.target.name}Id"
|
435
|
+
],
|
430
436
|
)
|
431
437
|
if prop.cardinality.is_star_to_many():
|
432
438
|
tstype = tstype.as_list_type()
|
@@ -461,7 +467,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
461
467
|
# we are going to store the whole object
|
462
468
|
tstype = TsTypeWithDep(
|
463
469
|
f"Draft{prop.target.name}",
|
464
|
-
|
470
|
+
[
|
471
|
+
f"@.models.{prop.target.get_tsmodule_name()}.Draft{prop.target.name}.Draft{prop.target.name}"
|
472
|
+
],
|
465
473
|
)
|
466
474
|
if prop.cardinality.is_star_to_many():
|
467
475
|
tstype = tstype.as_list_type()
|
@@ -515,9 +523,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
515
523
|
)
|
516
524
|
)
|
517
525
|
|
518
|
-
|
526
|
+
for dep in tstype.deps:
|
519
527
|
program.import_(
|
520
|
-
|
528
|
+
dep,
|
521
529
|
True,
|
522
530
|
)
|
523
531
|
|
@@ -698,12 +706,16 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
698
706
|
comment="Check if the draft is valid (only check the required fields as the non-required fields if it's invalid will be set to undefined)",
|
699
707
|
)(
|
700
708
|
stmt.ReturnStatement(
|
701
|
-
|
709
|
+
expr.ExprRawTypescript(
|
702
710
|
" && ".join(
|
703
711
|
f"{draft_validators}.{prop2tsname[prop.name]}(this.{prop2tsname[prop.name]}).isValid"
|
704
712
|
for prop in cls.properties.values()
|
705
713
|
if not prop.is_optional
|
706
714
|
)
|
715
|
+
if any(
|
716
|
+
not prop.is_optional for prop in cls.properties.values()
|
717
|
+
)
|
718
|
+
else "true"
|
707
719
|
)
|
708
720
|
)
|
709
721
|
),
|
@@ -859,8 +871,8 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
859
871
|
# if prop.name == idprop.name:
|
860
872
|
# # use id type alias
|
861
873
|
# tstype = TsTypeWithDep(f"{cls.name}Id")
|
862
|
-
|
863
|
-
program.import_(
|
874
|
+
for dep in tstype.deps:
|
875
|
+
program.import_(dep, True)
|
864
876
|
tsprop = [
|
865
877
|
(expr.ExprIdent("datatype"), expr.ExprConstant(tstype.type)),
|
866
878
|
(
|
@@ -885,7 +897,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
885
897
|
# we are going to store the whole object
|
886
898
|
tstype = TsTypeWithDep(
|
887
899
|
prop.target.name,
|
888
|
-
|
900
|
+
[
|
901
|
+
f"@.models.{prop.target.get_tsmodule_name()}.{prop.target.name}.{prop.target.name}"
|
902
|
+
],
|
889
903
|
)
|
890
904
|
|
891
905
|
# we don't store the type itself, but just the name of the type
|
@@ -901,7 +915,12 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
901
915
|
expr.ExprIdent("targetClass"),
|
902
916
|
expr.ExprConstant(prop.target.name),
|
903
917
|
),
|
904
|
-
(
|
918
|
+
(
|
919
|
+
expr.ExprIdent("datatype"),
|
920
|
+
expr.ExprConstant(
|
921
|
+
tstype.type if prop.target.db is not None else "embedded"
|
922
|
+
),
|
923
|
+
),
|
905
924
|
(
|
906
925
|
expr.ExprIdent("cardinality"),
|
907
926
|
expr.ExprConstant(prop.cardinality.value),
|
@@ -957,24 +976,36 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
957
976
|
)
|
958
977
|
)
|
959
978
|
|
960
|
-
for type in ["
|
979
|
+
for type in ["ObjectProperty", "DataProperty"]:
|
961
980
|
program.import_(f"sera-db.{type}", True)
|
981
|
+
if cls.db is not None:
|
982
|
+
program.import_(f"sera-db.Schema", True)
|
983
|
+
else:
|
984
|
+
program.import_(f"sera-db.EmbeddedSchema", True)
|
962
985
|
|
963
986
|
program.import_(f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}", True)
|
964
|
-
program.import_(f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}Id", True)
|
965
987
|
program.import_(
|
966
988
|
f"@.models.{pkg.dir.name}.Draft{cls.name}.Draft{cls.name}", True
|
967
989
|
)
|
968
990
|
program.import_(
|
969
991
|
f"@.models.{pkg.dir.name}.Draft{cls.name}.draft{cls.name}Validators", True
|
970
992
|
)
|
993
|
+
if cls.db is not None:
|
994
|
+
program.import_(f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}Id", True)
|
995
|
+
|
971
996
|
program.root(
|
972
997
|
stmt.LineBreak(),
|
973
998
|
stmt.TypescriptStatement(
|
974
999
|
f"export type {cls.name}SchemaType = "
|
975
1000
|
+ PredefinedFn.dict(
|
976
|
-
|
977
|
-
|
1001
|
+
(
|
1002
|
+
[
|
1003
|
+
(expr.ExprIdent("id"), expr.ExprIdent(f"{cls.name}Id")),
|
1004
|
+
]
|
1005
|
+
if cls.db is not None
|
1006
|
+
else []
|
1007
|
+
)
|
1008
|
+
+ [
|
978
1009
|
(
|
979
1010
|
expr.ExprIdent("publicProperties"),
|
980
1011
|
expr.ExprIdent(
|
@@ -1038,7 +1069,11 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
1038
1069
|
),
|
1039
1070
|
stmt.LineBreak(),
|
1040
1071
|
stmt.TypescriptStatement(
|
1041
|
-
|
1072
|
+
(
|
1073
|
+
f"export const {cls.name}Schema: Schema<{cls.name}SchemaType['id'], {cls.name}SchemaType['cls'], {cls.name}SchemaType['draftCls'], {cls.name}SchemaType['publicProperties'], {cls.name}SchemaType['allProperties'], {cls.name}SchemaType> = "
|
1074
|
+
if cls.db is not None
|
1075
|
+
else f"export const {cls.name}Schema: EmbeddedSchema<{cls.name}SchemaType['cls'], {cls.name}SchemaType['draftCls'], {cls.name}SchemaType['publicProperties'], {cls.name}SchemaType['allProperties']> = "
|
1076
|
+
)
|
1042
1077
|
+ PredefinedFn.dict(
|
1043
1078
|
[
|
1044
1079
|
(
|
@@ -1211,7 +1246,7 @@ def _inject_type_for_invalid_value(tstype: TsTypeWithDep) -> TsTypeWithDep:
|
|
1211
1246
|
else:
|
1212
1247
|
# Need to add parentheses
|
1213
1248
|
inner_type = f"({inner_type} | string)"
|
1214
|
-
return TsTypeWithDep(inner_type + "[]", tstype.
|
1249
|
+
return TsTypeWithDep(inner_type + "[]", tstype.deps)
|
1215
1250
|
|
1216
1251
|
m = re.match(r"^\(?[a-zA-Z \|]+\)?$", tstype.type)
|
1217
1252
|
if m is not None:
|
@@ -1222,7 +1257,7 @@ def _inject_type_for_invalid_value(tstype: TsTypeWithDep) -> TsTypeWithDep:
|
|
1222
1257
|
else:
|
1223
1258
|
# Needs parentheses for clarity
|
1224
1259
|
new_type = f"({tstype.type} | string)"
|
1225
|
-
return TsTypeWithDep(new_type, tstype.
|
1260
|
+
return TsTypeWithDep(new_type, tstype.deps)
|
1226
1261
|
return tstype
|
1227
1262
|
|
1228
1263
|
raise NotImplementedError(tstype.type)
|
sera/models/_datatype.py
CHANGED
@@ -24,7 +24,7 @@ SQLAlchemyDataType = Literal[
|
|
24
24
|
@dataclass
|
25
25
|
class PyTypeWithDep:
|
26
26
|
type: str
|
27
|
-
|
27
|
+
deps: list[str] = field(default_factory=list)
|
28
28
|
|
29
29
|
def get_python_type(self) -> type:
|
30
30
|
"""Get the Python type from the type string for typing annotation in Python."""
|
@@ -50,13 +50,25 @@ class PyTypeWithDep:
|
|
50
50
|
|
51
51
|
def as_list_type(self) -> PyTypeWithDep:
|
52
52
|
"""Convert the type to a list type."""
|
53
|
-
return PyTypeWithDep(type=f"list[{self.type}]",
|
53
|
+
return PyTypeWithDep(type=f"list[{self.type}]", deps=self.deps)
|
54
|
+
|
55
|
+
def as_optional_type(self) -> PyTypeWithDep:
|
56
|
+
"""Convert the type to an optional type."""
|
57
|
+
if "typing.Optional" not in self.deps:
|
58
|
+
deps = self.deps + ["typing.Optional"]
|
59
|
+
else:
|
60
|
+
deps = self.deps
|
61
|
+
if "Optional[" in self.type:
|
62
|
+
raise NotImplementedError(
|
63
|
+
f"Have not handle nested optional yet: {self.type}"
|
64
|
+
)
|
65
|
+
return PyTypeWithDep(type=f"Optional[{self.type}]", deps=deps)
|
54
66
|
|
55
67
|
|
56
68
|
@dataclass
|
57
69
|
class TsTypeWithDep:
|
58
70
|
type: str
|
59
|
-
|
71
|
+
deps: list[str] = field(default_factory=list)
|
60
72
|
|
61
73
|
def get_default(self) -> expr.ExprConstant:
|
62
74
|
if self.type.endswith("[]"):
|
@@ -83,7 +95,7 @@ class TsTypeWithDep:
|
|
83
95
|
list_type = f"({self.type})[]"
|
84
96
|
else:
|
85
97
|
list_type = f"{self.type}[]"
|
86
|
-
return TsTypeWithDep(type=list_type,
|
98
|
+
return TsTypeWithDep(type=list_type, deps=self.deps)
|
87
99
|
|
88
100
|
|
89
101
|
@dataclass
|
@@ -137,16 +149,6 @@ predefined_datatypes = {
|
|
137
149
|
tstype=TsTypeWithDep(type="string"),
|
138
150
|
is_list=False,
|
139
151
|
),
|
140
|
-
"optional[string]": DataType(
|
141
|
-
pytype=PyTypeWithDep(type="Optional[str]", dep="typing.Optional"),
|
142
|
-
sqltype=SQLTypeWithDep(
|
143
|
-
type="String",
|
144
|
-
mapped_pytype="Optional[str]",
|
145
|
-
deps=["sqlalchemy.String", "typing.Optional"],
|
146
|
-
),
|
147
|
-
tstype=TsTypeWithDep(type="string | undefined"),
|
148
|
-
is_list=False,
|
149
|
-
),
|
150
152
|
"integer": DataType(
|
151
153
|
pytype=PyTypeWithDep(type="int"),
|
152
154
|
sqltype=SQLTypeWithDep(
|
@@ -156,7 +158,7 @@ predefined_datatypes = {
|
|
156
158
|
is_list=False,
|
157
159
|
),
|
158
160
|
"datetime": DataType(
|
159
|
-
pytype=PyTypeWithDep(type="datetime",
|
161
|
+
pytype=PyTypeWithDep(type="datetime", deps=["datetime.datetime"]),
|
160
162
|
sqltype=SQLTypeWithDep(
|
161
163
|
type="DateTime",
|
162
164
|
mapped_pytype="datetime",
|
sera/models/_parse.py
CHANGED
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
6
6
|
from typing import Sequence
|
7
7
|
|
8
8
|
import serde.yaml
|
9
|
+
|
9
10
|
from sera.models._class import Class, ClassDBMapInfo, Index
|
10
11
|
from sera.models._constraints import Constraint, predefined_constraints
|
11
12
|
from sera.models._datatype import (
|
@@ -101,22 +102,24 @@ def _parse_property(
|
|
101
102
|
schema: Schema, prop_name: str, prop: dict
|
102
103
|
) -> DataProperty | ObjectProperty:
|
103
104
|
if isinstance(prop, str):
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
105
|
+
# deprecated
|
106
|
+
assert False, prop
|
107
|
+
# datatype = prop
|
108
|
+
# if datatype in schema.classes:
|
109
|
+
# return ObjectProperty(
|
110
|
+
# name=prop_name,
|
111
|
+
# label=_parse_multi_lingual_string(prop_name),
|
112
|
+
# description=_parse_multi_lingual_string(""),
|
113
|
+
# target=schema.classes[datatype],
|
114
|
+
# cardinality=Cardinality.ONE_TO_ONE,
|
115
|
+
# )
|
116
|
+
# else:
|
117
|
+
# return DataProperty(
|
118
|
+
# name=prop_name,
|
119
|
+
# label=_parse_multi_lingual_string(prop_name),
|
120
|
+
# description=_parse_multi_lingual_string(""),
|
121
|
+
# datatype=_parse_datatype(schema, datatype),
|
122
|
+
# )
|
120
123
|
|
121
124
|
db = prop.get("db", {})
|
122
125
|
_data = prop.get("data", {})
|
@@ -128,6 +131,7 @@ def _parse_property(
|
|
128
131
|
constraints=[
|
129
132
|
_parse_constraint(constraint) for constraint in _data.get("constraints", [])
|
130
133
|
],
|
134
|
+
is_system_controlled=prop.get("is_system_controlled", False),
|
131
135
|
)
|
132
136
|
|
133
137
|
assert isinstance(prop, dict), prop
|
sera/models/_property.py
CHANGED
@@ -71,6 +71,9 @@ class PropDataAttrs:
|
|
71
71
|
# list of constraints applied to the data model's field
|
72
72
|
constraints: list[Constraint] = field(default_factory=list)
|
73
73
|
|
74
|
+
# whether this property is controlled by the system or not
|
75
|
+
is_system_controlled: bool = False
|
76
|
+
|
74
77
|
|
75
78
|
@dataclass(kw_only=True)
|
76
79
|
class Property:
|
@@ -1,18 +1,21 @@
|
|
1
1
|
sera/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
sera/constants.py,sha256=mzAaMyIx8TJK0-RYYJ5I24C4s0Uvj26OLMJmBo0pxHI,123
|
3
3
|
sera/libs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
sera/libs/api_helper.py,sha256=
|
4
|
+
sera/libs/api_helper.py,sha256=47y1kcwk3Xd2ZEMnUj_0OwCuUmgwOs5kYrE95BDVUn4,5411
|
5
5
|
sera/libs/base_orm.py,sha256=dyh0OT2sbHku5qPJXvRzYAHRTSXvvbQaS-Qwg65Bw04,2918
|
6
|
-
sera/libs/base_service.py,sha256=
|
6
|
+
sera/libs/base_service.py,sha256=8uKMaIicLc8LbN5vyaK9dGWoCLf_wuaaOw9ooSiqSWM,4614
|
7
7
|
sera/libs/dag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
sera/libs/dag/_dag.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
sera/libs/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
sera/libs/middlewares/auth.py,sha256=aH2fJshQpJT_dw3lUr67doZ8zdf90XyakdY35qZ1iZs,2443
|
11
|
+
sera/libs/middlewares/uscp.py,sha256=H5umW8iEQSCdb_MJ5Im49kxg1E7TpxSg1p2_2A5zI1U,2600
|
9
12
|
sera/make/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
13
|
sera/make/__main__.py,sha256=G5O7s6135-708honwqMFn2yPTs06WbGQTHpupID0eZ4,1417
|
11
14
|
sera/make/make_app.py,sha256=n9NtW73O3s_5Q31VHIRmnd-jEIcpDO7ksAsOdovde2s,5999
|
12
|
-
sera/make/make_python_api.py,sha256=
|
13
|
-
sera/make/make_python_model.py,sha256=
|
15
|
+
sera/make/make_python_api.py,sha256=kq5DClmEeeNgg-a3Bb_8GN9jxvjnhjmW3RfBHNzynO8,25407
|
16
|
+
sera/make/make_python_model.py,sha256=MyEhVXeeHxAzqUkjSUuVzQJLUCCe7RKL6VxEMiCfTxk,41507
|
14
17
|
sera/make/make_python_services.py,sha256=RsinYZdfkrTlTn9CT50VgqGs9w6IZawsJx-KEmqfnEY,2062
|
15
|
-
sera/make/make_typescript_model.py,sha256
|
18
|
+
sera/make/make_typescript_model.py,sha256=-FNeUlcrqz2Zd1Eg6P14usXTrgTXd9oGhpOApLqntOw,51395
|
16
19
|
sera/misc/__init__.py,sha256=Dh4uDq0D4N53h3zhvmwfa5a0TPVRSUvLzb0hkFuPirk,411
|
17
20
|
sera/misc/_formatter.py,sha256=aCGYL08l8f3aLODHxSocxBBwkRYEo3K1QzCDEn3suj0,1685
|
18
21
|
sera/misc/_utils.py,sha256=V5g4oLGHOhUCR75Kkcn1w01pAvGvaepK-T8Z3pIgHjI,1450
|
@@ -20,15 +23,15 @@ sera/models/__init__.py,sha256=vJC5Kzo_N7wd16ocNPy1VvAZDGNiWeiAhWJ4ihATKvA,780
|
|
20
23
|
sera/models/_class.py,sha256=Wf0e8x6-szG9TzoFkAlqj7_dG0SCICMBw_333n3paxk,2514
|
21
24
|
sera/models/_collection.py,sha256=ZnQEriKC4X88Zz48Kn1AVZKH-1_l8OgWa-zf2kcQOOE,1414
|
22
25
|
sera/models/_constraints.py,sha256=L6QwKL3hRJ5DvvIB4JNgLoyvTKbE9FrpYRzezM4CweE,1484
|
23
|
-
sera/models/_datatype.py,sha256=
|
26
|
+
sera/models/_datatype.py,sha256=zkqOKOTZIkoYhJkIpytWWhGp-LtD10Jzud7lxOSd5UQ,6533
|
24
27
|
sera/models/_default.py,sha256=ABggW6qdPR4ZDqIPJdJ0GCGQ-7kfsfZmQ_DchgZEa-I,137
|
25
28
|
sera/models/_enum.py,sha256=sy0q7E646F-APsqrVQ52r1fAQ_DCAeaNq5YM5QN3zIk,2070
|
26
29
|
sera/models/_module.py,sha256=8QRSCubZmdDP9rL58rGAS6X5VCrkc1ZHvuMu1I1KrWk,5043
|
27
30
|
sera/models/_multi_lingual_string.py,sha256=JETN6k00VH4wrA4w5vAHMEJV8fp3SY9bJebskFTjQLA,1186
|
28
|
-
sera/models/_parse.py,sha256=
|
29
|
-
sera/models/_property.py,sha256=
|
31
|
+
sera/models/_parse.py,sha256=uw6fvvh1ucGqE2jFTCCr-e6_qMfZfSVpaPolNxmrHww,9897
|
32
|
+
sera/models/_property.py,sha256=SJSm5fZJimd2rQuL4UH_aZuNyp9v7x64xMbEVbtYx8Q,5633
|
30
33
|
sera/models/_schema.py,sha256=r-Gqg9Lb_wR3UrbNvfXXgt_qs5bts0t2Ve7aquuF_OI,1155
|
31
34
|
sera/typing.py,sha256=Q4QMfbtfrCjC9tFfsZPhsAnbNX4lm4NHQ9lmjNXYdV0,772
|
32
|
-
sera_2-1.
|
33
|
-
sera_2-1.
|
34
|
-
sera_2-1.
|
35
|
+
sera_2-1.11.2.dist-info/METADATA,sha256=t32pt4lTWNgWc27jVQKlmlK_xf9c99crcgWGVVQjo-Y,857
|
36
|
+
sera_2-1.11.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
37
|
+
sera_2-1.11.2.dist-info/RECORD,,
|
File without changes
|