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.
- {pyxecm-3.4.3 → pyxecm-3.4.5}/PKG-INFO +1 -1
- {pyxecm-3.4.3 → pyxecm-3.4.5}/pyproject.toml +1 -1
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otcs.py +254 -17
- {pyxecm-3.4.3 → pyxecm-3.4.5}/.gitignore +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/README.md +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/avts.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/coreshare.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/assoc.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/data.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/logadapter.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/web.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/helper/xml.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otac.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otawp.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otca.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otds.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otiv.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otkd.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otmm.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/otpd.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm/py.typed +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/__main__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/app.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/auth/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/auth/functions.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/auth/models.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/auth/router.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/functions.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/metrics.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/models.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/common/router.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/settings.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/terminal/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/terminal/router.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_maintenance/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_maintenance/functions.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_maintenance/models.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_maintenance/router.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_otcs/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_otcs/functions.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_otcs/router.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_payload/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_payload/functions.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_payload/models.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_api/v1_payload/router.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/__main__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/browser_automation.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/customizer.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/exceptions.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/guidewire.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/k8s.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/log.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/m365.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/payload.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/payload_list.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/salesforce.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/sap.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/servicenow.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/settings.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/successfactors.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_customizer/translate.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/__init__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/__main__.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/app.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/settings.py +0 -0
- {pyxecm-3.4.3 → pyxecm-3.4.5}/src/pyxecm_maintenance_page/static/favicon.avif +0 -0
- {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
|
+
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>
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
24314
|
-
|
|
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
|
-
|
|
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': {}
|
|
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
|
|
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
|
|
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
|
|
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
|