albert 1.13.0b1__tar.gz → 1.14.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 (296) hide show
  1. {albert-1.13.0b1 → albert-1.14.0}/CHANGELOG.md +8 -1
  2. {albert-1.13.0b1 → albert-1.14.0}/PKG-INFO +1 -1
  3. albert-1.14.0/docs/examples/inventory.md +37 -0
  4. albert-1.14.0/docs/resources/acls.md +1 -0
  5. {albert-1.13.0b1 → albert-1.14.0}/mkdocs.yml +2 -0
  6. {albert-1.13.0b1 → albert-1.14.0}/scripts/validate_version_bump.py +11 -3
  7. {albert-1.13.0b1 → albert-1.14.0}/src/albert/__init__.py +1 -1
  8. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/attachments.py +17 -11
  9. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/projects.py +9 -9
  10. albert-1.14.0/src/albert/collections/worksheets.py +283 -0
  11. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/auth/_listener.py +4 -2
  12. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/shared/enums.py +1 -0
  13. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/acls.py +26 -2
  14. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/notebooks.py +26 -2
  15. {albert-1.13.0b1 → albert-1.14.0}/src/albert/utils/property_data.py +61 -1
  16. albert-1.14.0/src/albert/utils/worksheets.py +90 -0
  17. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_inventory.py +3 -2
  18. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_property_data.py +152 -1
  19. albert-1.13.0b1/src/albert/collections/worksheets.py +0 -118
  20. {albert-1.13.0b1 → albert-1.14.0}/.circleci/config.yml +0 -0
  21. {albert-1.13.0b1 → albert-1.14.0}/.gitignore +0 -0
  22. {albert-1.13.0b1 → albert-1.14.0}/.pre-commit-config.yaml +0 -0
  23. {albert-1.13.0b1 → albert-1.14.0}/.vscode/settings.json +0 -0
  24. {albert-1.13.0b1 → albert-1.14.0}/CONTRIBUTING.md +0 -0
  25. {albert-1.13.0b1 → albert-1.14.0}/LICENSE +0 -0
  26. {albert-1.13.0b1 → albert-1.14.0}/README.md +0 -0
  27. {albert-1.13.0b1 → albert-1.14.0}/docs/CONTRIBUTING.md +0 -0
  28. {albert-1.13.0b1 → albert-1.14.0}/docs/albert.md +0 -0
  29. {albert-1.13.0b1 → albert-1.14.0}/docs/assets/Vector_Favicon_Blue.svg +0 -0
  30. {albert-1.13.0b1 → albert-1.14.0}/docs/assets/Wordmark_Black.png +0 -0
  31. {albert-1.13.0b1 → albert-1.14.0}/docs/assets/Wordmark_White.png +0 -0
  32. {albert-1.13.0b1 → albert-1.14.0}/docs/authentication.md +0 -0
  33. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/activities.md +0 -0
  34. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/attachments.md +0 -0
  35. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/base.md +0 -0
  36. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/batch_data.md +0 -0
  37. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/btdataset.md +0 -0
  38. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/btinsight.md +0 -0
  39. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/btmodel.md +0 -0
  40. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/cas.md +0 -0
  41. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/companies.md +0 -0
  42. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/custom_fields.md +0 -0
  43. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/custom_templates.md +0 -0
  44. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/data_columns.md +0 -0
  45. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/data_templates.md +0 -0
  46. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/entity_types.md +0 -0
  47. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/files.md +0 -0
  48. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/hazards.md +0 -0
  49. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/inventory.md +0 -0
  50. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/links.md +0 -0
  51. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/lists.md +0 -0
  52. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/locations.md +0 -0
  53. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/lots.md +0 -0
  54. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/notebooks.md +0 -0
  55. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/notes.md +0 -0
  56. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/parameter_groups.md +0 -0
  57. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/parameters.md +0 -0
  58. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/pricings.md +0 -0
  59. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/product_design.md +0 -0
  60. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/projects.md +0 -0
  61. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/property_data.md +0 -0
  62. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/reports.md +0 -0
  63. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/roles.md +0 -0
  64. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/storage_classes.md +0 -0
  65. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/storage_locations.md +0 -0
  66. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/substances.md +0 -0
  67. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/synthesis.md +0 -0
  68. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/tags.md +0 -0
  69. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/tasks.md +0 -0
  70. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/un_numbers.md +0 -0
  71. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/units.md +0 -0
  72. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/users.md +0 -0
  73. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/workflows.md +0 -0
  74. {albert-1.13.0b1 → albert-1.14.0}/docs/collections/worksheets.md +0 -0
  75. {albert-1.13.0b1 → albert-1.14.0}/docs/concepts.md +0 -0
  76. {albert-1.13.0b1 → albert-1.14.0}/docs/configuration.md +0 -0
  77. {albert-1.13.0b1 → albert-1.14.0}/docs/credentials.md +0 -0
  78. {albert-1.13.0b1 → albert-1.14.0}/docs/examples/notebook.md +0 -0
  79. {albert-1.13.0b1 → albert-1.14.0}/docs/examples/property_data.md +0 -0
  80. {albert-1.13.0b1 → albert-1.14.0}/docs/examples/tasks.md +0 -0
  81. {albert-1.13.0b1 → albert-1.14.0}/docs/index.md +0 -0
  82. {albert-1.13.0b1 → albert-1.14.0}/docs/installation.md +0 -0
  83. {albert-1.13.0b1 → albert-1.14.0}/docs/migration.md +0 -0
  84. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/activities.md +0 -0
  85. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/attachments.md +0 -0
  86. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/batch_data.md +0 -0
  87. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/btdataset.md +0 -0
  88. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/btinsight.md +0 -0
  89. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/btmodel.md +0 -0
  90. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/cas.md +0 -0
  91. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/companies.md +0 -0
  92. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/custom_fields.md +0 -0
  93. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/custom_templates.md +0 -0
  94. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/data_columns.md +0 -0
  95. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/data_templates.md +0 -0
  96. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/entity_types.md +0 -0
  97. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/files.md +0 -0
  98. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/hazards.md +0 -0
  99. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/identifiers.md +0 -0
  100. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/inventory.md +0 -0
  101. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/links.md +0 -0
  102. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/lists.md +0 -0
  103. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/locations.md +0 -0
  104. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/lots.md +0 -0
  105. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/notebooks.md +0 -0
  106. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/notes.md +0 -0
  107. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/parameter_groups.md +0 -0
  108. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/parameters.md +0 -0
  109. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/pricings.md +0 -0
  110. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/product_design.md +0 -0
  111. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/projects.md +0 -0
  112. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/property_data.md +0 -0
  113. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/reports.md +0 -0
  114. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/roles.md +0 -0
  115. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/sheets.md +0 -0
  116. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/storage_classes.md +0 -0
  117. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/storage_locations.md +0 -0
  118. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/substances.md +0 -0
  119. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/synthesis.md +0 -0
  120. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/tags.md +0 -0
  121. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/tasks.md +0 -0
  122. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/un_numbers.md +0 -0
  123. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/units.md +0 -0
  124. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/users.md +0 -0
  125. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/workflows.md +0 -0
  126. {albert-1.13.0b1 → albert-1.14.0}/docs/resources/worksheets.md +0 -0
  127. {albert-1.13.0b1 → albert-1.14.0}/docs/sso.md +0 -0
  128. {albert-1.13.0b1 → albert-1.14.0}/docs/styles/extra.css +0 -0
  129. {albert-1.13.0b1 → albert-1.14.0}/pyproject.toml +0 -0
  130. {albert-1.13.0b1 → albert-1.14.0}/scripts/validate_release_tag.py +0 -0
  131. {albert-1.13.0b1 → albert-1.14.0}/setup.sh +0 -0
  132. {albert-1.13.0b1 → albert-1.14.0}/src/albert/client.py +0 -0
  133. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/__init__.py +0 -0
  134. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/activities.py +0 -0
  135. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/base.py +0 -0
  136. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/batch_data.py +0 -0
  137. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/btdataset.py +0 -0
  138. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/btinsight.py +0 -0
  139. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/btmodel.py +0 -0
  140. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/cas.py +0 -0
  141. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/companies.py +0 -0
  142. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/custom_fields.py +0 -0
  143. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/custom_templates.py +0 -0
  144. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/data_columns.py +0 -0
  145. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/data_templates.py +0 -0
  146. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/entity_types.py +0 -0
  147. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/files.py +0 -0
  148. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/hazards.py +0 -0
  149. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/inventory.py +0 -0
  150. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/links.py +0 -0
  151. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/lists.py +0 -0
  152. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/locations.py +0 -0
  153. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/lots.py +0 -0
  154. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/notebooks.py +0 -0
  155. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/notes.py +0 -0
  156. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/parameter_groups.py +0 -0
  157. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/parameters.py +0 -0
  158. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/pricings.py +0 -0
  159. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/product_design.py +0 -0
  160. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/property_data.py +0 -0
  161. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/report_templates.py +0 -0
  162. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/reports.py +0 -0
  163. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/roles.py +0 -0
  164. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/storage_classes.py +0 -0
  165. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/storage_locations.py +0 -0
  166. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/substance.py +0 -0
  167. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/synthesis.py +0 -0
  168. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/tags.py +0 -0
  169. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/tasks.py +0 -0
  170. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/un_numbers.py +0 -0
  171. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/units.py +0 -0
  172. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/users.py +0 -0
  173. {albert-1.13.0b1 → albert-1.14.0}/src/albert/collections/workflows.py +0 -0
  174. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/__init__.py +0 -0
  175. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/auth/__init__.py +0 -0
  176. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/auth/_manager.py +0 -0
  177. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/auth/credentials.py +0 -0
  178. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/auth/sso.py +0 -0
  179. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/base.py +0 -0
  180. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/logging.py +0 -0
  181. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/pagination.py +0 -0
  182. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/session.py +0 -0
  183. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/shared/__init__.py +0 -0
  184. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/shared/identifiers.py +0 -0
  185. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/shared/models/base.py +0 -0
  186. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/shared/models/patch.py +0 -0
  187. {albert-1.13.0b1 → albert-1.14.0}/src/albert/core/shared/types.py +0 -0
  188. {albert-1.13.0b1 → albert-1.14.0}/src/albert/exceptions.py +0 -0
  189. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/__init__.py +0 -0
  190. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/_mixins.py +0 -0
  191. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/activities.py +0 -0
  192. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/attachments.py +0 -0
  193. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/batch_data.py +0 -0
  194. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/btdataset.py +0 -0
  195. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/btinsight.py +0 -0
  196. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/btmodel.py +0 -0
  197. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/cas.py +0 -0
  198. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/companies.py +0 -0
  199. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/custom_fields.py +0 -0
  200. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/custom_templates.py +0 -0
  201. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/data_columns.py +0 -0
  202. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/data_templates.py +0 -0
  203. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/entity_types.py +0 -0
  204. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/facet.py +0 -0
  205. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/files.py +0 -0
  206. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/hazards.py +0 -0
  207. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/inventory.py +0 -0
  208. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/links.py +0 -0
  209. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/lists.py +0 -0
  210. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/locations.py +0 -0
  211. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/lots.py +0 -0
  212. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/notes.py +0 -0
  213. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/parameter_groups.py +0 -0
  214. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/parameters.py +0 -0
  215. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/pricings.py +0 -0
  216. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/product_design.py +0 -0
  217. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/projects.py +0 -0
  218. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/property_data.py +0 -0
  219. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/report_templates.py +0 -0
  220. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/reports.py +0 -0
  221. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/roles.py +0 -0
  222. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/sheets.py +0 -0
  223. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/storage_classes.py +0 -0
  224. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/storage_locations.py +0 -0
  225. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/substance.py +0 -0
  226. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/synthesis.py +0 -0
  227. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/tagged_base.py +0 -0
  228. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/tags.py +0 -0
  229. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/tasks.py +0 -0
  230. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/un_numbers.py +0 -0
  231. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/units.py +0 -0
  232. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/users.py +0 -0
  233. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/worker_jobs.py +0 -0
  234. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/workflows.py +0 -0
  235. {albert-1.13.0b1 → albert-1.14.0}/src/albert/resources/worksheets.py +0 -0
  236. {albert-1.13.0b1 → albert-1.14.0}/src/albert/utils/__init__.py +0 -0
  237. {albert-1.13.0b1 → albert-1.14.0}/src/albert/utils/_auth.py +0 -0
  238. {albert-1.13.0b1 → albert-1.14.0}/src/albert/utils/_patch.py +0 -0
  239. {albert-1.13.0b1 → albert-1.14.0}/src/albert/utils/data_template.py +0 -0
  240. {albert-1.13.0b1 → albert-1.14.0}/src/albert/utils/inventory.py +0 -0
  241. {albert-1.13.0b1 → albert-1.14.0}/src/albert/utils/tasks.py +0 -0
  242. {albert-1.13.0b1 → albert-1.14.0}/tests/__init__.py +0 -0
  243. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/__init__.py +0 -0
  244. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_activities.py +0 -0
  245. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_attachments.py +0 -0
  246. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_batch_data.py +0 -0
  247. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_btdataset.py +0 -0
  248. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_btinsight.py +0 -0
  249. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_btmodel.py +0 -0
  250. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_cas.py +0 -0
  251. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_company.py +0 -0
  252. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_custom_fields.py +0 -0
  253. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_custom_templates.py +0 -0
  254. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_data_columns.py +0 -0
  255. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_data_templates.py +0 -0
  256. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_entity_types.py +0 -0
  257. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_files.py +0 -0
  258. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_hazards.py +0 -0
  259. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_links.py +0 -0
  260. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_lists.py +0 -0
  261. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_locations.py +0 -0
  262. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_lots.py +0 -0
  263. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_notebooks.py +0 -0
  264. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_notes.py +0 -0
  265. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_parameter_groups.py +0 -0
  266. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_parameters.py +0 -0
  267. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_pricings.py +0 -0
  268. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_product_design.py +0 -0
  269. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_projects.py +0 -0
  270. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_report_templates.py +0 -0
  271. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_reports.py +0 -0
  272. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_roles.py +0 -0
  273. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_storage_classes.py +0 -0
  274. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_storage_locations.py +0 -0
  275. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_substance.py +0 -0
  276. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_tags.py +0 -0
  277. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_tasks.py +0 -0
  278. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_un_number.py +0 -0
  279. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_units.py +0 -0
  280. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_users.py +0 -0
  281. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_workflows.py +0 -0
  282. {albert-1.13.0b1 → albert-1.14.0}/tests/collections/test_worksheet.py +0 -0
  283. {albert-1.13.0b1 → albert-1.14.0}/tests/conftest.py +0 -0
  284. {albert-1.13.0b1 → albert-1.14.0}/tests/core/shared/__init__.py +0 -0
  285. {albert-1.13.0b1 → albert-1.14.0}/tests/core/shared/test_identifiers.py +0 -0
  286. {albert-1.13.0b1 → albert-1.14.0}/tests/core/shared/test_types.py +0 -0
  287. {albert-1.13.0b1 → albert-1.14.0}/tests/core/test_auth.py +0 -0
  288. {albert-1.13.0b1 → albert-1.14.0}/tests/data/SDS_HCL.pdf +0 -0
  289. {albert-1.13.0b1 → albert-1.14.0}/tests/data/dontpanic.jpg +0 -0
  290. {albert-1.13.0b1 → albert-1.14.0}/tests/resources/test_inventory.py +0 -0
  291. {albert-1.13.0b1 → albert-1.14.0}/tests/resources/test_lots.py +0 -0
  292. {albert-1.13.0b1 → albert-1.14.0}/tests/resources/test_notebooks.py +0 -0
  293. {albert-1.13.0b1 → albert-1.14.0}/tests/resources/test_sheets.py +0 -0
  294. {albert-1.13.0b1 → albert-1.14.0}/tests/seeding.py +0 -0
  295. {albert-1.13.0b1 → albert-1.14.0}/tests/utils/fake_session.py +0 -0
  296. {albert-1.13.0b1 → albert-1.14.0}/tests/utils/test_patches.py +0 -0
@@ -5,7 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [1.14.0] - 2025-01-29
9
+
10
+ ### Added
11
+
12
+ - Added `ACLContainer` model for `{class, fgclist}` ACL payloads.
13
+ - Added `WorksheetCollection.duplicate_sheet` functionality.
14
+ - Added `WorksheetCollection.create_sheet_template` functionality.
15
+ - Added a deprecation warning for `NotebookCopyACL`; formal deprecation planned for 2.0 (use `ACLContainer`).
9
16
 
10
17
  ## [1.2.0] - 2025-07-25
11
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: albert
3
- Version: 1.13.0b1
3
+ Version: 1.14.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
@@ -0,0 +1,37 @@
1
+ # Inventory
2
+
3
+ Albert Inventory serves as a digital manifestation of your physical inventory. It enables you to sort, filter, trace, and manage all types of inventory.
4
+
5
+ ## Inventory function on CAS
6
+
7
+ Inventory function is a business-controlled, multi-select list on the Inventory ↔ CAS relationship.
8
+ Use it by updating an existing inventory item.
9
+
10
+ !!! example "Add inventory function values to a CAS entry"
11
+ ```python
12
+ from albert import Albert
13
+ from albert.resources.lists import ListItem, ListItemCategory
14
+
15
+ client = Albert.from_client_credentials()
16
+
17
+ inventory_id = "INV123"
18
+ cas_id = "CAS123"
19
+
20
+ # Optional: create a new inventoryFunction list item first.
21
+ list_item = ListItem(
22
+ name="Primary Function",
23
+ category=ListItemCategory.INVENTORY,
24
+ list_type="inventoryFunction",
25
+ )
26
+ list_item = client.lists.create(list_item=list_item)
27
+
28
+ inventory_item = client.inventory.get_by_id(id=inventory_id)
29
+ if inventory_item.cas:
30
+ for cas_amount in inventory_item.cas:
31
+ if cas_amount.id == cas_id:
32
+ cas_amount.inventory_function = [list_item]
33
+ break
34
+
35
+ updated_item = client.inventory.update(inventory_item=inventory_item)
36
+ print(updated_item.id)
37
+ ```
@@ -0,0 +1 @@
1
+ ::: albert.resources.acls
@@ -177,6 +177,7 @@ nav:
177
177
  - Resources:
178
178
  - Activities: resources/activities.md
179
179
  - Attachments: resources/attachments.md
180
+ - ACL: resources/acls.md
180
181
  - Batch Data: resources/batch_data.md
181
182
  - Breakthrough:
182
183
  - Breakthrough Datasets: resources/btdataset.md
@@ -221,6 +222,7 @@ nav:
221
222
  - Worksheets: resources/worksheets.md
222
223
  - Sheets: resources/sheets.md
223
224
  - Examples:
225
+ - Inventory: examples/inventory.md
224
226
  - Property Data: examples/property_data.md
225
227
  - Tasks: examples/tasks.md
226
228
  - Notebooks: examples/notebook.md
@@ -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.13.0b1"
7
+ __version__ = "1.14.0"
@@ -152,20 +152,30 @@ class AttachmentCollection(BaseCollection):
152
152
  The name of the file, by default ""
153
153
  upload_key : str | None, optional
154
154
  Override the storage key used when signing and uploading the file.
155
- Defaults to the provided ``file_name``.
155
+ Defaults to ``{parent_id}/{note_id}/{file_name}``.
156
156
 
157
157
  Returns
158
158
  -------
159
159
  Note
160
160
  The created note.
161
161
  """
162
- upload_name = upload_key or file_name
163
- if not upload_name:
162
+ if not (upload_key or file_name):
164
163
  raise ValueError("A file name or upload key must be provided for attachment upload.")
165
164
 
166
- file_type = mimetypes.guess_type(file_name or upload_name)[0]
167
- file_collection = self._get_file_collection()
168
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()
169
179
 
170
180
  file_collection.sign_and_upload_file(
171
181
  data=file_data,
@@ -176,16 +186,12 @@ class AttachmentCollection(BaseCollection):
176
186
  file_info = file_collection.get_by_name(
177
187
  name=upload_name, namespace=FileNamespace.RESULT.value
178
188
  )
179
- note = Note(
180
- parent_id=parent_id,
181
- note=note_text,
182
- )
183
- registered_note = note_collection.create(note=note)
184
189
  self.attach_file_to_note(
185
190
  note_id=registered_note.id,
186
- file_name=file_name or Path(upload_name).name,
191
+ file_name=attachment_name,
187
192
  file_key=file_info.name,
188
193
  )
194
+
189
195
  return note_collection.get_by_id(id=registered_note.id)
190
196
 
191
197
  @validate_call
@@ -142,17 +142,17 @@ class ProjectCollection(BaseCollection):
142
142
  ----------
143
143
  text : str, optional
144
144
  Full-text search query.
145
- status : list of str, optional
145
+ status : list[str], optional
146
146
  Filter by project statuses.
147
- market_segment : list of str, optional
147
+ market_segment : list[str], optional
148
148
  Filter by market segment.
149
- application : list of str, optional
149
+ application : list[str], optional
150
150
  Filter by application.
151
- technology : list of str, optional
151
+ technology : list[str], optional
152
152
  Filter by technology tags.
153
- created_by : list of str, optional
153
+ created_by : list[str], optional
154
154
  Filter by user names who created the project.
155
- location : list of str, optional
155
+ location : list[str], optional
156
156
  Filter by location(s).
157
157
  from_created_at : str, optional
158
158
  Earliest creation date in 'YYYY-MM-DD' format.
@@ -162,15 +162,15 @@ class ProjectCollection(BaseCollection):
162
162
  Facet field to filter on.
163
163
  facet_text : str, optional
164
164
  Facet text to search for.
165
- contains_field : list of str, optional
165
+ contains_field : list[str], optional
166
166
  Fields to search inside.
167
- contains_text : list of str, optional
167
+ contains_text : list[str], optional
168
168
  Values to search for within the `contains_field`.
169
169
  linked_to : str, optional
170
170
  Entity ID the project is linked to.
171
171
  my_project : bool, optional
172
172
  If True, return only projects owned by current user.
173
- my_role : list of str, optional
173
+ my_role : list[str], optional
174
174
  User roles to filter by.
175
175
  order_by : OrderBy, optional
176
176
  Sort order. Default is DESCENDING.
@@ -0,0 +1,283 @@
1
+ from pydantic import validate_call
2
+
3
+ from albert.collections.base import BaseCollection
4
+ from albert.collections.custom_templates import CustomTemplatesCollection
5
+ from albert.core.session import AlbertSession
6
+ from albert.core.shared.identifiers import ProjectId
7
+ from albert.resources.acls import ACLContainer
8
+ from albert.resources.custom_templates import CustomTemplate
9
+ from albert.resources.worksheets import Worksheet
10
+ from albert.utils.worksheets import (
11
+ get_columns_to_copy,
12
+ get_prg_rows_to_copy,
13
+ get_sheet_from_worksheet,
14
+ get_task_rows_to_copy,
15
+ )
16
+
17
+
18
+ class WorksheetCollection(BaseCollection):
19
+ """WorksheetCollection is a collection class for managing Worksheet entities in the Albert platform."""
20
+
21
+ _api_version = "v3"
22
+
23
+ def __init__(self, *, session: AlbertSession):
24
+ super().__init__(session=session)
25
+ self.base_path = f"/api/{WorksheetCollection._api_version}/worksheet"
26
+
27
+ def _add_session_to_sheets(self, response_json: dict):
28
+ sheets = response_json.get("Sheets")
29
+ if sheets:
30
+ for s in sheets:
31
+ s["session"] = self.session
32
+ s["project_id"] = response_json["projectId"]
33
+ response_json["session"] = self.session
34
+ return response_json
35
+
36
+ @validate_call
37
+ def get_by_project_id(self, *, project_id: ProjectId) -> Worksheet:
38
+ """Retrieve a worksheet by its project ID. Projects and Worksheets are 1:1 in the Albert platform.
39
+
40
+ Parameters
41
+ ----------
42
+ project_id : str
43
+ The project ID to retrieve the worksheet for.
44
+
45
+ Returns
46
+ -------
47
+ Worksheet
48
+ The Worksheet object for that project.
49
+ """
50
+
51
+ params = {"type": "project", "id": project_id}
52
+ response = self.session.get(self.base_path, params=params)
53
+
54
+ response_json = response.json()
55
+
56
+ # Sheets are themselves collections, and therefore need access to the session
57
+ response_json = self._add_session_to_sheets(response_json)
58
+ return Worksheet(**response_json)
59
+
60
+ @validate_call
61
+ def setup_worksheet(self, *, project_id: ProjectId, add_sheet=False) -> Worksheet:
62
+ """Setup a new worksheet for a project.
63
+
64
+ Parameters
65
+ ----------
66
+ project_id : str
67
+ The project ID to setup the worksheet for.
68
+ add_sheet : bool, optional
69
+ Whether to add a blank sheet to the worksheet, by default False
70
+
71
+ Returns
72
+ -------
73
+ Worksheet
74
+ The Worksheet object for the project.
75
+ """
76
+
77
+ params = {"sheets": str(add_sheet).lower()}
78
+ path = f"{self.base_path}/{project_id}/setup"
79
+ self.session.post(path, json=params)
80
+ return self.get_by_project_id(project_id=project_id)
81
+
82
+ @validate_call
83
+ def setup_new_sheet_from_template(
84
+ self, *, project_id: ProjectId, sheet_template_id: str, sheet_name: str
85
+ ) -> Worksheet:
86
+ """Create a new sheet in the Worksheet related to the specified Project from a template.
87
+
88
+ Parameters
89
+ ----------
90
+ project_id : str
91
+ _description_
92
+ sheet_template_id : str
93
+ _description_
94
+ sheet_name : str
95
+ _description_
96
+
97
+ Returns
98
+ -------
99
+ Worksheet
100
+ The Worksheet object for the project.
101
+ """
102
+ payload = {"name": sheet_name}
103
+ params = {"templateId": sheet_template_id}
104
+ path = f"{self.base_path}/project/{project_id}/sheets"
105
+ self.session.post(path, json=payload, params=params)
106
+ return self.get_by_project_id(project_id=project_id)
107
+
108
+ @validate_call
109
+ def add_sheet(self, *, project_id: ProjectId, sheet_name: str) -> Worksheet:
110
+ """Create a new blank sheet in the Worksheet with the specified name.
111
+
112
+ Parameters
113
+ ----------
114
+ project_id : str
115
+ The project ID for the Worksheet to add the sheet to.
116
+ sheet_name : str
117
+ The name of the new sheet.
118
+
119
+ Returns
120
+ -------
121
+ Worksheet
122
+ The Worksheet object for the project.
123
+ """
124
+ payload = {"name": sheet_name}
125
+ url = f"{self.base_path}/project/{project_id}/sheets"
126
+ self.session.put(url=url, json=payload)
127
+ return self.get_by_project_id(project_id=project_id)
128
+
129
+ @validate_call
130
+ def duplicate_sheet(
131
+ self,
132
+ *,
133
+ project_id: ProjectId,
134
+ source_sheet_name: str,
135
+ new_sheet_name: str,
136
+ copy_all_pd_rows: bool = True,
137
+ copy_all_pinned_columns: bool = True,
138
+ copy_all_unpinned_columns: bool = True,
139
+ column_names: list[str] | None = None,
140
+ task_row_names: list[str] | None = None,
141
+ ) -> Worksheet:
142
+ """Duplicate an existing sheet within the same project.
143
+
144
+ This creates a new sheet based on the specified source sheet. You can control
145
+ which Product Design (PD) & Results rows and columns are copied using the available options.
146
+ The final list of columns copied is the union of:
147
+ - all pinned columns (if copy_all_pinned_columns is True)
148
+ - all unpinned columns (if copy_all_unpinned_columns is True)
149
+ - explicitly listed column names (column_names)
150
+
151
+ Parameters
152
+ ----------
153
+ project_id : str
154
+ The project ID under which the sheet exists.
155
+ source_sheet_name : str
156
+ The name of the existing sheet to duplicate.
157
+ new_sheet_name : str
158
+ The name of the new sheet to create.
159
+ copy_all_pd_rows : bool, optional
160
+ When True, all PD (Product Design) rows from the source sheet are copied.
161
+ When False, only rows corresponding to the selected columns will be copied.
162
+ Default is True.
163
+ copy_all_pinned_columns : bool, optional
164
+ If True, includes all pinned columns from the source sheet. Default is True.
165
+ copy_all_unpinned_columns : bool, optional
166
+ If True, includes all unpinned columns from the source sheet. Default is True.
167
+ column_names : list[str], optional
168
+ A list of column names to explicitly copy. These are resolved internally
169
+ to column IDs using the sheet's product design grid.
170
+ task_row_names : list[str], optional
171
+ List of task row names to include from the tasks.
172
+
173
+ Returns
174
+ -------
175
+ Worksheet
176
+ The Worksheet entity containing newly created sheet.
177
+ """
178
+
179
+ worksheet = self.get_by_project_id(project_id=project_id)
180
+ sheet = get_sheet_from_worksheet(sheet_name=source_sheet_name, worksheet=worksheet)
181
+ columns = get_columns_to_copy(
182
+ sheet=sheet,
183
+ copy_all_pinned_columns=copy_all_pinned_columns,
184
+ copy_all_unpinned_columns=copy_all_unpinned_columns,
185
+ input_column_names=column_names,
186
+ )
187
+ task_rows = get_task_rows_to_copy(sheet=sheet, input_row_names=task_row_names)
188
+
189
+ payload = {
190
+ "name": new_sheet_name,
191
+ "sourceData": {
192
+ "projectId": project_id,
193
+ "sheetId": sheet.id,
194
+ "Columns": [{"id": col_id} for col_id in columns],
195
+ "copyAllPDRows": copy_all_pd_rows,
196
+ "TaskRows": [{"id": row_id} for row_id in task_rows],
197
+ },
198
+ }
199
+
200
+ path = f"{self.base_path}/project/{project_id}/sheets"
201
+ self.session.put(path, json=payload)
202
+ return self.get_by_project_id(project_id=project_id)
203
+
204
+ @validate_call
205
+ def create_sheet_template(
206
+ self,
207
+ *,
208
+ project_id: ProjectId,
209
+ source_sheet_name: str,
210
+ template_name: str,
211
+ copy_all_pd_rows: bool = True,
212
+ copy_all_pinned_columns: bool = True,
213
+ copy_all_unpinned_columns: bool = True,
214
+ column_names: list[str] | None = None,
215
+ task_row_names: list[str] | None = None,
216
+ prg_row_names: list[str] | None = None,
217
+ acl: ACLContainer | None = None,
218
+ ) -> CustomTemplate:
219
+ """Create a new sheet template from an existing sheet.
220
+
221
+ Parameters
222
+ ----------
223
+ project_id : str
224
+ The project ID under which the sheet exists.
225
+ source_sheet_name : str
226
+ The name of the existing sheet to use as the template source.
227
+ template_name : str
228
+ The name of the new template.
229
+ copy_all_pd_rows : bool, optional
230
+ When True, all PD (Product Design) rows from the source sheet are copied.
231
+ When False, only rows corresponding to the selected columns will be copied.
232
+ copy_all_pinned_columns : bool, optional
233
+ If True, includes all pinned columns from the source sheet. Default is True.
234
+ copy_all_unpinned_columns : bool, optional
235
+ If True, includes all unpinned columns from the source sheet. Default is True.
236
+ column_names : list[str], optional
237
+ A list of column names to explicitly copy. These are resolved internally
238
+ to column IDs using the sheet's product design grid.
239
+ task_row_names : list[str], optional
240
+ List of task row names to include from the tasks.
241
+ prg_row_names : list[str], optional
242
+ List of parameter group row names to include.
243
+ acl : ACLContainer, optional
244
+ ACL for the template.
245
+
246
+ Returns
247
+ -------
248
+ CustomTemplate
249
+ The CustomTemplate for the created sheet template.
250
+ """
251
+ worksheet = self.get_by_project_id(project_id=project_id)
252
+ sheet = get_sheet_from_worksheet(sheet_name=source_sheet_name, worksheet=worksheet)
253
+ columns = get_columns_to_copy(
254
+ sheet=sheet,
255
+ copy_all_pinned_columns=copy_all_pinned_columns,
256
+ copy_all_unpinned_columns=copy_all_unpinned_columns,
257
+ input_column_names=column_names,
258
+ )
259
+ if not columns:
260
+ raise ValueError("At least one column must be selected to create a template.")
261
+ task_rows = get_task_rows_to_copy(sheet=sheet, input_row_names=task_row_names)
262
+ prg_rows = get_prg_rows_to_copy(sheet=sheet, input_row_names=prg_row_names)
263
+
264
+ payload = {
265
+ "name": template_name,
266
+ "sourceData": {
267
+ "projectId": project_id,
268
+ "sheetId": sheet.id,
269
+ "Columns": [{"id": col_id} for col_id in columns],
270
+ "copyAllPDRows": copy_all_pd_rows,
271
+ "TaskRows": [{"id": row_id} for row_id in task_rows],
272
+ "PRGRows": [{"id": row_id} for row_id in prg_rows],
273
+ },
274
+ }
275
+
276
+ if acl is not None:
277
+ payload["ACL"] = acl.model_dump(exclude_none=True, by_alias=True, mode="json")
278
+
279
+ path = f"{self.base_path}/sheet/template"
280
+ response = self.session.post(path, json=payload)
281
+ response_json = response.json()
282
+ ctp_id = response_json.get("ctpId")
283
+ return CustomTemplatesCollection(session=self.session).get_by_id(id=ctp_id)
@@ -31,6 +31,10 @@ class RequestHandler(BaseHTTPRequestHandler):
31
31
  status = "successful" if self.server.token else "failed (no token found)"
32
32
  self.send_response(200)
33
33
  self.send_header("Content-Type", "text/html")
34
+ self.send_header(
35
+ "Content-Security-Policy",
36
+ "default-src 'none'; frame-ancestors 'none'; base-uri 'none';",
37
+ )
34
38
  self.end_headers()
35
39
  self.wfile.write(
36
40
  f"""
@@ -38,8 +42,6 @@ class RequestHandler(BaseHTTPRequestHandler):
38
42
  <body>
39
43
  <h1>Authentication {status}</h1>
40
44
  <p>You can close this window now.</p>
41
- <script>window.close()</script>
42
- <button onclick="window.close()">Close Window</button>
43
45
  </body>
44
46
  </html>
45
47
  """.encode()
@@ -19,6 +19,7 @@ class SecurityClass(str, Enum):
19
19
  SHARED = "shared"
20
20
  RESTRICTED = "restricted"
21
21
  CONFIDENTIAL = "confidential"
22
+ # only used by "PROJECTS"
22
23
  PRIVATE = "private"
23
24
 
24
25
 
@@ -3,10 +3,11 @@ from enum import Enum
3
3
  from pydantic import Field
4
4
 
5
5
  from albert.core.base import BaseAlbertModel
6
+ from albert.core.shared.models.base import BaseResource
6
7
 
7
8
 
8
9
  class AccessControlLevel(str, Enum):
9
- """The fine grain control"""
10
+ """Access control levels you can grant users."""
10
11
 
11
12
  PROJECT_OWNER = "ProjectOwner"
12
13
  PROJECT_EDITOR = "ProjectEditor"
@@ -22,9 +23,32 @@ class AccessControlLevel(str, Enum):
22
23
 
23
24
 
24
25
  class ACL(BaseAlbertModel):
25
- """The Access Control List (ACL) for a user"""
26
+ """A single access rule for a user.
27
+
28
+ Attributes
29
+ ----------
30
+ id : str
31
+ The user or team this rule applies to.
32
+ fgc : AccessControlLevel | None
33
+ The access level for that user or team.
34
+ """
26
35
 
27
36
  id: str = Field(description="The id of the user for which this ACL applies")
28
37
  fgc: AccessControlLevel | None = Field(
29
38
  default=None, description="The Fine-Grain Control Level"
30
39
  )
40
+
41
+
42
+ class ACLContainer(BaseResource):
43
+ """Access settings with a default class and a list of rules.
44
+
45
+ Attributes
46
+ ----------
47
+ acl_class : str | None
48
+ The default access class (for example, "restricted" or "confidential").
49
+ fgclist : list[ACL] | None
50
+ Specific access rules for users or teams.
51
+ """
52
+
53
+ acl_class: str | None = Field(default=None, alias="class")
54
+ fgclist: list[ACL] | None = Field(default=None, alias="fgclist")
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  import uuid
5
+ import warnings
5
6
  from datetime import datetime
6
7
  from enum import Enum
7
8
  from pathlib import Path
@@ -14,7 +15,7 @@ from albert.core.base import BaseAlbertModel
14
15
  from albert.core.shared.identifiers import LinkId, NotebookId, ProjectId, SynthesisId, TaskId
15
16
  from albert.core.shared.models.base import BaseResource, EntityLink
16
17
  from albert.exceptions import AlbertException
17
- from albert.resources.acls import ACL
18
+ from albert.resources.acls import ACL, ACLContainer
18
19
 
19
20
 
20
21
  class ListBlockStyle(str, Enum):
@@ -297,13 +298,36 @@ class PutBlockPayload(BaseAlbertModel):
297
298
 
298
299
 
299
300
  class NotebookCopyACL(BaseResource):
301
+ """
302
+ Access settings applied to a copied notebook.
303
+
304
+ Warning
305
+ -----
306
+ Deprecated and will be removed in 2.0. Use ``ACLContainer`` instead.
307
+
308
+ Attributes
309
+ ----------
310
+ fgclist : list[ACL]
311
+ Specific access rules for users or teams.
312
+ acl_class : str
313
+ Default access class (for example, "restricted" or "confidential").
314
+ """
315
+
300
316
  fgclist: list[ACL] = Field(default=None)
301
317
  acl_class: str = Field(alias="class")
302
318
 
319
+ def __init__(self, **data):
320
+ warnings.warn(
321
+ "NotebookCopyACL is deprecated and will be removed in 2.0; use ACLContainer instead.",
322
+ DeprecationWarning,
323
+ stacklevel=2,
324
+ )
325
+ super().__init__(**data)
326
+
303
327
 
304
328
  class NotebookCopyInfo(BaseAlbertModel):
305
329
  id: NotebookId
306
330
  parent_id: str = Field(alias="parentId")
307
331
  notebook_name: str | None = Field(default=None, alias="notebookName")
308
332
  name: str | None = Field(default=None)
309
- acl: NotebookCopyACL | None = Field(default=None)
333
+ acl: ACLContainer | NotebookCopyACL | None = Field(default=None)