liminal-orm 1.0.6__py3-none-any.whl → 1.1.1__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.
Files changed (34) hide show
  1. liminal/base/base_operation.py +9 -11
  2. liminal/base/properties/base_field_properties.py +11 -0
  3. liminal/base/properties/base_schema_properties.py +6 -2
  4. liminal/cli/live_test_dropdown_migration.py +8 -9
  5. liminal/cli/live_test_entity_schema_migration.py +24 -27
  6. liminal/connection/benchling_connection.py +8 -2
  7. liminal/connection/benchling_service.py +11 -18
  8. liminal/dropdowns/operations.py +1 -1
  9. liminal/entity_schemas/compare.py +17 -7
  10. liminal/entity_schemas/entity_schema_models.py +6 -9
  11. liminal/entity_schemas/generate_files.py +7 -6
  12. liminal/entity_schemas/operations.py +77 -102
  13. liminal/entity_schemas/tag_schema_models.py +7 -2
  14. liminal/entity_schemas/utils.py +4 -2
  15. liminal/enums/benchling_api_field_type.py +3 -0
  16. liminal/enums/benchling_entity_type.py +1 -0
  17. liminal/enums/benchling_field_type.py +4 -0
  18. liminal/enums/benchling_folder_item_type.py +1 -0
  19. liminal/external/__init__.py +0 -2
  20. liminal/mappers.py +25 -1
  21. liminal/migrate/components.py +9 -4
  22. liminal/orm/base_model.py +8 -7
  23. liminal/orm/column.py +15 -5
  24. liminal/orm/mixins.py +9 -0
  25. liminal/orm/schema_properties.py +8 -2
  26. liminal/tests/conftest.py +84 -22
  27. liminal/tests/test_entity_schema_compare.py +22 -5
  28. liminal/utils.py +11 -4
  29. {liminal_orm-1.0.6.dist-info → liminal_orm-1.1.1.dist-info}/METADATA +5 -2
  30. liminal_orm-1.1.1.dist-info/RECORD +61 -0
  31. {liminal_orm-1.0.6.dist-info → liminal_orm-1.1.1.dist-info}/WHEEL +1 -1
  32. liminal_orm-1.0.6.dist-info/RECORD +0 -61
  33. {liminal_orm-1.0.6.dist-info → liminal_orm-1.1.1.dist-info}/LICENSE.md +0 -0
  34. {liminal_orm-1.0.6.dist-info → liminal_orm-1.1.1.dist-info}/entry_points.txt +0 -0
@@ -14,16 +14,14 @@ Order of operations based on order class var:
14
14
  7. ReorderDropdownOptions
15
15
  8. CreateSchema
16
16
  9. UpdateSchema
17
- 10. UpdateSchemaName
18
- 11. UnarchiveSchema
19
- 12. CreateField
20
- 13. UnarchiveField
21
- 14. UpdateField
22
- 15. UpdateFieldName
23
- 16. ArchiveField
24
- 17. ReorderFields
25
- 18. ArchiveSchema
26
- 19. ArchiveDropdown
17
+ 10. UnarchiveSchema
18
+ 11. CreateField
19
+ 12. UnarchiveField
20
+ 13. UpdateField
21
+ 14. ArchiveField
22
+ 15. ReorderFields
23
+ 16. ArchiveSchema
24
+ 17. ArchiveDropdown
27
25
  """
28
26
 
29
27
 
@@ -36,7 +34,7 @@ class BaseOperation(ABC):
36
34
  self.args = args
37
35
  self.kwargs = kwargs
38
36
 
39
- def validate(self) -> None:
37
+ def validate(self, benchling_service: BenchlingService) -> None:
40
38
  """Validate the operation before running it. This is not called at runtime,
41
39
  but is used to validate operations before a migration is run."""
42
40
  pass
@@ -18,6 +18,8 @@ class BaseFieldProperties(BaseModel):
18
18
  ----------
19
19
  name : str | None
20
20
  The external facing name of the field.
21
+ warehouse_name : str | None
22
+ The sql column name in the benchling warehouse.
21
23
  type : BenchlingFieldType | None
22
24
  The type of the field.
23
25
  required : bool | None
@@ -39,6 +41,7 @@ class BaseFieldProperties(BaseModel):
39
41
  """
40
42
 
41
43
  name: str | None = None
44
+ warehouse_name: str | None = None
42
45
  type: BenchlingFieldType | None = None
43
46
  required: bool | None = None
44
47
  is_multi: bool | None = None
@@ -50,10 +53,18 @@ class BaseFieldProperties(BaseModel):
50
53
 
51
54
  model_config = ConfigDict(arbitrary_types_allowed=True)
52
55
 
56
+ def __init__(self, **data: Any):
57
+ super().__init__(**data)
58
+ self._archived = data.get("_archived", None)
59
+
53
60
  def set_archived(self, value: bool) -> BaseFieldProperties:
54
61
  self._archived = value
55
62
  return self
56
63
 
64
+ def set_warehouse_name(self, wh_name: str) -> BaseFieldProperties:
65
+ self.warehouse_name = wh_name
66
+ return self
67
+
57
68
  def validate_column(self, wh_name: str) -> bool:
58
69
  """If the Field Properties are meant to represent a column in Benchling,
59
70
  this will validate the properties and ensure that the entity_link and dropdowns are valid names that exist in our code.
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Any
4
4
 
5
- from pydantic import BaseModel, ConfigDict, PrivateAttr, model_validator
5
+ from pydantic import BaseModel, ConfigDict, model_validator
6
6
 
7
7
  from liminal.enums import BenchlingEntityType, BenchlingNamingStrategy
8
8
 
@@ -69,7 +69,11 @@ class BaseSchemaProperties(BaseModel):
69
69
  entity_type: BenchlingEntityType | None = None
70
70
  naming_strategies: set[BenchlingNamingStrategy] | None = None
71
71
  mixture_schema_config: MixtureSchemaConfig | None = None
72
- _archived: bool | None = PrivateAttr(default=None)
72
+ _archived: bool | None = None
73
+
74
+ def __init__(self, **data: Any):
75
+ super().__init__(**data)
76
+ self._archived = data.get("_archived", None)
73
77
 
74
78
  model_config = ConfigDict(arbitrary_types_allowed=True)
75
79
 
@@ -1,5 +1,3 @@
1
- import uuid
2
-
3
1
  from liminal.base.base_operation import BaseOperation
4
2
  from liminal.connection.benchling_service import BenchlingService
5
3
  from liminal.dropdowns.operations import (
@@ -13,6 +11,7 @@ from liminal.dropdowns.operations import (
13
11
  UpdateDropdownOption,
14
12
  )
15
13
  from liminal.migrate.components import execute_operations, execute_operations_dry_run
14
+ from liminal.utils import generate_random_id
16
15
 
17
16
 
18
17
  def mock_dropdown_full_migration(
@@ -24,15 +23,15 @@ def mock_dropdown_full_migration(
24
23
  If dry_run is set to False, the operations will be executed.
25
24
  If dry_run is set to True, the operations will be executed in dry run mode and only the description of the operations will be printed.
26
25
  """
27
- random_id = f"{str(uuid.uuid4())[0:8]}_arch"
26
+ random_id = generate_random_id()
27
+ test_dropdown_name = f"{test_dropdown_name}_{random_id}"
28
28
 
29
29
  dropdown_options = ["Option 1", "Option 2"]
30
30
  create_dropdown_op = CreateDropdown(test_dropdown_name, dropdown_options)
31
31
 
32
- update_dropdown_op = UpdateDropdownName(
33
- test_dropdown_name, f"{test_dropdown_name}_{random_id}"
34
- )
35
- test_dropdown_name = f"{test_dropdown_name}_{random_id}"
32
+ test_dropdown_name = f"{test_dropdown_name}_arch"
33
+
34
+ update_dropdown_op = UpdateDropdownName(test_dropdown_name, test_dropdown_name)
36
35
 
37
36
  create_dropdown_option_op = CreateDropdownOption(
38
37
  test_dropdown_name, "Option 3 New", 2
@@ -70,7 +69,7 @@ def mock_dropdown_full_migration(
70
69
  unarchive_dropdown_op,
71
70
  rearchive_dropdown_op,
72
71
  ]
73
- execute_operations_dry_run(ops) if dry_run else execute_operations(
72
+ execute_operations_dry_run(
74
73
  benchling_service, ops
75
- )
74
+ ) if dry_run else execute_operations(benchling_service, ops)
76
75
  print("Dry run migration complete!") if dry_run else print("Migration complete!")
@@ -1,5 +1,3 @@
1
- import uuid
2
-
3
1
  from liminal.base.base_operation import BaseOperation
4
2
  from liminal.base.properties.base_field_properties import BaseFieldProperties
5
3
  from liminal.base.properties.base_schema_properties import BaseSchemaProperties
@@ -14,7 +12,6 @@ from liminal.entity_schemas.operations import (
14
12
  UnarchiveEntitySchemaField,
15
13
  UpdateEntitySchema,
16
14
  UpdateEntitySchemaField,
17
- UpdateEntitySchemaFieldName,
18
15
  )
19
16
  from liminal.enums import (
20
17
  BenchlingEntityType,
@@ -23,6 +20,7 @@ from liminal.enums import (
23
20
  )
24
21
  from liminal.migrate.components import execute_operations, execute_operations_dry_run
25
22
  from liminal.orm.schema_properties import SchemaProperties
23
+ from liminal.utils import generate_random_id
26
24
 
27
25
 
28
26
  def mock_entity_schema_full_migration(
@@ -34,8 +32,9 @@ def mock_entity_schema_full_migration(
34
32
  If dry_run is set to False, the operations will be executed.
35
33
  If dry_run is set to True, the operations will be executed in dry run mode and only the description of the operations will be printed.
36
34
  """
37
- random_id = f"{str(uuid.uuid4())[0:8]}_arch"
38
- if len(test_model_wh_name) + len(random_id) > 32:
35
+ random_id = generate_random_id()
36
+ test_model_wh_name = f"{test_model_wh_name}_{random_id}"
37
+ if len(test_model_wh_name) > 32:
39
38
  raise ValueError(
40
39
  f"Test model warehouse name is too long. Must be less than {32 - len(random_id)} characters."
41
40
  )
@@ -49,37 +48,37 @@ def mock_entity_schema_full_migration(
49
48
  )
50
49
  create_entity_schema_op = CreateEntitySchema(
51
50
  schema_properties=schema_properties,
52
- fields={
53
- "test_column_1": BaseFieldProperties(
51
+ fields=[
52
+ BaseFieldProperties(
54
53
  name="Test Column 1",
54
+ warehouse_name="test_column_1",
55
55
  type=BenchlingFieldType.TEXT,
56
56
  required=False,
57
57
  is_multi=False,
58
58
  parent_link=False,
59
59
  ),
60
- "test_column_2": BaseFieldProperties(
60
+ BaseFieldProperties(
61
61
  name="Test Column 2",
62
+ warehouse_name="test_column_2",
62
63
  type=BenchlingFieldType.INTEGER,
63
64
  required=False,
64
65
  is_multi=False,
65
66
  parent_link=False,
66
67
  ),
67
- "test_column_4": BaseFieldProperties(
68
+ BaseFieldProperties(
68
69
  name="Test Column 4",
69
- type=BenchlingFieldType.ENTITY_LINK,
70
- entity_link="ngs_sample",
70
+ warehouse_name="test_column_4",
71
+ type=BenchlingFieldType.CUSTOM_ENTITY_LINK,
71
72
  required=False,
72
73
  is_multi=False,
73
- parent_link=True,
74
+ parent_link=False,
74
75
  ),
75
- },
76
+ ],
76
77
  )
77
78
 
78
- test_model_wh_name = f"{test_model_wh_name}_{random_id}"
79
79
  update_schema_properties = BaseSchemaProperties(
80
- warehouse_name=test_model_wh_name,
81
- name=test_model_wh_name,
82
- prefix=test_model_wh_name,
80
+ name=f"{test_model_wh_name}_arch",
81
+ prefix=f"{test_model_wh_name}_arch",
83
82
  entity_type=BenchlingEntityType.AA_SEQUENCE,
84
83
  naming_strategies=[
85
84
  BenchlingNamingStrategy.IDS_FROM_NAMES,
@@ -92,24 +91,23 @@ def mock_entity_schema_full_migration(
92
91
 
93
92
  test_column_3_field_properties = BaseFieldProperties(
94
93
  name="Test Column 3 New",
94
+ warehouse_name="test_column_3",
95
95
  type=BenchlingFieldType.TEXT,
96
96
  required=False,
97
97
  is_multi=False,
98
98
  parent_link=False,
99
99
  )
100
100
  create_entity_schema_field_op = CreateEntitySchemaField(
101
- test_model_wh_name, "test_column_3_new", test_column_3_field_properties, 2
101
+ test_model_wh_name, test_column_3_field_properties, 2
102
102
  )
103
103
 
104
104
  update_test_column_3_field_properties = BaseFieldProperties(
105
- type=BenchlingFieldType.CUSTOM_ENTITY_LINK, is_multi=True
105
+ type=BenchlingFieldType.CUSTOM_ENTITY_LINK,
106
+ is_multi=True,
107
+ name="Test Column 3",
106
108
  )
107
109
  update_entity_schema_field_op = UpdateEntitySchemaField(
108
- test_model_wh_name, "test_column_3_new", update_test_column_3_field_properties
109
- )
110
-
111
- update_entity_schema_field_name_op = UpdateEntitySchemaFieldName(
112
- test_model_wh_name, "test_column_3_new", "test_column_3"
110
+ test_model_wh_name, "test_column_3", update_test_column_3_field_properties
113
111
  )
114
112
 
115
113
  archive_entity_schema_field_op = ArchiveEntitySchemaField(
@@ -134,7 +132,6 @@ def mock_entity_schema_full_migration(
134
132
  update_entity_schema_op,
135
133
  create_entity_schema_field_op,
136
134
  update_entity_schema_field_op,
137
- update_entity_schema_field_name_op,
138
135
  archive_entity_schema_field_op,
139
136
  unarchive_entity_schema_field_op,
140
137
  reorder_entity_schema_field_op,
@@ -142,7 +139,7 @@ def mock_entity_schema_full_migration(
142
139
  unarchive_entity_schema_op,
143
140
  rearchive_entity_schema_op,
144
141
  ]
145
- execute_operations_dry_run(ops) if dry_run else execute_operations(
142
+ execute_operations_dry_run(
146
143
  benchling_service, ops
147
- )
144
+ ) if dry_run else execute_operations(benchling_service, ops)
148
145
  print("Dry run migration complete!") if dry_run else print("Migration complete!")
@@ -27,20 +27,26 @@ class BenchlingConnection(BaseModel):
27
27
  The email of the internal API admin.
28
28
  internal_api_admin_password: str | None = None
29
29
  The password of the internal API admin.
30
+ warehouse_access: bool = False
31
+ Whether your Benchling tenant has access to the warehouse. If warehouse_connection_string is provided, this will default to True.
32
+ warehouse_access is required to set a custom warehouse names on entity schemas and their fields.
30
33
  """
31
34
 
32
35
  tenant_name: str
33
36
  tenant_alias: str | None = None
34
37
  current_revision_id_var_name: str = ""
35
- api_client_id: str | None = None
36
- api_client_secret: str | None = None
38
+ api_client_id: str
39
+ api_client_secret: str
37
40
  warehouse_connection_string: str | None = None
38
41
  internal_api_admin_email: str | None = None
39
42
  internal_api_admin_password: str | None = None
43
+ warehouse_access: bool = False
40
44
 
41
45
  @model_validator(mode="before")
42
46
  @classmethod
43
47
  def set_current_revision_id_var_name(cls, values: dict) -> dict:
48
+ if values.get("warehouse_connection_string"):
49
+ values["warehouse_access"] = True
44
50
  if not values.get("current_revision_id_var_name"):
45
51
  tenant_alias = values.get("tenant_alias")
46
52
  tenant_name = values.get("tenant_name")
@@ -50,24 +50,17 @@ class BenchlingService(Benchling):
50
50
  self.use_api = use_api
51
51
  self.benchling_tenant = connection.tenant_name
52
52
  if use_api:
53
- if connection.api_client_id and connection.api_client_secret:
54
- retry_strategy = RetryStrategy(max_tries=10)
55
- auth_method = ClientCredentialsOAuth2(
56
- client_id=connection.api_client_id,
57
- client_secret=connection.api_client_secret,
58
- token_url=f"https://{connection.tenant_name}.benchling.com/api/v2/token",
59
- )
60
- url = f"https://{connection.tenant_name}.benchling.com"
61
- super().__init__(
62
- url=url, auth_method=auth_method, retry_strategy=retry_strategy
63
- )
64
- logger.info(
65
- f"Tenant {connection.tenant_name}: Connected to Benchling API."
66
- )
67
- else:
68
- raise ValueError(
69
- "use_api is True but api_client_id and api_client_secret not provided in BenchlingConnection."
70
- )
53
+ retry_strategy = RetryStrategy(max_tries=10)
54
+ auth_method = ClientCredentialsOAuth2(
55
+ client_id=connection.api_client_id,
56
+ client_secret=connection.api_client_secret,
57
+ token_url=f"https://{connection.tenant_name}.benchling.com/api/v2/token",
58
+ )
59
+ url = f"https://{connection.tenant_name}.benchling.com"
60
+ super().__init__(
61
+ url=url, auth_method=auth_method, retry_strategy=retry_strategy
62
+ )
63
+ logger.info(f"Tenant {connection.tenant_name}: Connected to Benchling API.")
71
64
  self.use_db = use_db
72
65
  if use_db:
73
66
  if connection.warehouse_connection_string:
@@ -87,7 +87,7 @@ class ArchiveDropdown(BaseOperation):
87
87
  raise ValueError(f"Dropdown {self.dropdown_name} is already archived.")
88
88
  return archive_dropdown(benchling_service, dropdown.id)
89
89
 
90
- def validate(self) -> None:
90
+ def validate(self, benchling_service: BenchlingService) -> None:
91
91
  if schemas_with_dropdown := get_schemas_with_dropdown(self.dropdown_name):
92
92
  raise ValueError(
93
93
  f"Dropdown {self.dropdown_name} is used in schemas {schemas_with_dropdown}. Cannot archive a dropdown that is in use in non-archived fields."
@@ -41,9 +41,13 @@ def compare_entity_schemas(
41
41
  )
42
42
  # If models are provided, filter the schemas from benchling so that only the models passed in are compared.
43
43
  # If you don't filter, it will compare to all the schemas in benchling and think that they are missing from code and should be archived.
44
- models = BaseModel.get_all_subclasses(schema_names)
44
+ models = [
45
+ m
46
+ for m in BaseModel.get_all_subclasses(schema_names)
47
+ if not m.__schema_properties__._archived
48
+ ]
45
49
  archived_benchling_schema_wh_names = [
46
- s[0].warehouse_name for s in benchling_schemas if s[0]._archived is True
50
+ s.warehouse_name for s, _ in benchling_schemas if s._archived is True
47
51
  ]
48
52
  # Running list of schema names from benchling. As each model is checked, remove the schema name from this list.
49
53
  # This is used at the end to check if there are any schemas left (schemas that exist in benchling but not in code) and archive them if they are.
@@ -63,7 +67,9 @@ def compare_entity_schemas(
63
67
  s.warehouse_name for s, _ in benchling_schemas
64
68
  ]:
65
69
  benchling_schema_props, benchling_schema_fields = next(
66
- t for t in benchling_schemas if t[0].warehouse_name == model_wh_name
70
+ (s, lof)
71
+ for s, lof in benchling_schemas
72
+ if s.warehouse_name == model_wh_name
67
73
  )
68
74
  archived_benchling_schema_fields = {
69
75
  k: v for k, v in benchling_schema_fields.items() if v._archived is True
@@ -193,12 +199,14 @@ def compare_entity_schemas(
193
199
  )
194
200
  )
195
201
  else:
202
+ new_field_props = model_columns[
203
+ column_name
204
+ ].properties.set_warehouse_name(column_name)
196
205
  ops.append(
197
206
  CompareOperation(
198
207
  op=CreateEntitySchemaField(
199
208
  model_wh_name,
200
- column_name,
201
- field_props=model_columns[column_name].properties,
209
+ field_props=new_field_props,
202
210
  index=list(model_columns.keys()).index(column_name),
203
211
  ),
204
212
  reverse_op=ArchiveEntitySchemaField(
@@ -233,8 +241,10 @@ def compare_entity_schemas(
233
241
  # Benchling api does not allow for setting a custom warehouse_name,
234
242
  # so we need to run another UpdateEntitySchema to set the warehouse_name if it is different from the snakecase version of the model name.
235
243
  else:
236
- model_props = {name: col.properties for name, col in model_columns.items()}
237
-
244
+ model_props = [
245
+ col.properties.set_warehouse_name(wh_name)
246
+ for wh_name, col in model_columns.items()
247
+ ]
238
248
  ops.append(
239
249
  CompareOperation(
240
250
  op=CreateEntitySchema(
@@ -34,7 +34,6 @@ class CreateEntitySchemaFieldModel(BaseModel):
34
34
  @classmethod
35
35
  def from_benchling_props(
36
36
  cls,
37
- wh_field_name: str,
38
37
  field_props: BaseFieldProperties,
39
38
  benchling_service: BenchlingService | None = None,
40
39
  ) -> CreateEntitySchemaFieldModel:
@@ -42,8 +41,6 @@ class CreateEntitySchemaFieldModel(BaseModel):
42
41
 
43
42
  Parameters
44
43
  ----------
45
- wh_field_name : str
46
- The warehouse name of the field.
47
44
  field_props : BaseFieldProperties
48
45
  The field properties.
49
46
  benchling_service : BenchlingService | None
@@ -79,7 +76,7 @@ class CreateEntitySchemaFieldModel(BaseModel):
79
76
  ).id
80
77
  return CreateEntitySchemaFieldModel(
81
78
  name=field_props.name,
82
- systemName=wh_field_name,
79
+ systemName=field_props.warehouse_name,
83
80
  isMulti=field_props.is_multi,
84
81
  isRequired=field_props.required,
85
82
  isParentLink=field_props.parent_link,
@@ -107,7 +104,7 @@ class CreateEntitySchemaModel(BaseModel):
107
104
  def from_benchling_props(
108
105
  cls,
109
106
  benchling_props: SchemaProperties,
110
- fields: dict[str, BaseFieldProperties],
107
+ fields: list[BaseFieldProperties],
111
108
  benchling_service: BenchlingService,
112
109
  ) -> CreateEntitySchemaModel:
113
110
  """Generates a CreateEntitySchemaModel from the given internal definition of benchling schema properties.
@@ -116,8 +113,8 @@ class CreateEntitySchemaModel(BaseModel):
116
113
  ----------
117
114
  benchling_props : Benchling SchemaProperties
118
115
  The schema properties.
119
- fields : dict[str, Benchling FieldProperties]
120
- Map of warehouse field names to field properties.
116
+ fields : list[Benchling FieldProperties]
117
+ List of field properties.
121
118
  benchling_service : BenchlingService | None
122
119
  The Benchling service instance used to fetch additional data if needed.
123
120
 
@@ -135,8 +132,8 @@ class CreateEntitySchemaModel(BaseModel):
135
132
  labelingStrategies=[s.value for s in benchling_props.naming_strategies],
136
133
  fields=[
137
134
  CreateEntitySchemaFieldModel.from_benchling_props(
138
- field_name, field_props, benchling_service
135
+ field_props, benchling_service
139
136
  )
140
- for field_name, field_props in fields.items()
137
+ for field_props in fields
141
138
  ],
142
139
  )
@@ -14,6 +14,7 @@ def get_entity_mixin(entity_type: BenchlingEntityType) -> str:
14
14
  type_to_mixin_map = {
15
15
  BenchlingEntityType.ENTRY: "EntryMixin",
16
16
  BenchlingEntityType.MIXTURE: "MixtureMixin",
17
+ BenchlingEntityType.MOLECULE: "MoleculeMixin",
17
18
  BenchlingEntityType.CUSTOM_ENTITY: "CustomEntityMixin",
18
19
  BenchlingEntityType.DNA_SEQUENCE: "DnaSequenceMixin",
19
20
  BenchlingEntityType.DNA_OLIGO: "DnaOligoMixin",
@@ -36,6 +37,7 @@ def get_file_subdirectory(entity_type: BenchlingEntityType) -> str:
36
37
  BenchlingEntityType.AA_SEQUENCE: "aa_sequences",
37
38
  BenchlingEntityType.ENTRY: "entries",
38
39
  BenchlingEntityType.MIXTURE: "mixtures",
40
+ BenchlingEntityType.MOLECULE: "molecules",
39
41
  }
40
42
  if entity_type not in type_to_subdir_map:
41
43
  raise ValueError(f"Unknown entity type: {entity_type}")
@@ -97,7 +99,10 @@ def generate_all_entity_schema_files(
97
99
  f"""{tab}{col_name}: {convert_benchling_type_to_python_type(col.type).__name__},"""
98
100
  )
99
101
 
100
- if col.type == BenchlingFieldType.DATE:
102
+ if (
103
+ col.type == BenchlingFieldType.DATE
104
+ or col.type == BenchlingFieldType.DATETIME
105
+ ):
101
106
  if not has_date:
102
107
  import_strings.append("from datetime import datetime")
103
108
  if (
@@ -140,11 +145,7 @@ def generate_all_entity_schema_files(
140
145
  relationship_string = "\n".join(relationship_strings)
141
146
  import_string = "\n".join(list(set(import_strings)))
142
147
  init_string = f"\n{tab}".join(init_strings) if len(columns) > 0 else ""
143
- functions_string = f"""
144
- @classmethod
145
- def query(self, session: Session) -> Query:
146
- return session.query({classname})
147
-
148
+ functions_string = """
148
149
  def get_validators(self) -> list[BenchlingValidator]:
149
150
  return []"""
150
151