pyxecm 2.0.4__py3-none-any.whl → 3.0.1__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.

Potentially problematic release.


This version of pyxecm might be problematic. Click here for more details.

Files changed (94) hide show
  1. pyxecm/coreshare.py +5 -3
  2. pyxecm/helper/data.py +4 -4
  3. pyxecm/helper/otel_config.py +26 -0
  4. pyxecm/helper/web.py +1 -2
  5. pyxecm/otca.py +1356 -16
  6. pyxecm/otcs.py +2354 -593
  7. pyxecm/otds.py +1 -1
  8. pyxecm/otmm.py +4 -5
  9. pyxecm/py.typed +0 -0
  10. pyxecm-3.0.1.dist-info/METADATA +126 -0
  11. pyxecm-3.0.1.dist-info/RECORD +96 -0
  12. {pyxecm-2.0.4.dist-info → pyxecm-3.0.1.dist-info}/WHEEL +1 -2
  13. pyxecm-3.0.1.dist-info/entry_points.txt +4 -0
  14. {pyxecm/customizer/api → pyxecm_api}/__main__.py +1 -1
  15. pyxecm_api/agents/__init__.py +7 -0
  16. pyxecm_api/agents/app.py +13 -0
  17. pyxecm_api/agents/functions.py +119 -0
  18. pyxecm_api/agents/models.py +10 -0
  19. pyxecm_api/agents/otcm_knowledgegraph/functions.py +85 -0
  20. pyxecm_api/agents/otcm_knowledgegraph/models.py +61 -0
  21. pyxecm_api/agents/otcm_knowledgegraph/router.py +74 -0
  22. pyxecm_api/agents/otcm_user_agent/models.py +20 -0
  23. pyxecm_api/agents/otcm_user_agent/router.py +65 -0
  24. pyxecm_api/agents/otcm_workspace_agent/models.py +40 -0
  25. pyxecm_api/agents/otcm_workspace_agent/router.py +200 -0
  26. pyxecm_api/app.py +221 -0
  27. {pyxecm/customizer/api → pyxecm_api}/auth/functions.py +10 -2
  28. {pyxecm/customizer/api → pyxecm_api}/auth/router.py +4 -3
  29. {pyxecm/customizer/api → pyxecm_api}/common/functions.py +39 -9
  30. {pyxecm/customizer/api → pyxecm_api}/common/metrics.py +1 -2
  31. {pyxecm/customizer/api → pyxecm_api}/common/router.py +7 -8
  32. {pyxecm/customizer/api → pyxecm_api}/settings.py +21 -6
  33. {pyxecm/customizer/api → pyxecm_api}/terminal/router.py +1 -1
  34. {pyxecm/customizer/api → pyxecm_api}/v1_csai/router.py +39 -10
  35. pyxecm_api/v1_csai/statics/bindings/utils.js +189 -0
  36. pyxecm_api/v1_csai/statics/tom-select/tom-select.complete.min.js +356 -0
  37. pyxecm_api/v1_csai/statics/tom-select/tom-select.css +334 -0
  38. pyxecm_api/v1_csai/statics/vis-9.1.2/vis-network.css +1 -0
  39. pyxecm_api/v1_csai/statics/vis-9.1.2/vis-network.min.js +27 -0
  40. pyxecm_api/v1_maintenance/__init__.py +1 -0
  41. {pyxecm/customizer/api → pyxecm_api}/v1_maintenance/functions.py +3 -3
  42. {pyxecm/customizer/api → pyxecm_api}/v1_maintenance/router.py +8 -8
  43. pyxecm_api/v1_otcs/__init__.py +1 -0
  44. {pyxecm/customizer/api → pyxecm_api}/v1_otcs/functions.py +7 -5
  45. {pyxecm/customizer/api → pyxecm_api}/v1_otcs/router.py +8 -7
  46. pyxecm_api/v1_payload/__init__.py +1 -0
  47. {pyxecm/customizer/api → pyxecm_api}/v1_payload/functions.py +10 -7
  48. {pyxecm/customizer/api → pyxecm_api}/v1_payload/router.py +11 -10
  49. {pyxecm/customizer → pyxecm_customizer}/__init__.py +8 -0
  50. {pyxecm/customizer → pyxecm_customizer}/__main__.py +15 -21
  51. {pyxecm/customizer → pyxecm_customizer}/browser_automation.py +414 -103
  52. {pyxecm/customizer → pyxecm_customizer}/customizer.py +178 -116
  53. {pyxecm/customizer → pyxecm_customizer}/guidewire.py +60 -20
  54. {pyxecm/customizer → pyxecm_customizer}/k8s.py +4 -4
  55. pyxecm_customizer/knowledge_graph.py +719 -0
  56. pyxecm_customizer/log.py +35 -0
  57. {pyxecm/customizer → pyxecm_customizer}/m365.py +41 -33
  58. {pyxecm/customizer → pyxecm_customizer}/payload.py +2265 -1933
  59. {pyxecm/customizer/api/common → pyxecm_customizer}/payload_list.py +18 -55
  60. {pyxecm/customizer → pyxecm_customizer}/salesforce.py +1 -1
  61. {pyxecm/customizer → pyxecm_customizer}/sap.py +6 -2
  62. {pyxecm/customizer → pyxecm_customizer}/servicenow.py +2 -4
  63. {pyxecm/customizer → pyxecm_customizer}/settings.py +7 -6
  64. {pyxecm/customizer → pyxecm_customizer}/successfactors.py +40 -28
  65. {pyxecm/customizer → pyxecm_customizer}/translate.py +1 -1
  66. {pyxecm/maintenance_page → pyxecm_maintenance_page}/__main__.py +1 -1
  67. {pyxecm/maintenance_page → pyxecm_maintenance_page}/app.py +14 -8
  68. pyxecm/customizer/api/app.py +0 -157
  69. pyxecm/customizer/log.py +0 -107
  70. pyxecm/customizer/nhc.py +0 -1169
  71. pyxecm/customizer/openapi.py +0 -258
  72. pyxecm/customizer/pht.py +0 -1357
  73. pyxecm-2.0.4.dist-info/METADATA +0 -119
  74. pyxecm-2.0.4.dist-info/RECORD +0 -78
  75. pyxecm-2.0.4.dist-info/licenses/LICENSE +0 -202
  76. pyxecm-2.0.4.dist-info/top_level.txt +0 -1
  77. {pyxecm/customizer/api → pyxecm_api}/__init__.py +0 -0
  78. {pyxecm/customizer/api/auth → pyxecm_api/agents/otcm_knowledgegraph}/__init__.py +0 -0
  79. {pyxecm/customizer/api/common → pyxecm_api/agents/otcm_user_agent}/__init__.py +0 -0
  80. {pyxecm/customizer/api/v1_csai → pyxecm_api/agents/otcm_workspace_agent}/__init__.py +0 -0
  81. {pyxecm/customizer/api/v1_maintenance → pyxecm_api/auth}/__init__.py +0 -0
  82. {pyxecm/customizer/api → pyxecm_api}/auth/models.py +0 -0
  83. {pyxecm/customizer/api/v1_otcs → pyxecm_api/common}/__init__.py +0 -0
  84. {pyxecm/customizer/api → pyxecm_api}/common/models.py +0 -0
  85. {pyxecm/customizer/api → pyxecm_api}/terminal/__init__.py +0 -0
  86. {pyxecm/customizer/api/v1_payload → pyxecm_api/v1_csai}/__init__.py +0 -0
  87. {pyxecm/customizer/api → pyxecm_api}/v1_csai/models.py +0 -0
  88. {pyxecm/customizer/api → pyxecm_api}/v1_maintenance/models.py +0 -0
  89. {pyxecm/customizer/api → pyxecm_api}/v1_payload/models.py +0 -0
  90. {pyxecm/customizer → pyxecm_customizer}/exceptions.py +0 -0
  91. {pyxecm/maintenance_page → pyxecm_maintenance_page}/__init__.py +0 -0
  92. {pyxecm/maintenance_page → pyxecm_maintenance_page}/settings.py +0 -0
  93. {pyxecm/maintenance_page → pyxecm_maintenance_page}/static/favicon.avif +0 -0
  94. {pyxecm/maintenance_page → pyxecm_maintenance_page}/templates/maintenance.html +0 -0
pyxecm/customizer/pht.py DELETED
@@ -1,1357 +0,0 @@
1
- """PHT stands for Product Hierarchy Tracker.
2
-
3
- It is an OpenText internal application aiming at creating a common naming reference for Engineering Products and
4
- track all product-related data. It also provides an approved reporting hierarchy.
5
-
6
- See: https://pht.opentext.com
7
-
8
- Request for User Access Token: https://confluence.opentext.com/display/RDOT/Request+a+User+Access+Token
9
- PHT API Documentation: https://confluence.opentext.com/display/RDOT/PHT+API+Documentation
10
- """
11
-
12
- __author__ = "Dr. Marc Diefenbruch"
13
- __copyright__ = "Copyright (C) 2024-2025, OpenText"
14
- __credits__ = ["Kai-Philip Gatzweiler"]
15
- __maintainer__ = "Dr. Marc Diefenbruch"
16
- __email__ = "mdiefenb@opentext.com"
17
-
18
- import json
19
- import logging
20
- import platform
21
- import sys
22
- import time
23
- from importlib.metadata import version
24
-
25
- import requests
26
- from requests.auth import HTTPBasicAuth
27
-
28
- from pyxecm.helper import Data
29
-
30
- APP_NAME = "pyxecm"
31
- APP_VERSION = version("pyxecm")
32
- MODULE_NAME = APP_NAME + ".customizer.pht"
33
-
34
- PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
35
- OS_INFO = f"{platform.system()} {platform.release()}"
36
- ARCH_INFO = platform.machine()
37
- REQUESTS_VERSION = requests.__version__
38
-
39
- USER_AGENT = (
40
- f"{APP_NAME}/{APP_VERSION} ({MODULE_NAME}/{APP_VERSION}; "
41
- f"Python/{PYTHON_VERSION}; {OS_INFO}; {ARCH_INFO}; Requests/{REQUESTS_VERSION})"
42
- )
43
-
44
- REQUEST_HEADERS = {"User-Agent": USER_AGENT, "accept": "application/json", "Content-Type": "application/json"}
45
-
46
- REQUEST_TIMEOUT = 60
47
- REQUEST_RETRY_DELAY = 20
48
- REQUEST_MAX_RETRIES = 2
49
-
50
- default_logger = logging.getLogger(MODULE_NAME)
51
-
52
-
53
- class PHT:
54
- """Class PHT is used to retrieve data from OpenText PHT. It is a pure read-only access."""
55
-
56
- logger: logging.Logger = (default_logger,)
57
-
58
- _config: dict
59
- _session = None
60
- _business_unit_exclusions = None
61
- _business_unit_inclusions = None
62
- _product_exclusions = None
63
- _product_inclusions = None
64
- _product_category_exclusions = None
65
- _product_category_inclusions = None
66
- _product_status_exclusions = None
67
- _product_status_inclusions = None
68
-
69
- def __init__(
70
- self,
71
- base_url: str,
72
- username: str,
73
- password: str,
74
- business_unit_exclusions: list | None = None,
75
- business_unit_inclusions: list | None = None,
76
- product_exclusions: list | None = None,
77
- product_inclusions: list | None = None,
78
- product_category_exclusions: list | None = None,
79
- product_category_inclusions: list | None = None,
80
- product_status_exclusions: list | None = None,
81
- product_status_inclusions: list | None = None,
82
- logger: logging.Logger = default_logger,
83
- ) -> None:
84
- """Initialize the PHT object.
85
-
86
- Args:
87
- base_url (str):
88
- The base URL of PHT.
89
- username (str):
90
- The user name to access PHT.
91
- password (str):
92
- The password of the user.
93
- business_unit_exclusions (list | None, optional):
94
- A black list for business units to exclude. Default = None.
95
- business_unit_inclusions (list | None, optional):
96
- A white list for business units to include. Default = None.
97
- product_exclusions (list | None, optional):
98
- A black list for products to exclude. Default = None.
99
- product_inclusions (list | None, optional):
100
- A white list for products to include. Default = None.
101
- product_category_exclusions (list | None, optional):
102
- A black list for product categories to exclude. Default = None.
103
- product_category_inclusions (list | None, optional):
104
- A white list for product categories to include. Default = None.
105
- product_status_exclusions (list | None, optional):
106
- A back list of product status to exclude. Only products with status NOT on
107
- this list will be included. Default = None.
108
- product_status_inclusions (list | None, optional):
109
- A white list of product status to exclude. Only products with status on
110
- this list will be included. Default = None.
111
- logger (logging.Logger):
112
- The logging object used for all log messages. Default = default_logger.
113
-
114
- """
115
-
116
- if logger != default_logger:
117
- self.logger = logger.getChild("pht")
118
- for logfilter in logger.filters:
119
- self.logger.addFilter(logfilter)
120
-
121
- pht_config = {}
122
-
123
- # Store the credentials and parameters in a config dictionary:
124
- pht_config["baseUrl"] = base_url
125
- pht_config["username"] = username
126
- pht_config["password"] = password
127
-
128
- pht_config["restUrl"] = pht_config["baseUrl"] + "/api"
129
- pht_config["attributeUrl"] = pht_config["restUrl"] + "/attribute"
130
- pht_config["businessUnitUrl"] = pht_config["restUrl"] + "/business-unit"
131
- pht_config["productFamilyUrl"] = pht_config["restUrl"] + "/product-family"
132
- pht_config["productUrl"] = pht_config["restUrl"] + "/product"
133
- pht_config["productFilteredUrl"] = pht_config["productUrl"] + "/filtered"
134
- pht_config["productSearchUrl"] = pht_config["productUrl"] + "/search"
135
- pht_config["productUsersUrl"] = pht_config["productUrl"] + "/users"
136
- pht_config["teamUrl"] = pht_config["restUrl"] + "/team"
137
- pht_config["masterProductUrl"] = pht_config["restUrl"] + "/master-product"
138
- pht_config["masterProductFilteredUrl"] = pht_config["masterProductUrl"] + "/filtered"
139
- pht_config["componentUrl"] = pht_config["restUrl"] + "/component"
140
- pht_config["componentFilteredUrl"] = pht_config["componentUrl"] + "/filtered"
141
- pht_config["componentSearchUrl"] = pht_config["componentUrl"] + "/search"
142
- pht_config["componentUsersUrl"] = pht_config["componentUrl"] + "/users"
143
-
144
- self._config = pht_config
145
-
146
- self._session = requests.Session()
147
-
148
- self._data = Data(logger=self.logger)
149
-
150
- self._business_unit_exclusions = business_unit_exclusions
151
- self._business_unit_inclusions = business_unit_inclusions
152
- self._product_exclusions = product_exclusions
153
- self._product_inclusions = product_inclusions
154
- self._product_category_exclusions = product_category_exclusions
155
- self._product_category_inclusions = product_category_inclusions
156
- self._product_status_exclusions = product_status_exclusions
157
- self._product_status_inclusions = product_status_inclusions
158
-
159
- # end method definition
160
-
161
- def config(self) -> dict:
162
- """Return the configuration dictionary.
163
-
164
- Returns:
165
- dict:
166
- The configuration dictionary.
167
-
168
- """
169
- return self._config
170
-
171
- # end method definition
172
-
173
- def get_data(self) -> Data:
174
- """Get the Data object that holds all processed PHT products.
175
-
176
- Returns:
177
- Data:
178
- Datastructure with all processed PHT product data.
179
-
180
- """
181
-
182
- return self._data
183
-
184
- # end method definition
185
-
186
- def request_header(self, content_type: str = "") -> dict:
187
- """Return the request header used for Application calls.
188
-
189
- Consists of Bearer access token and Content Type
190
-
191
- Args:
192
- content_type (str, optional):
193
- Custom content type for the request.
194
- Typical values:
195
- * application/json - Used for sending JSON-encoded data
196
- * application/x-www-form-urlencoded - The default for HTML forms.
197
- Data is sent as key-value pairs in the body of the request, similar to query parameters.
198
- * multipart/form-data - Used for file uploads or when a form includes non-ASCII characters
199
-
200
- Returns:
201
- dict:
202
- The request header values.
203
-
204
- """
205
-
206
- request_header = {}
207
-
208
- request_header = REQUEST_HEADERS
209
-
210
- if content_type:
211
- request_header["Content-Type"] = content_type
212
-
213
- return request_header
214
-
215
- # end method definition
216
-
217
- def do_request(
218
- self,
219
- url: str,
220
- method: str = "GET",
221
- headers: dict | None = None,
222
- data: dict | None = None,
223
- json_data: dict | None = None,
224
- files: dict | None = None,
225
- timeout: int | None = REQUEST_TIMEOUT,
226
- show_error: bool = True,
227
- failure_message: str = "",
228
- success_message: str = "",
229
- max_retries: int = REQUEST_MAX_RETRIES,
230
- retry_forever: bool = False,
231
- ) -> dict | None:
232
- """Call an PHT REST API in a safe way.
233
-
234
- Args:
235
- url (str):
236
- The URL to send the request to.
237
- method (str, optional):
238
- HTTP method (GET, POST, etc.). Defaults to "GET".
239
- headers (dict | None, optional):
240
- Request Headers. Defaults to None.
241
- data (dict | None, optional):
242
- Request payload. Defaults to None.
243
- json_data (dict | None, optional):
244
- Request payload. Defaults to None.
245
- files (dict | None, optional):
246
- Dictionary of {"name": file-tuple} for multipart encoding upload.
247
- The file-tuple can be a 2-tuple ("filename", fileobj) or a 3-tuple ("filename", fileobj, "content_type")
248
- timeout (int | None, optional):
249
- Timeout for the request in seconds. Defaults to REQUEST_TIMEOUT.
250
- show_error (bool, optional):
251
- Whether or not an error should be logged in case of a failed REST call.
252
- If False, then only a warning is logged. Defaults to True.
253
- failure_message (str, optional):
254
- Specific error message. Defaults to "".
255
- success_message (str, optional):
256
- Specific success message. Defaults to "".
257
- max_retries (int, optional):
258
- How many retries on Connection errors? Default is REQUEST_MAX_RETRIES.
259
- retry_forever (bool, optional):
260
- Eventually wait forever - without timeout. Defaults to False.
261
-
262
- Returns:
263
- dict | None:
264
- Response of PHT REST API or None in case of an error.
265
-
266
- """
267
-
268
- retries = 0
269
- while True:
270
- try:
271
- response = self._session.request(
272
- method=method,
273
- url=url,
274
- data=data,
275
- json=json_data,
276
- files=files,
277
- headers=headers,
278
- timeout=timeout,
279
- )
280
-
281
- if response.ok:
282
- if success_message:
283
- self.logger.debug(success_message)
284
- return self.parse_request_response(response)
285
- # Check if Session has expired - then re-authenticate and try once more
286
- elif response.status_code == 401 and retries == 0:
287
- self.logger.debug("Session has expired - try to re-authenticate...")
288
- self.authenticate()
289
- retries += 1
290
- else:
291
- # Handle plain HTML responses to not pollute the logs
292
- content_type = response.headers.get("content-type", None)
293
- response_text = "HTML content (see debug log)" if content_type == "text/html" else response.text
294
-
295
- if show_error:
296
- self.logger.error(
297
- "%s; status -> %s; error -> %s",
298
- failure_message,
299
- response.status_code,
300
- response_text,
301
- )
302
- else:
303
- self.logger.warning(
304
- "%s; status -> %s; warning -> %s",
305
- failure_message,
306
- response.status_code,
307
- response_text,
308
- )
309
-
310
- if content_type == "text/html":
311
- self.logger.debug(
312
- "%s; status -> %s; warning -> %s",
313
- failure_message,
314
- response.status_code,
315
- response.text,
316
- )
317
-
318
- return None
319
- except requests.exceptions.Timeout:
320
- if retries <= max_retries:
321
- self.logger.warning(
322
- "Request timed out. Retrying in %s seconds...",
323
- str(REQUEST_RETRY_DELAY),
324
- )
325
- retries += 1
326
- time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
327
- else:
328
- self.logger.error(
329
- "%s; timeout error,",
330
- failure_message,
331
- )
332
- if retry_forever:
333
- # If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
334
- self.logger.warning("Turn timeouts off and wait forever...")
335
- timeout = None
336
- else:
337
- return None
338
- except requests.exceptions.ConnectionError:
339
- if retries <= max_retries:
340
- self.logger.warning(
341
- "Connection error. Retrying in %s seconds...",
342
- str(REQUEST_RETRY_DELAY),
343
- )
344
- retries += 1
345
- time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
346
- else:
347
- self.logger.error(
348
- "%s; connection error.",
349
- failure_message,
350
- )
351
- if retry_forever:
352
- # If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
353
- self.logger.warning("Turn timeouts off and wait forever...")
354
- timeout = None
355
- time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
356
- else:
357
- return None
358
-
359
- # end method definition
360
-
361
- def parse_request_response(
362
- self,
363
- response_object: requests.Response,
364
- additional_error_message: str = "",
365
- show_error: bool = True,
366
- ) -> list | None:
367
- """Convert the request response (JSon) to a Python list in a safe way that also handles exceptions.
368
-
369
- It first tries to load the response.text via json.loads() that produces
370
- a dict output. Only if response.text is not set or is empty it just converts
371
- the response_object to a dict using the vars() built-in method.
372
-
373
- Args:
374
- response_object (object):
375
- This is reponse object delivered by the request call.
376
- additional_error_message (str, optional):
377
- Used to provide a more specific error message.
378
- show_error (bool):
379
- If True, write an error to the log file.
380
- If False, write a warning to the log file.
381
-
382
- Returns:
383
- list: response information or None in case of an error
384
-
385
- """
386
-
387
- if not response_object:
388
- return None
389
-
390
- try:
391
- list_object = json.loads(response_object.text) if response_object.text else vars(response_object)
392
- except json.JSONDecodeError as exception:
393
- if additional_error_message:
394
- message = "Cannot decode response as JSON. {}; error -> {}".format(
395
- additional_error_message,
396
- exception,
397
- )
398
- else:
399
- message = "Cannot decode response as JSON; error -> {}".format(
400
- exception,
401
- )
402
- if show_error:
403
- self.logger.error(message)
404
- else:
405
- self.logger.warning(message)
406
- return None
407
- else:
408
- return list_object
409
-
410
- # end method definition
411
-
412
- def authenticate(self) -> HTTPBasicAuth | None:
413
- """Authenticate at PHT with basic authentication.
414
-
415
- Returns:
416
- str | None:
417
- Session authorization string.
418
-
419
- """
420
-
421
- self._session.headers.update(self.request_header())
422
-
423
- username = self.config()["username"]
424
- password = self.config()["password"]
425
- if not self._session:
426
- self._session = requests.Session()
427
- self._session.auth = HTTPBasicAuth(username, password)
428
-
429
- return self._session.auth
430
-
431
- # end method definition
432
-
433
- def get_attributes(self) -> list | None:
434
- """Get a list of all product attributes (schema) of PHT.
435
-
436
- Returns:
437
- list | None: list of product attributes
438
-
439
- Example:
440
- [
441
- {
442
- 'id': 28,
443
- 'uuid': '43ba5852-eb83-11ed-a752-00505682262c',
444
- 'name': 'UBM SCM Migration JIRA/ValueEdge',
445
- 'description': 'Identifies the Issue to track work for the SCM migration for this project.',
446
- 'type': 'TEXT',
447
- 'attributeCategory': {
448
- 'id': 2,
449
- 'name': 'Auxiliary assignment'
450
- },
451
- 'showDefault': False,
452
- 'restricted': True,
453
- 'allowScopeChain': True,
454
- 'visibleToAll': False,
455
- 'deleted': False,
456
- 'attributeOptions': [],
457
- 'attributeScopes': [],
458
- 'allowedTeams': []
459
- }
460
- ]
461
-
462
- """
463
-
464
- request_header = self.request_header()
465
- request_url = self.config()["attributeUrl"]
466
-
467
- return self.do_request(
468
- url=request_url,
469
- method="GET",
470
- headers=request_header,
471
- timeout=None,
472
- failure_message="Failed to get PHT attributes!",
473
- )
474
-
475
- # end method definition
476
-
477
- def get_business_units(self) -> list | None:
478
- """Get the list of PHT Business Units.
479
-
480
- Returns:
481
- list | None:
482
- The list of the known business units.
483
-
484
- Example:
485
- [
486
- {
487
- 'id': 1,
488
- 'name': 'Content Services',
489
- 'leaderModel': {
490
- 'id': 219,
491
- 'domain': 'mcybala',
492
- 'email': 'mcybala@opentext.com',
493
- 'name': 'Michael Cybala',
494
- 'role': None,
495
- 'status': 'ACTIVE',
496
- 'location': 'Kempten, DEU',
497
- 'title': 'VP, Software Engineering',
498
- 'type': 'OTHERS'
499
- },
500
- 'pmLeaderModel': {
501
- 'id': 350,
502
- 'domain': 'mdiefenb',
503
- 'email': 'mdiefenb@opentext.com',
504
- 'name': 'Marc Diefenbruch',
505
- 'role': None,
506
- 'status': 'ACTIVE',
507
- 'location': 'Virtual, DEU',
508
- 'title': 'VP, Product Management',
509
- 'type': 'OTHERS'
510
- },
511
- 'sltOwnerModel': {
512
- 'id': 450,
513
- 'domain': 'jradko',
514
- 'email': 'jradko@opentext.com',
515
- 'name': 'John Radko',
516
- 'role': None,
517
- 'status': 'ACTIVE',
518
- 'location': 'Gaithersburg, MD, USA',
519
- 'title': 'SVP, Software Engineering',
520
- 'type': 'OTHERS'
521
- },
522
- 'status': 'ACTIVE',
523
- 'engineering': True,
524
- 'attributes': [{...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}],
525
- 'leader': 'Michael Cybala',
526
- 'leaderDomain': 'mcybala',
527
- 'pmLeader': 'Marc Diefenbruch',
528
- 'pmLeaderDomain': 'mdiefenb',
529
- 'sltOwner': 'John Radko',
530
- 'sltOwnerDomain': 'jradko'
531
- }
532
- ]
533
-
534
- """
535
-
536
- request_header = self.request_header()
537
- request_url = self.config()["businessUnitUrl"]
538
-
539
- return self.do_request(
540
- url=request_url,
541
- method="GET",
542
- headers=request_header,
543
- timeout=None,
544
- failure_message="Failed to get PHT business units!",
545
- )
546
-
547
- # end method definition
548
-
549
- def get_product_families(self) -> list | None:
550
- """Get the list of PHT product families (LoBs).
551
-
552
- Returns:
553
- list | None:
554
- A list of the known product families.
555
-
556
- """
557
-
558
- request_header = self.request_header()
559
- request_url = self.config()["productFamilyUrl"]
560
-
561
- return self.do_request(
562
- url=request_url,
563
- method="GET",
564
- headers=request_header,
565
- timeout=None,
566
- failure_message="Failed to get PHT product families",
567
- )
568
-
569
- # end method definition
570
-
571
- def get_products(self) -> list | None:
572
- """Get the list of PHT products.
573
-
574
- Returns:
575
- list | None:
576
- A list of the known products.
577
-
578
- """
579
-
580
- request_header = self.request_header()
581
- request_url = self.config()["productUrl"]
582
-
583
- return self.do_request(
584
- url=request_url,
585
- method="GET",
586
- headers=request_header,
587
- timeout=None,
588
- failure_message="Failed to get PHT products",
589
- )
590
-
591
- # end method definition
592
-
593
- def get_products_filtered(
594
- self,
595
- filter_definition: dict | None = None,
596
- ) -> list | None:
597
- """Get a list of filtered PHT products.
598
-
599
- Args:
600
- filter_definition (dict | None, optional):
601
- A dictionary of filter conditions. Default is None (no filter).
602
- Example filters:
603
- {
604
- businessUnitName: <String>,
605
- productFamilyName: <String>,
606
- productName: <String>,
607
- productSyncId: <String>,
608
- productStatus: ACTIVE | INACTIVE | MAINTENANCE,
609
- productManager: <String>,
610
- developmentManager: <String>,
611
- attributeOperator: AND | OR,
612
- attributes: {
613
- "<AttributeName>": {
614
- "compare": CONTAINS | EXISTS | DOES_NOT_EXISTS,
615
- "values": List<String>
616
- },
617
- ...
618
- },
619
- includeAttributes: true | false
620
- statuses: ["ACTIVE"],
621
- }
622
-
623
- Returns:
624
- list | None: list of matching products.
625
-
626
- """
627
-
628
- if not filter_definition:
629
- return self.get_products()
630
-
631
- request_header = self.request_header()
632
- request_url = self.config()["productFilteredUrl"]
633
- request_data = filter_definition
634
-
635
- return self.do_request(
636
- url=request_url,
637
- method="POST",
638
- headers=request_header,
639
- json_data=request_data,
640
- timeout=None,
641
- failure_message="Failed to get filtered PHT products",
642
- )
643
-
644
- # end method definition
645
-
646
- def get_product(self, sync_id: str) -> dict | None:
647
- """Get a specific product in PHT.
648
-
649
- Args:
650
- sync_id (str): Unique ID of the PHT product.
651
-
652
- Returns:
653
- dict | None: product data matching the sync ID
654
-
655
- """
656
-
657
- request_header = self.request_header()
658
- request_url = self.config()["productUrl"] + "/" + str(sync_id)
659
-
660
- return self.do_request(
661
- url=request_url,
662
- method="GET",
663
- headers=request_header,
664
- timeout=None,
665
- failure_message="Failed to retrieve PHT product with sync ID -> {}!".format(
666
- sync_id,
667
- ),
668
- )
669
-
670
- # end method definition
671
-
672
- def search_products(
673
- self,
674
- query: str,
675
- business_unit: str | None = None,
676
- family: str | None = None,
677
- ) -> list | None:
678
- """Search for specific product in PHT by the product name, business unit or product family (or a combination).
679
-
680
- Args:
681
- query (str):
682
- Query to search for specific products.
683
- business_unit (str | None, optional):
684
- Used to focus the search on a specific Business Unit.
685
- family (str | None, optional):
686
- Used to focus the search on a specific product family (Line of Business)
687
-
688
- Returns:
689
- list | None:
690
- Search term matches any part of the component name.
691
-
692
- """
693
-
694
- request_header = self.request_header()
695
- request_url = self.config()["componentSearchUrl"] + "?q=" + query
696
- if business_unit:
697
- request_url += "&businessUnit=" + business_unit
698
- if family:
699
- request_url += "&family=" + family
700
-
701
- return self.do_request(
702
- url=request_url,
703
- method="GET",
704
- headers=request_header,
705
- timeout=None,
706
- failure_message="Failed to retrieve PHT components matching -> {}!".format(
707
- query,
708
- ),
709
- )
710
-
711
- # end method definition
712
-
713
- def get_master_products(self) -> list | None:
714
- """Get the list of PHT master products.
715
-
716
- Returns:
717
- list | None:
718
- A list of the known master products.
719
-
720
- """
721
-
722
- request_header = self.request_header()
723
- request_url = self.config()["masterProductUrl"]
724
-
725
- return self.do_request(
726
- url=request_url,
727
- method="GET",
728
- headers=request_header,
729
- timeout=None,
730
- failure_message="Failed to get PHT master products",
731
- )
732
-
733
- # end method definition
734
-
735
- def get_master_products_filtered(
736
- self,
737
- filter_definition: dict | None = None,
738
- ) -> list | None:
739
- """Get a list of filtered PHT master products.
740
-
741
- Args:
742
- filter_definition (dict | None, optional):
743
- A dictionary of filter conditions.
744
- Example filters:
745
- {
746
- businessUnitName: <String>,
747
- productFamilyName: <String>,
748
- masterproductName: <String>,
749
- masterproductSyncId: <String>,
750
- masterproductStatus: ACTIVE | INACTIVE | MAINTENANCE,
751
- productManagerDomain: <String>,
752
- attributeOperator: AND | OR,
753
- attributes: {
754
- "<AttributeName>": {
755
- "compare": CONTAINS | EXISTS | DOES_NOT_EXISTS,
756
- "values": List<String>
757
- },
758
- ...
759
- },
760
- includeAttributes: true | false
761
- includeLinkedProducts: true | false
762
- }
763
-
764
- Returns:
765
- list | None: List of matching products.
766
-
767
- """
768
-
769
- if not filter_definition:
770
- return self.get_products()
771
-
772
- request_header = self.request_header()
773
- request_url = self.config()["masterProductFilteredUrl"]
774
- request_data = filter_definition
775
-
776
- return self.do_request(
777
- url=request_url,
778
- method="POST",
779
- headers=request_header,
780
- json_data=request_data,
781
- timeout=None,
782
- failure_message="Failed to get filtered PHT master products",
783
- )
784
-
785
- # end method definition
786
-
787
- def get_master_product(self, sync_id: str) -> dict | None:
788
- """Get a specific product in PHT.
789
-
790
- Args:
791
- sync_id (str): Unique PHT ID of the master product.
792
-
793
- Returns:
794
- dict | None: product data matching the sync ID
795
-
796
- """
797
-
798
- request_header = self.request_header()
799
- request_url = self.config()["productUrl"] + "/" + str(sync_id)
800
-
801
- return self.do_request(
802
- url=request_url,
803
- method="GET",
804
- headers=request_header,
805
- timeout=None,
806
- failure_message="Failed to retrieve PHT product with sync ID -> {}".format(
807
- sync_id,
808
- ),
809
- )
810
-
811
- # end method definition
812
-
813
- def get_teams(self) -> list | None:
814
- """Get a list of all teams in PHT.
815
-
816
- Returns:
817
- list | None: list of PHT teams
818
-
819
- """
820
-
821
- request_header = self.request_header()
822
- request_url = self.config()["teamUrl"]
823
-
824
- return self.do_request(
825
- url=request_url,
826
- method="GET",
827
- headers=request_header,
828
- timeout=None,
829
- failure_message="Failed to retrieve PHT teams",
830
- )
831
-
832
- # end method definition
833
-
834
- def get_team(self, team_id: str) -> dict | None:
835
- """Get a specific team in PHT.
836
-
837
- Args:
838
- team_id (str): Unique PHT ID of the team.
839
-
840
- Returns:
841
- dict | None: Details of the PHT team.
842
-
843
- """
844
-
845
- request_header = self.request_header()
846
- request_url = self.config()["teamUrl"] + "/" + str(team_id)
847
-
848
- return self.do_request(
849
- url=request_url,
850
- method="GET",
851
- headers=request_header,
852
- timeout=None,
853
- failure_message="Failed to retrieve PHT team with ID -> {}".format(team_id),
854
- )
855
-
856
- # end method definition
857
-
858
- def get_components(self) -> list:
859
- """Get a list of all components in PHT.
860
-
861
- Returns:
862
- list: list of PHT components
863
-
864
- Example:
865
- [
866
- {
867
- 'id': 468,
868
- 'syncId': '6380c3da-8ded-40cd-8071-61f6721956f5',
869
- 'name': 'XOTE',
870
- 'developmentManager': {
871
- 'id': 237,
872
- 'domain': 'kurt',
873
- 'email': 'kurt.junker@opentext.com',
874
- 'name': 'Kurt Junker',
875
- 'role': None,
876
- 'status': 'ACTIVE',
877
- 'location': 'Grasbrunn, DEU',
878
- 'title': 'Sr. Manager, Software Engineering',
879
- 'type': 'OTHERS'
880
- },
881
- 'componentCategory': {
882
- 'id': 2,
883
- 'name': 'Testing scripts',
884
- 'shortName': 'Testing scripts'
885
- },
886
- 'comment': 'Test Framework maintained and used by Core Archive Team',
887
- 'status': 'MAINTENANCE',
888
- 'attributes': [
889
- {
890
- 'id': 409,
891
- 'attribute': {
892
- 'id': 4,
893
- 'uuid': '03e228b5-9eae-11ea-96ab-00505682bce9',
894
- 'name': 'Build Advocate',
895
- 'description': 'Primary contact for build items.',
896
- 'type': 'USER'
897
- },
898
- 'value': 'burkhard',
899
- 'textAttributeValue': None,
900
- 'userAttributeValue': {
901
- 'id': 414,
902
- 'domain': 'burkhard',
903
- 'email': 'burkhard.meier@opentext.com',
904
- 'name': 'Burkhard Meier',
905
- 'role': None,
906
- 'status': 'ACTIVE',
907
- 'location': 'Virtual, DEU',
908
- 'title': 'Principal Software Engineer',
909
- 'type': 'DEV'
910
- },
911
- 'listAttributeValue': None
912
- },
913
- ...
914
- ],
915
- 'sourceRepos': [],
916
- 'artifacts': [],
917
- 'products': [],
918
- 'teams': [],
919
- 'users': [],
920
- 'guestTeams': [],
921
- 'guestUsers': [],
922
- 'relatedLOBS': []
923
- }
924
- ]
925
-
926
- """
927
-
928
- request_header = self.request_header()
929
- request_url = self.config()["componentUrl"]
930
-
931
- return self.do_request(
932
- url=request_url,
933
- method="GET",
934
- headers=request_header,
935
- timeout=None,
936
- failure_message="Failed to retrieve PHT components",
937
- )
938
-
939
- # end method definition
940
-
941
- def get_components_filtered(
942
- self,
943
- filter_definition: dict | None = None,
944
- ) -> list | None:
945
- """Get a list of filtered PHT components.
946
-
947
- Args:
948
- filter_definition (dict | None, optional):
949
- A dictionary of filter conditions.
950
- Example filters:
951
- {
952
- componentName: <String>,
953
- componentSyncId: <String>,
954
- componentStatus: ACTIVE | INACTIVE | MAINTENANCE,
955
- developmentManager: <String>,
956
- attributeOperator: AND | OR,
957
- attributes: {
958
- "<AttributeName>": {
959
- "compare": CONTAINS | EXISTS | DOES_NOT_EXISTS,
960
- "values": List<String>
961
- },
962
- ...
963
- },
964
- includeAttributes: true | false
965
- }
966
-
967
- Returns:
968
- list | None: list of matching components.
969
-
970
- """
971
-
972
- if not filter_definition:
973
- return self.get_products()
974
-
975
- request_header = self.request_header()
976
- request_url = self.config()["masterProductFilteredUrl"]
977
- request_data = filter_definition
978
-
979
- return self.do_request(
980
- url=request_url,
981
- method="POST",
982
- headers=request_header,
983
- json_data=request_data,
984
- timeout=None,
985
- failure_message="Failed to get filtered PHT components",
986
- )
987
-
988
- # end method definition
989
-
990
- def get_component(self, sync_id: str) -> dict | None:
991
- """Get a specific component in PHT.
992
-
993
- Args:
994
- sync_id (str):
995
- Unique PHT ID of the component.
996
-
997
- Returns:
998
- dict | None:
999
- Details of the PHT component. None in case of an error.
1000
-
1001
- """
1002
-
1003
- request_header = self.request_header()
1004
- request_url = self.config()["componentUrl"] + "/" + str(sync_id)
1005
-
1006
- return self.do_request(
1007
- url=request_url,
1008
- method="GET",
1009
- headers=request_header,
1010
- timeout=None,
1011
- failure_message="Failed to retrieve PHT component with sync ID -> {}!".format(
1012
- sync_id,
1013
- ),
1014
- )
1015
-
1016
- # end method definition
1017
-
1018
- def search_components(self, query: str) -> list | None:
1019
- """Search for specific components in PHT by the component name.
1020
-
1021
- Args:
1022
- query (str): Search term to match any part of the component name.
1023
-
1024
- Returns:
1025
- list | None: List of matching components.
1026
-
1027
- """
1028
-
1029
- request_header = self.request_header()
1030
- request_url = self.config()["componentSearchUrl"] + "?q=" + query
1031
-
1032
- return self.do_request(
1033
- url=request_url,
1034
- method="GET",
1035
- headers=request_header,
1036
- timeout=None,
1037
- failure_message="Failed to retrieve PHT components matching -> {}".format(
1038
- query,
1039
- ),
1040
- )
1041
-
1042
- # end method definition
1043
-
1044
- def load_business_units(self, business_unit_list: list | None = None) -> bool:
1045
- """Load business units into a data frame in the self._data object.
1046
-
1047
- Args:
1048
- business_unit_list (list, optional):
1049
- List of business units - if already avaiable. Defaults to None.
1050
- If None, then the list of all business units is created on-the-fly.
1051
-
1052
- Returns:
1053
- bool:
1054
- True if successful, False otherwise.
1055
-
1056
- """
1057
-
1058
- if not business_unit_list:
1059
- self.logger.info("Load PHT business unit list...")
1060
- # First, get the list of all products:
1061
- business_unit_list = self.get_business_units()
1062
- if business_unit_list:
1063
- self.logger.info(
1064
- "Completed loading of -> %s PHT business units",
1065
- str(len(business_unit_list)),
1066
- )
1067
- else:
1068
- self.logger.error("Failed to load PHT business units!")
1069
- return False
1070
-
1071
- # Put the business unit list in an initial data frame.
1072
- # This makes it easy to filter with powerful Pandas capabilities:
1073
- self._data = Data(business_unit_list, logger=self.logger)
1074
-
1075
- # Filter based on black list for business units:
1076
- if self._business_unit_exclusions:
1077
- self.logger.info("Found PHT business unit exclusions...")
1078
- condition = [
1079
- {
1080
- "field": "name",
1081
- "value": self._business_unit_exclusions,
1082
- "equal": False,
1083
- },
1084
- ]
1085
- self._data.filter(conditions=condition)
1086
-
1087
- # Filter based on white list for business units:
1088
- if self._business_unit_inclusions:
1089
- self.logger.info("Found PHT business unit inclusions...")
1090
- condition = [
1091
- {"field": "name", "value": self._business_unit_inclusions},
1092
- ]
1093
- self._data.filter(conditions=condition)
1094
-
1095
- return bool(self._data)
1096
-
1097
- # end method definition
1098
-
1099
- def load_product_families(self, product_family_list: list | None = None, append: bool = False) -> bool:
1100
- """Load product families (LoBs) into a data frame in the self._data object.
1101
-
1102
- Args:
1103
- product_family_list (list, optional):
1104
- List of product families (LoBs) - if already avaiable. Defaults to None.
1105
- If None, then the list of all product families is created on-the-fly.
1106
- append (bool):
1107
- Whether or not the product families should be added to an existing data frame
1108
- or if the data frame should be reset with the product family data only.
1109
- Default is False (drop existing data rows).
1110
-
1111
- Returns:
1112
- bool:
1113
- True if successful, False otherwise.
1114
-
1115
- """
1116
-
1117
- if not product_family_list:
1118
- self.logger.info("Load PHT product family (LoB) list...")
1119
- # First, get the list of all products:
1120
- product_family_list = self.get_product_families()
1121
- if product_family_list:
1122
- self.logger.info(
1123
- "Completed loading of -> %s PHT product families (LoBs)",
1124
- str(len(product_family_list)),
1125
- )
1126
- else:
1127
- self.logger.error("Failed to load PHT product families!")
1128
- return False
1129
-
1130
- # Put the product family (LoB) list in an initial data frame.
1131
- # This makes it easy to filter with powerful Pandas capabilities:
1132
- data = Data(product_family_list, logger=self.logger)
1133
-
1134
- # Filter based on black list for business units:
1135
- if self._business_unit_exclusions:
1136
- self.logger.info("Found PHT business unit exclusions...")
1137
- condition = [
1138
- {
1139
- "field": "businessUnit.name",
1140
- "value": self._business_unit_exclusions,
1141
- "equal": False,
1142
- },
1143
- ]
1144
- data.filter(conditions=condition)
1145
-
1146
- # Filter based on white list for business units:
1147
- if self._business_unit_inclusions:
1148
- self.logger.info("Found PHT business unit inclusions...")
1149
- condition = [
1150
- {"field": "businessUnit.name", "value": self._business_unit_inclusions},
1151
- ]
1152
- data.filter(conditions=condition)
1153
-
1154
- if self.get_data() and not data.get_data_frame().empty and append:
1155
- self.get_data().append(add_data=data)
1156
- else:
1157
- self._data = data
1158
-
1159
- return bool(self._data)
1160
-
1161
- # end method definition
1162
-
1163
- def load_products(
1164
- self,
1165
- product_list: list | None = None,
1166
- append: bool = False,
1167
- attributes_to_extract: list | None = None,
1168
- ) -> bool:
1169
- """Load products into a data frame in the self._data object.
1170
-
1171
- The data frame has these columns:
1172
- "syncId"
1173
- "id"
1174
- "name"
1175
- "shortCode"
1176
- "family"
1177
- "businessUnit"
1178
- "familySyncId"
1179
- "businessUnitSyncId"
1180
- "manager"
1181
- "developmentManager"
1182
- "status"
1183
- "category"
1184
- "attributes"
1185
-
1186
- Args:
1187
- product_list (list, optional):
1188
- List of products - if already avaiable. Defaults to None.
1189
- If None, then the list of all products is created on-the-fly.
1190
- append (bool):
1191
- Whether or not the products should be added to an existing data frame
1192
- or if the data frame should be reset with the product data only.
1193
- Default is False (drop existing data rows).
1194
- attributes_to_extract (list):
1195
- A list of attributes names that should be extracted for the PHT
1196
- "attributes" data structure inside product.
1197
-
1198
- Returns:
1199
- bool:
1200
- True if successful, False otherwise.
1201
-
1202
- """
1203
-
1204
- if not product_list:
1205
- self.logger.info("Load PHT product list...")
1206
- # First, get the list of all products:
1207
- product_list = self.get_products()
1208
- if product_list:
1209
- self.logger.info(
1210
- "Completed loading of -> %s PHT products",
1211
- str(len(product_list)),
1212
- )
1213
- else:
1214
- self.logger.error("Failed to load PHT products!")
1215
- return False
1216
-
1217
- attribute_columns = []
1218
-
1219
- for product in product_list:
1220
- product_family = product["productFamily"]
1221
- business_unit = product_family["businessUnit"]
1222
- category = product.get("productCategory")
1223
-
1224
- product["businessUnitSyncId"] = business_unit["syncId"]
1225
- product["familySyncId"] = product_family["syncId"]
1226
- if category:
1227
- product["category"] = category["name"]
1228
-
1229
- attributes = product.get("attributes")
1230
- # Does this product have attributes and do we want to extract any?
1231
- if attributes and attributes_to_extract:
1232
- for attribute in attributes:
1233
- if attribute.get("name") in attributes_to_extract:
1234
- # We fist check if there's a text value
1235
- value = None
1236
- value = attribute.get("textAttributeValue")
1237
- # If we don't have a text value we try to get a list value:
1238
- if not value and attribute.get("listAttributeValue"):
1239
- value = attribute.get("listAttributeValue")["name"]
1240
- # Create a new key / value pait with the extracted attribute and its value:
1241
- product[attribute.get("name")] = value
1242
- # We keep the attribute name as a column below:
1243
- if attribute.get("name") not in attribute_columns:
1244
- attribute_columns.append(attribute.get("name"))
1245
-
1246
- # Put the product list in an initial data frame.
1247
- # This makes it easy to filter with powerful Pandas capabilities:
1248
- data = Data(product_list, logger=self.logger)
1249
-
1250
- data.keep_columns(
1251
- column_names=[
1252
- "syncId",
1253
- "id",
1254
- "name",
1255
- "shortCode",
1256
- "family",
1257
- "businessUnit",
1258
- "familySyncId",
1259
- "businessUnitSyncId",
1260
- "manager",
1261
- "developmentManager",
1262
- "status",
1263
- "category",
1264
- "attributes",
1265
- "comment",
1266
- ]
1267
- + attribute_columns,
1268
- )
1269
- # Filter based on black list for Business Units:
1270
- if self._business_unit_exclusions:
1271
- self.logger.info("Found PHT business unit exclusions...")
1272
- condition = [
1273
- {
1274
- "field": "businessUnit",
1275
- "value": self._business_unit_exclusions,
1276
- "equal": False,
1277
- },
1278
- ]
1279
- data.filter(conditions=condition)
1280
-
1281
- # Filter based on white list for Business Units:
1282
- if self._business_unit_inclusions:
1283
- self.logger.info("Found PHT business unit inclusions...")
1284
- condition = [
1285
- {"field": "businessUnit", "value": self._business_unit_inclusions},
1286
- ]
1287
- data.filter(conditions=condition)
1288
-
1289
- # Filter based on black list for products:
1290
- if self._product_exclusions:
1291
- self.logger.info("Found PHT product exclusions...")
1292
- condition = [
1293
- {"field": "name", "value": self._product_exclusions, "equal": False},
1294
- ]
1295
- data.filter(conditions=condition)
1296
-
1297
- # Filter based on white list for products:
1298
- if self._product_inclusions:
1299
- self.logger.info("Found PHT product inclusions...")
1300
- condition = [{"field": "name", "value": self._product_inclusions}]
1301
- data.filter(conditions=condition)
1302
-
1303
- # Filter based on black list for product categories:
1304
- if self._product_category_exclusions:
1305
- self.logger.info("Found PHT product category exclusions...")
1306
- condition = [
1307
- {
1308
- "field": "category",
1309
- "value": self._product_category_exclusions,
1310
- "equal": False,
1311
- },
1312
- ]
1313
- data.filter(conditions=condition)
1314
-
1315
- # Filter based on white list for product categories:
1316
- if self._product_category_inclusions:
1317
- self.logger.info("Found PHT product category inclusions...")
1318
- condition = [
1319
- {
1320
- "field": "category",
1321
- "value": self._product_category_inclusions,
1322
- },
1323
- ]
1324
- data.filter(conditions=condition)
1325
-
1326
- # Filter based on product status exclusions:
1327
- if self._product_status_exclusions:
1328
- self.logger.info("Found PHT product status exclusions...")
1329
- condition = [
1330
- {
1331
- "field": "status",
1332
- "value": self._product_status_exclusions,
1333
- "equal": False,
1334
- },
1335
- ]
1336
- data.filter(conditions=condition)
1337
-
1338
- # Filter based on product status inclusions:
1339
- if self._product_status_inclusions:
1340
- self.logger.info("Found PHT product status inclusions...")
1341
- condition = [
1342
- {
1343
- "field": "status",
1344
- "value": self._product_status_inclusions,
1345
- "equal": True,
1346
- },
1347
- ]
1348
- data.filter(conditions=condition)
1349
-
1350
- if self.get_data() and not data.get_data_frame().empty and append:
1351
- self.get_data().append(data)
1352
- else:
1353
- self._data = data
1354
-
1355
- return bool(self._data)
1356
-
1357
- # end method definition