albert 1.9.5__tar.gz → 1.12.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 (293) hide show
  1. {albert-1.9.5 → albert-1.12.0}/.circleci/config.yml +5 -3
  2. {albert-1.9.5 → albert-1.12.0}/PKG-INFO +2 -1
  3. albert-1.12.0/docs/collections/synthesis.md +1 -0
  4. {albert-1.9.5 → albert-1.12.0}/docs/concepts.md +11 -11
  5. albert-1.12.0/docs/examples/notebook.md +139 -0
  6. albert-1.12.0/docs/examples/property_data.md +184 -0
  7. albert-1.12.0/docs/examples/tasks.md +46 -0
  8. albert-1.12.0/docs/resources/synthesis.md +1 -0
  9. {albert-1.9.5 → albert-1.12.0}/mkdocs.yml +9 -3
  10. {albert-1.9.5 → albert-1.12.0}/pyproject.toml +1 -0
  11. {albert-1.9.5 → albert-1.12.0}/scripts/validate_version_bump.py +11 -3
  12. {albert-1.9.5 → albert-1.12.0}/src/albert/__init__.py +1 -1
  13. {albert-1.9.5 → albert-1.12.0}/src/albert/client.py +5 -0
  14. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/attachments.py +37 -14
  15. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/custom_templates.py +3 -0
  16. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/data_templates.py +121 -99
  17. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/entity_types.py +9 -2
  18. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/inventory.py +1 -1
  19. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/notebooks.py +154 -26
  20. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/parameters.py +1 -0
  21. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/property_data.py +384 -279
  22. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/reports.py +4 -0
  23. albert-1.12.0/src/albert/collections/synthesis.py +292 -0
  24. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/tasks.py +266 -191
  25. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/worksheets.py +3 -0
  26. {albert-1.9.5 → albert-1.12.0}/src/albert/core/auth/_listener.py +4 -2
  27. {albert-1.9.5 → albert-1.12.0}/src/albert/core/shared/models/base.py +3 -1
  28. {albert-1.9.5 → albert-1.12.0}/src/albert/core/shared/models/patch.py +1 -1
  29. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/attachments.py +8 -2
  30. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/batch_data.py +4 -2
  31. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/cas.py +3 -1
  32. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/custom_fields.py +3 -1
  33. albert-1.12.0/src/albert/resources/data_templates.py +193 -0
  34. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/entity_types.py +15 -4
  35. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/inventory.py +6 -4
  36. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/lists.py +3 -1
  37. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/notebooks.py +12 -7
  38. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/notes.py +5 -3
  39. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/parameter_groups.py +3 -1
  40. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/property_data.py +64 -5
  41. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/sheets.py +16 -14
  42. albert-1.12.0/src/albert/resources/synthesis.py +61 -0
  43. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/tags.py +3 -1
  44. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/tasks.py +156 -0
  45. albert-1.12.0/src/albert/resources/worker_jobs.py +60 -0
  46. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/workflows.py +4 -2
  47. {albert-1.9.5 → albert-1.12.0}/src/albert/utils/_patch.py +44 -1
  48. albert-1.12.0/src/albert/utils/data_template.py +791 -0
  49. albert-1.12.0/src/albert/utils/property_data.py +698 -0
  50. albert-1.12.0/src/albert/utils/tasks.py +555 -0
  51. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_property_data.py +152 -1
  52. albert-1.9.5/src/albert/resources/data_templates.py +0 -76
  53. {albert-1.9.5 → albert-1.12.0}/.gitignore +0 -0
  54. {albert-1.9.5 → albert-1.12.0}/.pre-commit-config.yaml +0 -0
  55. {albert-1.9.5 → albert-1.12.0}/.vscode/settings.json +0 -0
  56. {albert-1.9.5 → albert-1.12.0}/CHANGELOG.md +0 -0
  57. {albert-1.9.5 → albert-1.12.0}/CONTRIBUTING.md +0 -0
  58. {albert-1.9.5 → albert-1.12.0}/LICENSE +0 -0
  59. {albert-1.9.5 → albert-1.12.0}/README.md +0 -0
  60. {albert-1.9.5 → albert-1.12.0}/docs/CONTRIBUTING.md +0 -0
  61. {albert-1.9.5 → albert-1.12.0}/docs/albert.md +0 -0
  62. {albert-1.9.5 → albert-1.12.0}/docs/assets/Vector_Favicon_Blue.svg +0 -0
  63. {albert-1.9.5 → albert-1.12.0}/docs/assets/Wordmark_Black.png +0 -0
  64. {albert-1.9.5 → albert-1.12.0}/docs/assets/Wordmark_White.png +0 -0
  65. {albert-1.9.5 → albert-1.12.0}/docs/authentication.md +0 -0
  66. {albert-1.9.5 → albert-1.12.0}/docs/collections/activities.md +0 -0
  67. {albert-1.9.5 → albert-1.12.0}/docs/collections/attachments.md +0 -0
  68. {albert-1.9.5 → albert-1.12.0}/docs/collections/base.md +0 -0
  69. {albert-1.9.5 → albert-1.12.0}/docs/collections/batch_data.md +0 -0
  70. {albert-1.9.5 → albert-1.12.0}/docs/collections/btdataset.md +0 -0
  71. {albert-1.9.5 → albert-1.12.0}/docs/collections/btinsight.md +0 -0
  72. {albert-1.9.5 → albert-1.12.0}/docs/collections/btmodel.md +0 -0
  73. {albert-1.9.5 → albert-1.12.0}/docs/collections/cas.md +0 -0
  74. {albert-1.9.5 → albert-1.12.0}/docs/collections/companies.md +0 -0
  75. {albert-1.9.5 → albert-1.12.0}/docs/collections/custom_fields.md +0 -0
  76. {albert-1.9.5 → albert-1.12.0}/docs/collections/custom_templates.md +0 -0
  77. {albert-1.9.5 → albert-1.12.0}/docs/collections/data_columns.md +0 -0
  78. {albert-1.9.5 → albert-1.12.0}/docs/collections/data_templates.md +0 -0
  79. {albert-1.9.5 → albert-1.12.0}/docs/collections/entity_types.md +0 -0
  80. {albert-1.9.5 → albert-1.12.0}/docs/collections/files.md +0 -0
  81. {albert-1.9.5 → albert-1.12.0}/docs/collections/hazards.md +0 -0
  82. {albert-1.9.5 → albert-1.12.0}/docs/collections/inventory.md +0 -0
  83. {albert-1.9.5 → albert-1.12.0}/docs/collections/links.md +0 -0
  84. {albert-1.9.5 → albert-1.12.0}/docs/collections/lists.md +0 -0
  85. {albert-1.9.5 → albert-1.12.0}/docs/collections/locations.md +0 -0
  86. {albert-1.9.5 → albert-1.12.0}/docs/collections/lots.md +0 -0
  87. {albert-1.9.5 → albert-1.12.0}/docs/collections/notebooks.md +0 -0
  88. {albert-1.9.5 → albert-1.12.0}/docs/collections/notes.md +0 -0
  89. {albert-1.9.5 → albert-1.12.0}/docs/collections/parameter_groups.md +0 -0
  90. {albert-1.9.5 → albert-1.12.0}/docs/collections/parameters.md +0 -0
  91. {albert-1.9.5 → albert-1.12.0}/docs/collections/pricings.md +0 -0
  92. {albert-1.9.5 → albert-1.12.0}/docs/collections/product_design.md +0 -0
  93. {albert-1.9.5 → albert-1.12.0}/docs/collections/projects.md +0 -0
  94. {albert-1.9.5 → albert-1.12.0}/docs/collections/property_data.md +0 -0
  95. {albert-1.9.5 → albert-1.12.0}/docs/collections/reports.md +0 -0
  96. {albert-1.9.5 → albert-1.12.0}/docs/collections/roles.md +0 -0
  97. {albert-1.9.5 → albert-1.12.0}/docs/collections/storage_classes.md +0 -0
  98. {albert-1.9.5 → albert-1.12.0}/docs/collections/storage_locations.md +0 -0
  99. {albert-1.9.5 → albert-1.12.0}/docs/collections/substances.md +0 -0
  100. {albert-1.9.5 → albert-1.12.0}/docs/collections/tags.md +0 -0
  101. {albert-1.9.5 → albert-1.12.0}/docs/collections/tasks.md +0 -0
  102. {albert-1.9.5 → albert-1.12.0}/docs/collections/un_numbers.md +0 -0
  103. {albert-1.9.5 → albert-1.12.0}/docs/collections/units.md +0 -0
  104. {albert-1.9.5 → albert-1.12.0}/docs/collections/users.md +0 -0
  105. {albert-1.9.5 → albert-1.12.0}/docs/collections/workflows.md +0 -0
  106. {albert-1.9.5 → albert-1.12.0}/docs/collections/worksheets.md +0 -0
  107. {albert-1.9.5 → albert-1.12.0}/docs/configuration.md +0 -0
  108. {albert-1.9.5 → albert-1.12.0}/docs/credentials.md +0 -0
  109. {albert-1.9.5 → albert-1.12.0}/docs/index.md +0 -0
  110. {albert-1.9.5 → albert-1.12.0}/docs/installation.md +0 -0
  111. {albert-1.9.5 → albert-1.12.0}/docs/migration.md +0 -0
  112. {albert-1.9.5 → albert-1.12.0}/docs/resources/activities.md +0 -0
  113. {albert-1.9.5 → albert-1.12.0}/docs/resources/attachments.md +0 -0
  114. {albert-1.9.5 → albert-1.12.0}/docs/resources/batch_data.md +0 -0
  115. {albert-1.9.5 → albert-1.12.0}/docs/resources/btdataset.md +0 -0
  116. {albert-1.9.5 → albert-1.12.0}/docs/resources/btinsight.md +0 -0
  117. {albert-1.9.5 → albert-1.12.0}/docs/resources/btmodel.md +0 -0
  118. {albert-1.9.5 → albert-1.12.0}/docs/resources/cas.md +0 -0
  119. {albert-1.9.5 → albert-1.12.0}/docs/resources/companies.md +0 -0
  120. {albert-1.9.5 → albert-1.12.0}/docs/resources/custom_fields.md +0 -0
  121. {albert-1.9.5 → albert-1.12.0}/docs/resources/custom_templates.md +0 -0
  122. {albert-1.9.5 → albert-1.12.0}/docs/resources/data_columns.md +0 -0
  123. {albert-1.9.5 → albert-1.12.0}/docs/resources/data_templates.md +0 -0
  124. {albert-1.9.5 → albert-1.12.0}/docs/resources/entity_types.md +0 -0
  125. {albert-1.9.5 → albert-1.12.0}/docs/resources/files.md +0 -0
  126. {albert-1.9.5 → albert-1.12.0}/docs/resources/hazards.md +0 -0
  127. {albert-1.9.5 → albert-1.12.0}/docs/resources/identifiers.md +0 -0
  128. {albert-1.9.5 → albert-1.12.0}/docs/resources/inventory.md +0 -0
  129. {albert-1.9.5 → albert-1.12.0}/docs/resources/links.md +0 -0
  130. {albert-1.9.5 → albert-1.12.0}/docs/resources/lists.md +0 -0
  131. {albert-1.9.5 → albert-1.12.0}/docs/resources/locations.md +0 -0
  132. {albert-1.9.5 → albert-1.12.0}/docs/resources/lots.md +0 -0
  133. {albert-1.9.5 → albert-1.12.0}/docs/resources/notebooks.md +0 -0
  134. {albert-1.9.5 → albert-1.12.0}/docs/resources/notes.md +0 -0
  135. {albert-1.9.5 → albert-1.12.0}/docs/resources/parameter_groups.md +0 -0
  136. {albert-1.9.5 → albert-1.12.0}/docs/resources/parameters.md +0 -0
  137. {albert-1.9.5 → albert-1.12.0}/docs/resources/pricings.md +0 -0
  138. {albert-1.9.5 → albert-1.12.0}/docs/resources/product_design.md +0 -0
  139. {albert-1.9.5 → albert-1.12.0}/docs/resources/projects.md +0 -0
  140. {albert-1.9.5 → albert-1.12.0}/docs/resources/property_data.md +0 -0
  141. {albert-1.9.5 → albert-1.12.0}/docs/resources/reports.md +0 -0
  142. {albert-1.9.5 → albert-1.12.0}/docs/resources/roles.md +0 -0
  143. {albert-1.9.5 → albert-1.12.0}/docs/resources/sheets.md +0 -0
  144. {albert-1.9.5 → albert-1.12.0}/docs/resources/storage_classes.md +0 -0
  145. {albert-1.9.5 → albert-1.12.0}/docs/resources/storage_locations.md +0 -0
  146. {albert-1.9.5 → albert-1.12.0}/docs/resources/substances.md +0 -0
  147. {albert-1.9.5 → albert-1.12.0}/docs/resources/tags.md +0 -0
  148. {albert-1.9.5 → albert-1.12.0}/docs/resources/tasks.md +0 -0
  149. {albert-1.9.5 → albert-1.12.0}/docs/resources/un_numbers.md +0 -0
  150. {albert-1.9.5 → albert-1.12.0}/docs/resources/units.md +0 -0
  151. {albert-1.9.5 → albert-1.12.0}/docs/resources/users.md +0 -0
  152. {albert-1.9.5 → albert-1.12.0}/docs/resources/workflows.md +0 -0
  153. {albert-1.9.5 → albert-1.12.0}/docs/resources/worksheets.md +0 -0
  154. {albert-1.9.5 → albert-1.12.0}/docs/sso.md +0 -0
  155. {albert-1.9.5 → albert-1.12.0}/docs/styles/extra.css +0 -0
  156. {albert-1.9.5 → albert-1.12.0}/scripts/validate_release_tag.py +0 -0
  157. {albert-1.9.5 → albert-1.12.0}/setup.sh +0 -0
  158. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/__init__.py +0 -0
  159. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/activities.py +0 -0
  160. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/base.py +0 -0
  161. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/batch_data.py +0 -0
  162. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/btdataset.py +0 -0
  163. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/btinsight.py +0 -0
  164. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/btmodel.py +0 -0
  165. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/cas.py +0 -0
  166. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/companies.py +0 -0
  167. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/custom_fields.py +0 -0
  168. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/data_columns.py +0 -0
  169. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/files.py +0 -0
  170. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/hazards.py +0 -0
  171. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/links.py +0 -0
  172. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/lists.py +0 -0
  173. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/locations.py +0 -0
  174. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/lots.py +0 -0
  175. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/notes.py +0 -0
  176. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/parameter_groups.py +0 -0
  177. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/pricings.py +0 -0
  178. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/product_design.py +0 -0
  179. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/projects.py +0 -0
  180. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/report_templates.py +0 -0
  181. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/roles.py +0 -0
  182. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/storage_classes.py +0 -0
  183. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/storage_locations.py +0 -0
  184. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/substance.py +0 -0
  185. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/tags.py +0 -0
  186. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/un_numbers.py +0 -0
  187. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/units.py +0 -0
  188. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/users.py +0 -0
  189. {albert-1.9.5 → albert-1.12.0}/src/albert/collections/workflows.py +0 -0
  190. {albert-1.9.5 → albert-1.12.0}/src/albert/core/__init__.py +0 -0
  191. {albert-1.9.5 → albert-1.12.0}/src/albert/core/auth/__init__.py +0 -0
  192. {albert-1.9.5 → albert-1.12.0}/src/albert/core/auth/_manager.py +0 -0
  193. {albert-1.9.5 → albert-1.12.0}/src/albert/core/auth/credentials.py +0 -0
  194. {albert-1.9.5 → albert-1.12.0}/src/albert/core/auth/sso.py +0 -0
  195. {albert-1.9.5 → albert-1.12.0}/src/albert/core/base.py +0 -0
  196. {albert-1.9.5 → albert-1.12.0}/src/albert/core/logging.py +0 -0
  197. {albert-1.9.5 → albert-1.12.0}/src/albert/core/pagination.py +0 -0
  198. {albert-1.9.5 → albert-1.12.0}/src/albert/core/session.py +0 -0
  199. {albert-1.9.5 → albert-1.12.0}/src/albert/core/shared/__init__.py +0 -0
  200. {albert-1.9.5 → albert-1.12.0}/src/albert/core/shared/enums.py +0 -0
  201. {albert-1.9.5 → albert-1.12.0}/src/albert/core/shared/identifiers.py +0 -0
  202. {albert-1.9.5 → albert-1.12.0}/src/albert/core/shared/types.py +0 -0
  203. {albert-1.9.5 → albert-1.12.0}/src/albert/exceptions.py +0 -0
  204. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/__init__.py +0 -0
  205. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/_mixins.py +0 -0
  206. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/acls.py +0 -0
  207. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/activities.py +0 -0
  208. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/btdataset.py +0 -0
  209. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/btinsight.py +0 -0
  210. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/btmodel.py +0 -0
  211. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/companies.py +0 -0
  212. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/custom_templates.py +0 -0
  213. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/data_columns.py +0 -0
  214. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/facet.py +0 -0
  215. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/files.py +0 -0
  216. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/hazards.py +0 -0
  217. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/links.py +0 -0
  218. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/locations.py +0 -0
  219. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/lots.py +0 -0
  220. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/parameters.py +0 -0
  221. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/pricings.py +0 -0
  222. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/product_design.py +0 -0
  223. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/projects.py +0 -0
  224. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/report_templates.py +0 -0
  225. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/reports.py +0 -0
  226. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/roles.py +0 -0
  227. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/storage_classes.py +0 -0
  228. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/storage_locations.py +0 -0
  229. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/substance.py +0 -0
  230. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/tagged_base.py +0 -0
  231. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/un_numbers.py +0 -0
  232. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/units.py +0 -0
  233. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/users.py +0 -0
  234. {albert-1.9.5 → albert-1.12.0}/src/albert/resources/worksheets.py +0 -0
  235. {albert-1.9.5 → albert-1.12.0}/src/albert/utils/__init__.py +0 -0
  236. {albert-1.9.5 → albert-1.12.0}/src/albert/utils/_auth.py +0 -0
  237. {albert-1.9.5 → albert-1.12.0}/src/albert/utils/inventory.py +0 -0
  238. {albert-1.9.5 → albert-1.12.0}/tests/__init__.py +0 -0
  239. {albert-1.9.5 → albert-1.12.0}/tests/collections/__init__.py +0 -0
  240. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_activities.py +0 -0
  241. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_attachments.py +0 -0
  242. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_batch_data.py +0 -0
  243. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_btdataset.py +0 -0
  244. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_btinsight.py +0 -0
  245. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_btmodel.py +0 -0
  246. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_cas.py +0 -0
  247. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_company.py +0 -0
  248. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_custom_fields.py +0 -0
  249. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_custom_templates.py +0 -0
  250. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_data_columns.py +0 -0
  251. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_data_templates.py +0 -0
  252. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_entity_types.py +0 -0
  253. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_files.py +0 -0
  254. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_hazards.py +0 -0
  255. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_inventory.py +0 -0
  256. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_links.py +0 -0
  257. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_lists.py +0 -0
  258. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_locations.py +0 -0
  259. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_lots.py +0 -0
  260. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_notebooks.py +0 -0
  261. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_notes.py +0 -0
  262. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_parameter_groups.py +0 -0
  263. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_parameters.py +0 -0
  264. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_pricings.py +0 -0
  265. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_product_design.py +0 -0
  266. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_projects.py +0 -0
  267. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_report_templates.py +0 -0
  268. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_reports.py +0 -0
  269. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_roles.py +0 -0
  270. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_storage_classes.py +0 -0
  271. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_storage_locations.py +0 -0
  272. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_substance.py +0 -0
  273. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_tags.py +0 -0
  274. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_tasks.py +0 -0
  275. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_un_number.py +0 -0
  276. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_units.py +0 -0
  277. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_users.py +0 -0
  278. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_workflows.py +0 -0
  279. {albert-1.9.5 → albert-1.12.0}/tests/collections/test_worksheet.py +0 -0
  280. {albert-1.9.5 → albert-1.12.0}/tests/conftest.py +0 -0
  281. {albert-1.9.5 → albert-1.12.0}/tests/core/shared/__init__.py +0 -0
  282. {albert-1.9.5 → albert-1.12.0}/tests/core/shared/test_identifiers.py +0 -0
  283. {albert-1.9.5 → albert-1.12.0}/tests/core/shared/test_types.py +0 -0
  284. {albert-1.9.5 → albert-1.12.0}/tests/core/test_auth.py +0 -0
  285. {albert-1.9.5 → albert-1.12.0}/tests/data/SDS_HCL.pdf +0 -0
  286. {albert-1.9.5 → albert-1.12.0}/tests/data/dontpanic.jpg +0 -0
  287. {albert-1.9.5 → albert-1.12.0}/tests/resources/test_inventory.py +0 -0
  288. {albert-1.9.5 → albert-1.12.0}/tests/resources/test_lots.py +0 -0
  289. {albert-1.9.5 → albert-1.12.0}/tests/resources/test_notebooks.py +0 -0
  290. {albert-1.9.5 → albert-1.12.0}/tests/resources/test_sheets.py +0 -0
  291. {albert-1.9.5 → albert-1.12.0}/tests/seeding.py +0 -0
  292. {albert-1.9.5 → albert-1.12.0}/tests/utils/fake_session.py +0 -0
  293. {albert-1.9.5 → albert-1.12.0}/tests/utils/test_patches.py +0 -0
@@ -123,9 +123,11 @@ jobs:
123
123
  exit 0
124
124
  fi
125
125
 
126
- if [[ "$CIRCLE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
126
+ SEMVER_REGEX='^v[0-9]+\.[0-9]+\.[0-9]+((a|b|rc|dev)[0-9]+|-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$'
127
+
128
+ if [[ "$CIRCLE_TAG" =~ $SEMVER_REGEX ]]; then
127
129
  VERSION="${CIRCLE_TAG#v}"
128
- echo "Stable semver tag detected. Deploying $VERSION with alias: latest"
130
+ echo "Semver tag detected. Deploying $VERSION with alias: latest"
129
131
  uv run mike deploy --push --update-aliases "$VERSION" latest
130
132
  else
131
133
  echo "Non-semver release tag. Skipping docs deployment."
@@ -179,6 +181,6 @@ workflows:
179
181
  - Validate Release Tag
180
182
  filters:
181
183
  tags:
182
- only: /^v[0-9]+\.[0-9]+\.[0-9]+$/
184
+ only: /^v[0-9]+\.[0-9]+\.[0-9]+(?:(?:a|b|rc|dev)[0-9]+|-[0-9A-Za-z]+(?:\.[0-9A-Za-z]+)*)?$/
183
185
  branches:
184
186
  ignore: /.*/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: albert
3
- Version: 1.9.5
3
+ Version: 1.12.0
4
4
  Summary: The official Python SDK for the Albert Invent platform.
5
5
  Project-URL: Homepage, https://www.albertinvent.com/
6
6
  Project-URL: Documentation, https://docs.developer.albertinvent.com/albert-python
@@ -220,6 +220,7 @@ Requires-Dist: pandas<3,>=2.2.2
220
220
  Requires-Dist: pydantic[email]<3,>=2.8.2
221
221
  Requires-Dist: pyjwt<3,>=2.10.0
222
222
  Requires-Dist: requests<3,>=2.32.3
223
+ Requires-Dist: tenacity>=8.2.3
223
224
  Description-Content-Type: text/markdown
224
225
 
225
226
  # Albert Python SDK
@@ -0,0 +1 @@
1
+ ::: albert.collections.synthesis.SynthesisCollection
@@ -67,7 +67,7 @@ project = Project(
67
67
  )
68
68
 
69
69
  # Or pass full object and let SDK convert
70
- location = client.locations.get_by_id("loc123")
70
+ location = client.locations.get_by_id(id="loc123")
71
71
  project = Project(
72
72
  description="Example",
73
73
  locations=[location] # Automatically converted
@@ -198,15 +198,15 @@ Tags are searchable and help categorize content.
198
198
 
199
199
  Concepts you should know:
200
200
 
201
- | Concept | Purpose |
202
- |-------------------------|----------------------------------------------|
203
- | Resource Models | Define the structure of data entities |
204
- | Resource Collections | Manage CRUD operations for each model |
205
- | EntityLink | Represents foreign key references |
206
- | Custom Fields & Lists | Add structured metadata to resources |
207
- | SerializeAsEntityLink | Allows transparent references or links |
208
- | Authentication | Supports static token, OAuth2, and SSO |
209
- | Partial Records | Lightweight search results |
210
- | Tags and Metadata | Categorize and extend data |
201
+ | Concept | Purpose |
202
+ | --------------------- | -------------------------------------- |
203
+ | Resource Models | Define the structure of data entities |
204
+ | Resource Collections | Manage CRUD operations for each model |
205
+ | EntityLink | Represents foreign key references |
206
+ | Custom Fields & Lists | Add structured metadata to resources |
207
+ | SerializeAsEntityLink | Allows transparent references or links |
208
+ | Authentication | Supports static token, OAuth2, and SSO |
209
+ | Partial Records | Lightweight search results |
210
+ | Tags and Metadata | Categorize and extend data |
211
211
 
212
212
  These concepts form the foundation of working effectively with the Albert SDK.
@@ -0,0 +1,139 @@
1
+ # Notebooks
2
+
3
+ Notebooks in Albert Invent are a tool for organizing your laboratory work in a centralized, secure place that is connected to other parts of the Albert platform. They allow you to capture unstructured notes and data, attachments, images, and documents within Projects, set up complex synthesis instructions, document Standard Operating Procedures (SOPs), and collaborate with team members.
4
+
5
+ ## Create a notebook
6
+
7
+ !!! example "Create an empty notebook"
8
+ ```python
9
+ from albert import Albert
10
+ from albert.resources.notebooks import Notebook
11
+
12
+ client = Albert.from_client_credentials()
13
+
14
+ notebook = Notebook(parent_id="PRJ123", name="Reaction Notes")
15
+ notebook = client.notebooks.create(notebook=notebook)
16
+ ```
17
+
18
+ ## Add different block types
19
+
20
+ !!! example "Add header, paragraph, checklist, table, image, and attachment blocks"
21
+ ```python
22
+ from pathlib import Path
23
+
24
+ from albert import Albert
25
+ from albert.resources.notebooks import (
26
+ AttachesBlock,
27
+ AttachesContent,
28
+ ChecklistBlock,
29
+ ChecklistContent,
30
+ ChecklistItem,
31
+ HeaderBlock,
32
+ HeaderContent,
33
+ ImageBlock,
34
+ ImageContent,
35
+ ParagraphBlock,
36
+ ParagraphContent,
37
+ TableBlock,
38
+ TableContent,
39
+ )
40
+
41
+ client = Albert.from_client_credentials()
42
+
43
+ notebook = client.notebooks.get_by_id(id="NTB123")
44
+
45
+ # Provide a file path to upload for image and attachment blocks.
46
+ image_path = Path("/path/to/image.png")
47
+ image_block = ImageBlock(content=ImageContent(file_path=image_path, with_background=True))
48
+ attaches_block = AttachesBlock(content=AttachesContent(file_path=image_path))
49
+
50
+ notebook.blocks = [
51
+ HeaderBlock(content=HeaderContent(level=2, text="Notebook overview")),
52
+ ParagraphBlock(content=ParagraphContent(text="Run conditions and observations.")),
53
+ ChecklistBlock(
54
+ content=ChecklistContent(
55
+ items=[
56
+ ChecklistItem(checked=False, text="Prepare reagents"),
57
+ ChecklistItem(checked=True, text="Start reaction"),
58
+ ]
59
+ )
60
+ ),
61
+ TableBlock(
62
+ content=TableContent(
63
+ content=[
64
+ ["Reagent", "Mass (mg)"],
65
+ ["A", "10"],
66
+ ["B", "25"],
67
+ ],
68
+ with_headings=True,
69
+ )
70
+ ),
71
+ image_block,
72
+ attaches_block,
73
+ ]
74
+
75
+ notebook = client.notebooks.update_block_content(notebook=notebook)
76
+ ```
77
+
78
+ !!! note
79
+ When `file_path` is set on image or attachment content, the SDK uploads the file and fills in
80
+ `file_key`, `format`, and (for attachments) `title`.
81
+
82
+ ## Add a Ketcher block
83
+
84
+ !!! example "Create a new Ketcher block with SMILES"
85
+ ```python
86
+ from albert import Albert
87
+ from albert.resources.notebooks import KetcherBlock, KetcherContent
88
+
89
+ client = Albert.from_client_credentials()
90
+
91
+ notebook = client.notebooks.get_by_id(id="NTB123")
92
+ notebook.blocks.append(
93
+ KetcherBlock(content=KetcherContent(smiles="CCO"))
94
+ )
95
+ notebook = client.notebooks.update_block_content(notebook=notebook)
96
+ ```
97
+
98
+ !!! warning
99
+ Updating existing Ketcher blocks is not supported. To change a Ketcher block, remove
100
+ it from `notebook.blocks` and add a new Ketcher block with the desired `smiles`.
101
+
102
+ ## Replace a Ketcher block
103
+
104
+ !!! example "Delete the old Ketcher block and add a new one"
105
+ ```python
106
+ from albert import Albert
107
+ from albert.resources.notebooks import KetcherBlock, KetcherContent
108
+
109
+ client = Albert.from_client_credentials()
110
+
111
+ notebook = client.notebooks.get_by_id(id="NTB123")
112
+
113
+ # Drop existing Ketcher blocks (this deletes them on update).
114
+ notebook.blocks = [b for b in notebook.blocks if b.type != "ketcher"]
115
+
116
+ # Add a new Ketcher block.
117
+ notebook.blocks.append(
118
+ KetcherBlock(content=KetcherContent(smiles="C1=CC=CC=C1"))
119
+ )
120
+
121
+ notebook = client.notebooks.update_block_content(notebook=notebook)
122
+ ```
123
+
124
+ ## Copy a notebook
125
+
126
+ !!! example "Copy a notebook to another parent"
127
+ ```python
128
+ from albert import Albert
129
+ from albert.resources.notebooks import NotebookCopyInfo, NotebookCopyType
130
+
131
+ client = Albert.from_client_credentials()
132
+
133
+ copy_info = NotebookCopyInfo(id="NTB123", parent_id="PRJ456")
134
+ notebook_copy = client.notebooks.copy(
135
+ notebook_copy_info=copy_info,
136
+ type=NotebookCopyType.PROJECT,
137
+ )
138
+ print(notebook_copy.id)
139
+ ```
@@ -0,0 +1,184 @@
1
+ # Property Data
2
+
3
+ Property data refers to the results collected from a Property Task in Albert. This data is captured using Data Templates, which allow for the collection of clean, structured data about your experiments.
4
+
5
+ ## Update an existing trial row
6
+
7
+ !!! example "Update a trial row"
8
+ ```python
9
+ from albert import Albert
10
+ from albert.resources.property_data import (
11
+ CurvePropertyValue,
12
+ ImagePropertyValue,
13
+ TaskDataColumn,
14
+ TaskPropertyCreate,
15
+ )
16
+
17
+ client = Albert.from_client_credentials()
18
+
19
+ task_id = "TAS123"
20
+ inventory_id = "INV123"
21
+ block_id = "BLK1"
22
+
23
+ task_ptd = client.property_data.get_task_block_properties(
24
+ inventory_id=inventory_id,
25
+ task_id=task_id,
26
+ block_id=block_id,
27
+ )
28
+ dt = task_ptd.data_template
29
+
30
+ # trial_number maps to the row number in the task data.
31
+ # when provided, it updates that row for the given columns.
32
+ properties = [
33
+ TaskPropertyCreate(
34
+ data_column=TaskDataColumn(data_column_id="DAC123", column_sequence="COL4"),
35
+ value="10",
36
+ trial_number=2,
37
+ data_template=dt,
38
+ ),
39
+ TaskPropertyCreate(
40
+ data_column=TaskDataColumn(data_column_id="DAC456", column_sequence="COL5"),
41
+ value="enum2",
42
+ trial_number=1,
43
+ data_template=dt,
44
+ ),
45
+ # image property data
46
+ TaskPropertyCreate(
47
+ data_column=TaskDataColumn(data_column_id="DAC789", column_sequence="COL1"),
48
+ value=ImagePropertyValue(file_path="path/to/image.png"),
49
+ trial_number=1,
50
+ data_template=dt,
51
+ ),
52
+ # curve property data (CSV import by default)
53
+ TaskPropertyCreate(
54
+ data_column=TaskDataColumn(data_column_id="DAC313", column_sequence="COL3"),
55
+ value=CurvePropertyValue(
56
+ file_path="path/to/curve.csv",
57
+ field_mapping={"temperature": "Temperature", "cnt": "Count"},
58
+ ),
59
+ trial_number=1,
60
+ data_template=dt,
61
+ ),
62
+ ]
63
+
64
+ client.property_data.update_or_create_task_properties(
65
+ inventory_id=inventory_id,
66
+ task_id=task_id,
67
+ block_id=block_id,
68
+ properties=properties,
69
+ return_scope="block",
70
+ )
71
+ ```
72
+
73
+ ## Add a new trial row
74
+
75
+ !!! example "Add a new trial row"
76
+ ```python
77
+ from albert.resources.property_data import TaskDataColumn, TaskPropertyCreate
78
+
79
+ task_ptd = client.property_data.get_task_block_properties(
80
+ inventory_id=inventory_id,
81
+ task_id=task_id,
82
+ block_id=block_id,
83
+ )
84
+ dt = task_ptd.data_template
85
+
86
+ # Omitting trial_number creates a new row in the task data table.
87
+ new_row = [
88
+ TaskPropertyCreate(
89
+ data_column=TaskDataColumn(data_column_id="DAC123", column_sequence="COL4"),
90
+ value="25",
91
+ data_template=dt,
92
+ )
93
+ ]
94
+
95
+ client.property_data.update_or_create_task_properties(
96
+ inventory_id=inventory_id,
97
+ task_id=task_id,
98
+ block_id=block_id,
99
+ properties=new_row,
100
+ return_scope="block",
101
+ )
102
+ ```
103
+
104
+ ## Void data row
105
+
106
+ !!! example "Void task data"
107
+ ```python
108
+ from albert import Albert
109
+
110
+ client = Albert.from_client_credentials()
111
+
112
+ task_id = "TAS123"
113
+ inventory_id = "INV123"
114
+ block_id = "BLK1"
115
+
116
+ client.property_data.void_task_data(
117
+ inventory_id=inventory_id,
118
+ task_id=task_id,
119
+ block_id=block_id,
120
+ )
121
+ ```
122
+
123
+ !!! example "Void interval data"
124
+ ```python
125
+ from albert import Albert
126
+
127
+ client = Albert.from_client_credentials()
128
+
129
+ task_id = "TAS123"
130
+ inventory_id = "INV123"
131
+ block_id = "BLK1"
132
+
133
+ interval_id = next(
134
+ (
135
+ combo.interval_id
136
+ for combo in client.property_data.check_for_task_data(task_id=task_id)
137
+ if combo.inventory_id == inventory_id and combo.block_id == block_id
138
+ ),
139
+ None,
140
+ )
141
+ if not interval_id:
142
+ raise ValueError("No interval data found for the block/inventory combination.")
143
+
144
+ client.property_data.void_interval_data(
145
+ inventory_id=inventory_id,
146
+ task_id=task_id,
147
+ block_id=block_id,
148
+ interval_id=interval_id,
149
+ )
150
+ ```
151
+
152
+ !!! example "Void trial data"
153
+ ```python
154
+ from albert import Albert
155
+
156
+ client = Albert.from_client_credentials()
157
+
158
+ task_id = "TAS123"
159
+ inventory_id = "INV123"
160
+ block_id = "BLK1"
161
+
162
+ interval_id = next(
163
+ (
164
+ combo.interval_id
165
+ for combo in client.property_data.check_for_task_data(task_id=task_id)
166
+ if combo.inventory_id == inventory_id and combo.block_id == block_id
167
+ ),
168
+ None,
169
+ )
170
+ if not interval_id:
171
+ raise ValueError("No interval data found for the block/inventory combination.")
172
+
173
+ client.property_data.void_trial_data(
174
+ inventory_id=inventory_id,
175
+ task_id=task_id,
176
+ block_id=block_id,
177
+ trial_number=2,
178
+ interval_id=interval_id,
179
+ )
180
+ ```
181
+
182
+ !!! note
183
+ Use the corresponding `unvoid_task_data`, `unvoid_interval_data`, and `unvoid_trial_data`
184
+ methods to unvoid records.
@@ -0,0 +1,46 @@
1
+ # Tasks
2
+
3
+ Tasks in Albert Invent are a way to manage and track your daily work and collaborate with colleagues. There are three types of tasks: Batch Tasks, Property Tasks, and General Tasks.
4
+
5
+ ## Import results
6
+
7
+ This feature enables users to import a .csv file straight into the Data Template of a Property Task allowing them to easily mass enter results without having to type them in manually or copy-paste.
8
+
9
+ !!! example "Import results from a CSV file"
10
+ ```python
11
+ from albert import Albert
12
+ from albert.resources.data_templates import ImportMode
13
+
14
+ client = Albert.from_client_credentials()
15
+
16
+ task = client.tasks.import_results(
17
+ task_id="TAS123",
18
+ inventory_id="INV123",
19
+ data_template_id="DT123",
20
+ file_path="path/to/results.csv",
21
+ field_mapping={"comm": "Comments", "Solvent": " Solvent, ppm"},
22
+ mode=ImportMode.CSV,
23
+ )
24
+ print(task)
25
+ ```
26
+
27
+ !!! example "Import results using a script"
28
+ ```python
29
+ from albert import Albert
30
+ from albert.resources.data_templates import ImportMode
31
+
32
+ client = Albert.from_client_credentials()
33
+
34
+ task = client.tasks.import_results(
35
+ task_id="TAS123",
36
+ inventory_id="INV123",
37
+ data_template_id="DT123",
38
+ block_id="BLK1",
39
+ file_path="path/to/results.csv",
40
+ mode=ImportMode.SCRIPT,
41
+ )
42
+ ```
43
+
44
+ !!! warning
45
+ `import_results` deletes existing property data for the matching task/block/inventory/lot/interval
46
+ before writing new values. Use with care if you need to preserve older results.
@@ -0,0 +1 @@
1
+ ::: albert.resources.synthesis
@@ -23,7 +23,7 @@ use_directory_urls: true
23
23
  theme:
24
24
  name: "material"
25
25
  font:
26
- text: Poppins
26
+ text: Aeonik Regular
27
27
  logo: assets/Wordmark_White.png
28
28
  favicon: assets/Vector_Favicon_Blue.svg
29
29
  features:
@@ -40,6 +40,7 @@ theme:
40
40
  - navigation.sections # Sidebar sections
41
41
  - navigation.top # Scroll-to-top button
42
42
  - navigation.tracking # Highlight section in sidebar
43
+ - navigation.collapse
43
44
  - search.suggest # Smart search suggestions
44
45
  - toc.follow # Highlight TOC entry while scrolling
45
46
 
@@ -123,11 +124,10 @@ nav:
123
124
  - Configuration: configuration.md
124
125
  - Migration Guide: migration.md
125
126
  - Contributing: CONTRIBUTING.md
126
- - Changelog: CHANGELOG.md
127
127
  - SDK Reference:
128
128
  - Clients:
129
129
  - Albert Client: albert.md
130
- - Authenticaton:
130
+ - Authentication:
131
131
  - Albert Client Credentials: credentials.md
132
132
  - Albert SSO Client: sso.md
133
133
  - Collections:
@@ -166,6 +166,7 @@ nav:
166
166
  - Storage Classes: collections/storage_classes.md
167
167
  - Storage Locations: collections/storage_locations.md
168
168
  - Substances: collections/substances.md
169
+ - Synthesis: collections/synthesis.md
169
170
  - Tags: collections/tags.md
170
171
  - Tasks: collections/tasks.md
171
172
  - UN Numbers: collections/un_numbers.md
@@ -209,6 +210,7 @@ nav:
209
210
  - Storage Classes: resources/storage_classes.md
210
211
  - Storage Locations: resources/storage_locations.md
211
212
  - Substances: resources/substances.md
213
+ - Synthesis: resources/synthesis.md
212
214
  - Tags: resources/tags.md
213
215
  - Tasks: resources/tasks.md
214
216
  - UN Numbers: resources/un_numbers.md
@@ -218,6 +220,10 @@ nav:
218
220
  - Worksheets:
219
221
  - Worksheets: resources/worksheets.md
220
222
  - Sheets: resources/sheets.md
223
+ - Examples:
224
+ - Property Data: examples/property_data.md
225
+ - Tasks: examples/tasks.md
226
+ - Notebooks: examples/notebook.md
221
227
 
222
228
  validation:
223
229
  omitted_files: warn
@@ -23,6 +23,7 @@ dependencies = [
23
23
  "pandas>=2.2.2,<3",
24
24
  "pydantic[email]>=2.8.2,<3",
25
25
  "pyjwt>=2.10.0,<3",
26
+ "tenacity>=8.2.3",
26
27
  ]
27
28
 
28
29
  [dependency-groups]
@@ -1,6 +1,7 @@
1
1
  import argparse
2
+ import re
3
+ import subprocess
2
4
  import sys
3
- from os import popen
4
5
  from pathlib import Path
5
6
 
6
7
  from packaging import version
@@ -19,8 +20,15 @@ def main(base_branch: str):
19
20
  local_path = Path(__file__).parents[1] / version_file
20
21
  local_version = extract_version(local_path.read_text())
21
22
 
22
- with popen(f"git fetch origin && git show {base_branch}:{version_file}") as fh:
23
- base_version = extract_version(fh.read())
23
+ if not re.fullmatch(r"[A-Za-z0-9._/-]+", base_branch) or base_branch.startswith("-"):
24
+ raise ValueError(f"Invalid base branch ref: {base_branch!r}")
25
+
26
+ subprocess.run(["git", "fetch", "origin"], check=True)
27
+ base_contents = subprocess.check_output(
28
+ ["git", "show", f"{base_branch}:{version_file}"],
29
+ text=True,
30
+ )
31
+ base_version = extract_version(base_contents)
24
32
 
25
33
  if is_version_bump(local_version, base_version):
26
34
  print(f"Version bump detected: {base_version} -> {local_version}")
@@ -4,4 +4,4 @@ from albert.core.auth.sso import AlbertSSOClient
4
4
 
5
5
  __all__ = ["Albert", "AlbertClientCredentials", "AlbertSSOClient"]
6
6
 
7
- __version__ = "1.9.5"
7
+ __version__ = "1.12.0"
@@ -38,6 +38,7 @@ from albert.collections.roles import RoleCollection
38
38
  from albert.collections.storage_classes import StorageClassesCollection
39
39
  from albert.collections.storage_locations import StorageLocationsCollection
40
40
  from albert.collections.substance import SubstanceCollection
41
+ from albert.collections.synthesis import SynthesisCollection
41
42
  from albert.collections.tags import TagCollection
42
43
  from albert.collections.tasks import TaskCollection
43
44
  from albert.collections.un_numbers import UnNumberCollection
@@ -188,6 +189,10 @@ class Albert:
188
189
  def lots(self) -> LotCollection:
189
190
  return LotCollection(session=self.session)
190
191
 
192
+ @property
193
+ def synthesis(self) -> SynthesisCollection:
194
+ return SynthesisCollection(session=self.session)
195
+
191
196
  @property
192
197
  def units(self) -> UnitCollection:
193
198
  return UnitCollection(session=self.session)
@@ -9,7 +9,7 @@ from pydantic import validate_call
9
9
  from albert.collections.base import BaseCollection
10
10
  from albert.collections.files import FileCollection
11
11
  from albert.collections.notes import NotesCollection
12
- from albert.core.shared.identifiers import AttachmentId, InventoryId
12
+ from albert.core.shared.identifiers import AttachmentId, DataColumnId, InventoryId
13
13
  from albert.core.shared.types import MetadataItem
14
14
  from albert.resources.attachments import Attachment, AttachmentCategory
15
15
  from albert.resources.files import FileCategory, FileNamespace
@@ -49,7 +49,9 @@ class AttachmentCollection(BaseCollection):
49
49
  response = self.session.get(url=f"{self.base_path}/{id}")
50
50
  return Attachment(**response.json())
51
51
 
52
- def get_by_parent_ids(self, *, parent_ids: list[str]) -> dict[str, list[Attachment]]:
52
+ def get_by_parent_ids(
53
+ self, *, parent_ids: list[str], data_column_ids: list[DataColumnId] | None = None
54
+ ) -> dict[str, list[Attachment]]:
53
55
  """Retrieves attachments by their parent IDs.
54
56
 
55
57
  Note: This method returns a dictionary where the keys are parent IDs
@@ -69,7 +71,10 @@ class AttachmentCollection(BaseCollection):
69
71
  dict[str, list[Attachment]]
70
72
  A dictionary mapping parent IDs to lists of Attachment objects associated with each parent ID.
71
73
  """
72
- response = self.session.get(url=f"{self.base_path}/parents", params={"id": parent_ids})
74
+ response = self.session.get(
75
+ url=f"{self.base_path}/parents",
76
+ params={"id": parent_ids, "dataColumnId": data_column_ids},
77
+ )
73
78
  response_data = response.json()
74
79
  return {
75
80
  parent["parentId"]: [
@@ -125,7 +130,12 @@ class AttachmentCollection(BaseCollection):
125
130
  self.session.delete(f"{self.base_path}/{id}")
126
131
 
127
132
  def upload_and_attach_file_as_note(
128
- self, parent_id: str, file_data: IO, note_text: str = "", file_name: str = ""
133
+ self,
134
+ parent_id: str,
135
+ file_data: IO,
136
+ note_text: str = "",
137
+ file_name: str = "",
138
+ upload_key: str | None = None,
129
139
  ) -> Note:
130
140
  """Uploads a file and attaches it to a new note. A user can be tagged in the note_text string by using f-string and the User.to_note_mention() method.
131
141
  This allows for easy tagging and referencing of users within notes. example: f"Hello {tagged_user.to_note_mention()}!"
@@ -140,35 +150,48 @@ class AttachmentCollection(BaseCollection):
140
150
  Any additional text to add to the note, by default ""
141
151
  file_name : str, optional
142
152
  The name of the file, by default ""
153
+ upload_key : str | None, optional
154
+ Override the storage key used when signing and uploading the file.
155
+ Defaults to ``{parent_id}/{note_id}/{file_name}``.
143
156
 
144
157
  Returns
145
158
  -------
146
159
  Note
147
160
  The created note.
148
161
  """
149
- file_type = mimetypes.guess_type(file_name)[0]
150
- file_collection = self._get_file_collection()
162
+ if not (upload_key or file_name):
163
+ raise ValueError("A file name or upload key must be provided for attachment upload.")
164
+
151
165
  note_collection = self._get_note_collection()
166
+ note = Note(
167
+ parent_id=parent_id,
168
+ note=note_text,
169
+ )
170
+ registered_note = note_collection.create(note=note)
171
+ if upload_key:
172
+ attachment_name = file_name or Path(upload_key).name
173
+ upload_name = upload_key
174
+ else:
175
+ attachment_name = file_name
176
+ upload_name = f"{parent_id}/{registered_note.id}/{file_name}"
177
+ file_type = mimetypes.guess_type(attachment_name or upload_name)[0]
178
+ file_collection = self._get_file_collection()
152
179
 
153
180
  file_collection.sign_and_upload_file(
154
181
  data=file_data,
155
- name=file_name,
182
+ name=upload_name,
156
183
  namespace=FileNamespace.RESULT.value,
157
184
  content_type=file_type,
158
185
  )
159
186
  file_info = file_collection.get_by_name(
160
- name=file_name, namespace=FileNamespace.RESULT.value
161
- )
162
- note = Note(
163
- parent_id=parent_id,
164
- note=note_text,
187
+ name=upload_name, namespace=FileNamespace.RESULT.value
165
188
  )
166
- registered_note = note_collection.create(note=note)
167
189
  self.attach_file_to_note(
168
190
  note_id=registered_note.id,
169
- file_name=file_name,
191
+ file_name=attachment_name,
170
192
  file_key=file_info.name,
171
193
  )
194
+
172
195
  return note_collection.get_by_id(id=registered_note.id)
173
196
 
174
197
  @validate_call