Office365-REST-Python-Client 3.1.0__py3-none-any.whl → 3.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. office365/communications/callrecords/call_record.py +18 -0
  2. office365/communications/calls/call.py +3 -0
  3. office365/communications/presences/presence.py +3 -0
  4. office365/communications/quotas.py +66 -0
  5. office365/communications/virtualevents/virtual_event.py +3 -0
  6. office365/directory/domains/domain.py +3 -0
  7. office365/directory/licenses/subscribed_sku.py +3 -0
  8. office365/directory/objects/object.py +3 -0
  9. office365/directory/quotas.py +34 -0
  10. office365/entity_collection.py +2 -2
  11. office365/graph_client.py +46 -2
  12. office365/limits.py +66 -0
  13. office365/migration/__init__.py +6 -1
  14. office365/migration/adapters/__init__.py +14 -4
  15. office365/migration/adapters/filesystem.py +4 -1
  16. office365/migration/assessment/export.py +5 -5
  17. office365/migration/assessment/issue.py +19 -0
  18. office365/migration/assessment/report.py +11 -0
  19. office365/migration/assessment/scanners/base.py +37 -49
  20. office365/migration/base.py +33 -8
  21. office365/migration/checkpoint.py +1 -1
  22. office365/migration/outlook/assessor.py +4 -4
  23. office365/migration/package/__init__.py +82 -0
  24. office365/migration/package/builder.py +603 -0
  25. office365/migration/package/crypto.py +54 -0
  26. office365/migration/package/manifest_xml.py +495 -0
  27. office365/migration/package/staging.py +180 -0
  28. office365/migration/report.py +7 -2
  29. office365/migration/report_io.py +79 -0
  30. office365/migration/runner.py +117 -10
  31. office365/migration/server_job.py +126 -14
  32. office365/migration/session.py +132 -49
  33. office365/migration/settings.py +67 -0
  34. office365/migration/sharepoint/__init__.py +2 -0
  35. office365/migration/sharepoint/adapters.py +207 -41
  36. office365/migration/sharepoint/assessor.py +45 -9
  37. office365/migration/sharepoint/options.py +57 -0
  38. office365/migration/sharepoint/package_target.py +209 -0
  39. office365/migration/sharepoint/registry.py +22 -0
  40. office365/migration/sharepoint/resolvers.py +96 -0
  41. office365/migration/sharepoint/scanners/__init__.py +29 -0
  42. office365/migration/sharepoint/scanners/browser_file_handling.py +55 -0
  43. office365/migration/sharepoint/scanners/checked_out_files.py +57 -0
  44. office365/migration/sharepoint/scanners/file_versions.py +56 -0
  45. office365/migration/sharepoint/scanners/files.py +5 -2
  46. office365/migration/sharepoint/scanners/large_excel_files.py +56 -0
  47. office365/migration/sharepoint/scanners/large_lists.py +58 -0
  48. office365/migration/sharepoint/scanners/large_sites.py +4 -3
  49. office365/migration/sharepoint/scanners/long_onedrive_urls.py +52 -0
  50. office365/migration/sharepoint/scanners/lookup.py +38 -0
  51. office365/migration/sharepoint/scanners/paths.py +9 -4
  52. office365/migration/sharepoint/scanners/permissions.py +20 -2
  53. office365/migration/sharepoint/scanners/smat.py +51 -0
  54. office365/migration/sharepoint/scanners/summary.py +1 -0
  55. office365/migration/sharepoint/scanners/thicket_folders.py +38 -0
  56. office365/migration/sharepoint/scanners/unsupported_site_templates.py +90 -0
  57. office365/migration/sharepoint/tenant_assessor.py +5 -3
  58. office365/migration/sharepoint/transfer.py +4 -2
  59. office365/migration/tasks.py +121 -0
  60. office365/migration/validators.py +2 -19
  61. office365/onedrive/driveitems/driveItem.py +116 -12
  62. office365/onedrive/termstore/groups/collection.py +2 -9
  63. office365/onedrive/termstore/sets/collection.py +2 -9
  64. office365/onedrive/termstore/terms/collection.py +3 -10
  65. office365/onedrive/workbooks/workbook.py +7 -0
  66. office365/outlook/contacts/folders/collection.py +4 -13
  67. office365/runtime/auth/authentication_context.py +5 -0
  68. office365/runtime/client_object.py +18 -0
  69. office365/runtime/client_object_collection.py +75 -245
  70. office365/runtime/client_request.py +77 -1
  71. office365/runtime/client_request_exception.py +127 -39
  72. office365/runtime/client_runtime_context.py +79 -8
  73. office365/runtime/converters/csv_writer.py +10 -4
  74. office365/runtime/converters/dataframe.py +84 -4
  75. office365/runtime/converters/json_file.py +12 -3
  76. office365/runtime/converters/parquet.py +67 -0
  77. office365/runtime/converters/records.py +31 -12
  78. office365/runtime/converters/registry.py +185 -0
  79. office365/runtime/converters/scalars.py +31 -0
  80. office365/runtime/converters/sql.py +69 -0
  81. office365/runtime/converters/streamers.py +105 -0
  82. office365/runtime/converters/streams.py +42 -0
  83. office365/runtime/converters/upsert.py +101 -0
  84. office365/runtime/exceptions.py +105 -0
  85. office365/runtime/http/prefer.py +26 -0
  86. office365/runtime/http/throttling.py +258 -56
  87. office365/runtime/imports.py +571 -0
  88. office365/runtime/limits.py +455 -0
  89. office365/runtime/odata/request.py +11 -2
  90. office365/runtime/odata/type.py +1 -0
  91. office365/runtime/odata/v3/batch_request.py +4 -2
  92. office365/runtime/odata/v4/batch_request.py +22 -7
  93. office365/runtime/operations.py +29 -0
  94. office365/runtime/parallel.py +12 -14
  95. office365/runtime/paths/v4/entity.py +2 -2
  96. office365/runtime/queries/batch.py +18 -1
  97. office365/runtime/queries/get_or_create.py +109 -0
  98. office365/runtime/record_collection.py +443 -0
  99. office365/runtime/retry.py +92 -26
  100. office365/runtime/transport/base.py +6 -1
  101. office365/runtime/transport/requests_transport.py +4 -0
  102. office365/runtime/transport/throttled_transport.py +67 -0
  103. office365/runtime/verification.py +40 -0
  104. office365/sharepoint/activities/logger.py +118 -8
  105. office365/sharepoint/administration/analytics/usage_service.py +15 -7
  106. office365/sharepoint/administration/sitemove/service.py +791 -8
  107. office365/sharepoint/alerts/alert.py +79 -0
  108. office365/sharepoint/apps/app_collection.py +25 -0
  109. office365/sharepoint/apps/launcher.py +16 -0
  110. office365/sharepoint/authpolicy/tenant/compliancereport.py +5 -0
  111. office365/sharepoint/businessdata/app_bdc_catalog.py +135 -0
  112. office365/sharepoint/businessdata/entityfieldvaluedictionary.py +65 -0
  113. office365/sharepoint/businessdata/entityidentifier.py +23 -0
  114. office365/sharepoint/businessdata/entityinstance.py +52 -0
  115. office365/sharepoint/businessdata/entityview.py +31 -0
  116. office365/sharepoint/businessdata/externalsubscriptionstore.py +9 -0
  117. office365/sharepoint/businessdata/typedescriptor.py +37 -0
  118. office365/sharepoint/client_context.py +40 -23
  119. office365/sharepoint/clientsidecomponent/hostedapps/app.py +21 -0
  120. office365/sharepoint/clientsidecomponent/hostedapps/manager.py +16 -0
  121. office365/sharepoint/comments/comment.py +84 -2
  122. office365/sharepoint/contentcenter/machinelearning/models/model.py +0 -23
  123. office365/sharepoint/contentcenter/machinelearning/publications/publication.py +0 -9
  124. office365/sharepoint/contentcenter/machinelearning/samples/sample.py +0 -14
  125. office365/sharepoint/contentcenter/machinelearning/workitems/item.py +0 -80
  126. office365/sharepoint/contentsolution/template_api.py +148 -0
  127. office365/sharepoint/contenttypes/collection.py +32 -10
  128. office365/sharepoint/convergence/addnoliveblobsinodcservicestateapi.py +9 -0
  129. office365/sharepoint/convergence/consumer_permissions.py +15 -0
  130. office365/sharepoint/convergence/migration_complete_state_api.py +25 -0
  131. office365/sharepoint/convergence/odcmetadatacleanedupapi.py +9 -0
  132. office365/sharepoint/documentmanagement/document_id.py +12 -0
  133. office365/sharepoint/entity.py +11 -2
  134. office365/sharepoint/entity_collection.py +2 -2
  135. office365/sharepoint/exceptions.py +194 -0
  136. office365/sharepoint/fields/builtin_field_id.py +45 -34
  137. office365/sharepoint/fields/builtin_field_name.py +123 -0
  138. office365/sharepoint/fields/coercion.py +131 -0
  139. office365/sharepoint/fields/collection.py +54 -37
  140. office365/sharepoint/fields/field.py +17 -0
  141. office365/sharepoint/fields/name.py +46 -2
  142. office365/sharepoint/files/collection.py +7 -1
  143. office365/sharepoint/files/file.py +54 -8
  144. office365/sharepoint/files/versions/version.py +38 -13
  145. office365/sharepoint/folders/collection.py +4 -0
  146. office365/sharepoint/folders/folder.py +91 -21
  147. office365/sharepoint/htmlpages/html_page.py +16 -0
  148. office365/sharepoint/htmlpages/html_page_service.py +19 -0
  149. office365/sharepoint/ir/migration.py +9 -0
  150. office365/sharepoint/listitems/caml/__init__.py +31 -0
  151. office365/sharepoint/listitems/caml/builder.py +129 -0
  152. office365/sharepoint/listitems/caml/expressions.py +424 -0
  153. office365/sharepoint/listitems/caml/fields.py +68 -0
  154. office365/sharepoint/listitems/caml/guard.py +63 -0
  155. office365/sharepoint/listitems/caml/query.py +71 -2
  156. office365/sharepoint/listitems/caml/values.py +113 -0
  157. office365/sharepoint/listitems/collection.py +21 -20
  158. office365/sharepoint/listitems/upsert.py +75 -0
  159. office365/sharepoint/lists/collection.py +29 -22
  160. office365/sharepoint/lists/exporter.py +4 -3
  161. office365/sharepoint/lists/list.py +397 -39
  162. office365/sharepoint/lists/smart_wiki_library_utility.py +58 -0
  163. office365/sharepoint/logger/export.py +12 -0
  164. office365/sharepoint/marketplace/app_metadata.py +97 -1
  165. office365/sharepoint/marketplace/appstatequeryjobdefinition.py +9 -0
  166. office365/sharepoint/marketplace/corporatecuratedgallery/carddesigns/card_designs.py +35 -0
  167. office365/sharepoint/marketplace/sitecollection/appcatalog/allowed_item.py +5 -0
  168. office365/sharepoint/marketplace/tenant/appcatalog/accessor.py +107 -30
  169. office365/sharepoint/microfeed/data.py +0 -6
  170. office365/sharepoint/microservice/manager.py +61 -0
  171. office365/sharepoint/migrationcenter/common/task.py +193 -4
  172. office365/sharepoint/migrationcenter/common/task_entity_data.py +1 -1
  173. office365/sharepoint/migrationcenter/deploy_status.py +13 -0
  174. office365/sharepoint/migrationcenter/service/properties.py +20 -0
  175. office365/sharepoint/migrationcenter/service/storage.py +34 -0
  176. office365/sharepoint/migrationcenter/spflight.py +13 -0
  177. office365/sharepoint/migrationcenter/urlparseresult.py +77 -0
  178. office365/sharepoint/multigeo/service/spmultigeoproxy.py +13 -0
  179. office365/sharepoint/navigation/servicerest.py +127 -0
  180. office365/sharepoint/oauth/authentication.py +13 -0
  181. office365/sharepoint/portal/theme_manager.py +120 -8
  182. office365/sharepoint/principal/users/user.py +10 -0
  183. office365/sharepoint/publishing/campaign/campaign.py +36 -1
  184. office365/sharepoint/publishing/feedvideopage.py +0 -7
  185. office365/sharepoint/publishing/pages/3d/page.py +0 -6
  186. office365/sharepoint/publishing/pages/page.py +0 -117
  187. office365/sharepoint/publishing/pages/reposts/repost.py +0 -7
  188. office365/sharepoint/publishing/pages/topics/topic.py +0 -7
  189. office365/sharepoint/publishing/subtitlefile.py +0 -40
  190. office365/sharepoint/quotamanagement/consumer/consumer_quota_info_api.py +14 -0
  191. office365/sharepoint/quotamanagement/consumer/non_quota_backfill_api.py +23 -0
  192. office365/sharepoint/quotamanagement/consumer/nonquotamigrationapi.py +9 -0
  193. office365/sharepoint/quotamanagement/consumer/quotamigrationapi.py +13 -0
  194. office365/sharepoint/request.py +57 -16
  195. office365/sharepoint/search/administration/document_crawl_log.py +24 -7
  196. office365/sharepoint/search/administration/providers/crawl_versions_info.py +29 -0
  197. office365/sharepoint/search/administration/providers/recycle_bin_info.py +23 -0
  198. office365/sharepoint/search/administration/providers/site_content_processing_info.py +67 -0
  199. office365/sharepoint/sites/site.py +976 -65
  200. office365/sharepoint/sites/siteprovisioninginfoprovider.py +13 -0
  201. office365/sharepoint/tenant/administration/datagovernance/insight_client.py +292 -23
  202. office365/sharepoint/tenant/administration/ddiadapter.py +97 -0
  203. office365/sharepoint/tenant/administration/internal/aad/collection.py +20 -0
  204. office365/sharepoint/tenant/administration/internal/aad/permission_grant.py +0 -19
  205. office365/sharepoint/tenant/administration/internal/appservice/permission_request.py +72 -1
  206. office365/sharepoint/tenant/administration/internal/appservice/principal.py +1 -3
  207. office365/sharepoint/tenant/administration/odb_license_enforcement_service.py +26 -0
  208. office365/sharepoint/tenant/administration/sites/properties.py +564 -6
  209. office365/sharepoint/tenant/administration/spdatagovernancesarrestapiclient.py +60 -1
  210. office365/sharepoint/tenant/insights/reports/manager.py +24 -12
  211. office365/sharepoint/tenant/management/office365_tenant.py +451 -2
  212. office365/sharepoint/tenantadministration/catalog_management_collection.py +69 -0
  213. office365/sharepoint/tenantadministration/catalog_management_custom_properties_collection.py +237 -0
  214. office365/sharepoint/tenantadministration/catalog_management_restricted_content_discovery_collection.py +56 -0
  215. office365/sharepoint/tenantadministration/sp_data_governance_client_exception.py +7 -0
  216. office365/sharepoint/thresholds.py +529 -0
  217. office365/sharepoint/translation/jobs/job.py +72 -2
  218. office365/sharepoint/translation/jobs/status.py +47 -9
  219. office365/sharepoint/translation/sync_translator.py +15 -2
  220. office365/sharepoint/utilities/move_copy_util.py +2 -1
  221. office365/sharepoint/viva/connections/page.py +27 -0
  222. office365/sharepoint/viva/home.py +38 -0
  223. office365/sharepoint/viva/learningmanager.py +9 -0
  224. office365/sharepoint/viva/vivaresources.py +60 -0
  225. office365/sharepoint/webhooks/subscription.py +0 -11
  226. office365/sharepoint/webparts/definitions/definition.py +13 -0
  227. office365/sharepoint/webparts/limited_manager.py +29 -7
  228. office365/sharepoint/webs/context_web_information.py +14 -9
  229. office365/sharepoint/webs/templates/__init__.py +11 -0
  230. office365/sharepoint/webs/templates/type.py +74 -15
  231. office365/sharepoint/workflow/definition.py +6 -0
  232. office365/sharepoint/workflow/deployment_service.py +155 -0
  233. office365/sharepoint/workflow/instances/service.py +33 -0
  234. office365/sharepoint/workflow/interopservice.py +68 -0
  235. office365/sharepoint/workflow/messagingservice.py +24 -0
  236. office365/sharepoint/workflow/servicesmanager.py +41 -2
  237. office365/sharepoint/workflow/subscriptions/service.py +67 -0
  238. office365/sharepoint/workflow/subscriptions/subscription.py +31 -0
  239. office365/sharepoint/workflow/task.py +425 -0
  240. office365_rest_python_client-3.2.0.dist-info/METADATA +605 -0
  241. {office365_rest_python_client-3.1.0.dist-info → office365_rest_python_client-3.2.0.dist-info}/RECORD +242 -185
  242. office365/migration/_util.py +0 -73
  243. office365/sharepoint/listitems/caml/types.py +0 -70
  244. office365_rest_python_client-3.1.0.dist-info/METADATA +0 -475
  245. {office365_rest_python_client-3.1.0.dist-info → office365_rest_python_client-3.2.0.dist-info}/WHEEL +0 -0
@@ -27,16 +27,18 @@ from office365.sharepoint.sharing.object_sharing_information import ObjectSharin
27
27
  from office365.sharepoint.sharing.user_role_assignment import UserRoleAssignment
28
28
  from office365.sharepoint.sharing.user_sharing_result import UserSharingResult
29
29
  from office365.sharepoint.storagemetrics.storage_metrics import StorageMetrics
30
+ from office365.sharepoint.thresholds import SAFE_PAGE_SIZE, Limits, limit
30
31
  from office365.sharepoint.types.resource_path import ResourcePath as SPResPath
31
32
  from office365.sharepoint.utilities.move_copy_options import MoveCopyOptions
32
33
  from office365.sharepoint.utilities.move_copy_util import MoveCopyUtil
33
34
 
34
35
  if TYPE_CHECKING:
36
+ from office365.runtime.converters.dataframe import DataFrameResult
35
37
  from office365.sharepoint.files.collection import FileCollection
36
38
  from office365.sharepoint.files.file import File
37
39
  from office365.sharepoint.folders.collection import FolderCollection
38
40
 
39
- _DEFAULT_CHUNK_SIZE = 4 * 1024 * 1024 # simple-upload threshold / upload-session chunk
41
+ _DEFAULT_CHUNK_SIZE = Limits.UPLOAD_SESSION_CHUNK.value # simple-upload threshold / upload-session chunk
40
42
 
41
43
 
42
44
  class Folder(Entity):
@@ -192,18 +194,22 @@ class Folder(Entity):
192
194
  self.context.add_query(placeholder).after_execute(lambda _: _get_folders(self))
193
195
  return return_type
194
196
 
197
+ @limit(Limits.LIST_VIEW, arg="page_size")
195
198
  def get_files(
196
199
  self,
197
200
  recursive: bool = False,
198
201
  progress: Optional[Callable[[Progress[File]], None]] = None,
202
+ page_size: int = SAFE_PAGE_SIZE,
199
203
  ) -> FileCollection:
200
- """Retrieves files
204
+ """Retrieves files (paged, so it works on folders with >5,000 items).
201
205
 
202
206
  Args:
203
207
  recursive (bool): Determines whether to enumerate folders recursively
204
- progress: Optional hook invoked per scanned folder with a
208
+ progress: Optional hook invoked per scanned page with a
205
209
  ``Progress[File]`` snapshot (``done`` = files discovered so far;
206
- ``items`` = the files found in the folder just scanned).
210
+ ``items`` = the files found in the page just scanned).
211
+ page_size (int): Items per page (kept below the 5,000-item list view
212
+ threshold so large folders are read without being throttled).
207
213
 
208
214
  The fluent ``.select([...])`` / ``.expand([...])`` applied to the returned
209
215
  collection is honored on every per-folder file load.
@@ -216,21 +222,35 @@ class Folder(Entity):
216
222
  return_type = FileCollection(self.context, resource_path, self)
217
223
 
218
224
  def _get_files(parent: Folder) -> None:
219
- def _on_files_loaded(col) -> None:
220
- for file in col:
221
- return_type.add_child(file)
222
- if callable(progress):
223
- progress(Progress(done=len(return_type), stage="scanning", items=list(col)))
224
- if recursive:
225
- subfolders = parent.folders
226
- subfolders.get().after_execute(lambda _: [_get_files(folder) for folder in subfolders])
227
-
228
225
  files = parent.files
229
226
  return_type.query_options.apply_to(files)
230
227
  if return_type.query_options.select:
231
228
  fields = sorted({"Id", "Name", "ServerRelativeUrl"} | set(return_type.query_options.select))
232
229
  files.select(fields)
233
- files.get().after_execute(_on_files_loaded)
230
+ seen = {"count": 0}
231
+
232
+ def _on_files_page(col: FileCollection) -> None:
233
+ new_files = list(col)[seen["count"] :]
234
+ seen["count"] = len(col)
235
+ for file in new_files:
236
+ return_type.add_child(file)
237
+ if callable(progress):
238
+ progress(Progress(done=len(return_type), stage="scanning", items=new_files))
239
+ if recursive and not col.has_next:
240
+ _scan_subfolders(parent)
241
+
242
+ files.get_all(page_size, page_loaded=_on_files_page)
243
+
244
+ def _scan_subfolders(parent: Folder) -> None:
245
+ subfolders = parent.folders
246
+ seen = {"count": 0}
247
+
248
+ def _on_folders_page(col: "FolderCollection") -> None:
249
+ for folder in list(col)[seen["count"] :]:
250
+ _get_files(folder)
251
+ seen["count"] = len(col)
252
+
253
+ subfolders.get_all(page_size, page_loaded=_on_folders_page)
234
254
 
235
255
  placeholder = DeferredOperationQuery(self.context)
236
256
  self.context.add_query(placeholder).after_execute(lambda _: _get_files(self))
@@ -443,6 +463,46 @@ class Folder(Entity):
443
463
  folder = self.ensure_folder("/".join(parts[:-1]))
444
464
  return folder.files.upload_content(content, parts[-1], chunk_size, progress)
445
465
 
466
+ def write_dataframe(
467
+ self,
468
+ relative_path: str,
469
+ df,
470
+ *,
471
+ format: str = "csv", # noqa: A002
472
+ index: bool = False,
473
+ **opts,
474
+ ) -> File:
475
+ """Serialize a pandas DataFrame and write it as a file (CSV/XLSX/...).
476
+
477
+ The DataFrame counterpart of :meth:`upload_file` — it writes the frame's
478
+ **content**, not metadata. CSV is written UTF-8 with a BOM
479
+ (``utf-8-sig``) so Excel opens it with the columns intact;
480
+ ``format="xlsx"`` writes a worksheet (requires the ``[excel]`` extra).
481
+ The returned :class:`File` is deferred — the caller executes it.
482
+
483
+ Args:
484
+ relative_path: File name or path relative to this folder.
485
+ df: A pandas DataFrame.
486
+ format: ``"csv"`` (default), ``"xlsx"``/``"excel"``, ``"json"``, ...
487
+ index: Whether to write the DataFrame index (default False).
488
+ opts: Extra kwargs forwarded to the pandas writer.
489
+ """
490
+ from office365.runtime.converters.dataframe import dataframe_to_bytes
491
+
492
+ return self.upload_file(relative_path, dataframe_to_bytes(df, format, index, **opts))
493
+
494
+ def read_dataframe(self, relative_path: str, *, format: str = "csv", **opts) -> "DataFrameResult": # noqa: A002
495
+ """Read a file in this folder's **content** into a DataFrame (deferred result).
496
+
497
+ Parses the file content (CSV/XLSX/JSON/Parquet) — not metadata. Run with
498
+ ``execute_query()`` and read ``.value``:
499
+
500
+ >>> df = folder.read_dataframe("stocks.csv").execute_query().value
501
+ """
502
+ url = f"{self.server_relative_url}/{relative_path}".replace("//", "/")
503
+ file = self.context.web.get_file_by_server_relative_url(url)
504
+ return file.read_dataframe(format=format, **opts)
505
+
446
506
  def update_document_sharing_info(
447
507
  self,
448
508
  user_role_assignments: List[UserRoleAssignment],
@@ -588,7 +648,12 @@ class Folder(Entity):
588
648
 
589
649
  @property
590
650
  def files(self) -> FileCollection:
591
- """Specifies the collection of files contained in the list folder."""
651
+ """Specifies the collection of files contained in the list folder.
652
+
653
+ A single-shot load (``ctx.load(folder, ["Files"])`` / ``.get()``) is trimmed
654
+ at the 5,000-item list view threshold; use :meth:`get_files` (paged) for
655
+ folders that may exceed it.
656
+ """
592
657
  from office365.sharepoint.files.collection import FileCollection
593
658
 
594
659
  return self.properties.get(
@@ -597,7 +662,12 @@ class Folder(Entity):
597
662
 
598
663
  @property
599
664
  def folders(self) -> FolderCollection:
600
- """Specifies the collection of list folders contained within the list folder."""
665
+ """Specifies the collection of list folders contained within the list folder.
666
+
667
+ A single-shot load (``ctx.load(folder, ["Folders"])`` / ``.get()``) is
668
+ trimmed at the 5,000-item list view threshold; use ``folders.get_all(
669
+ page_size=2000)`` for folders that may exceed it.
670
+ """
601
671
  from office365.sharepoint.folders.collection import FolderCollection
602
672
 
603
673
  return self.properties.get(
@@ -616,23 +686,23 @@ class Folder(Entity):
616
686
  """
617
687
  return self.folders.ensure_by_path(relative_path)
618
688
 
619
- def ensure_folders(self, relative_paths: Iterable[str]) -> Folder:
689
+ def ensure_folders(self, relative_paths: Iterable[str]) -> list[Folder]:
620
690
  """Ensure a set of nested folder paths under this folder — deduplicated.
621
691
 
622
692
  Since :meth:`ensure_folder` already creates intermediate folders for a
623
693
  nested path, only the *deepest* paths are ensured: a path that is an
624
694
  ancestor of another is covered by it. All ensures are queued as one
625
695
  deferred batch; the caller executes them in a single round-trip.
626
- Returns ``self`` for chaining.
627
696
 
628
697
  Args:
629
698
  relative_paths (Iterable[str]): Paths relative to this folder.
699
+
700
+ Returns:
701
+ list[Folder]: The ensured folders.
630
702
  """
631
703
  paths = sorted(set(relative_paths))
632
704
  deepest = [path for path in paths if not any(other.startswith(f"{path}/") for other in paths if other != path)]
633
- for path in deepest:
634
- self.ensure_folder(path)
635
- return self
705
+ return [self.ensure_folder(path) for path in deepest]
636
706
 
637
707
  @odata(name="ParentFolder")
638
708
  @property
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ from office365.sharepoint.entity import Entity
6
+
7
+
8
+ class HtmlPage(Entity):
9
+ @property
10
+ def id_(self) -> Optional[int]:
11
+ """Gets the Id property"""
12
+ return self.properties.get("Id", None)
13
+
14
+ @property
15
+ def entity_type_name(self) -> str:
16
+ return "SP.HtmlPages.HtmlPage"
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from office365.runtime.paths.resource_path import ResourcePath
4
+ from office365.sharepoint.entity import Entity
5
+ from office365.sharepoint.entity_collection import EntityCollection
6
+ from office365.sharepoint.htmlpages.html_page import HtmlPage
7
+
8
+
9
+ class HtmlPageService(Entity):
10
+ @property
11
+ def pages(self) -> EntityCollection[HtmlPage]:
12
+ """Gets the Pages property"""
13
+ return self.properties.get(
14
+ "Pages", EntityCollection[HtmlPage](self.context, HtmlPage, ResourcePath("Pages", self.resource_path))
15
+ )
16
+
17
+ @property
18
+ def entity_type_name(self) -> str:
19
+ return "SP.HtmlPages.HtmlPageService"
@@ -1,3 +1,6 @@
1
+ from typing_extensions import Self
2
+
3
+ from office365.runtime.queries.service_operation import ServiceOperationQuery
1
4
  from office365.sharepoint.entity import Entity
2
5
 
3
6
 
@@ -7,3 +10,9 @@ class IRMigration(Entity):
7
10
  @property
8
11
  def entity_type_name(self) -> str:
9
12
  return "Microsoft.SharePoint.IR.IRMigration"
13
+
14
+ def delete_and_recreate_ir_list(self) -> Self:
15
+ """DeleteAndRecreateIRList operation."""
16
+ qry = ServiceOperationQuery(self, "DeleteAndRecreateIRList", None, {}, None, None)
17
+ self.context.add_query(qry)
18
+ return self
@@ -0,0 +1,31 @@
1
+ """Typed CAML query builder.
2
+
3
+ Build CAML ``ViewXml`` from composable Python objects instead of raw strings::
4
+
5
+ from office365.sharepoint.listitems.caml import Caml, CamlQuery
6
+
7
+ query = (
8
+ CamlQuery.builder()
9
+ .where(Caml.text("Status").eq("Active"))
10
+ .order_by("Created", ascending=False)
11
+ .row_limit(2000)
12
+ .build()
13
+ )
14
+ """
15
+
16
+ from office365.sharepoint.listitems.caml.builder import QueryBuilder
17
+ from office365.sharepoint.listitems.caml.expressions import Caml, CamlExpr, Field
18
+ from office365.sharepoint.listitems.caml.fields import FieldRef
19
+ from office365.sharepoint.listitems.caml.query import CamlQuery
20
+ from office365.sharepoint.listitems.caml.values import Value, ValueType
21
+
22
+ __all__ = [
23
+ "Caml",
24
+ "CamlExpr",
25
+ "CamlQuery",
26
+ "Field",
27
+ "FieldRef",
28
+ "QueryBuilder",
29
+ "Value",
30
+ "ValueType",
31
+ ]
@@ -0,0 +1,129 @@
1
+ """Fluent builder for the CAML ``<View>`` document.
2
+
3
+ Assembles ``ViewFields``/``Where``/``GroupBy``/``OrderBy``/``RowLimit``/``Scope``
4
+ into a ``ViewXml`` string (or a :class:`CamlQuery`):
5
+
6
+ from office365.sharepoint.listitems.caml import Caml, CamlQuery
7
+ from office365.sharepoint.views.scope import ViewScope
8
+
9
+ query = (CamlQuery.builder()
10
+ .where(Caml.text("Status").eq("Active"))
11
+ .order_by("Created", ascending=False)
12
+ .row_limit(2000)
13
+ .scope(ViewScope.RecursiveAll)
14
+ .build())
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import TYPE_CHECKING, Any, Optional
20
+ from xml.sax.saxutils import quoteattr
21
+
22
+ from typing_extensions import Self
23
+
24
+ from office365.sharepoint.listitems.caml.expressions import And, CamlExpr, _as_field_ref, _fold
25
+ from office365.sharepoint.listitems.caml.fields import FieldRef
26
+ from office365.sharepoint.views.scope import ViewScope
27
+
28
+ if TYPE_CHECKING:
29
+ from office365.sharepoint.listitems.caml.query import CamlQuery
30
+
31
+
32
+ class QueryBuilder:
33
+ """Fluent builder that renders the CAML ``<View>`` element."""
34
+
35
+ def __init__(self) -> None:
36
+ self._where: Optional[CamlExpr] = None
37
+ self._order_by: list[FieldRef] = []
38
+ self._group_by: list[FieldRef] = []
39
+ self._view_fields: list[FieldRef] = []
40
+ self._row_limit: Optional[int] = None
41
+ self._paged = True
42
+ self._scope: ViewScope = ViewScope.DefaultValue
43
+
44
+ def where(self, *conditions: CamlExpr) -> Self:
45
+ """Set the ``<Where>`` filter (multiple conditions are combined with ``<And>``)."""
46
+ if conditions:
47
+ self._where = _fold(And, conditions)
48
+ return self
49
+
50
+ def order_by(self, *fields: Any, ascending: bool = True) -> Self:
51
+ """Add one or more ``<OrderBy>`` fields (call again to append)."""
52
+ for field in fields:
53
+ ref = _as_field_ref(field)
54
+ self._order_by.append(ref if ref.ascending is not None else ref._copy(ascending=ascending))
55
+ return self
56
+
57
+ def group_by(self, field: Any, *, collapse: bool = True) -> Self:
58
+ """Add a ``<GroupBy>`` field."""
59
+ ref = _as_field_ref(field)
60
+ self._group_by.append(ref if ref.collapse is not None else ref._copy(collapse=collapse))
61
+ return self
62
+
63
+ def view_fields(self, *names: Any) -> Self:
64
+ """Add ``<ViewFields>`` entries (a ``str``, ``Field`` or ``FieldRef``)."""
65
+ self._view_fields.extend(_as_field_ref(name) for name in names)
66
+ return self
67
+
68
+ def row_limit(self, top: int, *, paged: bool = True) -> Self:
69
+ """Set ``<RowLimit>`` (``Paged="TRUE"`` by default)."""
70
+ self._row_limit = top
71
+ self._paged = paged
72
+ return self
73
+
74
+ def scope(self, scope: ViewScope) -> Self:
75
+ """Set the ``Scope`` attribute of ``<View>``."""
76
+ self._scope = scope
77
+ return self
78
+
79
+ @property
80
+ def is_paged(self) -> bool:
81
+ """Whether the query enables server-driven paging (``RowLimit Paged="TRUE"``)."""
82
+ return self._row_limit is not None and self._paged
83
+
84
+ @property
85
+ def field_refs(self) -> set[str]:
86
+ """Fields the query filters or sorts on (``ViewFields`` are excluded)."""
87
+ names: set[str] = set()
88
+ if self._where is not None:
89
+ names |= self._where.field_refs
90
+ for ref in (*self._order_by, *self._group_by):
91
+ names.add(ref.name)
92
+ return names
93
+
94
+ def to_xml(self) -> str:
95
+ """Render the CAML ``<View>`` document."""
96
+ scope = self._scope.value if isinstance(self._scope, ViewScope) else str(self._scope)
97
+ parts = [f"<View Scope={quoteattr(scope)}>"]
98
+ if self._view_fields:
99
+ parts.append("<ViewFields>" + "".join(ref.to_xml() for ref in self._view_fields) + "</ViewFields>")
100
+ query = self._query_xml()
101
+ if query:
102
+ parts.append(query)
103
+ if self._row_limit is not None:
104
+ paged = ' Paged="TRUE"' if self._paged else ""
105
+ parts.append(f"<RowLimit{paged}>{self._row_limit}</RowLimit>")
106
+ parts.append("</View>")
107
+ return "".join(parts)
108
+
109
+ def _query_xml(self) -> str:
110
+ query: list[str] = []
111
+ if self._where is not None:
112
+ query.append(f"<Where>{self._where.to_xml()}</Where>")
113
+ if self._group_by:
114
+ query.append("<GroupBy>" + "".join(ref.to_xml() for ref in self._group_by) + "</GroupBy>")
115
+ if self._order_by:
116
+ query.append("<OrderBy>" + "".join(ref.to_xml() for ref in self._order_by) + "</OrderBy>")
117
+ return "<Query>" + "".join(query) + "</Query>" if query else ""
118
+
119
+ def build(self) -> "CamlQuery":
120
+ """Build a :class:`CamlQuery` (raw ``ViewXml`` + the expression AST)."""
121
+ from office365.sharepoint.listitems.caml.query import CamlQuery
122
+
123
+ query = CamlQuery()
124
+ query.ViewXml = self.to_xml()
125
+ query._expr = self
126
+ return query
127
+
128
+ def __repr__(self) -> str:
129
+ return self.to_xml()