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.
Files changed (55) hide show
  1. {sera_2-1.25.0 → sera_2-1.26.1}/PKG-INFO +1 -1
  2. {sera_2-1.25.0 → sera_2-1.26.1}/pyproject.toml +1 -1
  3. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_python_model.py +36 -40
  4. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_typescript_model.py +6 -45
  5. sera_2-1.26.1/sera/make/py_backend/misc.py +13 -0
  6. sera_2-1.26.1/sera/make/ts_frontend/__init__.py +0 -0
  7. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/ts_frontend/make_enums.py +1 -20
  8. sera_2-1.26.1/sera/make/ts_frontend/make_query.py +146 -0
  9. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/ts_frontend/misc.py +2 -10
  10. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_collection.py +5 -1
  11. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_module.py +0 -1
  12. {sera_2-1.25.0 → sera_2-1.26.1}/README.md +0 -0
  13. {sera_2-1.25.0 → sera_2-1.26.1}/sera/__init__.py +0 -0
  14. {sera_2-1.25.0 → sera_2-1.26.1}/sera/constants.py +0 -0
  15. {sera_2-1.25.0 → sera_2-1.26.1}/sera/exports/__init__.py +0 -0
  16. {sera_2-1.25.0 → sera_2-1.26.1}/sera/exports/schema.py +0 -0
  17. {sera_2-1.25.0 → sera_2-1.26.1}/sera/exports/test.py +0 -0
  18. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/__init__.py +0 -0
  19. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/api_helper.py +0 -0
  20. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/api_test_helper.py +0 -0
  21. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/base_orm.py +0 -0
  22. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/base_service.py +0 -0
  23. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/__init__.py +0 -0
  24. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_dcg.py +0 -0
  25. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_edge.py +0 -0
  26. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_flow.py +0 -0
  27. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_fn_signature.py +0 -0
  28. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_node.py +0 -0
  29. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_runtime.py +0 -0
  30. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/directed_computing_graph/_type_conversion.py +0 -0
  31. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/middlewares/__init__.py +0 -0
  32. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/middlewares/auth.py +0 -0
  33. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/middlewares/uscp.py +0 -0
  34. {sera_2-1.25.0 → sera_2-1.26.1}/sera/libs/search_helper.py +0 -0
  35. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/__init__.py +0 -0
  36. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/__main__.py +0 -0
  37. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_app.py +0 -0
  38. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_python_api.py +0 -0
  39. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/make_python_services.py +0 -0
  40. {sera_2-1.25.0/sera/make/ts_frontend → sera_2-1.26.1/sera/make/py_backend}/__init__.py +0 -0
  41. {sera_2-1.25.0 → sera_2-1.26.1}/sera/make/ts_frontend/make_class_schema.py +0 -0
  42. {sera_2-1.25.0 → sera_2-1.26.1}/sera/misc/__init__.py +0 -0
  43. {sera_2-1.25.0 → sera_2-1.26.1}/sera/misc/_formatter.py +0 -0
  44. {sera_2-1.25.0 → sera_2-1.26.1}/sera/misc/_utils.py +0 -0
  45. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/__init__.py +0 -0
  46. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_class.py +0 -0
  47. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_constraints.py +0 -0
  48. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_datatype.py +0 -0
  49. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_default.py +0 -0
  50. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_enum.py +0 -0
  51. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_multi_lingual_string.py +0 -0
  52. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_parse.py +0 -0
  53. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_property.py +0 -0
  54. {sera_2-1.25.0 → sera_2-1.26.1}/sera/models/_schema.py +0 -0
  55. {sera_2-1.25.0 → sera_2-1.26.1}/sera/typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sera-2
3
- Version: 1.25.0
3
+ Version: 1.26.1
4
4
  Summary:
5
5
  Author: Binh Vu
6
6
  Author-email: bvu687@gmail.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "sera-2"
3
- version = "1.25.0"
3
+ version = "1.26.1"
4
4
  description = ""
5
5
  authors = ["Binh Vu <bvu687@gmail.com>"]
6
6
  readme = "README.md"
@@ -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(prop.name + "_id")
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
- if isinstance(prop, ObjectProperty) and prop.target.db is not None:
151
- propname = prop.name + "_id"
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
- f"{prop.name}_id", conversion_fn(item)
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(prop.name)
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(prop.name),
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.name
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(prop.name, pytype.type))
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}.{prop.name}"
829
- as_composite_args.append(DeferredVar.simple(prop.name))
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(prop.name), expr.ExprConstant(None))
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(""), prop, expr.ExprIdent(prop.name)
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}_{get_property_name(prop)}_gin"
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', {get_property_name(prop)})"
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}_{get_property_name(prop)}_gist"
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({get_property_name(prop)}) gist_trgm_ops(siglen=256)"
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
- get_property_name(cls.properties[prop])
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 = f"{prop.name}_id"
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
- make_query_processor(cls, pkg)
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 AST, ImportHelper, PredefinedFn, Program, expr, stmt
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 AST, ImportHelper, PredefinedFn, Program, expr, stmt
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 isinstance(prop, DataProperty) and prop.db.foreign_key is not None:
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
@@ -183,4 +183,3 @@ class App:
183
183
  def name(self) -> str:
184
184
  """Get the name of the application"""
185
185
  return self.root.dir.name
186
- return self.root.dir.name
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