pyxecm 0.0.18__py3-none-any.whl → 0.0.19__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 +10 -10
- pyxecm/assoc.py +139 -0
- pyxecm/m365.py +11 -4
- pyxecm/main.py +28 -22
- pyxecm/otcs.py +79 -73
- pyxecm/otds.py +372 -77
- pyxecm/payload.py +1683 -382
- pyxecm/xml.py +436 -0
- {pyxecm-0.0.18.dist-info → pyxecm-0.0.19.dist-info}/METADATA +4 -5
- pyxecm-0.0.19.dist-info/RECORD +20 -0
- pyxecm/llm.py +0 -451
- pyxecm-0.0.18.dist-info/RECORD +0 -19
- {pyxecm-0.0.18.dist-info → pyxecm-0.0.19.dist-info}/LICENSE +0 -0
- {pyxecm-0.0.18.dist-info → pyxecm-0.0.19.dist-info}/WHEEL +0 -0
- {pyxecm-0.0.18.dist-info → pyxecm-0.0.19.dist-info}/top_level.txt +0 -0
pyxecm/xml.py
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
""" XML helper module
|
|
2
|
+
|
|
3
|
+
Class: XML
|
|
4
|
+
Methods:
|
|
5
|
+
|
|
6
|
+
searchSetting: search a JSON-like setting inside an XML text telement
|
|
7
|
+
replaceInXmlFiles: Replace all occurrences of the search pattern with the replace string in all
|
|
8
|
+
XML files in the directory and its subdirectories.
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__author__ = "Dr. Marc Diefenbruch"
|
|
13
|
+
__copyright__ = "Copyright 2023, OpenText"
|
|
14
|
+
__credits__ = ["Kai-Philip Gatzweiler"]
|
|
15
|
+
__maintainer__ = "Dr. Marc Diefenbruch"
|
|
16
|
+
__email__ = "mdiefenb@opentext.com"
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
# import regex as re
|
|
22
|
+
|
|
23
|
+
# we need lxml instead of stadard xml.etree to have xpath capabilities!
|
|
24
|
+
from lxml import etree
|
|
25
|
+
|
|
26
|
+
# import xml.etree.ElementTree as etree
|
|
27
|
+
from pyxecm.assoc import *
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(os.path.basename(__file__))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class XML:
|
|
33
|
+
@classmethod
|
|
34
|
+
def getXmlElement(cls, xml_content: str, xpath: str):
|
|
35
|
+
# Parse XML content into an etree
|
|
36
|
+
tree = etree.fromstring(xml_content)
|
|
37
|
+
|
|
38
|
+
# Find the XML element specified by XPath
|
|
39
|
+
element = tree.find(xpath)
|
|
40
|
+
|
|
41
|
+
return element
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def modifyXmlElement(cls, xml_content: str, xpath: str, new_value: str):
|
|
45
|
+
"""Update the text (= content) of an XML element
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
xml_content (str): the content of an XML file
|
|
49
|
+
xpath (str): XML Path to identify the XML element
|
|
50
|
+
new_value (str): new text (content)
|
|
51
|
+
"""
|
|
52
|
+
element = cls.getXmlElement(xml_content=xml_content, xpath=xpath)
|
|
53
|
+
|
|
54
|
+
if element is not None:
|
|
55
|
+
# Modify the XML element with the new value
|
|
56
|
+
element.text = new_value
|
|
57
|
+
else:
|
|
58
|
+
logger.warning("XML Element -> {} not found.".format(xpath))
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def searchSetting(
|
|
62
|
+
cls,
|
|
63
|
+
element_text: str,
|
|
64
|
+
setting_key: str,
|
|
65
|
+
is_simple: bool = True,
|
|
66
|
+
is_escaped: bool = False,
|
|
67
|
+
):
|
|
68
|
+
# the simple case covers settings like this:
|
|
69
|
+
# "syncCandidates":true,
|
|
70
|
+
# "syncCandidates":true,
|
|
71
|
+
# in this case the setting value is a scalar like true, false, a number or none
|
|
72
|
+
# the regular expression pattern searches for a setting name in "..." followed
|
|
73
|
+
# by a colon (:). The value is taken from what follows the colon until the next comma (,)
|
|
74
|
+
if is_simple:
|
|
75
|
+
if is_escaped:
|
|
76
|
+
pattern = r""{0}":[^,]*".format(setting_key)
|
|
77
|
+
else:
|
|
78
|
+
pattern = r'"{0}":[^,]*'.format(setting_key)
|
|
79
|
+
# the more complex case is a string value that may itself have commas,
|
|
80
|
+
# so we cannot look for comma as a delimiter like in the simple case
|
|
81
|
+
# but we take the value for a string delimited by double quotes ("...")
|
|
82
|
+
else:
|
|
83
|
+
if is_escaped:
|
|
84
|
+
pattern = r""{0}":".*"".format(setting_key)
|
|
85
|
+
else:
|
|
86
|
+
pattern = r'"{0}":"([^"]*)"'.format(setting_key)
|
|
87
|
+
|
|
88
|
+
match = re.search(pattern, element_text)
|
|
89
|
+
if match:
|
|
90
|
+
setting_line = match.group(0)
|
|
91
|
+
setting_value = setting_line.split(":")[1]
|
|
92
|
+
return setting_value
|
|
93
|
+
else:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def replaceSetting(
|
|
98
|
+
cls,
|
|
99
|
+
element_text,
|
|
100
|
+
setting_key,
|
|
101
|
+
new_value: str,
|
|
102
|
+
is_simple: bool = True,
|
|
103
|
+
is_escaped: bool = False,
|
|
104
|
+
):
|
|
105
|
+
if is_simple:
|
|
106
|
+
if is_escaped:
|
|
107
|
+
pattern = r""{0}":[^,]*".format(setting_key)
|
|
108
|
+
else:
|
|
109
|
+
pattern = r'"{0}":[^,]*'.format(setting_key)
|
|
110
|
+
else:
|
|
111
|
+
if is_escaped:
|
|
112
|
+
pattern = r""{0}":".*"".format(setting_key)
|
|
113
|
+
else:
|
|
114
|
+
pattern = r'"{0}":"([^"]*)"'.format(setting_key)
|
|
115
|
+
|
|
116
|
+
new_text = re.sub(pattern, new_value, element_text)
|
|
117
|
+
|
|
118
|
+
return new_text
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
def replaceInXmlFiles(
|
|
122
|
+
cls,
|
|
123
|
+
directory: str,
|
|
124
|
+
search_pattern: str,
|
|
125
|
+
replace_string: str,
|
|
126
|
+
xpath: str = "",
|
|
127
|
+
setting: str = "",
|
|
128
|
+
assoc_elem: str = "",
|
|
129
|
+
) -> bool:
|
|
130
|
+
"""Replaces all occurrences of the search pattern with the replace string in all XML files
|
|
131
|
+
in the directory and its subdirectories.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
directory (string): directory to traverse for XML files
|
|
135
|
+
search_pattern (sting): string to search in the XML file. This can be empty
|
|
136
|
+
if xpath is used!
|
|
137
|
+
replace_string (string): replacement string
|
|
138
|
+
xpath (string): narrow down the replacement to an XML element that es defined by the XPath
|
|
139
|
+
for now the XPath needs to be constructed in a way the it returns
|
|
140
|
+
one or none element.
|
|
141
|
+
setting (string): narrow down the replacement to the line that includes the setting with this name.
|
|
142
|
+
This parameter is optional.
|
|
143
|
+
assoc_elem (string): lookup a specific assoc element. This parameter is optional.
|
|
144
|
+
Returns:
|
|
145
|
+
boolean: True if a replacement happened, False otherwise
|
|
146
|
+
"""
|
|
147
|
+
# Define the regular expression pattern to search for
|
|
148
|
+
# search pattern can be empty if an xpath is used. So
|
|
149
|
+
# be careful here:
|
|
150
|
+
if search_pattern:
|
|
151
|
+
pattern = re.compile(search_pattern)
|
|
152
|
+
|
|
153
|
+
found = False
|
|
154
|
+
|
|
155
|
+
# Traverse the directory and its subdirectories
|
|
156
|
+
for subdir, dirs, files in os.walk(directory):
|
|
157
|
+
for file in files:
|
|
158
|
+
# Check if the file is an XML file
|
|
159
|
+
if file.endswith(".xml"):
|
|
160
|
+
# Read the contents of the file
|
|
161
|
+
file_path = os.path.join(subdir, file)
|
|
162
|
+
|
|
163
|
+
# if xpath is given we do an intelligent replacement
|
|
164
|
+
if xpath:
|
|
165
|
+
xml_modified = False
|
|
166
|
+
logger.info("Replacement with xpath...")
|
|
167
|
+
logger.info(
|
|
168
|
+
"XML path -> {}, setting -> {}, assoc element -> {}".format(
|
|
169
|
+
xpath, setting, assoc_elem
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
tree = etree.parse(file_path)
|
|
173
|
+
if not tree:
|
|
174
|
+
logger.erro(
|
|
175
|
+
"Cannot parse XML tree -> {}. Skipping...".format(
|
|
176
|
+
file_path
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
continue
|
|
180
|
+
root = tree.getroot()
|
|
181
|
+
# find the matching XML element using the given XPath:
|
|
182
|
+
elements = root.xpath(xpath)
|
|
183
|
+
if not elements:
|
|
184
|
+
logger.info(
|
|
185
|
+
"The XML file -> {} does not have any element with the given XML path -> {}. Skipping...".format(
|
|
186
|
+
file_path, xpath
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
continue
|
|
190
|
+
for element in elements:
|
|
191
|
+
# as XPath returns a list
|
|
192
|
+
# element = elements[0]
|
|
193
|
+
logger.info(
|
|
194
|
+
"Found XML element -> {} in -> {}".format(
|
|
195
|
+
element.tag, xpath
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
# the simple case: replace the complete text of the XML element
|
|
199
|
+
if not setting and not assoc_elem:
|
|
200
|
+
logger.info(
|
|
201
|
+
"Replace complete text of XML element -> {} from -> {} to -> {}".format(
|
|
202
|
+
xpath, element.text, replace_string
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
element.text = replace_string
|
|
206
|
+
xml_modified = True
|
|
207
|
+
# In this case we want to set a complete value of a setting (basically replacing a whole line)
|
|
208
|
+
elif setting and not assoc_elem:
|
|
209
|
+
logger.info(
|
|
210
|
+
"Replace single setting -> {} in XML element -> {} with new value -> {}".format(
|
|
211
|
+
setting, xpath, replace_string
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
setting_value = cls.searchSetting(
|
|
215
|
+
element.text, setting, is_simple=True
|
|
216
|
+
)
|
|
217
|
+
if setting_value:
|
|
218
|
+
logger.info(
|
|
219
|
+
"Found existing setting value -> {}".format(
|
|
220
|
+
setting_value
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
# replace_string = """ + setting + "":" + replace_string + ","
|
|
224
|
+
if (
|
|
225
|
+
replace_string == "true"
|
|
226
|
+
or replace_string == "false"
|
|
227
|
+
or replace_string == "none"
|
|
228
|
+
or replace_string.isnumeric()
|
|
229
|
+
):
|
|
230
|
+
replace_setting = (
|
|
231
|
+
'"' + setting + '":' + replace_string
|
|
232
|
+
)
|
|
233
|
+
else:
|
|
234
|
+
replace_setting = (
|
|
235
|
+
'"' + setting + '":"' + replace_string + '"'
|
|
236
|
+
)
|
|
237
|
+
logger.info(
|
|
238
|
+
"Replacement setting -> {}".format(
|
|
239
|
+
replace_setting
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
element.text = cls.replaceSetting(
|
|
243
|
+
element_text=element.text,
|
|
244
|
+
setting_key=setting,
|
|
245
|
+
new_value=replace_setting,
|
|
246
|
+
is_simple=True,
|
|
247
|
+
)
|
|
248
|
+
xml_modified = True
|
|
249
|
+
else:
|
|
250
|
+
logger.warning(
|
|
251
|
+
"Cannot find the value for setting -> {}. Skipping...".format(
|
|
252
|
+
setting
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
continue
|
|
256
|
+
# in this case the text is just one assoc (no setting substructure)
|
|
257
|
+
elif not setting and assoc_elem:
|
|
258
|
+
logger.info(
|
|
259
|
+
"Replace single Assoc value -> {} in XML element -> {} with -> {}".format(
|
|
260
|
+
assoc_elem, xpath, replace_string
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
assoc_string: str = Assoc.extractAssocString(
|
|
264
|
+
input_string=element.text
|
|
265
|
+
)
|
|
266
|
+
logger.debug("Assoc String -> {}".format(assoc_string))
|
|
267
|
+
assoc_dict = Assoc.stringToDict(
|
|
268
|
+
assoc_string=assoc_string
|
|
269
|
+
)
|
|
270
|
+
logger.debug("Assoc Dict -> {}".format(assoc_dict))
|
|
271
|
+
assoc_dict[
|
|
272
|
+
assoc_elem
|
|
273
|
+
] = replace_string # escaped_replace_string
|
|
274
|
+
assoc_string_new: str = Assoc.dictToString(
|
|
275
|
+
assoc_dict=assoc_dict
|
|
276
|
+
)
|
|
277
|
+
logger.info(
|
|
278
|
+
"Replace assoc with -> {}".format(assoc_string_new)
|
|
279
|
+
)
|
|
280
|
+
element.text = assoc_string_new
|
|
281
|
+
element.text = element.text.replace('"', """)
|
|
282
|
+
xml_modified = True
|
|
283
|
+
# In this case we have multiple settings with their own assocs
|
|
284
|
+
elif setting and assoc_elem:
|
|
285
|
+
logger.info(
|
|
286
|
+
"Replace single Assoc value -> {} in setting -> {} in XML element -> {} with -> {}".format(
|
|
287
|
+
assoc_elem, setting, xpath, replace_string
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
setting_value = cls.searchSetting(
|
|
291
|
+
element.text, setting, is_simple=False
|
|
292
|
+
)
|
|
293
|
+
if setting_value:
|
|
294
|
+
logger.info(
|
|
295
|
+
"Found setting value -> {}".format(
|
|
296
|
+
setting_value
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
assoc_string: str = Assoc.extractAssocString(
|
|
300
|
+
input_string=setting_value
|
|
301
|
+
)
|
|
302
|
+
logger.debug(
|
|
303
|
+
"Assoc String -> {}".format(assoc_string)
|
|
304
|
+
)
|
|
305
|
+
assoc_dict = Assoc.stringToDict(
|
|
306
|
+
assoc_string=assoc_string
|
|
307
|
+
)
|
|
308
|
+
logger.debug("Assoc Dict -> {}".format(assoc_dict))
|
|
309
|
+
escaped_replace_string = replace_string.replace(
|
|
310
|
+
"'", "\\\\\u0027"
|
|
311
|
+
)
|
|
312
|
+
logger.info(
|
|
313
|
+
"Escaped replacement string -> {}".format(
|
|
314
|
+
escaped_replace_string
|
|
315
|
+
)
|
|
316
|
+
)
|
|
317
|
+
assoc_dict[
|
|
318
|
+
assoc_elem
|
|
319
|
+
] = escaped_replace_string # escaped_replace_string
|
|
320
|
+
assoc_string_new: str = Assoc.dictToString(
|
|
321
|
+
assoc_dict=assoc_dict
|
|
322
|
+
)
|
|
323
|
+
assoc_string_new = assoc_string_new.replace(
|
|
324
|
+
"'", "\\u0027"
|
|
325
|
+
)
|
|
326
|
+
# replace_setting = """ + setting + "":"" + assoc_string_new + """
|
|
327
|
+
replace_setting = (
|
|
328
|
+
'"' + setting + '":"' + assoc_string_new + '"'
|
|
329
|
+
)
|
|
330
|
+
logger.info(
|
|
331
|
+
"Replacement setting -> {}".format(
|
|
332
|
+
replace_setting
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
# here we need to apply a "trick". It is required
|
|
336
|
+
# as regexp cannot handle the special unicode escapes \u0027
|
|
337
|
+
# we require. We first insert a placeholder "PLACEHOLDER"
|
|
338
|
+
# and let regexp find the right place for it. Then further
|
|
339
|
+
# down we use a simple search&replace to switch the PLACEHOLDER
|
|
340
|
+
# to the real value (replace() does not have the issues with unicode escapes)
|
|
341
|
+
element.text = cls.replaceSetting(
|
|
342
|
+
element_text=element.text,
|
|
343
|
+
setting_key=setting,
|
|
344
|
+
# new_value=replace_setting,
|
|
345
|
+
new_value="PLACEHOLDER",
|
|
346
|
+
is_simple=False,
|
|
347
|
+
is_escaped=False,
|
|
348
|
+
)
|
|
349
|
+
element.text = element.text.replace(
|
|
350
|
+
"PLACEHOLDER", replace_setting
|
|
351
|
+
)
|
|
352
|
+
element.text = element.text.replace('"', """)
|
|
353
|
+
xml_modified = True
|
|
354
|
+
else:
|
|
355
|
+
logger.warning(
|
|
356
|
+
"Cannot find the value for setting -> {}. Skipping...".format(
|
|
357
|
+
setting
|
|
358
|
+
)
|
|
359
|
+
)
|
|
360
|
+
continue
|
|
361
|
+
if xml_modified:
|
|
362
|
+
logger.info(
|
|
363
|
+
"XML tree has been modified. Write updated file -> {}...".format(
|
|
364
|
+
file_path
|
|
365
|
+
)
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
new_contents = etree.tostring(
|
|
369
|
+
tree,
|
|
370
|
+
pretty_print=True,
|
|
371
|
+
xml_declaration=True,
|
|
372
|
+
encoding="UTF-8",
|
|
373
|
+
)
|
|
374
|
+
# we need to undo some of the stupid things tostring() did:
|
|
375
|
+
new_contents = new_contents.replace(
|
|
376
|
+
b""", b"""
|
|
377
|
+
)
|
|
378
|
+
new_contents = new_contents.replace(
|
|
379
|
+
b"'", b"'"
|
|
380
|
+
)
|
|
381
|
+
new_contents = new_contents.replace(b">", b">")
|
|
382
|
+
new_contents = new_contents.replace(b"&lt;", b"<")
|
|
383
|
+
|
|
384
|
+
# Replace single quotes inside double quotes strings with "'" (manual escaping)
|
|
385
|
+
# This is required as we next want to replace all double quotes with single quotes
|
|
386
|
+
pattern = b'"([^"]*)"'
|
|
387
|
+
new_contents = re.sub(
|
|
388
|
+
pattern,
|
|
389
|
+
lambda m: m.group(0).replace(b"'", b"'"),
|
|
390
|
+
new_contents,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
# Replace single quotes in XML text elements with "'"
|
|
394
|
+
# and replace double quotes in XML text elements with """
|
|
395
|
+
# This is required as we next want to replace all double quotes with single quotes
|
|
396
|
+
pattern = b'>([^<>]+?)<'
|
|
397
|
+
replacement = lambda match: match.group(0).replace(b'"', b""")
|
|
398
|
+
new_contents = re.sub(pattern, replacement, new_contents)
|
|
399
|
+
replacement = lambda match: match.group(0).replace(b"'", b"'")
|
|
400
|
+
new_contents = re.sub(pattern, replacement, new_contents)
|
|
401
|
+
|
|
402
|
+
# Change double quotes to single quotes across the XML file - Extended ECM has it that way:
|
|
403
|
+
new_contents = new_contents.replace(b'"', b"'")
|
|
404
|
+
|
|
405
|
+
# Write the updated contents to the file.
|
|
406
|
+
# We DO NOT want to use tree.write() here
|
|
407
|
+
# as it would undo the manual XML tweaks we
|
|
408
|
+
# need for Extended ECM. We also need "wb"
|
|
409
|
+
# as we have bytes and not str as a data type
|
|
410
|
+
with open(file_path, "wb") as f:
|
|
411
|
+
f.write(new_contents)
|
|
412
|
+
|
|
413
|
+
found = True
|
|
414
|
+
# this is not using xpath - do a simple search and replace
|
|
415
|
+
else:
|
|
416
|
+
logger.info("Replacement without xpath...")
|
|
417
|
+
with open(file_path, "r") as f:
|
|
418
|
+
contents = f.read()
|
|
419
|
+
# Replace all occurrences of the search pattern with the replace string
|
|
420
|
+
new_contents = pattern.sub(replace_string, contents)
|
|
421
|
+
|
|
422
|
+
# Write the updated contents to the file if there were replacements
|
|
423
|
+
if contents != new_contents:
|
|
424
|
+
logger.info(
|
|
425
|
+
"Found search string -> {} in XML file -> {}. Write updated file...".format(
|
|
426
|
+
search_pattern, file_path
|
|
427
|
+
)
|
|
428
|
+
)
|
|
429
|
+
# Write the updated contents to the file
|
|
430
|
+
with open(file_path, "w") as f:
|
|
431
|
+
f.write(new_contents)
|
|
432
|
+
found = True
|
|
433
|
+
|
|
434
|
+
return found
|
|
435
|
+
|
|
436
|
+
# end method definition
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyxecm
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.19
|
|
4
4
|
Summary: A Python library to interact with Opentext Extended ECM REST API
|
|
5
|
-
Home-page: https://pypi.org/project/pyxecm/
|
|
6
5
|
Author-email: Kai Gatzweiler <kgatzweiler@opentext.com>, "Dr. Marc Diefenbruch" <mdiefenb@opentext.com>
|
|
7
|
-
Project-URL: Homepage, https://
|
|
8
|
-
Keywords: opentext
|
|
6
|
+
Project-URL: Homepage, https://gitlab.otxlab.net/ecm/pyxecm
|
|
7
|
+
Keywords: opentext,extendedecm,contentserver,otds,appworks,archivecenter
|
|
9
8
|
Classifier: Development Status :: 4 - Beta
|
|
10
9
|
Classifier: Programming Language :: Python :: 3
|
|
11
10
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -25,7 +24,7 @@ Requires-Dist: suds
|
|
|
25
24
|
|
|
26
25
|
# PYXECM
|
|
27
26
|
|
|
28
|
-
A python library to interact with Opentext Extended ECM REST API.
|
|
27
|
+
A python library to interact with Opentext Extended ECM REST API.
|
|
29
28
|
API documentation is available on [OpenText Developer](https://developer.opentext.com/ce/products/extendedecm)
|
|
30
29
|
|
|
31
30
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
pyxecm/__init__.py,sha256=aTminqJ8W9rcA4kIQUDSX3d7EE1f_8DhfbWwY1g6ZVw,448
|
|
2
|
+
pyxecm/assoc.py,sha256=G5QZVkAwIX-MDc7zog-otna60SyMhz-Gf1oZ9XhxNBk,4518
|
|
3
|
+
pyxecm/k8s.py,sha256=kNGc1kVFYKF9o3dRDdxQ4FMwC4b3QGgFX3SCnMv6k5k,33694
|
|
4
|
+
pyxecm/m365.py,sha256=9IguLxs3TqqbEFYABrYAEVAbjnwN8PLXHie6SdkmGtk,77794
|
|
5
|
+
pyxecm/main.py,sha256=hn-Xd6azipKptpw4ivvRwaQhot6V5AoQZLPgFP3J8oQ,51479
|
|
6
|
+
pyxecm/otac.py,sha256=sgdqHQu9tBMm5pMGKe5wb1dgMbHfxPGKgn4t5BCv-7E,9554
|
|
7
|
+
pyxecm/otcs.py,sha256=u0AKoaSmng2_EIg-Dhs_JOVEiEl-V_UhkzK7RPyKubw,256466
|
|
8
|
+
pyxecm/otds.py,sha256=lV0qmVuQj6cLQ0kMv8g7GPWBTTAnp9imYALnqUNQkVM,129349
|
|
9
|
+
pyxecm/otiv.py,sha256=i3-z0tJttNkaq1VoOfEkKgcVDjvkixUZeLRkxITom2o,1627
|
|
10
|
+
pyxecm/otpd.py,sha256=Djxno-r3XMkz6hb9qSLMecvdSa9RVmu6LpJeteLCx7o,10240
|
|
11
|
+
pyxecm/payload.py,sha256=K0RFd2y_CvwwAWNsS8Hk4FNT3EBNKMOgA4oebpPDCXs,297179
|
|
12
|
+
pyxecm/sap.py,sha256=T93T9mfE5HAJSZxF_0Cwvb-y7sNeef7GPgZenucnBok,5986
|
|
13
|
+
pyxecm/translate.py,sha256=dEqQAg6ZWcorjgobNnW9p-IP9iOQ6ouklntr4qrLqsI,2718
|
|
14
|
+
pyxecm/web.py,sha256=4ITGEQ7vOjMrp79e13k3lFB__q-4k5gDxg4T7kw765A,2719
|
|
15
|
+
pyxecm/xml.py,sha256=RsA30-rmNlY3smku6Fnt8V9PX2g4Jw1Fas5nVH_Cixc,21850
|
|
16
|
+
pyxecm-0.0.19.dist-info/LICENSE,sha256=z5DWWd5cHmQYJnq4BDt1bmVQjuXY1Qsp6y0v5ETCw-s,11360
|
|
17
|
+
pyxecm-0.0.19.dist-info/METADATA,sha256=TKigQumI5OrgBzxMksvuKZZWTvFevKWbgBlGx9tFoi0,1825
|
|
18
|
+
pyxecm-0.0.19.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
19
|
+
pyxecm-0.0.19.dist-info/top_level.txt,sha256=TGak3_dYN67ugKFbmRxRG1leDyOt0T7dypjdX4Ij1WE,7
|
|
20
|
+
pyxecm-0.0.19.dist-info/RECORD,,
|