pyxecm 3.4.2__tar.gz → 3.4.3__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.2 → pyxecm-3.4.3}/PKG-INFO +1 -1
  2. {pyxecm-3.4.2 → pyxecm-3.4.3}/pyproject.toml +1 -1
  3. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/otcs.py +206 -35
  4. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/payload.py +10 -6
  5. {pyxecm-3.4.2 → pyxecm-3.4.3}/.gitignore +0 -0
  6. {pyxecm-3.4.2 → pyxecm-3.4.3}/README.md +0 -0
  7. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/__init__.py +0 -0
  8. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/avts.py +0 -0
  9. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/coreshare.py +0 -0
  10. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/helper/__init__.py +0 -0
  11. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/helper/assoc.py +0 -0
  12. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/helper/data.py +0 -0
  13. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/helper/logadapter.py +0 -0
  14. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/helper/web.py +0 -0
  15. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/helper/xml.py +0 -0
  16. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/otac.py +0 -0
  17. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/otawp.py +0 -0
  18. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/otca.py +0 -0
  19. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/otds.py +0 -0
  20. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/otiv.py +0 -0
  21. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/otkd.py +0 -0
  22. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/otmm.py +0 -0
  23. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/otpd.py +0 -0
  24. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm/py.typed +0 -0
  25. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/__init__.py +0 -0
  26. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/__main__.py +0 -0
  27. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/app.py +0 -0
  28. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/auth/__init__.py +0 -0
  29. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/auth/functions.py +0 -0
  30. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/auth/models.py +0 -0
  31. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/auth/router.py +0 -0
  32. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/common/__init__.py +0 -0
  33. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/common/functions.py +0 -0
  34. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/common/metrics.py +0 -0
  35. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/common/models.py +0 -0
  36. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/common/router.py +0 -0
  37. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/settings.py +0 -0
  38. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/terminal/__init__.py +0 -0
  39. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/terminal/router.py +0 -0
  40. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_maintenance/__init__.py +0 -0
  41. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_maintenance/functions.py +0 -0
  42. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_maintenance/models.py +0 -0
  43. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_maintenance/router.py +0 -0
  44. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_otcs/__init__.py +0 -0
  45. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_otcs/functions.py +0 -0
  46. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_otcs/router.py +0 -0
  47. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_payload/__init__.py +0 -0
  48. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_payload/functions.py +0 -0
  49. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_payload/models.py +0 -0
  50. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_api/v1_payload/router.py +0 -0
  51. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/__init__.py +0 -0
  52. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/__main__.py +0 -0
  53. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/browser_automation.py +0 -0
  54. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/customizer.py +0 -0
  55. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/exceptions.py +0 -0
  56. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/guidewire.py +0 -0
  57. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/k8s.py +0 -0
  58. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/log.py +0 -0
  59. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/m365.py +0 -0
  60. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/payload_list.py +0 -0
  61. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/salesforce.py +0 -0
  62. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/sap.py +0 -0
  63. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/servicenow.py +0 -0
  64. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/settings.py +0 -0
  65. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/successfactors.py +0 -0
  66. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_customizer/translate.py +0 -0
  67. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_maintenance_page/__init__.py +0 -0
  68. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_maintenance_page/__main__.py +0 -0
  69. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_maintenance_page/app.py +0 -0
  70. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_maintenance_page/settings.py +0 -0
  71. {pyxecm-3.4.2 → pyxecm-3.4.3}/src/pyxecm_maintenance_page/static/favicon.avif +0 -0
  72. {pyxecm-3.4.2 → pyxecm-3.4.3}/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.2
3
+ Version: 3.4.3
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.2"
3
+ version = "3.4.3"
4
4
  description = 'A Python library to interact with Opentext Content Management Rest API'
5
5
  keywords = [
6
6
  'opentext',
@@ -2692,6 +2692,55 @@ class OTCS:
2692
2692
 
2693
2693
  # end method definition
2694
2694
 
2695
+ @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="get_system_default_metadata_language")
2696
+ def get_system_default_metadata_language(self) -> str | None:
2697
+ """Get the Content Server system default metadata language as a 2-letter ISO 639-1 code.
2698
+
2699
+ Derives the language from the server's ``metadata_languages`` list by finding
2700
+ the entry with ``'default': True`` (e.g. ``{'default': True, 'language_code': 'en'}`` → ``'en'``).
2701
+
2702
+ This is the language in which ``wksp_type_name`` and other default metadata fields are stored.
2703
+
2704
+ The result is cached because the system language does not change at runtime.
2705
+
2706
+ Returns:
2707
+ str | None:
2708
+ Two-letter language code (e.g. 'en', 'de', 'fr'),
2709
+ or None if the server info cannot be retrieved.
2710
+
2711
+ """
2712
+
2713
+ language = self.__get_system_default_metadata_language_cached()
2714
+ if language is None:
2715
+ self.__get_system_default_metadata_language_cached.cache_clear()
2716
+
2717
+ return language
2718
+
2719
+ @cache
2720
+ def __get_system_default_metadata_language_cached(self) -> str | None:
2721
+ response = self.get_server_info()
2722
+ if not response:
2723
+ return None
2724
+
2725
+ server_info = response.get("server")
2726
+ if not server_info:
2727
+ return None
2728
+
2729
+ # metadata_languages is a list of dicts, e.g.:
2730
+ # [{'default': True, 'display_name': 'English', 'language_code': 'en'}, ...]
2731
+ # The entry with 'default': True defines the system's metadata language.
2732
+ metadata_languages = server_info.get("metadata_languages")
2733
+ if not metadata_languages:
2734
+ return None
2735
+
2736
+ for lang in metadata_languages:
2737
+ if lang.get("default"):
2738
+ return lang.get("language_code")
2739
+
2740
+ return None
2741
+
2742
+ # end method definition
2743
+
2695
2744
  @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="apply_config")
2696
2745
  def apply_config(self, xml_file_path: str) -> dict | None:
2697
2746
  """Apply Content Server administration settings from XML file.
@@ -11167,11 +11216,24 @@ class OTCS:
11167
11216
  # end method definition
11168
11217
 
11169
11218
  @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="get_business_object_types")
11170
- def get_business_object_types(self) -> dict | None:
11219
+ def get_business_object_types(
11220
+ self,
11221
+ where_is_default_search: bool | None = None,
11222
+ where_workspace_type_id: int | None = None,
11223
+ drop_empty: bool = True,
11224
+ ) -> dict | None:
11171
11225
  """Get information for all configured business object types.
11172
11226
 
11173
11227
  Args:
11174
- None
11228
+ where_is_default_search (bool | None, optional):
11229
+ If True, only return business object types that are marked as default search.
11230
+ where_workspace_type_id (int | None, optional):
11231
+ If provided, only return business object types that are associated with the given workspace type ID.
11232
+ drop_empty (bool, optional):
11233
+ If True, drop any business object types that have empty properties. Default is True.
11234
+ It seems OTCM is returning some business object types with all properties empty
11235
+ except for the workspace type ID. These are likely the workspace types that
11236
+ don't have any business object types associated with them. We drop them by default.
11175
11237
 
11176
11238
  Returns:
11177
11239
  dict | None:
@@ -11210,15 +11272,28 @@ class OTCS:
11210
11272
 
11211
11273
  """
11212
11274
 
11213
- request_url = self.config()["businessObjectTypesUrl"]
11275
+ query = {}
11276
+ if where_is_default_search is not None:
11277
+ query["where_is_default_search"] = where_is_default_search
11278
+
11279
+ encoded_query = urllib.parse.urlencode(query=query, doseq=True)
11280
+
11281
+ request_url = self.config()["businessObjectTypesUrl"] + "?{}".format(encoded_query)
11214
11282
  request_header = self.request_form_header()
11215
11283
 
11216
11284
  self.logger.debug(
11217
- "Get all business object types; calling -> %s",
11285
+ "Get all business object types%s%s%s; calling -> %s",
11286
+ " where is default search = {}".format(where_is_default_search)
11287
+ if where_is_default_search is not None
11288
+ else "",
11289
+ " and" if where_is_default_search is not None and where_workspace_type_id is not None else "",
11290
+ " where workspace type ID = {}".format(where_workspace_type_id)
11291
+ if where_workspace_type_id is not None
11292
+ else "",
11218
11293
  request_url,
11219
11294
  )
11220
11295
 
11221
- return self.do_request(
11296
+ response = self.do_request(
11222
11297
  url=request_url,
11223
11298
  method="GET",
11224
11299
  headers=request_header,
@@ -11226,6 +11301,26 @@ class OTCS:
11226
11301
  failure_message="Failed to get business object types",
11227
11302
  )
11228
11303
 
11304
+ # Filter for workspace type ID - the REST API above does not provide such option
11305
+ # so we need to do it manually:
11306
+ if where_workspace_type_id is not None and response and "results" in response:
11307
+ response["results"] = [
11308
+ item
11309
+ for item in response["results"]
11310
+ if "data" in item
11311
+ and "properties" in item["data"]
11312
+ and item["data"]["properties"]["workspace_type_id"] == where_workspace_type_id
11313
+ ]
11314
+ # Drop empty business object types if requested:
11315
+ elif drop_empty and response and "results" in response:
11316
+ response["results"] = [
11317
+ item
11318
+ for item in response["results"]
11319
+ if "data" in item and "properties" in item["data"] and item["data"]["properties"]["bo_type"] is not None
11320
+ ]
11321
+
11322
+ return response
11323
+
11229
11324
  # end method definition
11230
11325
 
11231
11326
  @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="get_business_object_types_iterator")
@@ -11663,7 +11758,7 @@ class OTCS:
11663
11758
  @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="get_business_objects_search")
11664
11759
  def get_business_objects_search(
11665
11760
  self,
11666
- external_system_id: str,
11761
+ external_system_id: str | None = None,
11667
11762
  type_name: str | None = None,
11668
11763
  type_id: int | None = None,
11669
11764
  ) -> dict | None:
@@ -11673,18 +11768,24 @@ class OTCS:
11673
11768
  get_business_objects method.
11674
11769
 
11675
11770
  Args:
11676
- external_system_id (str):
11677
- The External system ID (such as "TM6").
11771
+ external_system_id (str | None):
11772
+ The External system ID (such as "TM6"). Optional parameter but
11773
+ needed if type_id is NOT provided ot type_name. If type_id is provided,
11774
+ the external_system_id parameter will be ignored.
11678
11775
  type_name (str | None):
11679
11776
  Type name of the business object (such as "SAP Customer").
11680
11777
  type_id (int | None):
11681
- The business object type ID. This is an alternative to the type_name parameter.
11682
- Either type_name or type_id needs to be provided.
11683
- If both are provided, type_id will be used and type_name will be ignored.
11778
+ The business object type ID. This is an alternative to the
11779
+ type_name + external_system_id parameters.
11780
+ Either external_system_id + type_name or type_id needs to be provided.
11781
+ If both are provided, type_id will be used and type_name and external_system_id
11782
+ will be ignored.
11684
11783
 
11685
11784
  Returns:
11686
11785
  dict | None:
11687
11786
  Business Object Search Form or None if the request fails.
11787
+ The first element in results contains the business object type information
11788
+ and the second element contains the search form information.
11688
11789
 
11689
11790
  Example:
11690
11791
  {
@@ -11848,14 +11949,16 @@ class OTCS:
11848
11949
  )
11849
11950
  return None
11850
11951
 
11851
- if not external_system_id:
11852
- self.logger.error("External system ID needs to be provided. Cannot get business object search form.")
11952
+ if type_name and not external_system_id:
11953
+ self.logger.error(
11954
+ "External system ID needs to be provided when type name is used. Cannot get business object search form."
11955
+ )
11853
11956
  return None
11854
11957
 
11855
- query = {
11856
- "ext_system_id": external_system_id,
11857
- }
11958
+ query = {}
11858
11959
 
11960
+ if external_system_id:
11961
+ query["ext_system_id"] = external_system_id
11859
11962
  if type_id:
11860
11963
  query["bo_type_id"] = type_id
11861
11964
  else:
@@ -11869,10 +11972,10 @@ class OTCS:
11869
11972
  request_header = self.request_form_header()
11870
11973
 
11871
11974
  self.logger.debug(
11872
- "Get search form for business object type%s -> '%s' and external system -> %s; calling -> %s",
11975
+ "Get search form for business object type%s -> '%s'%s; calling -> %s",
11873
11976
  " ID" if type_id else "",
11874
11977
  type_id or type_name,
11875
- external_system_id,
11978
+ " and external system -> {}".format(external_system_id) if external_system_id else "",
11876
11979
  request_url,
11877
11980
  )
11878
11981
 
@@ -11881,10 +11984,10 @@ class OTCS:
11881
11984
  method="GET",
11882
11985
  headers=request_header,
11883
11986
  timeout=None,
11884
- failure_message="Failed to get search form for business object type{} -> '{}' and external system -> {}".format(
11987
+ failure_message="Failed to get search form for business object type{} -> '{}'{}".format(
11885
11988
  " ID" if type_id else "",
11886
11989
  type_id or type_name,
11887
- external_system_id,
11990
+ " and external system -> {}".format(external_system_id) if external_system_id else "",
11888
11991
  ),
11889
11992
  )
11890
11993
 
@@ -13150,6 +13253,8 @@ class OTCS:
13150
13253
  @tracer.start_as_current_span(attributes=OTEL_TRACING_ATTRIBUTES, name="get_workspace_types")
13151
13254
  def get_workspace_types(
13152
13255
  self,
13256
+ ext_system_id: int | None = None,
13257
+ bo_type: str | None = None,
13153
13258
  expand_workspace_info: bool = True,
13154
13259
  expand_templates: bool = True,
13155
13260
  show_error: bool = True,
@@ -13162,6 +13267,16 @@ class OTCS:
13162
13267
  This endpoint may throw an HTTP 500 error if no workspace types are in OTCS.
13163
13268
 
13164
13269
  Args:
13270
+ ext_system_id (int | None, optional):
13271
+ External system ID to filter workspace types by. Needs to be provided
13272
+ together with bo_type. If ext_system_id is provided but bo_type is not,
13273
+ the REST API will fail.
13274
+ bo_type (str | None, optional):
13275
+ Business Object type name to filter workspace types by. The is the
13276
+ technical name of the business object type - NOT the ID! E.g. "KNA1"
13277
+ for customers in SAP S/4HANA. Needs to be provided together with ext_system_id.
13278
+ If bo_type is provided but ext_system_id is not, the REST API will fail.
13279
+ It is OK to omit both parameters to get all workspace types in OTCS.
13165
13280
  expand_workspace_info (bool, optional):
13166
13281
  Controls if the workspace info is returned as well
13167
13282
  expand_templates (bool, optional):
@@ -13181,26 +13296,73 @@ class OTCS:
13181
13296
  ```json
13182
13297
  {
13183
13298
  'links': {
13184
- 'data': {...}
13299
+ 'data': {
13300
+ 'self': {
13301
+ 'body': '',
13302
+ 'content_type': '',
13303
+ 'href': '/api/v2/businessworkspacetypes?bo_type=KNA1&expand_templates=true&expand_wksp_info=true&ext_system_id=TE1',
13304
+ 'method': 'GET',
13305
+ 'name': ''
13306
+ }
13307
+ }
13185
13308
  },
13186
13309
  'results': [
13187
13310
  {
13188
13311
  'data': {
13189
13312
  'properties': {
13190
- 'rm_enabled': False,
13191
- 'templates': [
13192
- {
13193
- 'id': 14471,
13194
- 'name': 'Campaign',
13195
- 'subtype': 848
13196
- },
13197
- ...
13198
- ],
13199
- 'wksp_type_id': 35,
13200
- 'wksp_type_name': 'Campaign'
13313
+ 'add_creator_to_lead_role': True,
13314
+ 'aviator_enabled': True,
13315
+ 'classification': {
13316
+ 'location': 'StaticNodeId',
13317
+ 'location_value': '30020'
13318
+ },
13319
+ 'config_node_id': 39309,
13320
+ 'external_document_storage': {
13321
+ 'rm_classification': {...},
13322
+ 'sub_location_path': {...}
13323
+ },
13324
+ 'indexing': {
13325
+ 'enabled': True,
13326
+ 'sub_types': [...]
13327
+ },
13328
+ 'relations': [{...}, {...}, {...}],
13329
+ 'rm_enabled': True,
13330
+ 'search': {
13331
+ 'follow_related_wksp': 'AlwaysEnabled',
13332
+ 'max_number_of_followed_wksp': 50
13333
+ },
13334
+ 'simplify_business_workspace_creation': False,
13335
+ 'templates': [{...}],
13336
+ 'widget_icon': {
13337
+ 'url': '/appimg/ot_bws/icons/39309.svg?v=162061_17502'
13338
+ },
13339
+ 'wksp_copy_enabled': True,
13340
+ 'wksp_creation': {
13341
+ 'create_wksp_with_fast_bulk_method': False,
13342
+ 'directly_open_created_wksp': True,
13343
+ 'location': 'StaticNodeId',
13344
+ 'location_value': '34962',
13345
+ 'sub_location_path': {...},
13346
+ 'use_also_for_manual_creation': True
13347
+ },
13348
+ 'wksp_creation_enabled': True,
13349
+ 'wksp_descriptions': {'ar': '', 'de': '', 'en': '', 'es': '', 'fr': '', 'it': '', 'iw': '', 'ja': '', 'nl': ''},
13350
+ 'wksp_icon': 'otsapxecm/wksp_customer.png',
13351
+ 'wksp_name_generation': {
13352
+ 'use_also_for_wksp_without_bo': False,
13353
+ 'wksp_names': {...}
13354
+ },
13355
+ 'wksp_policies_enabled': False,
13356
+ 'wksp_type_id': 16,
13357
+ 'wksp_type_name': 'Customer',
13358
+ 'wksp_type_names': {'ar': '', 'de': 'Kunde', 'en': 'Customer', 'es': '', 'fr': 'Client', 'it': 'Cliente', 'iw': '', 'ja': 'お客様', 'nl': ''}
13201
13359
  },
13202
13360
  'wksp_info': {
13203
- 'wksp_type_icon': '/appimg/ot_bws/icons/13147%2Esvg?v=161108_84584'
13361
+ 'in_use': True,
13362
+ 'indexing_status': 'UpToDate',
13363
+ 'modified': '2026-06-30T01:32:24',
13364
+ 'perspective_folder_id': 39316,
13365
+ 'wksp_type_icon': '/appimg/ot_bws/icons/39309.svg?v=162061_17502'
13204
13366
  }
13205
13367
  }
13206
13368
  }
@@ -13212,6 +13374,15 @@ class OTCS:
13212
13374
 
13213
13375
  query = {"expand_templates": expand_templates, "expand_wksp_info": expand_workspace_info}
13214
13376
 
13377
+ if (ext_system_id is not None and bo_type is None) or (ext_system_id is None and bo_type is not None):
13378
+ self.logger.error("Both ext_system_id and bo_type must be provided together.")
13379
+ return None
13380
+
13381
+ if ext_system_id is not None:
13382
+ query["ext_system_id"] = ext_system_id
13383
+ if bo_type is not None:
13384
+ query["bo_type"] = bo_type
13385
+
13215
13386
  encoded_query = urllib.parse.urlencode(query=query, doseq=True)
13216
13387
 
13217
13388
  request_url = self.config()["businessWorkspaceTypesUrlv2"] + "?{}".format(encoded_query)
@@ -14564,7 +14735,7 @@ class OTCS:
14564
14735
  self,
14565
14736
  node_id: int,
14566
14737
  ) -> list | None:
14567
- """Get a workspace references to business objects in external systems.
14738
+ """Get the workspace references to business objects in external systems.
14568
14739
 
14569
14740
  Args:
14570
14741
  node_id (int):
@@ -14572,7 +14743,7 @@ class OTCS:
14572
14743
 
14573
14744
  Returns:
14574
14745
  list | None:
14575
- A List of references to business objects in external systems.
14746
+ A list of references to business objects in external systems.
14576
14747
 
14577
14748
  Example:
14578
14749
  [
@@ -9010,11 +9010,12 @@ class Payload:
9010
9010
  # that is used for display purposes in the SmartView if given.
9011
9011
  # It can be either a string or a dict with language keys (e.g. "en", "de", ...)
9012
9012
  # and text values for multilingual support.
9013
- # If it is a string we convert it to the dict format with "en"
9014
- # as default language key:
9013
+ # If it is a string we convert it to the dict format with the
9014
+ # system default metadata language key:
9015
9015
  description = external_system.get("description")
9016
9016
  if description and isinstance(description, str):
9017
- description = {"en": description}
9017
+ default_locale = self._otcs.get_system_default_metadata_language() or "en"
9018
+ description = {default_locale: description}
9018
9019
 
9019
9020
  self._log_header_callback(
9020
9021
  text="Process External System -> '{}' ({})".format(system_name, system_type),
@@ -13447,7 +13448,7 @@ class Payload:
13447
13448
 
13448
13449
  """
13449
13450
 
13450
- if not self._workspaces:
13451
+ if not self._ontologies:
13451
13452
  self.logger.info(
13452
13453
  "Payload section -> '%s' is empty. Skipping...",
13453
13454
  section_name,
@@ -13505,6 +13506,8 @@ class Payload:
13505
13506
  )
13506
13507
  continue
13507
13508
 
13509
+ default_locale = ontology.get("locale", self._otcs.get_system_default_metadata_language() or "en")
13510
+
13508
13511
  for entity in entities:
13509
13512
  workspace_type_name = entity.get("name")
13510
13513
  if not workspace_type_name:
@@ -13624,12 +13627,13 @@ class Payload:
13624
13627
  for r in entity_relations
13625
13628
  ):
13626
13629
  self.logger.debug(
13627
- "Relationship from entity type -> '%s' (%s) to -> '%s' (%s) of relationship type -> '%s' already exists. Skipping duplicate...",
13630
+ "Relationship from entity type -> '%s' (%s) to -> '%s' (%s) of relationship type -> '%s' already exists (existing relationships -> %s). Skipping duplicate...",
13628
13631
  workspace_type_name,
13629
13632
  workspace_type_id,
13630
13633
  target_wksp_type_name,
13631
13634
  target_wksp_type_id,
13632
13635
  rel_type,
13636
+ str(entity_relations),
13633
13637
  )
13634
13638
  continue
13635
13639
 
@@ -13638,7 +13642,7 @@ class Payload:
13638
13642
  "target_wksp_type_id": int(target_wksp_type_id),
13639
13643
  "rel_type": rel_type, # the REST API requires Child and Parent capitalized
13640
13644
  "predicates": [
13641
- predicate if isinstance(predicate, dict) else {"en": predicate}
13645
+ predicate if isinstance(predicate, dict) else {default_locale: predicate}
13642
13646
  for predicate in rel.get("predicates", [])
13643
13647
  ], # the REST API supports multi-lingual. If the payload has plain strings as elements we interpret this as english only
13644
13648
  }
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