liminal-orm 3.2.0__tar.gz → 4.0.0__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 (172) hide show
  1. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/PKG-INFO +2 -2
  2. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/base/name_template_parts.py +1 -1
  3. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/base/properties/base_field_properties.py +19 -0
  4. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/cli/cli.py +42 -7
  5. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/cli/controller.py +26 -6
  6. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/cli/live_test_dropdown_migration.py +4 -2
  7. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/cli/utils.py +1 -1
  8. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/dropdowns/generate_files.py +14 -2
  9. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/entity_schemas/compare.py +56 -34
  10. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/entity_schemas/generate_files.py +62 -18
  11. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/entity_schemas/operations.py +6 -3
  12. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/base_model.py +2 -2
  13. liminal_orm-4.0.0/liminal/orm/base_results_model.py +207 -0
  14. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/relationship.py +2 -2
  15. liminal_orm-4.0.0/liminal/orm/results_schema_properties.py +23 -0
  16. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/schema_properties.py +4 -0
  17. liminal_orm-4.0.0/liminal/results_schemas/generate_files.py +197 -0
  18. liminal_orm-4.0.0/liminal/results_schemas/models/results_schema_model.py +145 -0
  19. liminal_orm-4.0.0/liminal/results_schemas/utils.py +48 -0
  20. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/tests/test_entity_schema_compare.py +6 -7
  21. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/pyproject.toml +2 -2
  22. liminal_orm-3.2.0/liminal/.DS_Store +0 -0
  23. liminal_orm-3.2.0/liminal/base/__pycache__/__init__.cpython-310.pyc +0 -0
  24. liminal_orm-3.2.0/liminal/base/__pycache__/base.cpython-310.pyc +0 -0
  25. liminal_orm-3.2.0/liminal/base/__pycache__/base_dropdown.cpython-310.pyc +0 -0
  26. liminal_orm-3.2.0/liminal/base/__pycache__/base_operation.cpython-310.pyc +0 -0
  27. liminal_orm-3.2.0/liminal/base/__pycache__/base_validation_filters.cpython-310.pyc +0 -0
  28. liminal_orm-3.2.0/liminal/base/__pycache__/benchling_base_model.cpython-310.pyc +0 -0
  29. liminal_orm-3.2.0/liminal/base/__pycache__/benchling_column.cpython-310.pyc +0 -0
  30. liminal_orm-3.2.0/liminal/base/__pycache__/benchling_dropdown.cpython-310.pyc +0 -0
  31. liminal_orm-3.2.0/liminal/base/__pycache__/benchling_field_properties.cpython-310.pyc +0 -0
  32. liminal_orm-3.2.0/liminal/base/__pycache__/benchling_schema_properties.cpython-310.pyc +0 -0
  33. liminal_orm-3.2.0/liminal/base/__pycache__/callable_operation.cpython-310.pyc +0 -0
  34. liminal_orm-3.2.0/liminal/base/__pycache__/column.cpython-310.pyc +0 -0
  35. liminal_orm-3.2.0/liminal/base/__pycache__/compare_operation.cpython-310.pyc +0 -0
  36. liminal_orm-3.2.0/liminal/base/__pycache__/field_properties.cpython-310.pyc +0 -0
  37. liminal_orm-3.2.0/liminal/base/__pycache__/name_template_components.cpython-310.pyc +0 -0
  38. liminal_orm-3.2.0/liminal/base/__pycache__/name_template_parts.cpython-310.pyc +0 -0
  39. liminal_orm-3.2.0/liminal/base/__pycache__/schema_properties.cpython-310.pyc +0 -0
  40. liminal_orm-3.2.0/liminal/base/__pycache__/str_enum.cpython-310.pyc +0 -0
  41. liminal_orm-3.2.0/liminal/base/__pycache__/unset.cpython-310.pyc +0 -0
  42. liminal_orm-3.2.0/liminal/base/__pycache__/user.cpython-310.pyc +0 -0
  43. liminal_orm-3.2.0/liminal/base/properties/__pycache__/NoDropdown.cpython-310.pyc +0 -0
  44. liminal_orm-3.2.0/liminal/base/properties/__pycache__/__init__.cpython-310.pyc +0 -0
  45. liminal_orm-3.2.0/liminal/base/properties/__pycache__/base_field_properties.cpython-310.pyc +0 -0
  46. liminal_orm-3.2.0/liminal/base/properties/__pycache__/base_name_template.cpython-310.pyc +0 -0
  47. liminal_orm-3.2.0/liminal/base/properties/__pycache__/base_schema_properties.cpython-310.pyc +0 -0
  48. liminal_orm-3.2.0/liminal/base/properties/__pycache__/field_properties.cpython-310.pyc +0 -0
  49. liminal_orm-3.2.0/liminal/base/properties/__pycache__/schema_properties.cpython-310.pyc +0 -0
  50. liminal_orm-3.2.0/liminal/cli/__pycache__/cli.cpython-310.pyc +0 -0
  51. liminal_orm-3.2.0/liminal/cli/__pycache__/controller.cpython-310.pyc +0 -0
  52. liminal_orm-3.2.0/liminal/cli/__pycache__/live_test_dropdown_migration.cpython-310.pyc +0 -0
  53. liminal_orm-3.2.0/liminal/cli/__pycache__/live_test_entity_schema_migration.cpython-310.pyc +0 -0
  54. liminal_orm-3.2.0/liminal/cli/__pycache__/utils.cpython-310.pyc +0 -0
  55. liminal_orm-3.2.0/liminal/connection/__pycache__/__init__.cpython-310.pyc +0 -0
  56. liminal_orm-3.2.0/liminal/connection/__pycache__/benchling_connection.cpython-310.pyc +0 -0
  57. liminal_orm-3.2.0/liminal/connection/__pycache__/benchling_service.cpython-310.pyc +0 -0
  58. liminal_orm-3.2.0/liminal/dropdowns/__pycache__/api.cpython-310.pyc +0 -0
  59. liminal_orm-3.2.0/liminal/dropdowns/__pycache__/compare.cpython-310.pyc +0 -0
  60. liminal_orm-3.2.0/liminal/dropdowns/__pycache__/generate_files.cpython-310.pyc +0 -0
  61. liminal_orm-3.2.0/liminal/dropdowns/__pycache__/operations.cpython-310.pyc +0 -0
  62. liminal_orm-3.2.0/liminal/dropdowns/__pycache__/utils.cpython-310.pyc +0 -0
  63. liminal_orm-3.2.0/liminal/entity_schemas/__pycache__/api.cpython-310.pyc +0 -0
  64. liminal_orm-3.2.0/liminal/entity_schemas/__pycache__/compare.cpython-310.pyc +0 -0
  65. liminal_orm-3.2.0/liminal/entity_schemas/__pycache__/entity_schema_models.cpython-310.pyc +0 -0
  66. liminal_orm-3.2.0/liminal/entity_schemas/__pycache__/generate_files.cpython-310.pyc +0 -0
  67. liminal_orm-3.2.0/liminal/entity_schemas/__pycache__/operations.cpython-310.pyc +0 -0
  68. liminal_orm-3.2.0/liminal/entity_schemas/__pycache__/schemas.cpython-310.pyc +0 -0
  69. liminal_orm-3.2.0/liminal/entity_schemas/__pycache__/tag_schema_models.cpython-310.pyc +0 -0
  70. liminal_orm-3.2.0/liminal/entity_schemas/__pycache__/utils.cpython-310.pyc +0 -0
  71. liminal_orm-3.2.0/liminal/enums/__pycache__/__init__.cpython-310.pyc +0 -0
  72. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_api_field_type.cpython-310.pyc +0 -0
  73. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_dropdown.cpython-310.pyc +0 -0
  74. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_entity_type.cpython-310.pyc +0 -0
  75. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_enum.cpython-310.pyc +0 -0
  76. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_field_api_type.cpython-310.pyc +0 -0
  77. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_field_type.cpython-310.pyc +0 -0
  78. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_folder_item_type.cpython-310.pyc +0 -0
  79. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_naming_strategy.cpython-310.pyc +0 -0
  80. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_report_level.cpython-310.pyc +0 -0
  81. liminal_orm-3.2.0/liminal/enums/__pycache__/benchling_sequence_type.cpython-310.pyc +0 -0
  82. liminal_orm-3.2.0/liminal/enums/__pycache__/name_template_part_type.cpython-310.pyc +0 -0
  83. liminal_orm-3.2.0/liminal/enums/__pycache__/sequence_constraint.cpython-310.pyc +0 -0
  84. liminal_orm-3.2.0/liminal/enums/__pycache__/sequence_constraints.cpython-310.pyc +0 -0
  85. liminal_orm-3.2.0/liminal/enums/__pycache__/str_enum.cpython-310.pyc +0 -0
  86. liminal_orm-3.2.0/liminal/enums/__pycache__/undefined.cpython-310.pyc +0 -0
  87. liminal_orm-3.2.0/liminal/external/__pycache__/__init__.cpython-310.pyc +0 -0
  88. liminal_orm-3.2.0/liminal/migrate/__pycache__/components.cpython-310.pyc +0 -0
  89. liminal_orm-3.2.0/liminal/migrate/__pycache__/revision.cpython-310.pyc +0 -0
  90. liminal_orm-3.2.0/liminal/migrate/__pycache__/revisions_timeline.cpython-310.pyc +0 -0
  91. liminal_orm-3.2.0/liminal/migrate/__pycache__/utils.cpython-310.pyc +0 -0
  92. liminal_orm-3.2.0/liminal/orm/__pycache__/__init__.cpython-310.pyc +0 -0
  93. liminal_orm-3.2.0/liminal/orm/__pycache__/base.cpython-310.pyc +0 -0
  94. liminal_orm-3.2.0/liminal/orm/__pycache__/base_model.cpython-310.pyc +0 -0
  95. liminal_orm-3.2.0/liminal/orm/__pycache__/base_results_schema_model.cpython-310.pyc +0 -0
  96. liminal_orm-3.2.0/liminal/orm/__pycache__/column.cpython-310.pyc +0 -0
  97. liminal_orm-3.2.0/liminal/orm/__pycache__/mixins.cpython-310.pyc +0 -0
  98. liminal_orm-3.2.0/liminal/orm/__pycache__/name_template.cpython-310.pyc +0 -0
  99. liminal_orm-3.2.0/liminal/orm/__pycache__/name_template_parts.cpython-310.pyc +0 -0
  100. liminal_orm-3.2.0/liminal/orm/__pycache__/relationship.cpython-310.pyc +0 -0
  101. liminal_orm-3.2.0/liminal/orm/__pycache__/results_schema_properties.cpython-310.pyc +0 -0
  102. liminal_orm-3.2.0/liminal/orm/__pycache__/schema_properties.cpython-310.pyc +0 -0
  103. liminal_orm-3.2.0/liminal/orm/__pycache__/user.cpython-310.pyc +0 -0
  104. liminal_orm-3.2.0/liminal/orm/base_tables/__pycache__/__init__.cpython-310.pyc +0 -0
  105. liminal_orm-3.2.0/liminal/orm/base_tables/__pycache__/registry_entity.cpython-310.pyc +0 -0
  106. liminal_orm-3.2.0/liminal/orm/base_tables/__pycache__/schema.cpython-310.pyc +0 -0
  107. liminal_orm-3.2.0/liminal/orm/base_tables/__pycache__/user.cpython-310.pyc +0 -0
  108. liminal_orm-3.2.0/liminal/results_schemas/__pycache__/generate_files.cpython-310.pyc +0 -0
  109. liminal_orm-3.2.0/liminal/results_schemas/__pycache__/utils.cpython-310.pyc +0 -0
  110. liminal_orm-3.2.0/liminal/results_schemas/models/__pycache__/results_schema_model.cpython-310.pyc +0 -0
  111. liminal_orm-3.2.0/liminal/tests/.DS_Store +0 -0
  112. liminal_orm-3.2.0/liminal/tests/__pycache__/__init__.cpython-310.pyc +0 -0
  113. liminal_orm-3.2.0/liminal/tests/__pycache__/conftest.cpython-310-pytest-8.3.3.pyc +0 -0
  114. liminal_orm-3.2.0/liminal/tests/__pycache__/test_dropdown_compare.cpython-310-pytest-8.3.3.pyc +0 -0
  115. liminal_orm-3.2.0/liminal/tests/__pycache__/test_entity_schema_compare.cpython-310-pytest-8.3.3.pyc +0 -0
  116. liminal_orm-3.2.0/liminal/unit_dictionary/__pycache__/utils.cpython-310.pyc +0 -0
  117. liminal_orm-3.2.0/liminal/validation/__pycache__/__init__.cpython-310.pyc +0 -0
  118. liminal_orm-3.2.0/liminal/validation/__pycache__/validation_report_level.cpython-310.pyc +0 -0
  119. liminal_orm-3.2.0/liminal/validation/__pycache__/validation_severity.cpython-310.pyc +0 -0
  120. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/LICENSE.md +0 -0
  121. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/README.md +0 -0
  122. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/__init__.py +0 -0
  123. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/base/base_dropdown.py +0 -0
  124. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/base/base_operation.py +0 -0
  125. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/base/base_validation_filters.py +0 -0
  126. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/base/compare_operation.py +0 -0
  127. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/base/properties/base_name_template.py +0 -0
  128. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/base/properties/base_schema_properties.py +0 -0
  129. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/base/str_enum.py +0 -0
  130. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/cli/live_test_entity_schema_migration.py +0 -0
  131. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/connection/__init__.py +0 -0
  132. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/connection/benchling_connection.py +0 -0
  133. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/connection/benchling_service.py +0 -0
  134. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/dropdowns/api.py +0 -0
  135. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/dropdowns/compare.py +0 -0
  136. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/dropdowns/operations.py +0 -0
  137. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/dropdowns/utils.py +0 -0
  138. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/entity_schemas/api.py +0 -0
  139. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/entity_schemas/entity_schema_models.py +0 -0
  140. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/entity_schemas/tag_schema_models.py +0 -0
  141. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/entity_schemas/utils.py +0 -0
  142. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/enums/__init__.py +0 -0
  143. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/enums/benchling_api_field_type.py +0 -0
  144. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/enums/benchling_entity_type.py +0 -0
  145. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/enums/benchling_field_type.py +0 -0
  146. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/enums/benchling_folder_item_type.py +0 -0
  147. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/enums/benchling_naming_strategy.py +0 -0
  148. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/enums/benchling_sequence_type.py +0 -0
  149. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/enums/name_template_part_type.py +0 -0
  150. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/enums/sequence_constraint.py +0 -0
  151. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/external/__init__.py +0 -0
  152. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/mappers.py +0 -0
  153. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/migrate/components.py +0 -0
  154. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/migrate/revision.py +0 -0
  155. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/migrate/revisions_timeline.py +0 -0
  156. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/base.py +0 -0
  157. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/base_tables/registry_entity.py +0 -0
  158. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/base_tables/schema.py +0 -0
  159. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/base_tables/user.py +0 -0
  160. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/column.py +0 -0
  161. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/mixins.py +0 -0
  162. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/name_template.py +0 -0
  163. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/orm/name_template_parts.py +0 -0
  164. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/py.typed +0 -0
  165. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/tests/__init__.py +0 -0
  166. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/tests/conftest.py +0 -0
  167. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/tests/from benchling_sdk.py +0 -0
  168. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/tests/test_dropdown_compare.py +0 -0
  169. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/unit_dictionary/utils.py +0 -0
  170. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/utils.py +0 -0
  171. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/validation/__init__.py +0 -0
  172. {liminal_orm-3.2.0 → liminal_orm-4.0.0}/liminal/validation/validation_severity.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: liminal-orm
3
- Version: 3.2.0
3
+ Version: 4.0.0
4
4
  Summary: An ORM and toolkit that builds on top of Benchling's platform to keep your schemas and downstream code dependencies in sync.
5
5
  Home-page: https://github.com/dynotx/liminal-orm
6
6
  Author: DynoTx Open Source
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
- Requires-Dist: benchling-sdk (>=1.8.0)
14
+ Requires-Dist: benchling-sdk (>=1.21.2,<2.0.0)
15
15
  Requires-Dist: bs4 (>=0.0.2,<0.0.3)
16
16
  Requires-Dist: lxml (>=5.3.0,<6.0.0)
17
17
  Requires-Dist: numpy (>=1.23.5,<2.0.0)
@@ -3,6 +3,6 @@ import warnings
3
3
  from liminal.orm.name_template_parts import * # noqa: F403
4
4
 
5
5
  warnings.warn(
6
- "Importing from 'liminal.base.name_template_parts' is deprecated. Please import from 'liminal.orm.name_template_parts' instead. This will be removed in v4.",
6
+ "Importing from 'liminal.base.name_template_parts' is deprecated. Please import from 'liminal.orm.name_template_parts' instead. This will be removed in v5.",
7
7
  FutureWarning,
8
8
  )
@@ -64,12 +64,31 @@ class BaseFieldProperties(BaseModel):
64
64
  self.warehouse_name = wh_name
65
65
  return self
66
66
 
67
+ def unset_tooltip(self) -> BaseFieldProperties:
68
+ if "tooltip" in self.__pydantic_fields_set__:
69
+ self.__pydantic_fields_set__.remove("tooltip")
70
+ return self
71
+
67
72
  def validate_column_definition(self, wh_name: str) -> bool:
68
73
  """If the Field Properties are meant to represent a column in Benchling,
69
74
  this will validate the properties and ensure that the entity_link and dropdowns are valid names that exist in our code.
70
75
  """
71
76
  return True
72
77
 
78
+ def column_dump(self) -> dict[str, Any]:
79
+ """This function returns a model dump or dictionary of the field properties.
80
+ However, it removes defaults based on the init in the Column class."""
81
+ column_props = self.model_dump(exclude_unset=True, exclude_none=True)
82
+ to_pop = []
83
+ for k, v in column_props.items():
84
+ if k == "is_multi" and v is False:
85
+ to_pop.append(k)
86
+ elif k == "parent_link" and v is False:
87
+ to_pop.append(k)
88
+ for k in to_pop:
89
+ column_props.pop(k)
90
+ return column_props
91
+
73
92
  def merge(self, new_props: BaseFieldProperties) -> dict[str, Any]:
74
93
  """Returns a diff of the two given Benchling FieldProperties as a dictionary.
75
94
  If the field is different, set it as the new field.
@@ -83,7 +83,7 @@ connection = BenchlingConnection(
83
83
 
84
84
  @app.command(
85
85
  name="generate-files",
86
- help="Generates the dropdown and entity schema files from your Benchling tenant and writes to the given path.",
86
+ help="Generates the dropdown, entity schema, and results schema files from your Benchling tenant and writes to the given path. By default, this will overwrite any existing files within the {write_path}/dropdowns/, {write_path}/entity_schemas/, and {write_path}/results_schemas/ directories.",
87
87
  )
88
88
  def generate_files(
89
89
  benchling_tenant: str = typer.Argument(
@@ -95,13 +95,48 @@ def generate_files(
95
95
  "--write-path",
96
96
  help="The path to write the generated files to.",
97
97
  ),
98
+ entity_schemas_flag: bool = typer.Option(
99
+ False,
100
+ "-es",
101
+ "--entity-schemas",
102
+ help="Generate entity schema files.",
103
+ ),
104
+ dropdowns_flag: bool = typer.Option(
105
+ False,
106
+ "-d",
107
+ "--dropdowns",
108
+ help="Generate dropdown files.",
109
+ ),
110
+ results_schemas_flag: bool = typer.Option(
111
+ False,
112
+ "-rs",
113
+ "--results-schemas",
114
+ help="Generate results schema files.",
115
+ ),
116
+ overwrite: bool = typer.Option(
117
+ False,
118
+ "-o",
119
+ "--overwrite",
120
+ help="Overwrite existing files within the {write_path}/dropdowns/, {write_path}/entity_schemas/, and {write_path}/results_schemas/ directories.",
121
+ ),
98
122
  ) -> None:
99
123
  _, benchling_connection = read_local_liminal_dir(LIMINAL_DIR_PATH, benchling_tenant)
100
124
  benchling_service = BenchlingService(benchling_connection, use_internal_api=True)
101
125
  if not write_path.exists():
102
126
  write_path.mkdir()
103
127
  print(f"[green]Created directory: {write_path}")
104
- generate_all_files(benchling_service, Path(write_path))
128
+ if not entity_schemas_flag and not dropdowns_flag and not results_schemas_flag:
129
+ entity_schemas_flag = True
130
+ dropdowns_flag = True
131
+ results_schemas_flag = True
132
+ generate_all_files(
133
+ benchling_service,
134
+ Path(write_path),
135
+ entity_schemas_flag,
136
+ dropdowns_flag,
137
+ results_schemas_flag,
138
+ overwrite,
139
+ )
105
140
 
106
141
 
107
142
  @app.command(
@@ -121,7 +156,7 @@ def current(
121
156
  remote_revision_id = benchling_service.get_remote_revision_id()
122
157
  if current_revision_id is not None:
123
158
  warnings.warn(
124
- f"Accessing and using the revision_id variable in {LIMINAL_DIR_PATH/'env.py'} is deprecated. Delete the variable set in the env.py file, the revision_id is now stored in your Benchling tenant within the '_liminal_remote' schema. Support for reading/writing the local revision_id will end with the v4 release.",
159
+ f"Accessing and using the revision_id variable in {LIMINAL_DIR_PATH/'env.py'} is deprecated. Delete the variable set in the env.py file, the revision_id is now stored in your Benchling tenant within the '_liminal_remote' schema. Support for reading/writing the local revision_id will end with the v5 release.",
125
160
  FutureWarning,
126
161
  )
127
162
  current_revision_id = remote_revision_id
@@ -165,7 +200,7 @@ def revision(
165
200
  remote_revision_id = benchling_service.get_remote_revision_id()
166
201
  if current_revision_id is not None:
167
202
  warnings.warn(
168
- f"Accessing and using the revision_id variable in {LIMINAL_DIR_PATH/'env.py'} is deprecated. Delete the variable set in the env.py file, the revision_id is now stored in your Benchling tenant within the '_liminal_remote' schema. Support for reading/writing the local revision_id will end with the v4 release.",
203
+ f"Accessing and using the revision_id variable in {LIMINAL_DIR_PATH/'env.py'} is deprecated. Delete the variable set in the env.py file, the revision_id is now stored in your Benchling tenant within the '_liminal_remote' schema. Support for reading/writing the local revision_id will end with the v5 release.",
169
204
  FutureWarning,
170
205
  )
171
206
  current_revision_id = remote_revision_id
@@ -194,7 +229,7 @@ def autogenerate(
194
229
  ),
195
230
  ) -> None:
196
231
  raise DeprecationWarning(
197
- "CLI command `liminal autogenerate ...` is deprecated and will be removed in v4. Please use `liminal revision ...` instead."
232
+ "CLI command `liminal autogenerate ...` is deprecated and will be removed in v5. Please use `liminal revision ...` instead."
198
233
  )
199
234
 
200
235
 
@@ -221,7 +256,7 @@ def upgrade(
221
256
  remote_revision_id = benchling_service.get_remote_revision_id()
222
257
  if current_revision_id is not None:
223
258
  warnings.warn(
224
- f"Accessing and using the revision_id variable in {LIMINAL_DIR_PATH/'env.py'} is deprecated. Delete the variable set in the env.py file, the revision_id is now stored in your Benchling tenant within the '_liminal_remote' schema. Support for reading/writing the local revision_id will end with the v4 release.",
259
+ f"Accessing and using the revision_id variable in {LIMINAL_DIR_PATH/'env.py'} is deprecated. Delete the variable set in the env.py file, the revision_id is now stored in your Benchling tenant within the '_liminal_remote' schema. Support for reading/writing the local revision_id will end with the v5 release.",
225
260
  FutureWarning,
226
261
  )
227
262
  current_revision_id = remote_revision_id
@@ -265,7 +300,7 @@ def downgrade(
265
300
  remote_revision_id = benchling_service.get_remote_revision_id()
266
301
  if current_revision_id is not None:
267
302
  warnings.warn(
268
- f"Accessing and using the revision_id variable in {LIMINAL_DIR_PATH/'env.py'} is deprecated. Delete the variable set in the env.py file, the revision_id is now stored in your Benchling tenant within the '_liminal_remote' schema. Support for reading/writing the local revision_id will end with the v4 release.",
303
+ f"Accessing and using the revision_id variable in {LIMINAL_DIR_PATH/'env.py'} is deprecated. Delete the variable set in the env.py file, the revision_id is now stored in your Benchling tenant within the '_liminal_remote' schema. Support for reading/writing the local revision_id will end with the v5 release.",
269
304
  FutureWarning,
270
305
  )
271
306
  current_revision_id = remote_revision_id
@@ -7,12 +7,20 @@ from liminal.dropdowns.generate_files import generate_all_dropdown_files
7
7
  from liminal.entity_schemas.generate_files import generate_all_entity_schema_files
8
8
  from liminal.migrate.components import execute_operations, get_full_migration_operations
9
9
  from liminal.migrate.revisions_timeline import RevisionsTimeline
10
+ from liminal.results_schemas.generate_files import generate_all_results_schema_files
10
11
 
11
12
 
12
- def generate_all_files(benchling_service: BenchlingService, write_path: Path) -> None:
13
- """Initializes all the dropdown and entity schema files from your Benchling tenant and writes to the given path.
14
- Creates and writes to the dropdowns/ and entity_schemas/ directories.
15
- Note: This will overwrite any existing dropdowns or entity schemas that exist in the given path.
13
+ def generate_all_files(
14
+ benchling_service: BenchlingService,
15
+ write_path: Path,
16
+ entity_schemas_flag: bool = True,
17
+ dropdowns_flag: bool = True,
18
+ results_schemas_flag: bool = True,
19
+ overwrite: bool = False,
20
+ ) -> None:
21
+ """Initializes all the dropdown, entity schema, and results schema files from your Benchling tenant and writes to the given path.
22
+ Creates and writes to the dropdowns/, entity_schemas/, and results_schemas/ directories.
23
+ Note: By default, this will overwrite any existing dropdowns, entity schemas, or results schemas that exist in the given path.
16
24
 
17
25
  Parameters
18
26
  ----------
@@ -20,9 +28,21 @@ def generate_all_files(benchling_service: BenchlingService, write_path: Path) ->
20
28
  The Benchling service object that is connected to a specified Benchling tenant.
21
29
  write_path : Path
22
30
  The path to write the generated files to.
31
+ entity_schemas_flag : bool
32
+ Whether to generate the entity schema files in the entity_schemas/ directory.
33
+ dropdowns_flag : bool
34
+ Whether to generate the dropdown files in the dropdowns/ directory.
35
+ results_schemas_flag : bool
36
+ Whether to generate the results schema files in the results_schemas/ directory.
37
+ overwrite : bool
38
+ Whether to overwrite existing the existing write_path directory.
23
39
  """
24
- generate_all_dropdown_files(benchling_service, write_path)
25
- generate_all_entity_schema_files(benchling_service, write_path)
40
+ if dropdowns_flag:
41
+ generate_all_dropdown_files(benchling_service, write_path, overwrite)
42
+ if entity_schemas_flag:
43
+ generate_all_entity_schema_files(benchling_service, write_path, overwrite)
44
+ if results_schemas_flag:
45
+ generate_all_results_schema_files(benchling_service, write_path, overwrite)
26
46
 
27
47
 
28
48
  def autogenerate_revision_file(
@@ -29,9 +29,11 @@ def mock_dropdown_full_migration(
29
29
  dropdown_options = ["Option 1", "Option 2"]
30
30
  create_dropdown_op = CreateDropdown(test_dropdown_name, dropdown_options)
31
31
 
32
- test_dropdown_name = f"{test_dropdown_name}_arch"
32
+ update_dropdown_op = UpdateDropdownName(
33
+ test_dropdown_name, f"{test_dropdown_name}_arch"
34
+ )
33
35
 
34
- update_dropdown_op = UpdateDropdownName(test_dropdown_name, test_dropdown_name)
36
+ test_dropdown_name = f"{test_dropdown_name}_arch"
35
37
 
36
38
  create_dropdown_option_op = CreateDropdownOption(
37
39
  test_dropdown_name, "Option 3 New", 2
@@ -81,7 +81,7 @@ def _read_local_env_file(
81
81
  def update_env_revision_id(
82
82
  env_file_path: Path, benchling_env: str, revision_id: str
83
83
  ) -> None:
84
- """Updates the CURRENT_REVISION_ID variable in the env.py file to the given revision id. REMOVE WITH v4 release."""
84
+ """Updates the CURRENT_REVISION_ID variable in the env.py file to the given revision id. REMOVE WITH v5 release."""
85
85
  env_file_content = env_file_path.read_text().split("\n")
86
86
  for i, line in enumerate(env_file_content):
87
87
  if f"{benchling_env}_CURRENT_REVISION_ID =" in line:
@@ -1,3 +1,4 @@
1
+ import shutil
1
2
  from pathlib import Path
2
3
 
3
4
  from rich import print
@@ -8,13 +9,24 @@ from liminal.utils import to_pascal_case, to_snake_case
8
9
 
9
10
 
10
11
  def generate_all_dropdown_files(
11
- benchling_service: BenchlingService, write_path: Path
12
+ benchling_service: BenchlingService, write_path: Path, overwrite: bool = False
12
13
  ) -> None:
13
14
  """Generate all dropdown files from your Benchling tenant and writes to the given dropdowns/ path.
14
15
  This is used to initialize your code for Liminal and transfer the information from your Benchling tenant to your local codebase.
15
- Note: This will overwrite any existing dropdowns that exist in the given path.
16
+
17
+ Parameters
18
+ ----------
19
+ benchling_service : BenchlingService
20
+ The Benchling service object that is connected to a specified Benchling tenant.
21
+ write_path : Path
22
+ The path to write the generated files to. dropdowns/ directory will be created within this path.
23
+ overwrite : bool
24
+ Whether to overwrite existing the existing dropdowns/ directory.
16
25
  """
17
26
  write_path = write_path / "dropdowns"
27
+ if write_path.exists() and overwrite:
28
+ shutil.rmtree(write_path)
29
+ print(f"[dim]Removed directory: {write_path}")
18
30
  if not write_path.exists():
19
31
  write_path.mkdir(parents=True, exist_ok=True)
20
32
  print(f"[green]Created directory: {write_path}")
@@ -272,10 +272,16 @@ def compare_entity_schemas(
272
272
  # Benchling api does not allow for setting a custom warehouse_name,
273
273
  # so we need to run another UpdateEntitySchema to set the warehouse_name if it is different from the snakecase version of the model name.
274
274
  else:
275
- model_props = [
275
+ field_props = [
276
276
  col.properties.set_warehouse_name(wh_name)
277
277
  for wh_name, col in model_columns.items()
278
278
  ]
279
+ tooltips_to_update = {
280
+ p.warehouse_name: p.tooltip
281
+ for p in field_props
282
+ if (p.tooltip and p.warehouse_name)
283
+ }
284
+ field_props = [f.unset_tooltip() for f in field_props]
279
285
  template_based_naming_strategies = {
280
286
  s
281
287
  for s in model.__schema_properties__.naming_strategies
@@ -286,19 +292,61 @@ def compare_entity_schemas(
286
292
  for s in model.__schema_properties__.naming_strategies
287
293
  if not BenchlingNamingStrategy.is_template_based(s)
288
294
  }
295
+ model_wh_name = model.__schema_properties__.warehouse_name
296
+ benchling_given_wh_name = to_snake_case(model.__schema_properties__.name)
289
297
  ops.append(
290
298
  CompareOperation(
291
299
  op=CreateEntitySchema(
292
300
  BaseSchemaProperties(
293
- **model.__schema_properties__.model_dump()
301
+ **model.__schema_properties__.set_warehouse_name(
302
+ benchling_given_wh_name
303
+ ).model_dump()
294
304
  ),
295
- fields=model_props,
296
- ),
297
- reverse_op=ArchiveEntitySchema(
298
- model.__schema_properties__.warehouse_name
305
+ fields=field_props,
299
306
  ),
307
+ reverse_op=ArchiveEntitySchema(benchling_given_wh_name),
300
308
  )
301
309
  )
310
+ new_schema_props = BaseSchemaProperties()
311
+ rollback_schema_props = BaseSchemaProperties()
312
+ if model_wh_name != benchling_given_wh_name:
313
+ new_schema_props.warehouse_name = model_wh_name
314
+ rollback_schema_props.warehouse_name = benchling_given_wh_name
315
+ if template_based_naming_strategies:
316
+ new_schema_props.naming_strategies = template_based_naming_strategies
317
+ rollback_schema_props.naming_strategies = (
318
+ model.__schema_properties__.naming_strategies
319
+ )
320
+ if new_schema_props.model_dump(exclude_unset=True) != {}:
321
+ ops.append(
322
+ CompareOperation(
323
+ op=UpdateEntitySchema(
324
+ benchling_given_wh_name,
325
+ new_schema_props,
326
+ ),
327
+ reverse_op=UpdateEntitySchema(
328
+ model_wh_name,
329
+ rollback_schema_props,
330
+ ),
331
+ )
332
+ )
333
+ # Benchling api also does not allow for setting of field tooltips
334
+ # so we need to run another UpdateEntitySchemaField to set the tooltip after the schema is created
335
+ for wh_field_name, tooltip_value in tooltips_to_update.items():
336
+ ops.append(
337
+ CompareOperation(
338
+ op=UpdateEntitySchemaField(
339
+ model_wh_name,
340
+ wh_field_name,
341
+ BaseFieldProperties(tooltip=tooltip_value),
342
+ ),
343
+ reverse_op=UpdateEntitySchemaField(
344
+ model_wh_name,
345
+ wh_field_name,
346
+ BaseFieldProperties(tooltip=None),
347
+ ),
348
+ )
349
+ )
302
350
  benchling_given_name_template = BaseNameTemplate(
303
351
  parts=[], order_name_parts_by_sequence=False
304
352
  )
@@ -306,7 +354,7 @@ def compare_entity_schemas(
306
354
  ops.append(
307
355
  CompareOperation(
308
356
  op=UpdateEntitySchemaNameTemplate(
309
- model.__schema_properties__.warehouse_name,
357
+ model_wh_name,
310
358
  BaseNameTemplate(
311
359
  **benchling_given_name_template.merge(
312
360
  model.__name_template__
@@ -314,7 +362,7 @@ def compare_entity_schemas(
314
362
  ),
315
363
  ),
316
364
  reverse_op=UpdateEntitySchemaNameTemplate(
317
- model.__schema_properties__.warehouse_name,
365
+ model_wh_name,
318
366
  BaseNameTemplate(
319
367
  **model.__name_template__.merge(
320
368
  benchling_given_name_template
@@ -323,32 +371,6 @@ def compare_entity_schemas(
323
371
  ),
324
372
  )
325
373
  )
326
- benchling_given_wh_name = to_snake_case(model.__schema_properties__.name)
327
- new_schema_props = BaseSchemaProperties()
328
- rollback_schema_props = BaseSchemaProperties()
329
- if model.__schema_properties__.warehouse_name != benchling_given_wh_name:
330
- new_schema_props.warehouse_name = (
331
- model.__schema_properties__.warehouse_name
332
- )
333
- rollback_schema_props.warehouse_name = benchling_given_wh_name
334
- if template_based_naming_strategies:
335
- new_schema_props.naming_strategies = template_based_naming_strategies
336
- rollback_schema_props.naming_strategies = (
337
- model.__schema_properties__.naming_strategies
338
- )
339
- if new_schema_props.model_dump(exclude_unset=True) != {}:
340
- ops.append(
341
- CompareOperation(
342
- op=UpdateEntitySchema(
343
- benchling_given_wh_name,
344
- new_schema_props,
345
- ),
346
- reverse_op=UpdateEntitySchema(
347
- model.__schema_properties__.warehouse_name,
348
- rollback_schema_props,
349
- ),
350
- )
351
- )
352
374
 
353
375
  model_operations[model.__schema_properties__.warehouse_name] = ops
354
376
  running_benchling_schema_names = [
@@ -1,7 +1,9 @@
1
+ import shutil
1
2
  from pathlib import Path
2
3
 
3
4
  from rich import print
4
5
 
6
+ from liminal.base.base_dropdown import BaseDropdown
5
7
  from liminal.connection.benchling_service import BenchlingService
6
8
  from liminal.dropdowns.utils import get_benchling_dropdowns_dict
7
9
  from liminal.entity_schemas.utils import get_converted_tag_schemas
@@ -45,23 +47,38 @@ def get_file_subdirectory(entity_type: BenchlingEntityType) -> str:
45
47
  return type_to_subdir_map[entity_type]
46
48
 
47
49
 
50
+ TAB = " "
51
+
52
+
48
53
  def generate_all_entity_schema_files(
49
- benchling_service: BenchlingService, write_path: Path
54
+ benchling_service: BenchlingService, write_path: Path, overwrite: bool = False
50
55
  ) -> None:
56
+ """Generate all entity schema files from your Benchling tenant and writes to the given entity_schemas/ path.
57
+ This is used to initialize your code for Liminal and transfer the information from your Benchling tenant to your local codebase.
58
+
59
+ Parameters
60
+ ----------
61
+ benchling_service : BenchlingService
62
+ The Benchling service object that is connected to a specified Benchling tenant.
63
+ write_path : Path
64
+ The path to write the generated files to. entity_schemas/ directory will be created within this path.
65
+ overwrite : bool
66
+ Whether to overwrite existing the existing entity_schemas/ directory.
67
+ """
51
68
  write_path = write_path / "entity_schemas"
69
+ if write_path.exists() and overwrite:
70
+ shutil.rmtree(write_path)
71
+ print(f"[dim]Removed directory: {write_path}")
52
72
  if not write_path.exists():
53
73
  write_path.mkdir(parents=True, exist_ok=True)
54
74
  print(f"[green]Created directory: {write_path}")
55
75
 
56
76
  models = get_converted_tag_schemas(benchling_service)
57
- tab = " "
58
77
  has_date = False
59
78
  subdirectory_map: dict[str, list[tuple[str, str]]] = {}
60
- benchling_dropdowns = get_benchling_dropdowns_dict(benchling_service)
61
- dropdown_name_to_classname_map: dict[str, str] = {
62
- dropdown_name: to_pascal_case(dropdown_name)
63
- for dropdown_name in benchling_dropdowns.keys()
64
- }
79
+ dropdown_name_to_classname_map = _get_dropdown_name_to_classname_map(
80
+ benchling_service
81
+ )
65
82
  wh_name_to_classname: dict[str, str] = {
66
83
  sp.warehouse_name: to_pascal_case(sp.name) for sp, _, _ in models
67
84
  }
@@ -81,21 +98,28 @@ def generate_all_entity_schema_files(
81
98
  "from liminal.enums import BenchlingEntityType, BenchlingFieldType, BenchlingNamingStrategy",
82
99
  f"from liminal.orm.mixins import {get_entity_mixin(schema_properties.entity_type)}",
83
100
  ]
84
- init_strings = [f"{tab}def __init__(", f"{tab}self,"]
101
+ init_strings = [f"{TAB}def __init__(", f"{TAB}self,"]
85
102
  column_strings = []
86
103
  dropdowns = []
87
104
  relationship_strings = []
88
105
  for col_name, col in columns.items():
106
+ column_props = col.column_dump()
89
107
  dropdown_classname = None
90
108
  if col.dropdown_link:
91
109
  dropdown_classname = dropdown_name_to_classname_map[col.dropdown_link]
92
110
  dropdowns.append(dropdown_classname)
93
- column_strings.append(
94
- f"""{tab}{col_name}: SqlColumn = Column(name="{col.name}", type={str(col.type)}, required={col.required}{', is_multi=True' if col.is_multi else ''}{', parent_link=True' if col.parent_link else ''}{f', entity_link="{col.entity_link}"' if col.entity_link else ''}{f', dropdown={dropdown_classname}' if dropdown_classname else ''}{f', tooltip="{col.tooltip}"' if col.tooltip else ''}{f', unit_name="{col.unit_name}"' if col.unit_name else ''}{f', decimal_places={col.decimal_places}' if col.decimal_places is not None else ''})"""
95
- )
111
+ column_props["dropdown_link"] = dropdown_classname
112
+ column_props_string = ""
113
+ for k, v in column_props.items():
114
+ if k == "dropdown_link":
115
+ column_props_string += f"""dropdown={v},"""
116
+ else:
117
+ column_props_string += f"""{k}={v.__repr__()},"""
118
+ column_string = f"""{TAB}{col_name}: SqlColumn = Column({column_props_string.rstrip(',')})"""
119
+ column_strings.append(column_string)
96
120
  if col.required and col.type:
97
121
  init_strings.append(
98
- f"""{tab}{col_name}: {convert_benchling_type_to_python_type(col.type).__name__},"""
122
+ f"""{TAB}{col_name}: {convert_benchling_type_to_python_type(col.type).__name__},"""
99
123
  )
100
124
 
101
125
  if (
@@ -110,14 +134,14 @@ def generate_all_entity_schema_files(
110
134
  ):
111
135
  if not col.is_multi:
112
136
  relationship_strings.append(
113
- f"""{tab}{col_name}_entity = single_relationship("{wh_name_to_classname[col.entity_link]}", {col_name})"""
137
+ f"""{TAB}{col_name}_entity = single_relationship("{wh_name_to_classname[col.entity_link]}", {col_name})"""
114
138
  )
115
139
  import_strings.append(
116
140
  "from liminal.orm.relationship import single_relationship"
117
141
  )
118
142
  else:
119
143
  relationship_strings.append(
120
- f"""{tab}{col_name}_entities = multi_relationship("{wh_name_to_classname[col.entity_link]}", {col_name})"""
144
+ f"""{TAB}{col_name}_entities = multi_relationship("{wh_name_to_classname[col.entity_link]}", {col_name})"""
121
145
  )
122
146
  import_strings.append(
123
147
  "from liminal.orm.relationship import multi_relationship"
@@ -125,11 +149,11 @@ def generate_all_entity_schema_files(
125
149
  for col_name, col in columns.items():
126
150
  if not col.required and col.type:
127
151
  init_strings.append(
128
- f"""{tab}{col_name}: {convert_benchling_type_to_python_type(col.type).__name__} | None = None,"""
152
+ f"""{TAB}{col_name}: {convert_benchling_type_to_python_type(col.type).__name__} | None = None,"""
129
153
  )
130
154
  init_strings.append("):")
131
155
  for col_name in columns.keys():
132
- init_strings.append(f"{tab}self.{col_name} = {col_name}")
156
+ init_strings.append(f"{TAB}self.{col_name} = {col_name}")
133
157
  if len(dropdowns) > 0:
134
158
  import_strings.append(f"from ...dropdowns import {', '.join(dropdowns)}")
135
159
  if name_template != NameTemplate():
@@ -141,7 +165,7 @@ def generate_all_entity_schema_files(
141
165
  for col_name, col in columns.items():
142
166
  if col.dropdown_link:
143
167
  init_strings.append(
144
- tab
168
+ TAB
145
169
  + dropdown_name_to_classname_map[col.dropdown_link]
146
170
  + f".validate({col_name})"
147
171
  )
@@ -149,7 +173,7 @@ def generate_all_entity_schema_files(
149
173
  columns_string = "\n".join(column_strings)
150
174
  relationship_string = "\n".join(relationship_strings)
151
175
  import_string = "\n".join(list(set(import_strings)))
152
- init_string = f"\n{tab}".join(init_strings) if len(columns) > 0 else ""
176
+ init_string = f"\n{TAB}".join(init_strings) if len(columns) > 0 else ""
153
177
  full_content = f"""{import_string}
154
178
 
155
179
 
@@ -198,3 +222,23 @@ class {classname}(BaseModel, {get_entity_mixin(schema_properties.entity_type)}):
198
222
  print(
199
223
  f"[green]Generated {write_path / '__init__.py'} with {len(models)} entity schema imports."
200
224
  )
225
+
226
+
227
+ def _get_dropdown_name_to_classname_map(
228
+ benchling_service: BenchlingService,
229
+ ) -> dict[str, str]:
230
+ """Gets the dropdown name to classname map.
231
+ If there are dropdowns imported, use BenchlingDropdown.get_all_subclasses()
232
+ Otherwise, it will query for Benchling dropdowns and use those.
233
+ """
234
+ if len(BaseDropdown.get_all_subclasses()) > 0:
235
+ return {
236
+ dropdown.__benchling_name__: dropdown.__name__
237
+ for dropdown in BaseDropdown.get_all_subclasses()
238
+ }
239
+ benchling_dropdowns = get_benchling_dropdowns_dict(benchling_service)
240
+ if len(benchling_dropdowns) > 0:
241
+ raise Exception(
242
+ "No dropdowns found locally. Please ensure your env.py file imports your dropdown classes or generate dropdowns from your Benchling tenant first."
243
+ )
244
+ return {}
@@ -536,9 +536,12 @@ class UpdateEntitySchemaField(BaseOperation):
536
536
  return f"{self.wh_schema_name}: Entity schema field '{self.wh_field_name}' in Benchling is different than in code: {str(self.update_props)}."
537
537
 
538
538
  def validate(self, benchling_service: BenchlingService) -> None:
539
- tag_schema = TagSchemaModel.get_one_cached(
540
- benchling_service, self.wh_schema_name
541
- )
539
+ try:
540
+ tag_schema = TagSchemaModel.get_one_cached(
541
+ benchling_service, self.wh_schema_name
542
+ )
543
+ except Exception:
544
+ return
542
545
  if (
543
546
  benchling_service.connection.config_flags.schemas_enable_change_warehouse_name
544
547
  is False
@@ -306,8 +306,8 @@ class BaseModel(Generic[T], Base):
306
306
 
307
307
  Returns
308
308
  -------
309
- list[T]
310
- List of all entities from the database.
309
+ DataFrame
310
+ A pandas dataframe of all entities from the database.
311
311
  """
312
312
  query = cls.query(session)
313
313
  return pd.read_sql(query.statement, session.connection())