pyxecm 3.4.3__tar.gz → 3.4.5__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 (72) hide show
  1. {pyxecm-3.4.3 → pyxecm-3.4.5}/PKG-INFO +1 -1
  2. {pyxecm-3.4.3 → pyxecm-3.4.5}/pyproject.toml +1 -1
  3. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otcs.py +254 -17
  4. {pyxecm-3.4.3 → pyxecm-3.4.5}/.gitignore +0 -0
  5. {pyxecm-3.4.3 → pyxecm-3.4.5}/README.md +0 -0
  6. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/__init__.py +0 -0
  7. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/avts.py +0 -0
  8. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/coreshare.py +0 -0
  9. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/__init__.py +0 -0
  10. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/assoc.py +0 -0
  11. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/data.py +0 -0
  12. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/logadapter.py +0 -0
  13. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/web.py +0 -0
  14. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/xml.py +0 -0
  15. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otac.py +0 -0
  16. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otawp.py +0 -0
  17. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otca.py +0 -0
  18. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otds.py +0 -0
  19. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otiv.py +0 -0
  20. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otkd.py +0 -0
  21. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otmm.py +0 -0
  22. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otpd.py +0 -0
  23. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/py.typed +0 -0
  24. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/__init__.py +0 -0
  25. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/__main__.py +0 -0
  26. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/app.py +0 -0
  27. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/auth/__init__.py +0 -0
  28. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/auth/functions.py +0 -0
  29. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/auth/models.py +0 -0
  30. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/auth/router.py +0 -0
  31. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/__init__.py +0 -0
  32. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/functions.py +0 -0
  33. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/metrics.py +0 -0
  34. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/models.py +0 -0
  35. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/router.py +0 -0
  36. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/settings.py +0 -0
  37. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/terminal/__init__.py +0 -0
  38. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/terminal/router.py +0 -0
  39. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_maintenance/__init__.py +0 -0
  40. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_maintenance/functions.py +0 -0
  41. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_maintenance/models.py +0 -0
  42. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_maintenance/router.py +0 -0
  43. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_otcs/__init__.py +0 -0
  44. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_otcs/functions.py +0 -0
  45. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_otcs/router.py +0 -0
  46. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_payload/__init__.py +0 -0
  47. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_payload/functions.py +0 -0
  48. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_payload/models.py +0 -0
  49. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_payload/router.py +0 -0
  50. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/__init__.py +0 -0
  51. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/__main__.py +0 -0
  52. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/browser_automation.py +0 -0
  53. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/customizer.py +0 -0
  54. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/exceptions.py +0 -0
  55. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/guidewire.py +0 -0
  56. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/k8s.py +0 -0
  57. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/log.py +0 -0
  58. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/m365.py +0 -0
  59. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/payload.py +0 -0
  60. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/payload_list.py +0 -0
  61. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/salesforce.py +0 -0
  62. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/sap.py +0 -0
  63. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/servicenow.py +0 -0
  64. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/settings.py +0 -0
  65. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/successfactors.py +0 -0
  66. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/translate.py +0 -0
  67. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/__init__.py +0 -0
  68. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/__main__.py +0 -0
  69. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/app.py +0 -0
  70. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/settings.py +0 -0
  71. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/static/favicon.avif +0 -0
  72. {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/templates/maintenance.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyxecm
3
- Version: 3.4.3
3
+ Version: 3.4.5
4
4
  Summary: A Python library to interact with Opentext Content Management Rest API
5
5
  Project-URL: Homepage, https://github.com/opentext/pyxecm
6
6
  Author-email: Kai Gatzweiler <kgatzweiler@opentext.com>, "Dr. Marc Diefenbruch" <mdiefenb@opentext.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyxecm"
3
- version = "3.4.3"
3
+ version = "3.4.5"
4
4
  description = 'A Python library to interact with Opentext Content Management Rest API'
5
5
  keywords = [
6
6
  'opentext',
@@ -542,6 +542,8 @@ class OTCS:
542
542
  otcs_config["businessObjectTypesUrl"] = otcs_rest_url + "/v2/businessobjecttypes"
543
543
  otcs_config["businessObjectsSearchUrl"] = otcs_rest_url + "/v2/forms/businessobjects/search"
544
544
  otcs_config["businessWorkspaceTypesUrl"] = otcs_rest_url + "/v1/businessworkspacetypes"
545
+ otcs_config["businessWorkspaceReportsUrl"] = otcs_rest_url + "/v2/businessworkspace"
546
+ otcs_config["businessWorkspaceMyTeamTodoUrl"] = otcs_rest_url + "/v2/businessworkspace/myteamtodo"
545
547
  otcs_config["businessWorkspaceTypesUrlv2"] = otcs_rest_url + "/v2/businessworkspacetypes"
546
548
  otcs_config["businessworkspacecreateform"] = otcs_rest_url + "/v2/forms/businessworkspaces/create"
547
549
  otcs_config["businessWorkspacesUrl"] = otcs_rest_url + "/v2/businessworkspaces"
@@ -608,6 +610,12 @@ class OTCS:
608
610
  self._soap_docman_client: Client | None = None
609
611
  self._soap_transport: Transport | None = None
610
612
 
613
+ # Memoized full reminder client/type list (client_id=None). Reminder client
614
+ # and type configuration is effectively static for the lifetime of an OTCS
615
+ # object, so caching it avoids repeated 1+N REST round-trips when a single
616
+ # add/update operation resolves and validates a reminder type multiple times.
617
+ self._reminder_clients_cache: list[dict] | None = None
618
+
611
619
  # Handle concurrent HTTP requests that may run into 401 errors and
612
620
  # re-authentication at the same time:
613
621
  self._authentication_lock = threading.Lock()
@@ -8520,6 +8528,9 @@ class OTCS:
8520
8528
  str(volume_type),
8521
8529
  )
8522
8530
 
8531
+ if not mime_type:
8532
+ mime_type = "application/octet-stream" # Default to binary stream if mime type cannot be determined
8533
+
8523
8534
  upload_post_files = [("file", (f"{file_name}", file_content, mime_type))]
8524
8535
 
8525
8536
  request_url = self.config()["nodesUrlv2"]
@@ -8811,6 +8822,9 @@ class OTCS:
8811
8822
  parent_id,
8812
8823
  )
8813
8824
 
8825
+ if not mime_type:
8826
+ mime_type = "application/octet-stream" # Default to binary stream if mime type cannot be determined
8827
+
8814
8828
  upload_post_files = [("file", (f"{file_name}", file_content, mime_type))]
8815
8829
 
8816
8830
  request_url = self.config()["nodesUrlv2"]
@@ -9179,6 +9193,9 @@ class OTCS:
9179
9193
  node_id,
9180
9194
  )
9181
9195
 
9196
+ if not mime_type:
9197
+ mime_type = "application/octet-stream" # Default to binary stream if mime type cannot be determined
9198
+
9182
9199
  upload_post_files = [("file", (f"{file_name}", file_content, mime_type))]
9183
9200
 
9184
9201
  request_url = self.config()["nodesUrlv2"] + "/" + str(node_id) + "/versions"
@@ -13530,16 +13547,22 @@ class OTCS:
13530
13547
  # end method definition
13531
13548
 
13532
13549
  @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="get_workspace_type_name")
13533
- def get_workspace_type_name(self, type_id: int) -> str | None:
13550
+ def get_workspace_type_name(self, type_id: int, renew: bool = False) -> str | None:
13534
13551
  """Get the name of a workspace type based on the provided workspace type ID.
13535
13552
 
13536
13553
  The name is taken from a OTCS object variable self._workspace_type_lookup if recorded there.
13537
- If not yet derived it is determined via the REST API and then stored
13538
- in self._workspace_type_lookup (as a lookup cache).
13554
+ If not yet derived - or if renew is requested - it is determined via the REST API and
13555
+ then stored in self._workspace_type_lookup (as a lookup cache).
13539
13556
 
13540
13557
  Args:
13541
13558
  type_id (int):
13542
13559
  The workspace type ID.
13560
+ renew (bool, optional):
13561
+ Whether to bypass the cache and force a fresh lookup via the REST API,
13562
+ updating the cache entry. Useful for callers that cannot tolerate a
13563
+ workspace type having been renamed/reconfigured since it was last cached
13564
+ (self._workspace_type_lookup has no expiry and is shared across calls
13565
+ for the lifetime of this OTCS instance).
13543
13566
 
13544
13567
  Returns:
13545
13568
  str | None:
@@ -13553,7 +13576,7 @@ class OTCS:
13553
13576
  """
13554
13577
 
13555
13578
  workspace_type = self._workspace_type_lookup.get(type_id)
13556
- if workspace_type:
13579
+ if workspace_type and not renew:
13557
13580
  return workspace_type.get("name")
13558
13581
 
13559
13582
  workspace_type = self.get_workspace_type(type_id=type_id)
@@ -14931,7 +14954,7 @@ class OTCS:
14931
14954
  def create_workspace(
14932
14955
  self,
14933
14956
  workspace_template_id: int,
14934
- workspace_name: str,
14957
+ workspace_name: str | None,
14935
14958
  workspace_description: str,
14936
14959
  workspace_type: int,
14937
14960
  category_data: dict | None = None,
@@ -14951,12 +14974,13 @@ class OTCS:
14951
14974
  template and type, with optional category data and business object details.
14952
14975
  It also supports linking to external systems and specifying metadata such as
14953
14976
  creation and modification dates.
14977
+ If the name is not specified it will be derived from workspace metadata.
14954
14978
 
14955
14979
  Args:
14956
14980
  workspace_template_id (int):
14957
14981
  The ID of the workspace template to be used.
14958
- workspace_name (str):
14959
- The name of the new workspace.
14982
+ workspace_name (str | None):
14983
+ The name of the new workspace. If not specified, it will be derived from workspace metadata.
14960
14984
  workspace_description (str):
14961
14985
  A description of the new workspace.
14962
14986
  workspace_type (int):
@@ -15024,13 +15048,16 @@ class OTCS:
15024
15048
 
15025
15049
  create_workspace_post_data = {
15026
15050
  "template_id": str(workspace_template_id),
15027
- "name": workspace_name,
15028
15051
  "description": workspace_description,
15029
15052
  "wksp_type_id": str(workspace_type),
15030
15053
  "type": str(self.ITEM_TYPE_BUSINESS_WORKSPACE),
15031
15054
  "roles": {}, # category_data,
15032
15055
  }
15033
15056
 
15057
+ # Yes, workspace name is optional. If not specified, it will be derived from workspace metadata.
15058
+ if workspace_name:
15059
+ create_workspace_post_data["name"] = workspace_name
15060
+
15034
15061
  if category_data:
15035
15062
  create_workspace_post_data["roles"]["categories"] = category_data
15036
15063
  if external_create_date:
@@ -15062,7 +15089,7 @@ class OTCS:
15062
15089
  if parent_id is not None:
15063
15090
  create_workspace_post_data["parent_id"] = parent_id
15064
15091
  self.logger.debug(
15065
- "Use specified location with node ID -> %d for workspace -> '%s'",
15092
+ "Use specified location with node ID -> %d for creation of workspace -> '%s'",
15066
15093
  parent_id,
15067
15094
  workspace_name,
15068
15095
  )
@@ -15211,7 +15238,7 @@ class OTCS:
15211
15238
  self,
15212
15239
  workspace_id: int,
15213
15240
  related_workspace_id: int,
15214
- relationship_type: str = "child",
15241
+ relationship_type: str | None = "child",
15215
15242
  show_error: bool = True,
15216
15243
  ) -> dict | None:
15217
15244
  """Create a relationship between two workspaces.
@@ -15231,7 +15258,7 @@ class OTCS:
15231
15258
  and the other of type 'child', but this is a less common use case.
15232
15259
 
15233
15260
  Adding a relationship R1: A --> Child --> B makes A to the parent of B _AND_ also
15234
- B to the child of A. Requesting the childs of A will include edge R1. Requesting the
15261
+ B to the child of A. Requesting the childs of A will include relationship R1. Requesting the
15235
15262
  parents of B will include R1 as well. But it is still possible to add a relationship
15236
15263
  R2: A --> Parent --> B. This will be a separate relationship.
15237
15264
 
@@ -15240,8 +15267,10 @@ class OTCS:
15240
15267
  The ID of the workspace.
15241
15268
  related_workspace_id (int):
15242
15269
  The ID of the related workspace.
15243
- relationship_type (str, optional):
15244
- Can be "parent" or "child" - "child" is default if not provided.
15270
+ relationship_type (str | None, optional):
15271
+ Can be "parent" or "child" - "child" is the default. If None (or empty)
15272
+ is provided, it falls back to "child" instead of sending an invalid
15273
+ relationship type to the REST API.
15245
15274
  show_error (bool, optional):
15246
15275
  If True, log an error if relationship cration fails.
15247
15276
  Otherwise log a warning.
@@ -15252,6 +15281,17 @@ class OTCS:
15252
15281
 
15253
15282
  """
15254
15283
 
15284
+ # Normalize the relationship type so that callers passing None explicitly
15285
+ # (which overrides the default) still get the intended "child" behavior:
15286
+ if not relationship_type:
15287
+ relationship_type = "child"
15288
+ elif relationship_type not in ("parent", "child"):
15289
+ self.logger.error(
15290
+ "Illegal relationship type -> '%s'! Must be either 'parent' or 'child'.",
15291
+ relationship_type,
15292
+ )
15293
+ return None
15294
+
15255
15295
  create_workspace_relationship_post_data = {
15256
15296
  "rel_bw_id": str(related_workspace_id),
15257
15297
  "rel_type": relationship_type,
@@ -23961,6 +24001,12 @@ class OTCS:
23961
24001
  for workspace_type in workspace_types:
23962
24002
  wksp_type_id = self.get_result_value(response=workspace_type, key="wksp_type_id")
23963
24003
  wksp_type_name = self.get_result_value(response=workspace_type, key="wksp_type_name")
24004
+ # Refresh the shared lookup cache with the current name (see the equivalent
24005
+ # refresh in traverse_workspaces_parallel's init_traversal_queue for why this
24006
+ # must overwrite rather than only fill in missing entries): this keeps
24007
+ # get_workspace_type_name() calls made later in this traversal (e.g. from
24008
+ # traverse_workspace()) from resolving a stale, previously-cached name.
24009
+ self._workspace_type_lookup[wksp_type_id] = {"location": None, "name": wksp_type_name}
23964
24010
  if not self._check_filter(
23965
24011
  workspace_type_name=wksp_type_name,
23966
24012
  workspace_type_id=wksp_type_id,
@@ -24310,8 +24356,14 @@ class OTCS:
24310
24356
  for workspace_type in workspace_types:
24311
24357
  wksp_type_id = self.get_result_value(response=workspace_type, key="wksp_type_id")
24312
24358
  wksp_type_name = self.get_result_value(response=workspace_type, key="wksp_type_name")
24313
- if wksp_type_id not in self._workspace_type_lookup:
24314
- self._workspace_type_lookup[wksp_type_id] = {"location": None, "name": wksp_type_name}
24359
+ # Refresh (not just fill-if-missing): this is a fresh, current listing of
24360
+ # workspace types, fetched on every traversal run. self._workspace_type_lookup
24361
+ # is a long-lived cache on this OTCS instance (spans many traversal runs over the
24362
+ # life of the process), so if we only filled in missing entries, a renamed
24363
+ # workspace type would keep resolving to its stale old name here - and via
24364
+ # get_workspace_type_name() - forever. Overwriting is free since we already have
24365
+ # the current name from this iteration; no extra API call is needed.
24366
+ self._workspace_type_lookup[wksp_type_id] = {"location": None, "name": wksp_type_name}
24315
24367
  if not self._check_filter(
24316
24368
  workspace_type_name=wksp_type_name,
24317
24369
  workspace_type_id=wksp_type_id,
@@ -26113,6 +26165,14 @@ class OTCS:
26113
26165
 
26114
26166
  # end method definition of _extract_field_mappings()
26115
26167
 
26168
+ # Serve the memoized full client/type list when no specific client filter
26169
+ # is requested (the common case for type resolution and validation). The
26170
+ # local `client_id` is reassigned inside the loop below, so capture the
26171
+ # original intent up front for both the cache read and the cache write:
26172
+ is_full_list = client_id is None
26173
+ if is_full_list and self._reminder_clients_cache is not None:
26174
+ return self._reminder_clients_cache
26175
+
26116
26176
  response = self.get_reminder_types(client_id=client_id)
26117
26177
  if not response:
26118
26178
  return None
@@ -26154,6 +26214,10 @@ class OTCS:
26154
26214
  str(clients),
26155
26215
  )
26156
26216
 
26217
+ # Memoize the full client/type list for subsequent lookups in this session:
26218
+ if is_full_list:
26219
+ self._reminder_clients_cache = clients
26220
+
26157
26221
  return clients
26158
26222
 
26159
26223
  # end method definition
@@ -27501,7 +27565,13 @@ class OTCS:
27501
27565
  dict | None:
27502
27566
  Response from the server, or None if the request fails.
27503
27567
 
27504
- Example:
27568
+ Note:
27569
+ On OTCM 26.2 and newer the `results` payload contains the ID of the
27570
+ newly created reminder (extractable via `get_result_value(response, key="id")`).
27571
+ On older releases `results` was empty even on success, so callers that
27572
+ need the reminder ID on those releases must fall back to `get_reminders()`.
27573
+
27574
+ Example (OTCM 26.2 and newer):
27505
27575
  {
27506
27576
  'links': {
27507
27577
  'data': {
@@ -27514,7 +27584,7 @@ class OTCS:
27514
27584
  }
27515
27585
  }
27516
27586
  },
27517
- 'results': {} # seems to be always empty - also on success
27587
+ 'results': {'id': 66} # ID of the newly created reminder (empty {} on releases before 26.2)
27518
27588
  }
27519
27589
 
27520
27590
  """
@@ -27890,3 +27960,170 @@ class OTCS:
27890
27960
  )
27891
27961
 
27892
27962
  # end method definition
27963
+
27964
+ @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="get_workspace_report_detail")
27965
+ def get_workspace_report_detail(
27966
+ self,
27967
+ workspace_id: int,
27968
+ report_type: str,
27969
+ ) -> dict | None:
27970
+ """Get the full document list for a single workspace report type.
27971
+
27972
+ REST operation: GET /v2/businessworkspace/{workspace_id}/reports?type={report_type}
27973
+
27974
+ Args:
27975
+ workspace_id (int):
27976
+ Business workspace node ID.
27977
+ report_type (str):
27978
+ Report type key. One of ``"documentsAdded"``, ``"outdatedDocuments"``,
27979
+ or ``"recentDocuments"``.
27980
+
27981
+ Returns:
27982
+ dict | None:
27983
+ Report detail response containing a ``results.data`` list and
27984
+ ``results.total_count``, or None on error.
27985
+
27986
+ """
27987
+
27988
+ request_url = "{}/{}/reports?type={}".format(
27989
+ self.config()["businessWorkspaceReportsUrl"],
27990
+ workspace_id,
27991
+ report_type,
27992
+ )
27993
+ request_header = self.request_json_header()
27994
+
27995
+ self.logger.debug(
27996
+ "Get workspace report detail for workspace with ID -> %d, type -> %s; calling -> %s",
27997
+ workspace_id,
27998
+ report_type,
27999
+ request_url,
28000
+ )
28001
+
28002
+ return self.do_request(
28003
+ url=request_url,
28004
+ method="GET",
28005
+ headers=request_header,
28006
+ timeout=REQUEST_TIMEOUT,
28007
+ failure_message="Failed to get '{}' report detail for workspace with ID -> {}".format(
28008
+ report_type, workspace_id
28009
+ ),
28010
+ )
28011
+
28012
+ # end method definition
28013
+
28014
+ @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="get_workspace_documents")
28015
+ def get_workspace_documents(
28016
+ self,
28017
+ workspace_id: int,
28018
+ document_type: Literal["missing", "inprocess"] = "missing",
28019
+ ) -> dict | None:
28020
+ """Get the list of missing or in-process documents for a Business Workspace.
28021
+
28022
+ REST operation: GET /v2/businessworkspaces/{workspace_id}/missingdocuments
28023
+ REST operation: GET /v2/businessworkspaces/{workspace_id}/inprocessdocuments
28024
+
28025
+ Missing documents are required document types that are absent from the workspace.
28026
+ In-process documents are currently being processed within the workspace
28027
+ (e.g. pending classification, approval, or ingestion workflows).
28028
+
28029
+ Args:
28030
+ workspace_id (int):
28031
+ Business workspace node ID.
28032
+ document_type (Literal["missing", "inprocess"], optional):
28033
+ Which document category to retrieve:
28034
+ * ``"missing"`` — required documents absent from the workspace
28035
+ (``/missingdocuments``).
28036
+ * ``"inprocess"`` — documents currently being processed
28037
+ (``/inprocessdocuments``).
28038
+ Defaults to ``"missing"``.
28039
+
28040
+ Returns:
28041
+ dict | None:
28042
+ Response containing a ``results.data`` list and ``results.total_count``,
28043
+ or None on error.
28044
+
28045
+ """
28046
+
28047
+ endpoint = "inprocessdocuments" if document_type == "inprocess" else "missingdocuments"
28048
+ request_url = "{}/{}/{}".format(
28049
+ self.config()["businessWorkspacesUrl"],
28050
+ workspace_id,
28051
+ endpoint,
28052
+ )
28053
+ request_header = self.request_json_header()
28054
+
28055
+ self.logger.debug(
28056
+ "Get %s for workspace with ID -> %d; calling -> %s",
28057
+ endpoint,
28058
+ workspace_id,
28059
+ request_url,
28060
+ )
28061
+
28062
+ return self.do_request(
28063
+ url=request_url,
28064
+ method="GET",
28065
+ headers=request_header,
28066
+ timeout=REQUEST_TIMEOUT,
28067
+ failure_message="Failed to get {} for workspace with ID -> {}".format(endpoint, workspace_id),
28068
+ )
28069
+
28070
+ # end method definition
28071
+
28072
+ @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="get_workspace_myteamtodo")
28073
+ def get_workspace_myteamtodo(self) -> dict | None:
28074
+ """Get the My Team Todo data for the currently authenticated user.
28075
+
28076
+ Returns the employee-file completeness status (missing and outdated document types)
28077
+ for all direct reports of the currently authenticated user.
28078
+
28079
+ REST operation: GET /v2/businessworkspace/myteamtodo
28080
+
28081
+ Returns:
28082
+ dict | None:
28083
+ Response containing a ``results`` list where each element holds a
28084
+ ``data.members`` list of direct reportees with their completeness status,
28085
+ or None on error.
28086
+
28087
+ Example:
28088
+ {
28089
+ 'results': [
28090
+ {
28091
+ 'data': {
28092
+ 'members': [
28093
+ {
28094
+ 'id': 1234,
28095
+ 'display_name': 'Ian Grant',
28096
+ 'first_name': 'Ian',
28097
+ 'last_name': 'Grant',
28098
+ 'business_email': 'ian.grant@example.com',
28099
+ 'employee_file_todos': {
28100
+ 'missing': [{'doc_type_name': 'Contract'}],
28101
+ 'outdated': [{'doc_type_name': 'Accident'}],
28102
+ },
28103
+ }
28104
+ ]
28105
+ },
28106
+ 'properties': None,
28107
+ }
28108
+ ]
28109
+ }
28110
+
28111
+ """
28112
+
28113
+ request_url = self.config()["businessWorkspaceMyTeamTodoUrl"]
28114
+ request_header = self.request_json_header()
28115
+
28116
+ self.logger.debug(
28117
+ "Get My Team Todo data for current user; calling -> %s",
28118
+ request_url,
28119
+ )
28120
+
28121
+ return self.do_request(
28122
+ url=request_url,
28123
+ method="GET",
28124
+ headers=request_header,
28125
+ timeout=REQUEST_TIMEOUT,
28126
+ failure_message="Failed to get My Team Todo data.",
28127
+ )
28128
+
28129
+ # end method definition
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes