pyxecm 1.4__py3-none-any.whl → 1.5__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.
- pyxecm/__init__.py +3 -0
- pyxecm/coreshare.py +2636 -0
- pyxecm/customizer/__init__.py +4 -0
- pyxecm/customizer/browser_automation.py +164 -54
- pyxecm/customizer/customizer.py +451 -235
- pyxecm/customizer/k8s.py +6 -6
- pyxecm/customizer/m365.py +1136 -221
- pyxecm/customizer/payload.py +13163 -5844
- pyxecm/customizer/pht.py +503 -0
- pyxecm/customizer/salesforce.py +694 -114
- pyxecm/customizer/sap.py +4 -4
- pyxecm/customizer/servicenow.py +1221 -0
- pyxecm/customizer/successfactors.py +1056 -0
- pyxecm/helper/__init__.py +2 -0
- pyxecm/helper/assoc.py +24 -1
- pyxecm/helper/data.py +1527 -0
- pyxecm/helper/web.py +170 -46
- pyxecm/helper/xml.py +170 -34
- pyxecm/otac.py +309 -23
- pyxecm/otcs.py +2779 -698
- pyxecm/otds.py +347 -108
- pyxecm/otmm.py +808 -0
- pyxecm/otpd.py +13 -10
- {pyxecm-1.4.dist-info → pyxecm-1.5.dist-info}/METADATA +3 -1
- pyxecm-1.5.dist-info/RECORD +30 -0
- {pyxecm-1.4.dist-info → pyxecm-1.5.dist-info}/WHEEL +1 -1
- pyxecm-1.4.dist-info/RECORD +0 -24
- {pyxecm-1.4.dist-info → pyxecm-1.5.dist-info}/LICENSE +0 -0
- {pyxecm-1.4.dist-info → pyxecm-1.5.dist-info}/top_level.txt +0 -0
pyxecm/helper/web.py
CHANGED
|
@@ -20,6 +20,7 @@ import logging
|
|
|
20
20
|
import socket
|
|
21
21
|
import time
|
|
22
22
|
import requests
|
|
23
|
+
from lxml import html
|
|
23
24
|
|
|
24
25
|
logger = logging.getLogger("pyxecm.web")
|
|
25
26
|
|
|
@@ -47,7 +48,7 @@ class HTTP(object):
|
|
|
47
48
|
bool: True is reachable, False otherwise
|
|
48
49
|
"""
|
|
49
50
|
|
|
50
|
-
logger.
|
|
51
|
+
logger.debug(
|
|
51
52
|
"Test if host -> %s is reachable on port -> %s ...", hostname, str(port)
|
|
52
53
|
)
|
|
53
54
|
try:
|
|
@@ -67,7 +68,7 @@ class HTTP(object):
|
|
|
67
68
|
)
|
|
68
69
|
return False
|
|
69
70
|
else:
|
|
70
|
-
logger.
|
|
71
|
+
logger.debug("Host is reachable at -> %s:%s", hostname, str(port))
|
|
71
72
|
return True
|
|
72
73
|
|
|
73
74
|
# end method definition
|
|
@@ -81,6 +82,8 @@ class HTTP(object):
|
|
|
81
82
|
timeout: int = 60,
|
|
82
83
|
retries: int = 0,
|
|
83
84
|
wait_time: int = 0,
|
|
85
|
+
wait_on_status: list | None = None,
|
|
86
|
+
show_error: bool = True,
|
|
84
87
|
):
|
|
85
88
|
"""Issues an http request to a given URL.
|
|
86
89
|
|
|
@@ -93,6 +96,9 @@ class HTTP(object):
|
|
|
93
96
|
timeout (int, optional): timeout in seconds
|
|
94
97
|
retries (int, optional): number of retries. If -1 then unlimited retries.
|
|
95
98
|
wait_time (int, optional): number of seconds to wait after each try
|
|
99
|
+
wait_on_status (list, optional): list of status codes we want to wait on. If None
|
|
100
|
+
or empty then we wait for all return codes if
|
|
101
|
+
wait_time > 0
|
|
96
102
|
Returns:
|
|
97
103
|
Response of call
|
|
98
104
|
"""
|
|
@@ -100,61 +106,179 @@ class HTTP(object):
|
|
|
100
106
|
if not headers:
|
|
101
107
|
headers = requestHeaders
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
url,
|
|
106
|
-
method,
|
|
107
|
-
str(payload),
|
|
108
|
-
str(retries),
|
|
109
|
+
message = "Make HTTP Request to URL -> {} using -> {} method".format(
|
|
110
|
+
url, method
|
|
109
111
|
)
|
|
112
|
+
if payload:
|
|
113
|
+
message += " with payload -> {}".format(payload)
|
|
114
|
+
if retries:
|
|
115
|
+
message += " (max number of retries -> {}, wait time between retries -> {})".format(
|
|
116
|
+
retries, wait_time
|
|
117
|
+
)
|
|
118
|
+
try:
|
|
119
|
+
retries = int(retries)
|
|
120
|
+
except ValueError:
|
|
121
|
+
logger.warning(
|
|
122
|
+
"HTTP request -> retries is not a valid integer value: %s, defaulting to 0 retries ",
|
|
123
|
+
retries,
|
|
124
|
+
)
|
|
125
|
+
retries = 0
|
|
126
|
+
|
|
127
|
+
logger.debug(message)
|
|
110
128
|
|
|
111
129
|
try_counter = 1
|
|
112
130
|
|
|
113
131
|
while True:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
method,
|
|
122
|
-
url,
|
|
123
|
-
response.status_code,
|
|
124
|
-
response.text,
|
|
132
|
+
try:
|
|
133
|
+
response = requests.request(
|
|
134
|
+
method=method,
|
|
135
|
+
url=url,
|
|
136
|
+
data=payload,
|
|
137
|
+
headers=headers,
|
|
138
|
+
timeout=timeout,
|
|
125
139
|
)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
logger.info(
|
|
130
|
-
"HTTP request -> %s to url -> %s succeeded with status -> %s!",
|
|
131
|
-
method,
|
|
132
|
-
url,
|
|
133
|
-
response.status_code,
|
|
134
|
-
)
|
|
135
|
-
if wait_time > 0:
|
|
136
|
-
logger.info("Sleeping %s seconds...", wait_time)
|
|
137
|
-
time.sleep(wait_time)
|
|
138
|
-
return response
|
|
139
|
-
|
|
140
|
-
else:
|
|
140
|
+
logger.debug("%s", response.text)
|
|
141
|
+
except Exception as exc:
|
|
142
|
+
response = None
|
|
141
143
|
logger.warning(
|
|
142
|
-
"HTTP request -> %s to url -> %s failed (try %s);
|
|
144
|
+
"HTTP request -> %s to url -> %s failed failed (try %s); error -> %s",
|
|
143
145
|
method,
|
|
144
146
|
url,
|
|
145
147
|
try_counter,
|
|
146
|
-
|
|
147
|
-
response.text,
|
|
148
|
+
exc,
|
|
148
149
|
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
|
|
151
|
+
# do we have an error and don't want to retry?
|
|
152
|
+
if response is not None:
|
|
153
|
+
# Do we have a successful result?
|
|
154
|
+
if response.ok:
|
|
155
|
+
logger.debug(
|
|
156
|
+
"HTTP request -> %s to url -> %s succeeded with status -> %s!",
|
|
157
|
+
method,
|
|
158
|
+
url,
|
|
159
|
+
response.status_code,
|
|
153
160
|
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
|
|
162
|
+
if wait_on_status and response.status_code in wait_on_status:
|
|
163
|
+
logger.debug(
|
|
164
|
+
"%s is in wait_on_status list: %s",
|
|
165
|
+
response.status_code,
|
|
166
|
+
wait_on_status,
|
|
167
|
+
)
|
|
168
|
+
else:
|
|
169
|
+
return response
|
|
170
|
+
|
|
171
|
+
elif not response.ok:
|
|
172
|
+
message = "HTTP request -> {} to url -> {} failed; status -> {}; error -> {}".format(
|
|
173
|
+
method,
|
|
174
|
+
url,
|
|
175
|
+
response.status_code,
|
|
176
|
+
(
|
|
177
|
+
response.text
|
|
178
|
+
if response.headers.get("content-type")
|
|
179
|
+
== "application/json"
|
|
180
|
+
else "see debug log"
|
|
181
|
+
),
|
|
182
|
+
)
|
|
183
|
+
if show_error and retries == 0:
|
|
184
|
+
logger.error(message)
|
|
185
|
+
else:
|
|
186
|
+
logger.warning(message)
|
|
187
|
+
|
|
188
|
+
# Check if another retry is allowed, if not return None
|
|
189
|
+
if retries == 0:
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
if wait_time > 0:
|
|
193
|
+
logger.warning(
|
|
194
|
+
"Sleeping %s seconds and then trying once more...",
|
|
195
|
+
str(wait_time),
|
|
196
|
+
)
|
|
197
|
+
time.sleep(wait_time)
|
|
198
|
+
|
|
199
|
+
retries -= 1
|
|
200
|
+
try_counter += 1
|
|
201
|
+
|
|
202
|
+
# end method definition
|
|
203
|
+
|
|
204
|
+
def download_file(
|
|
205
|
+
self,
|
|
206
|
+
url: str,
|
|
207
|
+
filename: str,
|
|
208
|
+
timeout: int = 120,
|
|
209
|
+
retries: int = 0,
|
|
210
|
+
wait_time: int = 0,
|
|
211
|
+
wait_on_status: list | None = None,
|
|
212
|
+
show_error: bool = True,
|
|
213
|
+
) -> bool:
|
|
214
|
+
"""Download a file from a URL
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
url (str): URL
|
|
218
|
+
filename (str): filename to save
|
|
219
|
+
timeout (int, optional): timeout in seconds
|
|
220
|
+
retries (int, optional): number of retries. If -1 then unlimited retries.
|
|
221
|
+
wait_time (int, optional): number of seconds to wait after each try
|
|
222
|
+
wait_on_status (list, optional): list of status codes we want to wait on. If None
|
|
223
|
+
or empty then we wait for all return codes if
|
|
224
|
+
wait_time > 0
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
bool: True if successful, False otherwise
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
response = self.http_request(
|
|
231
|
+
url=url,
|
|
232
|
+
method="GET",
|
|
233
|
+
retries=retries,
|
|
234
|
+
timeout=timeout,
|
|
235
|
+
wait_time=wait_time,
|
|
236
|
+
wait_on_status=wait_on_status,
|
|
237
|
+
show_error=show_error,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if response is None:
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
if response.ok:
|
|
244
|
+
with open(filename, "wb") as f:
|
|
245
|
+
f.write(response.content)
|
|
246
|
+
logger.debug("File downloaded successfully as -> %s", filename)
|
|
247
|
+
return True
|
|
248
|
+
|
|
249
|
+
return False
|
|
159
250
|
|
|
160
251
|
# end method definition
|
|
252
|
+
|
|
253
|
+
def extract_content(self, url: str, xpath: str) -> str | None:
|
|
254
|
+
"""Extract a string from a response of a HTTP request
|
|
255
|
+
based on an XPath.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
url (str): URL to open
|
|
259
|
+
xpath (str): XPath expression to apply to the result
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
str | None: Extracted string or None in case of an error.
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
# Send a GET request to the URL
|
|
266
|
+
response = requests.get(url, timeout=None)
|
|
267
|
+
|
|
268
|
+
# Check if request was successful
|
|
269
|
+
if response.status_code == 200:
|
|
270
|
+
# Parse the HTML content
|
|
271
|
+
tree = html.fromstring(response.content)
|
|
272
|
+
|
|
273
|
+
# Extract content using XPath
|
|
274
|
+
elements = tree.xpath(xpath)
|
|
275
|
+
|
|
276
|
+
# Get text content of all elements and join them
|
|
277
|
+
content = "\n".join([elem.text_content().strip() for elem in elements])
|
|
278
|
+
|
|
279
|
+
# Return the extracted content
|
|
280
|
+
return content
|
|
281
|
+
else:
|
|
282
|
+
# If request was not successful, print error message
|
|
283
|
+
logger.error(response.status_code)
|
|
284
|
+
return None
|
pyxecm/helper/xml.py
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
Class: XML
|
|
4
4
|
Methods:
|
|
5
5
|
|
|
6
|
+
load_xml_file: Load an XML file into a Python list of dictionaries
|
|
7
|
+
load_xml_files_from_directory: Load all XML files from a directory that matches defined file names
|
|
8
|
+
then using the XPath to identify a set of elements and convert them
|
|
9
|
+
into a Python list of dictionaries.
|
|
6
10
|
get_xml_element: Retrieve an XML Element from a string using an XPath expression
|
|
7
11
|
modify_xml_element: Update the text (= content) of an XML element
|
|
8
12
|
search_setting: Search a JSON-like setting inside an XML text telement
|
|
@@ -21,10 +25,12 @@ __email__ = "mdiefenb@opentext.com"
|
|
|
21
25
|
import logging
|
|
22
26
|
import os
|
|
23
27
|
import re
|
|
28
|
+
import fnmatch
|
|
24
29
|
|
|
25
30
|
# we need lxml instead of stadard xml.etree to have xpath capabilities!
|
|
26
31
|
from lxml import etree
|
|
27
32
|
import xmltodict
|
|
33
|
+
import zipfile
|
|
28
34
|
|
|
29
35
|
# import xml.etree.ElementTree as etree
|
|
30
36
|
from pyxecm.helper.assoc import Assoc
|
|
@@ -33,7 +39,129 @@ logger = logging.getLogger("pyxecm.xml")
|
|
|
33
39
|
|
|
34
40
|
|
|
35
41
|
class XML:
|
|
36
|
-
"""XML Class to parse and update Extended ECM transport packages"""
|
|
42
|
+
"""XML Class to handle XML processing, e.g. to parse and update Extended ECM transport packages"""
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def load_xml_file(
|
|
46
|
+
cls, file_path: str, xpath: str, dir_name: str | None = None
|
|
47
|
+
) -> list | None:
|
|
48
|
+
"""Load an XML file into a Python list of dictionaries
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
file_path (str): Path to XML file
|
|
52
|
+
xpath (str): XPath to select sub-elements
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
dict | None: _description_
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
|
|
60
|
+
tree = etree.parse(file_path)
|
|
61
|
+
if not tree:
|
|
62
|
+
return []
|
|
63
|
+
|
|
64
|
+
# Perform the XPath query to select 'child' elements
|
|
65
|
+
elements = tree.xpath(xpath) # Adjust XPath as needed
|
|
66
|
+
|
|
67
|
+
# Convert the selected elements to dictionaries
|
|
68
|
+
results = []
|
|
69
|
+
tag = xpath.split("/")[-1]
|
|
70
|
+
for element in elements:
|
|
71
|
+
element_dict = xmltodict.parse(etree.tostring(element))
|
|
72
|
+
if tag in element_dict:
|
|
73
|
+
element_dict = element_dict[tag]
|
|
74
|
+
if dir_name:
|
|
75
|
+
element_dict["directory"] = dir_name
|
|
76
|
+
results.append(element_dict)
|
|
77
|
+
|
|
78
|
+
except IOError as e:
|
|
79
|
+
logger.error("IO Error -> %s", str(e))
|
|
80
|
+
except etree.XMLSyntaxError as e:
|
|
81
|
+
logger.error("XML Syntax Error -> %s", str(e))
|
|
82
|
+
except etree.DocumentInvalid as e:
|
|
83
|
+
logger.error("Document Invalid -> %s", str(e))
|
|
84
|
+
|
|
85
|
+
return results
|
|
86
|
+
|
|
87
|
+
# end method definition
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def load_xml_files_from_directory(
|
|
91
|
+
cls, path_to_root: str, filenames: list | None, xpath: str | None = None
|
|
92
|
+
) -> list | None:
|
|
93
|
+
"""Load all XML files from a directory that matches defined file names
|
|
94
|
+
then using the XPath to identify a set of elements and convert them
|
|
95
|
+
into a Python list of dictionaries.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
path_to_root (str): Path to the root element of the
|
|
99
|
+
directory structure
|
|
100
|
+
filenames (list): list of filenames. If empty all filenames ending
|
|
101
|
+
with ".xml" are used.
|
|
102
|
+
xpath (str, optional): XPath to the elements we want to select
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
list: List of dictionaries
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
|
|
110
|
+
# Check if the provided path is a directory
|
|
111
|
+
if not os.path.isdir(path_to_root) and not path_to_root.endswith(".zip"):
|
|
112
|
+
logger.error(
|
|
113
|
+
"The provided path '%s' is not a valid directory or Zip file.",
|
|
114
|
+
path_to_root,
|
|
115
|
+
)
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
if path_to_root.endswith(".zip"):
|
|
119
|
+
zip_file_folder = os.path.splitext(path_to_root)[0]
|
|
120
|
+
if not os.path.exists(zip_file_folder):
|
|
121
|
+
logger.info(
|
|
122
|
+
"Unzipping -> '%s' into folder -> '%s'...",
|
|
123
|
+
path_to_root,
|
|
124
|
+
zip_file_folder,
|
|
125
|
+
)
|
|
126
|
+
with zipfile.ZipFile(path_to_root, "r") as zfile:
|
|
127
|
+
zfile.extractall(zip_file_folder)
|
|
128
|
+
else:
|
|
129
|
+
logger.info(
|
|
130
|
+
"Zip file is already extracted (path -> '%s' exists). Reusing extracted data...",
|
|
131
|
+
zip_file_folder,
|
|
132
|
+
)
|
|
133
|
+
path_to_root = zip_file_folder
|
|
134
|
+
|
|
135
|
+
results = []
|
|
136
|
+
|
|
137
|
+
# Walk through the directory
|
|
138
|
+
for root, _, files in os.walk(path_to_root):
|
|
139
|
+
for file_data in files:
|
|
140
|
+
file_path = os.path.join(root, file_data)
|
|
141
|
+
file_size = os.path.getsize(file_path)
|
|
142
|
+
file_name = os.path.basename(file_path)
|
|
143
|
+
dir_name = os.path.dirname(file_path)
|
|
144
|
+
|
|
145
|
+
if any(
|
|
146
|
+
fnmatch.fnmatch(file_path, pattern) for pattern in filenames
|
|
147
|
+
) and file_name.endswith(".xml"):
|
|
148
|
+
logger.info(
|
|
149
|
+
"Load XML file -> '%s' of size -> %s", file_path, file_size
|
|
150
|
+
)
|
|
151
|
+
results += cls.load_xml_file(
|
|
152
|
+
file_path, xpath=xpath, dir_name=dir_name
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
except NotADirectoryError as nde:
|
|
156
|
+
logger.error("Error -> %s", str(nde))
|
|
157
|
+
except FileNotFoundError as fnfe:
|
|
158
|
+
logger.error("Error -> %s", str(fnfe))
|
|
159
|
+
except PermissionError as pe:
|
|
160
|
+
logger.error("Error -> %s", str(pe))
|
|
161
|
+
|
|
162
|
+
return results
|
|
163
|
+
|
|
164
|
+
# end method definition
|
|
37
165
|
|
|
38
166
|
@classmethod
|
|
39
167
|
def get_xml_element(cls, xml_content: str, xpath: str):
|
|
@@ -55,6 +183,8 @@ class XML:
|
|
|
55
183
|
|
|
56
184
|
return element
|
|
57
185
|
|
|
186
|
+
# end method definition
|
|
187
|
+
|
|
58
188
|
@classmethod
|
|
59
189
|
def modify_xml_element(cls, xml_content: str, xpath: str, new_value: str):
|
|
60
190
|
"""Update the text (= content) of an XML element
|
|
@@ -72,6 +202,8 @@ class XML:
|
|
|
72
202
|
else:
|
|
73
203
|
logger.warning("XML Element -> %s not found.", xpath)
|
|
74
204
|
|
|
205
|
+
# end method definition
|
|
206
|
+
|
|
75
207
|
@classmethod
|
|
76
208
|
def search_setting(
|
|
77
209
|
cls,
|
|
@@ -122,6 +254,8 @@ class XML:
|
|
|
122
254
|
else:
|
|
123
255
|
return None
|
|
124
256
|
|
|
257
|
+
# end method definition
|
|
258
|
+
|
|
125
259
|
@classmethod
|
|
126
260
|
def replace_setting(
|
|
127
261
|
cls,
|
|
@@ -170,6 +304,8 @@ class XML:
|
|
|
170
304
|
|
|
171
305
|
return new_text
|
|
172
306
|
|
|
307
|
+
# end method definition
|
|
308
|
+
|
|
173
309
|
@classmethod
|
|
174
310
|
def replace_in_xml_files(
|
|
175
311
|
cls,
|
|
@@ -216,8 +352,8 @@ class XML:
|
|
|
216
352
|
# if xpath is given we do an intelligent replacement
|
|
217
353
|
if xpath:
|
|
218
354
|
xml_modified = False
|
|
219
|
-
logger.
|
|
220
|
-
logger.
|
|
355
|
+
logger.debug("Replacement with xpath...")
|
|
356
|
+
logger.debug(
|
|
221
357
|
"XML path -> %s, setting -> %s, assoc element -> %s",
|
|
222
358
|
xpath,
|
|
223
359
|
setting,
|
|
@@ -225,17 +361,15 @@ class XML:
|
|
|
225
361
|
)
|
|
226
362
|
tree = etree.parse(file_path)
|
|
227
363
|
if not tree:
|
|
228
|
-
logger.
|
|
229
|
-
"Cannot parse XML tree ->
|
|
230
|
-
file_path
|
|
231
|
-
)
|
|
364
|
+
logger.error(
|
|
365
|
+
"Cannot parse XML tree -> %s. Skipping...", file_path
|
|
232
366
|
)
|
|
233
367
|
continue
|
|
234
368
|
root = tree.getroot()
|
|
235
369
|
# find the matching XML elements using the given XPath:
|
|
236
370
|
elements = root.xpath(xpath)
|
|
237
371
|
if not elements:
|
|
238
|
-
logger.
|
|
372
|
+
logger.debug(
|
|
239
373
|
"The XML file -> %s does not have any element with the given XML path -> %s. Skipping...",
|
|
240
374
|
file_path,
|
|
241
375
|
xpath,
|
|
@@ -243,7 +377,7 @@ class XML:
|
|
|
243
377
|
continue
|
|
244
378
|
for element in elements:
|
|
245
379
|
# as XPath returns a list
|
|
246
|
-
logger.
|
|
380
|
+
logger.debug(
|
|
247
381
|
"Found XML element -> %s in file -> %s using xpath -> %s",
|
|
248
382
|
element.tag,
|
|
249
383
|
filename,
|
|
@@ -251,7 +385,7 @@ class XML:
|
|
|
251
385
|
)
|
|
252
386
|
# the simple case: replace the complete text of the XML element
|
|
253
387
|
if not setting and not assoc_elem:
|
|
254
|
-
logger.
|
|
388
|
+
logger.debug(
|
|
255
389
|
"Replace complete text of XML element -> %s from -> %s to -> %s",
|
|
256
390
|
xpath,
|
|
257
391
|
element.text,
|
|
@@ -261,7 +395,7 @@ class XML:
|
|
|
261
395
|
xml_modified = True
|
|
262
396
|
# In this case we want to set a complete value of a setting (basically replacing a whole line)
|
|
263
397
|
elif setting and not assoc_elem:
|
|
264
|
-
logger.
|
|
398
|
+
logger.debug(
|
|
265
399
|
"Replace single setting -> %s in XML element -> %s with new value -> %s",
|
|
266
400
|
setting,
|
|
267
401
|
xpath,
|
|
@@ -271,7 +405,7 @@ class XML:
|
|
|
271
405
|
element.text, setting, is_simple=True
|
|
272
406
|
)
|
|
273
407
|
if setting_value:
|
|
274
|
-
logger.
|
|
408
|
+
logger.debug(
|
|
275
409
|
"Found existing setting value -> %s",
|
|
276
410
|
setting_value,
|
|
277
411
|
)
|
|
@@ -290,7 +424,7 @@ class XML:
|
|
|
290
424
|
replace_setting = (
|
|
291
425
|
'"' + setting + '":"' + replace_string + '"'
|
|
292
426
|
)
|
|
293
|
-
logger.
|
|
427
|
+
logger.debug(
|
|
294
428
|
"Replacement setting -> %s", replace_setting
|
|
295
429
|
)
|
|
296
430
|
element.text = cls.replace_setting(
|
|
@@ -308,7 +442,7 @@ class XML:
|
|
|
308
442
|
continue
|
|
309
443
|
# in this case the text is just one assoc (no setting substructure)
|
|
310
444
|
elif not setting and assoc_elem:
|
|
311
|
-
logger.
|
|
445
|
+
logger.debug(
|
|
312
446
|
"Replace single Assoc value -> %s in XML element -> %s with -> %s",
|
|
313
447
|
assoc_elem,
|
|
314
448
|
xpath,
|
|
@@ -322,13 +456,13 @@ class XML:
|
|
|
322
456
|
assoc_string=assoc_string
|
|
323
457
|
)
|
|
324
458
|
logger.debug("Assoc Dict -> %s", str(assoc_dict))
|
|
325
|
-
assoc_dict[
|
|
326
|
-
|
|
327
|
-
|
|
459
|
+
assoc_dict[assoc_elem] = (
|
|
460
|
+
replace_string # escaped_replace_string
|
|
461
|
+
)
|
|
328
462
|
assoc_string_new: str = Assoc.dict_to_string(
|
|
329
463
|
assoc_dict=assoc_dict
|
|
330
464
|
)
|
|
331
|
-
logger.
|
|
465
|
+
logger.debug(
|
|
332
466
|
"Replace assoc with -> %s", assoc_string_new
|
|
333
467
|
)
|
|
334
468
|
element.text = assoc_string_new
|
|
@@ -336,7 +470,7 @@ class XML:
|
|
|
336
470
|
xml_modified = True
|
|
337
471
|
# In this case we have multiple settings with their own assocs
|
|
338
472
|
elif setting and assoc_elem:
|
|
339
|
-
logger.
|
|
473
|
+
logger.debug(
|
|
340
474
|
"Replace single Assoc value -> %s in setting -> %s in XML element -> %s with -> %s",
|
|
341
475
|
assoc_elem,
|
|
342
476
|
setting,
|
|
@@ -347,7 +481,7 @@ class XML:
|
|
|
347
481
|
element.text, setting, is_simple=False
|
|
348
482
|
)
|
|
349
483
|
if setting_value:
|
|
350
|
-
logger.
|
|
484
|
+
logger.debug(
|
|
351
485
|
"Found setting value -> %s", setting_value
|
|
352
486
|
)
|
|
353
487
|
assoc_string: str = Assoc.extract_assoc_string(
|
|
@@ -361,13 +495,13 @@ class XML:
|
|
|
361
495
|
escaped_replace_string = replace_string.replace(
|
|
362
496
|
"'", "\\\\\u0027"
|
|
363
497
|
)
|
|
364
|
-
logger.
|
|
498
|
+
logger.debug(
|
|
365
499
|
"Escaped replacement string -> %s",
|
|
366
500
|
escaped_replace_string,
|
|
367
501
|
)
|
|
368
|
-
assoc_dict[
|
|
369
|
-
|
|
370
|
-
|
|
502
|
+
assoc_dict[assoc_elem] = (
|
|
503
|
+
escaped_replace_string # escaped_replace_string
|
|
504
|
+
)
|
|
371
505
|
assoc_string_new: str = Assoc.dict_to_string(
|
|
372
506
|
assoc_dict=assoc_dict
|
|
373
507
|
)
|
|
@@ -378,7 +512,7 @@ class XML:
|
|
|
378
512
|
replace_setting = (
|
|
379
513
|
'"' + setting + '":"' + assoc_string_new + '"'
|
|
380
514
|
)
|
|
381
|
-
logger.
|
|
515
|
+
logger.debug(
|
|
382
516
|
"Replacement setting -> %s", replace_setting
|
|
383
517
|
)
|
|
384
518
|
# here we need to apply a "trick". It is required
|
|
@@ -407,7 +541,7 @@ class XML:
|
|
|
407
541
|
)
|
|
408
542
|
continue
|
|
409
543
|
if xml_modified:
|
|
410
|
-
logger.
|
|
544
|
+
logger.debug(
|
|
411
545
|
"XML tree has been modified. Write updated file -> %s...",
|
|
412
546
|
file_path,
|
|
413
547
|
)
|
|
@@ -465,7 +599,7 @@ class XML:
|
|
|
465
599
|
found = True
|
|
466
600
|
# this is not using xpath - do a simple search and replace
|
|
467
601
|
else:
|
|
468
|
-
logger.
|
|
602
|
+
logger.debug("Replacement without xpath...")
|
|
469
603
|
with open(file_path, "r", encoding="UTF-8") as f:
|
|
470
604
|
contents = f.read()
|
|
471
605
|
# Replace all occurrences of the search pattern with the replace string
|
|
@@ -473,7 +607,7 @@ class XML:
|
|
|
473
607
|
|
|
474
608
|
# Write the updated contents to the file if there were replacements
|
|
475
609
|
if contents != new_contents:
|
|
476
|
-
logger.
|
|
610
|
+
logger.debug(
|
|
477
611
|
"Found search string -> %s in XML file -> %s. Write updated file...",
|
|
478
612
|
search_pattern,
|
|
479
613
|
file_path,
|
|
@@ -485,6 +619,8 @@ class XML:
|
|
|
485
619
|
|
|
486
620
|
return found
|
|
487
621
|
|
|
622
|
+
# end method definition
|
|
623
|
+
|
|
488
624
|
@classmethod
|
|
489
625
|
def extract_from_xml_files(
|
|
490
626
|
cls,
|
|
@@ -511,18 +647,18 @@ class XML:
|
|
|
511
647
|
# Read the contents of the file
|
|
512
648
|
file_path = os.path.join(subdir, filename)
|
|
513
649
|
|
|
514
|
-
logger.
|
|
650
|
+
logger.debug("Extraction with xpath -> %s...", xpath)
|
|
515
651
|
tree = etree.parse(file_path)
|
|
516
652
|
if not tree:
|
|
517
|
-
logger.
|
|
518
|
-
"Cannot parse XML
|
|
653
|
+
logger.error(
|
|
654
|
+
"Cannot parse XML file -> '%s'. Skipping...", file_path
|
|
519
655
|
)
|
|
520
656
|
continue
|
|
521
657
|
root = tree.getroot()
|
|
522
658
|
# find the matching XML elements using the given XPath:
|
|
523
659
|
elements = root.xpath(xpath)
|
|
524
660
|
if not elements:
|
|
525
|
-
logger.
|
|
661
|
+
logger.debug(
|
|
526
662
|
"The XML file -> %s does not have any element with the given XML path -> %s. Skipping...",
|
|
527
663
|
file_path,
|
|
528
664
|
xpath,
|
|
@@ -530,7 +666,7 @@ class XML:
|
|
|
530
666
|
continue
|
|
531
667
|
for element in elements:
|
|
532
668
|
# as XPath returns a list
|
|
533
|
-
logger.
|
|
669
|
+
logger.debug(
|
|
534
670
|
"Found XML element -> %s in file -> %s using xpath -> %s. Add it to result list.",
|
|
535
671
|
element.tag,
|
|
536
672
|
filename,
|
|
@@ -551,4 +687,4 @@ class XML:
|
|
|
551
687
|
|
|
552
688
|
return extracted_data_list
|
|
553
689
|
|
|
554
|
-
|
|
690
|
+
# end method definition
|