liminal-orm 2.0.3a1__tar.gz → 3.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 (161) hide show
  1. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/PKG-INFO +1 -1
  2. liminal_orm-3.0.0/liminal/.DS_Store +0 -0
  3. liminal_orm-3.0.0/liminal/base/__pycache__/__init__.cpython-310.pyc +0 -0
  4. liminal_orm-3.0.0/liminal/base/__pycache__/base.cpython-310.pyc +0 -0
  5. liminal_orm-3.0.0/liminal/base/__pycache__/base_dropdown.cpython-310.pyc +0 -0
  6. liminal_orm-3.0.0/liminal/base/__pycache__/base_operation.cpython-310.pyc +0 -0
  7. liminal_orm-3.0.0/liminal/base/__pycache__/base_validation_filters.cpython-310.pyc +0 -0
  8. liminal_orm-3.0.0/liminal/base/__pycache__/benchling_base_model.cpython-310.pyc +0 -0
  9. liminal_orm-3.0.0/liminal/base/__pycache__/benchling_column.cpython-310.pyc +0 -0
  10. liminal_orm-3.0.0/liminal/base/__pycache__/benchling_dropdown.cpython-310.pyc +0 -0
  11. liminal_orm-3.0.0/liminal/base/__pycache__/benchling_field_properties.cpython-310.pyc +0 -0
  12. liminal_orm-3.0.0/liminal/base/__pycache__/benchling_schema_properties.cpython-310.pyc +0 -0
  13. liminal_orm-3.0.0/liminal/base/__pycache__/callable_operation.cpython-310.pyc +0 -0
  14. liminal_orm-3.0.0/liminal/base/__pycache__/column.cpython-310.pyc +0 -0
  15. liminal_orm-3.0.0/liminal/base/__pycache__/compare_operation.cpython-310.pyc +0 -0
  16. liminal_orm-3.0.0/liminal/base/__pycache__/field_properties.cpython-310.pyc +0 -0
  17. liminal_orm-3.0.0/liminal/base/__pycache__/name_template_components.cpython-310.pyc +0 -0
  18. liminal_orm-3.0.0/liminal/base/__pycache__/name_template_parts.cpython-310.pyc +0 -0
  19. liminal_orm-3.0.0/liminal/base/__pycache__/schema_properties.cpython-310.pyc +0 -0
  20. liminal_orm-3.0.0/liminal/base/__pycache__/str_enum.cpython-310.pyc +0 -0
  21. liminal_orm-3.0.0/liminal/base/__pycache__/unset.cpython-310.pyc +0 -0
  22. liminal_orm-3.0.0/liminal/base/__pycache__/user.cpython-310.pyc +0 -0
  23. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/base/base_operation.py +7 -7
  24. liminal_orm-3.0.0/liminal/base/properties/__pycache__/NoDropdown.cpython-310.pyc +0 -0
  25. liminal_orm-3.0.0/liminal/base/properties/__pycache__/__init__.cpython-310.pyc +0 -0
  26. liminal_orm-3.0.0/liminal/base/properties/__pycache__/base_field_properties.cpython-310.pyc +0 -0
  27. liminal_orm-3.0.0/liminal/base/properties/__pycache__/base_name_template.cpython-310.pyc +0 -0
  28. liminal_orm-3.0.0/liminal/base/properties/__pycache__/base_schema_properties.cpython-310.pyc +0 -0
  29. liminal_orm-3.0.0/liminal/base/properties/__pycache__/field_properties.cpython-310.pyc +0 -0
  30. liminal_orm-3.0.0/liminal/base/properties/__pycache__/schema_properties.cpython-310.pyc +0 -0
  31. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/base/properties/base_field_properties.py +3 -1
  32. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/base/properties/base_schema_properties.py +8 -2
  33. liminal_orm-3.0.0/liminal/cli/__pycache__/cli.cpython-310.pyc +0 -0
  34. liminal_orm-3.0.0/liminal/cli/__pycache__/controller.cpython-310.pyc +0 -0
  35. liminal_orm-3.0.0/liminal/cli/__pycache__/live_test_dropdown_migration.cpython-310.pyc +0 -0
  36. liminal_orm-3.0.0/liminal/cli/__pycache__/live_test_entity_schema_migration.cpython-310.pyc +0 -0
  37. liminal_orm-3.0.0/liminal/connection/__pycache__/__init__.cpython-310.pyc +0 -0
  38. liminal_orm-3.0.0/liminal/connection/__pycache__/benchling_connection.cpython-310.pyc +0 -0
  39. liminal_orm-3.0.0/liminal/connection/__pycache__/benchling_service.cpython-310.pyc +0 -0
  40. liminal_orm-3.0.0/liminal/dropdowns/__pycache__/api.cpython-310.pyc +0 -0
  41. liminal_orm-3.0.0/liminal/dropdowns/__pycache__/compare.cpython-310.pyc +0 -0
  42. liminal_orm-3.0.0/liminal/dropdowns/__pycache__/generate_files.cpython-310.pyc +0 -0
  43. liminal_orm-3.0.0/liminal/dropdowns/__pycache__/operations.cpython-310.pyc +0 -0
  44. liminal_orm-3.0.0/liminal/dropdowns/__pycache__/utils.cpython-310.pyc +0 -0
  45. liminal_orm-3.0.0/liminal/entity_schemas/__pycache__/api.cpython-310.pyc +0 -0
  46. liminal_orm-3.0.0/liminal/entity_schemas/__pycache__/compare.cpython-310.pyc +0 -0
  47. liminal_orm-3.0.0/liminal/entity_schemas/__pycache__/entity_schema_models.cpython-310.pyc +0 -0
  48. liminal_orm-3.0.0/liminal/entity_schemas/__pycache__/generate_files.cpython-310.pyc +0 -0
  49. liminal_orm-3.0.0/liminal/entity_schemas/__pycache__/operations.cpython-310.pyc +0 -0
  50. liminal_orm-3.0.0/liminal/entity_schemas/__pycache__/schemas.cpython-310.pyc +0 -0
  51. liminal_orm-3.0.0/liminal/entity_schemas/__pycache__/tag_schema_models.cpython-310.pyc +0 -0
  52. liminal_orm-3.0.0/liminal/entity_schemas/__pycache__/utils.cpython-310.pyc +0 -0
  53. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/entity_schemas/compare.py +40 -21
  54. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/entity_schemas/entity_schema_models.py +40 -13
  55. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/entity_schemas/generate_files.py +2 -2
  56. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/entity_schemas/operations.py +61 -11
  57. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/entity_schemas/tag_schema_models.py +72 -25
  58. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/entity_schemas/utils.py +36 -9
  59. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/enums/__init__.py +1 -0
  60. liminal_orm-3.0.0/liminal/enums/__pycache__/__init__.cpython-310.pyc +0 -0
  61. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_api_field_type.cpython-310.pyc +0 -0
  62. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_dropdown.cpython-310.pyc +0 -0
  63. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_entity_type.cpython-310.pyc +0 -0
  64. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_enum.cpython-310.pyc +0 -0
  65. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_field_api_type.cpython-310.pyc +0 -0
  66. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_field_type.cpython-310.pyc +0 -0
  67. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_folder_item_type.cpython-310.pyc +0 -0
  68. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_naming_strategy.cpython-310.pyc +0 -0
  69. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_report_level.cpython-310.pyc +0 -0
  70. liminal_orm-3.0.0/liminal/enums/__pycache__/benchling_sequence_type.cpython-310.pyc +0 -0
  71. liminal_orm-3.0.0/liminal/enums/__pycache__/name_template_part_type.cpython-310.pyc +0 -0
  72. liminal_orm-3.0.0/liminal/enums/__pycache__/sequence_constraint.cpython-310.pyc +0 -0
  73. liminal_orm-3.0.0/liminal/enums/__pycache__/sequence_constraints.cpython-310.pyc +0 -0
  74. liminal_orm-3.0.0/liminal/enums/__pycache__/str_enum.cpython-310.pyc +0 -0
  75. liminal_orm-3.0.0/liminal/enums/__pycache__/undefined.cpython-310.pyc +0 -0
  76. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/enums/benchling_entity_type.py +9 -0
  77. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/enums/benchling_field_type.py +8 -0
  78. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/enums/benchling_naming_strategy.py +1 -0
  79. liminal_orm-3.0.0/liminal/enums/sequence_constraint.py +13 -0
  80. liminal_orm-3.0.0/liminal/external/__pycache__/__init__.cpython-310.pyc +0 -0
  81. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/mappers.py +8 -11
  82. liminal_orm-3.0.0/liminal/migrate/__pycache__/components.cpython-310.pyc +0 -0
  83. liminal_orm-3.0.0/liminal/migrate/__pycache__/revision.cpython-310.pyc +0 -0
  84. liminal_orm-3.0.0/liminal/migrate/__pycache__/revisions_timeline.cpython-310.pyc +0 -0
  85. liminal_orm-3.0.0/liminal/migrate/__pycache__/utils.cpython-310.pyc +0 -0
  86. liminal_orm-3.0.0/liminal/orm/__pycache__/__init__.cpython-310.pyc +0 -0
  87. liminal_orm-3.0.0/liminal/orm/__pycache__/base.cpython-310.pyc +0 -0
  88. liminal_orm-3.0.0/liminal/orm/__pycache__/base_model.cpython-310.pyc +0 -0
  89. liminal_orm-3.0.0/liminal/orm/__pycache__/column.cpython-310.pyc +0 -0
  90. liminal_orm-3.0.0/liminal/orm/__pycache__/mixins.cpython-310.pyc +0 -0
  91. liminal_orm-3.0.0/liminal/orm/__pycache__/name_template.cpython-310.pyc +0 -0
  92. liminal_orm-3.0.0/liminal/orm/__pycache__/name_template_parts.cpython-310.pyc +0 -0
  93. liminal_orm-3.0.0/liminal/orm/__pycache__/relationship.cpython-310.pyc +0 -0
  94. liminal_orm-3.0.0/liminal/orm/__pycache__/schema_properties.cpython-310.pyc +0 -0
  95. liminal_orm-3.0.0/liminal/orm/__pycache__/user.cpython-310.pyc +0 -0
  96. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/base_model.py +46 -11
  97. liminal_orm-3.0.0/liminal/orm/base_tables/__pycache__/__init__.cpython-310.pyc +0 -0
  98. liminal_orm-3.0.0/liminal/orm/base_tables/__pycache__/registry_entity.cpython-310.pyc +0 -0
  99. liminal_orm-3.0.0/liminal/orm/base_tables/__pycache__/schema.cpython-310.pyc +0 -0
  100. liminal_orm-3.0.0/liminal/orm/base_tables/__pycache__/user.cpython-310.pyc +0 -0
  101. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/column.py +23 -5
  102. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/schema_properties.py +14 -5
  103. liminal_orm-3.0.0/liminal/tests/.DS_Store +0 -0
  104. liminal_orm-3.0.0/liminal/tests/__pycache__/__init__.cpython-310.pyc +0 -0
  105. liminal_orm-3.0.0/liminal/tests/__pycache__/conftest.cpython-310-pytest-8.3.3.pyc +0 -0
  106. liminal_orm-3.0.0/liminal/tests/__pycache__/test_dropdown_compare.cpython-310-pytest-8.3.3.pyc +0 -0
  107. liminal_orm-3.0.0/liminal/tests/__pycache__/test_entity_schema_compare.cpython-310-pytest-8.3.3.pyc +0 -0
  108. liminal_orm-3.0.0/liminal/unit_dictionary/__pycache__/utils.cpython-310.pyc +0 -0
  109. liminal_orm-3.0.0/liminal/unit_dictionary/utils.py +48 -0
  110. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/validation/__init__.py +2 -9
  111. liminal_orm-3.0.0/liminal/validation/__pycache__/__init__.cpython-310.pyc +0 -0
  112. liminal_orm-3.0.0/liminal/validation/__pycache__/validation_report_level.cpython-310.pyc +0 -0
  113. liminal_orm-3.0.0/liminal/validation/__pycache__/validation_severity.cpython-310.pyc +0 -0
  114. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/pyproject.toml +1 -1
  115. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/LICENSE.md +0 -0
  116. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/README.md +0 -0
  117. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/__init__.py +0 -0
  118. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/base/base_dropdown.py +0 -0
  119. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/base/base_validation_filters.py +0 -0
  120. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/base/compare_operation.py +0 -0
  121. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/base/name_template_parts.py +0 -0
  122. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/base/properties/base_name_template.py +0 -0
  123. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/base/str_enum.py +0 -0
  124. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/cli/cli.py +0 -0
  125. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/cli/controller.py +0 -0
  126. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/cli/live_test_dropdown_migration.py +0 -0
  127. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/cli/live_test_entity_schema_migration.py +0 -0
  128. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/connection/__init__.py +0 -0
  129. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/connection/benchling_connection.py +0 -0
  130. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/connection/benchling_service.py +0 -0
  131. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/dropdowns/api.py +0 -0
  132. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/dropdowns/compare.py +0 -0
  133. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/dropdowns/generate_files.py +0 -0
  134. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/dropdowns/operations.py +0 -0
  135. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/dropdowns/utils.py +0 -0
  136. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/entity_schemas/api.py +0 -0
  137. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/enums/benchling_api_field_type.py +0 -0
  138. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/enums/benchling_folder_item_type.py +0 -0
  139. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/enums/benchling_sequence_type.py +0 -0
  140. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/enums/name_template_part_type.py +0 -0
  141. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/external/__init__.py +0 -0
  142. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/migrate/components.py +0 -0
  143. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/migrate/revision.py +0 -0
  144. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/migrate/revisions_timeline.py +0 -0
  145. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/migrate/utils.py +0 -0
  146. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/base.py +0 -0
  147. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/base_tables/registry_entity.py +0 -0
  148. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/base_tables/schema.py +0 -0
  149. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/base_tables/user.py +0 -0
  150. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/mixins.py +0 -0
  151. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/name_template.py +0 -0
  152. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/name_template_parts.py +0 -0
  153. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/orm/relationship.py +0 -0
  154. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/py.typed +0 -0
  155. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/tests/__init__.py +0 -0
  156. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/tests/conftest.py +0 -0
  157. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/tests/from benchling_sdk.py +0 -0
  158. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/tests/test_dropdown_compare.py +0 -0
  159. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/tests/test_entity_schema_compare.py +0 -0
  160. {liminal_orm-2.0.3a1 → liminal_orm-3.0.0}/liminal/utils.py +0 -0
  161. {liminal_orm-2.0.3a1 → liminal_orm-3.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: 2.0.3a1
3
+ Version: 3.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
Binary file
@@ -13,13 +13,13 @@ Order of operations based on order class var:
13
13
  6. ArchiveDropdownOption
14
14
  7. ReorderDropdownOptions
15
15
  8. CreateSchema
16
- 9. UpdateSchema
17
- 10. UnarchiveSchema
18
- 11. CreateField
19
- 12. UnarchiveField
20
- 13. UpdateField
21
- 14. ArchiveField
22
- 15. UpdateEntitySchemaNameTemplate
16
+ 9. UnarchiveSchema
17
+ 10. CreateField
18
+ 11. UpdateEntitySchemaNameTemplate
19
+ 12. UpdateSchema
20
+ 13. UnarchiveField
21
+ 14. UpdateField
22
+ 15. ArchiveField
23
23
  16. ReorderFields
24
24
  17. ArchiveSchema
25
25
  18. ArchiveDropdown
@@ -49,6 +49,8 @@ class BaseFieldProperties(BaseModel):
49
49
  dropdown_link: str | None = None
50
50
  entity_link: str | None = None
51
51
  tooltip: str | None = None
52
+ decimal_places: int | None = None
53
+ unit_name: str | None = None
52
54
  _archived: bool | None = PrivateAttr(default=None)
53
55
 
54
56
  model_config = ConfigDict(arbitrary_types_allowed=True)
@@ -122,4 +124,4 @@ class BaseFieldProperties(BaseModel):
122
124
 
123
125
  def __repr__(self) -> str:
124
126
  """Generates a string representation of the class so that it can be executed."""
125
- return f"{self.__class__.__name__}({', '.join([f'{k}={v.__repr__()}' for k, v in self.model_dump(exclude_unset=True, exclude_defaults=True).items()])})"
127
+ return f"{self.__class__.__name__}({', '.join([f'{k}={v.__repr__()}' for k, v in self.model_dump(exclude_unset=True).items()])})"
@@ -66,8 +66,13 @@ class BaseSchemaProperties(BaseModel):
66
66
  include_registry_id_in_chips : bool | None = None
67
67
  Flag for configuring the chip label for entities. Determines if the chip will include the Registry ID in the chip label.
68
68
  constraint_fields : set[str] | None
69
- Set of constraints for field values for the schema. Must be a set of column names that specify that their values must be a unique combination within an entity.
70
- If the entity type is a Sequence, "bases" can be a constraint field.
69
+ Set of constraints for field values for the schema. Must be a set of warehouse column names. This specifies that their entity field values must be a unique combination within an entity.
70
+ The following sequence constraints are also supported:
71
+ - bases: only supported for nucleotide sequence entity types. hasUniqueResidues=True
72
+ - amino_acids_ignore_case: only supported for amino acid sequence entity types. hasUniqueResidues=True
73
+ - amino_acids_exact_match: only supported for amino acid sequence entity types. hasUniqueResidues=True, areUniqueResiduesCaseSensitive=True
74
+ show_bases_in_expanded_view : bool | None
75
+ Whether the bases should be shown in the expanded view of the entity.
71
76
  _archived : bool | None
72
77
  Whether the schema is archived in Benchling.
73
78
  """
@@ -81,6 +86,7 @@ class BaseSchemaProperties(BaseModel):
81
86
  use_registry_id_as_label: bool | None = None
82
87
  include_registry_id_in_chips: bool | None = None
83
88
  constraint_fields: set[str] | None = None
89
+ show_bases_in_expanded_view: bool | None = None
84
90
  _archived: bool | None = None
85
91
 
86
92
  def __init__(self, **data: Any):
@@ -18,6 +18,7 @@ from liminal.entity_schemas.operations import (
18
18
  UpdateEntitySchemaNameTemplate,
19
19
  )
20
20
  from liminal.entity_schemas.utils import get_converted_tag_schemas
21
+ from liminal.enums.benchling_naming_strategy import BenchlingNamingStrategy
21
22
  from liminal.orm.base_model import BaseModel
22
23
  from liminal.orm.column import Column
23
24
  from liminal.utils import to_snake_case
@@ -275,6 +276,16 @@ def compare_entity_schemas(
275
276
  col.properties.set_warehouse_name(wh_name)
276
277
  for wh_name, col in model_columns.items()
277
278
  ]
279
+ template_based_naming_strategies = {
280
+ s
281
+ for s in model.__schema_properties__.naming_strategies
282
+ if BenchlingNamingStrategy.is_template_based(s)
283
+ }
284
+ model.__schema_properties__.naming_strategies = {
285
+ s
286
+ for s in model.__schema_properties__.naming_strategies
287
+ if not BenchlingNamingStrategy.is_template_based(s)
288
+ }
278
289
  ops.append(
279
290
  CompareOperation(
280
291
  op=CreateEntitySchema(
@@ -288,28 +299,10 @@ def compare_entity_schemas(
288
299
  ),
289
300
  )
290
301
  )
291
- benchling_given_wh_name = to_snake_case(model.__schema_properties__.name)
292
- if model.__schema_properties__.warehouse_name != benchling_given_wh_name:
293
- ops.append(
294
- CompareOperation(
295
- op=UpdateEntitySchema(
296
- benchling_given_wh_name,
297
- BaseSchemaProperties(
298
- warehouse_name=model.__schema_properties__.warehouse_name
299
- ),
300
- ),
301
- reverse_op=UpdateEntitySchema(
302
- model.__schema_properties__.warehouse_name,
303
- BaseSchemaProperties(
304
- warehouse_name=benchling_given_wh_name
305
- ),
306
- ),
307
- )
308
- )
309
302
  benchling_given_name_template = BaseNameTemplate(
310
303
  parts=[], order_name_parts_by_sequence=False
311
304
  )
312
- if benchling_name_template != model.__name_template__:
305
+ if benchling_given_name_template != model.__name_template__:
313
306
  ops.append(
314
307
  CompareOperation(
315
308
  op=UpdateEntitySchemaNameTemplate(
@@ -323,13 +316,39 @@ def compare_entity_schemas(
323
316
  reverse_op=UpdateEntitySchemaNameTemplate(
324
317
  model.__schema_properties__.warehouse_name,
325
318
  BaseNameTemplate(
326
- **benchling_given_name_template.merge(
327
- model.__name_template__
319
+ **model.__name_template__.merge(
320
+ benchling_given_name_template
328
321
  )
329
322
  ),
330
323
  ),
331
324
  )
332
325
  )
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
+ )
333
352
 
334
353
  model_operations[model.__schema_properties__.warehouse_name] = ops
335
354
  running_benchling_schema_names = [
@@ -9,8 +9,10 @@ from liminal.connection import BenchlingService
9
9
  from liminal.dropdowns.utils import get_benchling_dropdown_summary_by_name
10
10
  from liminal.entity_schemas.tag_schema_models import TagSchemaModel
11
11
  from liminal.enums import BenchlingEntityType
12
+ from liminal.enums.sequence_constraint import SequenceConstraint
12
13
  from liminal.mappers import convert_field_type_to_api_field_type
13
14
  from liminal.orm.schema_properties import MixtureSchemaConfig, SchemaProperties
15
+ from liminal.unit_dictionary.utils import get_unit_id_from_name
14
16
 
15
17
 
16
18
  class FieldLinkShortModel(BaseModel):
@@ -26,28 +28,44 @@ class EntitySchemaConstraint(BaseModel):
26
28
  """
27
29
 
28
30
  areUniqueResiduesCaseSensitive: bool | None = None
29
- fields: dict[str, Any] | None = None
30
- hasUniqueCanonicalSmilers: bool | None = None
31
+ fields: list[dict[str, Any]] | None = None
32
+ hasUniqueCanonicalSmiles: bool | None = None
31
33
  hasUniqueResidues: bool | None = None
32
34
 
33
35
  @classmethod
34
36
  def from_constraint_fields(
35
- cls, constraint_fields: set[str]
37
+ cls,
38
+ constraint_fields: set[str],
39
+ benchling_service: BenchlingService | None = None,
36
40
  ) -> EntitySchemaConstraint:
37
41
  """
38
42
  Generates a Constraint object from a set of constraint fields to create a constraint on a schema.
39
43
  """
40
- if constraint_fields is None:
41
- return None
42
44
  hasUniqueResidues = False
43
- if "bases" in constraint_fields:
44
- constraint_fields.discard("bases")
45
+ areUniqueResiduesCaseSensitive = False
46
+ if SequenceConstraint.BASES in constraint_fields:
47
+ constraint_fields.discard(SequenceConstraint.BASES)
48
+ hasUniqueResidues = True
49
+ elif SequenceConstraint.AMINO_ACIDS_IGNORE_CASE in constraint_fields:
50
+ constraint_fields.discard(SequenceConstraint.AMINO_ACIDS_IGNORE_CASE)
45
51
  hasUniqueResidues = True
52
+ elif SequenceConstraint.AMINO_ACIDS_EXACT_MATCH in constraint_fields:
53
+ constraint_fields.discard(SequenceConstraint.AMINO_ACIDS_EXACT_MATCH)
54
+ hasUniqueResidues = True
55
+ areUniqueResiduesCaseSensitive = True
56
+ fields = []
57
+ for field_name in constraint_fields:
58
+ if benchling_service is None:
59
+ raise ValueError(
60
+ "Benchling SDK must be provided to update constraint fields."
61
+ )
62
+ field = TagSchemaModel.get_one(benchling_service, field_name)
63
+ fields.append({"name": field.name, "id": field.id})
46
64
  return cls(
47
- fields=[{"name": f} for f in constraint_fields],
65
+ fields=fields,
48
66
  hasUniqueResidues=hasUniqueResidues,
49
- hasUniqueCanonicalSmilers=False,
50
- areUniqueResiduesCaseSensitive=False,
67
+ hasUniqueCanonicalSmiles=False,
68
+ areUniqueResiduesCaseSensitive=areUniqueResiduesCaseSensitive,
51
69
  )
52
70
 
53
71
 
@@ -63,6 +81,8 @@ class CreateEntitySchemaFieldModel(BaseModel):
63
81
  isParentLink: bool = False
64
82
  dropdownId: str | None = None
65
83
  link: FieldLinkShortModel | None = None
84
+ unitId: str | None = None
85
+ decimalPrecision: int | None = None
66
86
 
67
87
  @classmethod
68
88
  def from_benchling_props(
@@ -107,6 +127,11 @@ class CreateEntitySchemaFieldModel(BaseModel):
107
127
  dropdown_summary_id = get_benchling_dropdown_summary_by_name(
108
128
  benchling_service, field_props.dropdown_link
109
129
  ).id
130
+ unit_id = None
131
+ if field_props.unit_name is not None:
132
+ if benchling_service is None:
133
+ raise ValueError("Benchling SDK must be provided to update unit field.")
134
+ unit_id = get_unit_id_from_name(benchling_service, field_props.unit_name)
110
135
  return CreateEntitySchemaFieldModel(
111
136
  name=field_props.name,
112
137
  systemName=field_props.warehouse_name,
@@ -118,6 +143,8 @@ class CreateEntitySchemaFieldModel(BaseModel):
118
143
  link=FieldLinkShortModel(
119
144
  tagSchema=tag_schema, folderItemType=folder_item_type
120
145
  ),
146
+ unitId=unit_id,
147
+ decimalPrecision=field_props.decimal_places,
121
148
  )
122
149
 
123
150
 
@@ -135,6 +162,7 @@ class CreateEntitySchemaModel(BaseModel):
135
162
  useOrganizationCollectionAliasForDisplayLabel: bool | None = None
136
163
  labelingStrategies: list[str] | None = None
137
164
  constraint: EntitySchemaConstraint | None = None
165
+ showResidues: bool | None = None
138
166
 
139
167
  @classmethod
140
168
  def from_benchling_props(
@@ -168,11 +196,10 @@ class CreateEntitySchemaModel(BaseModel):
168
196
  includeRegistryIdInChips=benchling_props.include_registry_id_in_chips,
169
197
  useOrganizationCollectionAliasForDisplayLabel=benchling_props.use_registry_id_as_label,
170
198
  labelingStrategies=[s.value for s in benchling_props.naming_strategies],
199
+ showResidues=benchling_props.show_bases_in_expanded_view,
171
200
  constraint=EntitySchemaConstraint.from_constraint_fields(
172
201
  benchling_props.constraint_fields
173
- )
174
- if benchling_props.constraint_fields
175
- else None,
202
+ ),
176
203
  fields=[
177
204
  CreateEntitySchemaFieldModel.from_benchling_props(
178
205
  field_props, benchling_service
@@ -91,7 +91,7 @@ def generate_all_entity_schema_files(
91
91
  dropdown_classname = dropdown_name_to_classname_map[col.dropdown_link]
92
92
  dropdowns.append(dropdown_classname)
93
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 ''})"""
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
95
  )
96
96
  if col.required and col.type:
97
97
  init_strings.append(
@@ -105,7 +105,7 @@ def generate_all_entity_schema_files(
105
105
  if not has_date:
106
106
  import_strings.append("from datetime import datetime")
107
107
  if (
108
- col.type == BenchlingFieldType.ENTITY_LINK
108
+ col.type in BenchlingFieldType.get_entity_link_types()
109
109
  and col.entity_link is not None
110
110
  ):
111
111
  if not col.is_multi:
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  from typing import Any, ClassVar
2
3
 
3
4
  from liminal.base.base_operation import BaseOperation
@@ -25,8 +26,14 @@ from liminal.entity_schemas.utils import (
25
26
  )
26
27
  from liminal.enums import BenchlingNamingStrategy
27
28
  from liminal.orm.schema_properties import SchemaProperties
29
+ from liminal.unit_dictionary.utils import (
30
+ get_unit_id_to_name_map,
31
+ get_unit_name_to_id_map,
32
+ )
28
33
  from liminal.utils import to_snake_case
29
34
 
35
+ LOGGER = logging.getLogger(__name__)
36
+
30
37
 
31
38
  class CreateEntitySchema(BaseOperation):
32
39
  order: ClassVar[int] = 80
@@ -39,7 +46,7 @@ class CreateEntitySchema(BaseOperation):
39
46
  self.schema_properties = schema_properties
40
47
  self.fields = fields
41
48
  self._validated_schema_properties = SchemaProperties(
42
- **schema_properties.model_dump()
49
+ **schema_properties.model_dump(exclude_unset=True)
43
50
  )
44
51
 
45
52
  def execute(self, benchling_service: BenchlingService) -> dict[str, Any]:
@@ -83,6 +90,15 @@ class CreateEntitySchema(BaseOperation):
83
90
  Either set warehouse_access to True in BenchlingConnection or set the schema warehouse_name to the given Benchling warehouse name: {to_snake_case(self._validated_schema_properties.name)}. \
84
91
  Reach out to Benchling support if you need help setting up warehouse access."
85
92
  )
93
+ for field in self.fields:
94
+ if (
95
+ field.unit_name
96
+ and field.unit_name
97
+ not in get_unit_name_to_id_map(benchling_service).keys()
98
+ ):
99
+ raise ValueError(
100
+ f"{self._validated_schema_properties.warehouse_name}: On field {field.warehouse_name}, unit {field.unit_name} not found in Benchling Unit Dictionary as a valid unit. Please check the field definition or your Unit Dictionary."
101
+ )
86
102
 
87
103
  def _validate_create(self, benchling_service: BenchlingService) -> None:
88
104
  all_schemas = TagSchemaModel.get_all_json(benchling_service)
@@ -123,8 +139,11 @@ class CreateEntitySchema(BaseOperation):
123
139
  f"Entity schema {self._validated_schema_properties.warehouse_name} is already active in Benchling."
124
140
  )
125
141
  dropdowns_map = get_benchling_dropdown_id_name_map(benchling_service)
142
+ unit_id_to_name_map = get_unit_id_to_name_map(benchling_service)
126
143
  benchling_schema_props, _, benchling_fields_props = (
127
- convert_tag_schema_to_internal_schema(schema, dropdowns_map)
144
+ convert_tag_schema_to_internal_schema(
145
+ schema, dropdowns_map, unit_id_to_name_map
146
+ )
128
147
  )
129
148
  if (
130
149
  self._validated_schema_properties != benchling_schema_props
@@ -162,7 +181,7 @@ class ArchiveEntitySchema(BaseOperation):
162
181
 
163
182
 
164
183
  class UnarchiveEntitySchema(BaseOperation):
165
- order: ClassVar[int] = 100
184
+ order: ClassVar[int] = 90
166
185
 
167
186
  def __init__(self, wh_schema_name: str) -> None:
168
187
  self.wh_schema_name = wh_schema_name
@@ -187,7 +206,7 @@ class UnarchiveEntitySchema(BaseOperation):
187
206
 
188
207
 
189
208
  class UpdateEntitySchema(BaseOperation):
190
- order: ClassVar[int] = 90
209
+ order: ClassVar[int] = 120
191
210
 
192
211
  def __init__(
193
212
  self,
@@ -251,7 +270,7 @@ class UpdateEntitySchema(BaseOperation):
251
270
 
252
271
 
253
272
  class UpdateEntitySchemaNameTemplate(BaseOperation):
254
- order: ClassVar[int] = 150
273
+ order: ClassVar[int] = 110
255
274
 
256
275
  def __init__(
257
276
  self,
@@ -283,7 +302,7 @@ class UpdateEntitySchemaNameTemplate(BaseOperation):
283
302
 
284
303
 
285
304
  class CreateEntitySchemaField(BaseOperation):
286
- order: ClassVar[int] = 110
305
+ order: ClassVar[int] = 100
287
306
 
288
307
  def __init__(
289
308
  self,
@@ -316,13 +335,20 @@ class CreateEntitySchemaField(BaseOperation):
316
335
  f"Field {self._wh_field_name} is already active on entity schema {self.wh_schema_name}."
317
336
  )
318
337
  dropdowns_map = get_benchling_dropdown_id_name_map(benchling_service)
338
+ unit_id_to_name_map = get_unit_id_to_name_map(benchling_service)
319
339
  if self.field_props == convert_tag_schema_field_to_field_properties(
320
- field, dropdowns_map
321
- ):
340
+ field, dropdowns_map, unit_id_to_name_map
341
+ ).set_warehouse_name(self._wh_field_name):
322
342
  return UnarchiveEntitySchemaField(
323
343
  self.wh_schema_name, self._wh_field_name, self.index
324
344
  ).execute(benchling_service)
325
345
  else:
346
+ print(self.field_props)
347
+ print(
348
+ convert_tag_schema_field_to_field_properties(
349
+ field, dropdowns_map, unit_id_to_name_map
350
+ )
351
+ )
326
352
  raise ValueError(
327
353
  f"Field {self._wh_field_name} on entity schema {self.wh_schema_name} is different in code versus Benchling."
328
354
  )
@@ -367,10 +393,18 @@ class CreateEntitySchemaField(BaseOperation):
367
393
  Either set warehouse_access to True in BenchlingConnection or set the column variable name to the given Benchling field warehouse name: {to_snake_case(self.field_props.name)}. \
368
394
  Reach out to Benchling support if you need help setting up warehouse access."
369
395
  )
396
+ if (
397
+ self.field_props.unit_name
398
+ and self.field_props.unit_name
399
+ not in get_unit_name_to_id_map(benchling_service).keys()
400
+ ):
401
+ raise ValueError(
402
+ f"{self.wh_schema_name}: On field {self._wh_field_name}, unit {self.field_props.unit_name} not found in Benchling Unit Dictionary as a valid unit. Please check the field definition or your Unit Dictionary."
403
+ )
370
404
 
371
405
 
372
406
  class ArchiveEntitySchemaField(BaseOperation):
373
- order: ClassVar[int] = 140
407
+ order: ClassVar[int] = 150
374
408
 
375
409
  def __init__(
376
410
  self, wh_schema_name: str, wh_field_name: str, index: int | None = None
@@ -421,7 +455,7 @@ class ArchiveEntitySchemaField(BaseOperation):
421
455
 
422
456
 
423
457
  class UnarchiveEntitySchemaField(BaseOperation):
424
- order: ClassVar[int] = 120
458
+ order: ClassVar[int] = 130
425
459
 
426
460
  def __init__(
427
461
  self, wh_schema_name: str, wh_field_name: str, index: int | None = None
@@ -468,7 +502,7 @@ class UnarchiveEntitySchemaField(BaseOperation):
468
502
 
469
503
 
470
504
  class UpdateEntitySchemaField(BaseOperation):
471
- order: ClassVar[int] = 130
505
+ order: ClassVar[int] = 140
472
506
 
473
507
  def __init__(
474
508
  self,
@@ -500,6 +534,9 @@ class UpdateEntitySchemaField(BaseOperation):
500
534
  return f"{self.wh_schema_name}: Entity schema field '{self.wh_field_name}' in Benchling is different than in code: {str(self.update_props)}."
501
535
 
502
536
  def validate(self, benchling_service: BenchlingService) -> None:
537
+ tag_schema = TagSchemaModel.get_one_cached(
538
+ benchling_service, self.wh_schema_name
539
+ )
503
540
  if (
504
541
  not benchling_service.connection.warehouse_access
505
542
  and self.update_props.warehouse_name is not None
@@ -509,6 +546,19 @@ class UpdateEntitySchemaField(BaseOperation):
509
546
  Either set warehouse_access to True in BenchlingConnection or do not change the warehouse name. \
510
547
  Reach out to Benchling support if you need help setting up warehouse access."
511
548
  )
549
+ if "unit_name" in self.update_props.model_dump(exclude_unset=True):
550
+ no_change_message = f"{self.wh_schema_name}: On field {self.wh_field_name}, updating unit name to {self.update_props.unit_name}. The unit of this field CANNOT be changed once it's been set."
551
+ if tag_schema.get_field(self.wh_field_name).unitApiIdentifier:
552
+ raise ValueError(no_change_message)
553
+ else:
554
+ LOGGER.warning(no_change_message)
555
+ if (
556
+ self.update_props.unit_name
557
+ not in get_unit_name_to_id_map(benchling_service).keys()
558
+ ):
559
+ raise ValueError(
560
+ f"{self.wh_schema_name}: On field {self.wh_field_name}, unit {self.update_props.unit_name} not found in Benchling Unit Dictionary as a valid unit. Please check the field definition or your Unit Dictionary."
561
+ )
512
562
 
513
563
  def _validate(self, benchling_service: BenchlingService) -> TagSchemaModel:
514
564
  tag_schema = TagSchemaModel.get_one(benchling_service, self.wh_schema_name)