pyxecm 3.9.2__tar.gz → 3.9.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyxecm-3.9.2 → pyxecm-3.9.4}/PKG-INFO +1 -1
- {pyxecm-3.9.2 → pyxecm-3.9.4}/pyproject.toml +1 -1
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/errors.py +37 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/helper/data.py +49 -9
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/otcs.py +325 -132
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/manager_push.py +27 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/payload.py +211 -46
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/sap.py +1185 -310
- pyxecm-3.9.4/src/pyxecm_customizer/sap_metadata.py +345 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/sap_models.py +125 -2
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/settings.py +4 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/.gitignore +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/README.md +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/avts.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/coreshare.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/helper/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/helper/assoc.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/helper/logadapter.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/helper/web.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/helper/xml.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/otac.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/otawp.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/otca.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/otds.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/otiv.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/otkd.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/otmm.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/otpd.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm/py.typed +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/__main__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/app.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/auth/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/auth/functions.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/auth/models.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/auth/router.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/common/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/common/functions.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/common/metrics.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/common/models.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/common/router.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/config/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/config/functions.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/config/models.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/config/router.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/pods/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/pods/functions.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/pods/log_spool.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/pods/models.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/pods/router.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/settings.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/terminal/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/terminal/router.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_maintenance/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_maintenance/functions.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_maintenance/models.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_maintenance/router.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_otcs/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_otcs/functions.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_otcs/router.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_payload/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_payload/functions.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_payload/log_reader.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_payload/models.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_api/v1_payload/router.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/__main__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/browser_automation.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/customizer.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/exceptions.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/guidewire.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/k8s.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/log.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/m365.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/payload_list.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/salesforce.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/sap_forms.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/servicenow.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/successfactors.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/translate.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_customizer/version.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_maintenance_page/__init__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_maintenance_page/__main__.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_maintenance_page/app.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_maintenance_page/settings.py +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_maintenance_page/static/favicon.avif +0 -0
- {pyxecm-3.9.2 → pyxecm-3.9.4}/src/pyxecm_maintenance_page/templates/maintenance.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pyxecm
|
|
3
|
-
Version: 3.9.
|
|
3
|
+
Version: 3.9.4
|
|
4
4
|
Summary: A Python library to interact with Opentext Content Management Rest API
|
|
5
5
|
Project-URL: Homepage, https://github.com/opentext/pyxecm
|
|
6
6
|
Author-email: Kai Gatzweiler <kgatzweiler@opentext.com>, "Dr. Marc Diefenbruch" <mdiefenb@opentext.com>
|
|
@@ -30,10 +30,13 @@ __credits__ = ["Kai-Philip Gatzweiler"]
|
|
|
30
30
|
__maintainer__ = "Dr. Marc Diefenbruch"
|
|
31
31
|
__email__ = "mdiefenb@opentext.com"
|
|
32
32
|
|
|
33
|
+
import contextlib
|
|
33
34
|
import json
|
|
34
35
|
import logging
|
|
36
|
+
import os
|
|
35
37
|
import threading
|
|
36
38
|
import urllib.parse
|
|
39
|
+
import warnings
|
|
37
40
|
from collections import deque
|
|
38
41
|
from collections.abc import Generator, Iterator
|
|
39
42
|
from contextlib import contextmanager, suppress
|
|
@@ -719,6 +722,40 @@ def record_error(
|
|
|
719
722
|
# end function definition
|
|
720
723
|
|
|
721
724
|
|
|
725
|
+
# The warning should point to the code that called pyxecm - not to the pyxecm
|
|
726
|
+
# method that passes the parameter on (e.g. OTCS.create_item() -> OTCS.do_request())
|
|
727
|
+
# or to the wrapper of the @tracer.start_as_current_span decorator (in contextlib).
|
|
728
|
+
# The contextlib entry has no ".py" extension: CPython does not compare the last
|
|
729
|
+
# character of the file name, so a prefix that is the complete file name never matches.
|
|
730
|
+
_PYXECM_PACKAGE_DIRS = (
|
|
731
|
+
*(
|
|
732
|
+
os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), package) + os.sep
|
|
733
|
+
for package in ("pyxecm", "pyxecm_customizer")
|
|
734
|
+
),
|
|
735
|
+
os.path.splitext(contextlib.__file__)[0],
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def warn_parse_error_response_deprecated() -> None:
|
|
740
|
+
"""Warn that the parse_error_response parameter is deprecated.
|
|
741
|
+
|
|
742
|
+
The error details are available via capture_errors() for all pyxecm calls,
|
|
743
|
+
so the parameter will be removed in a future version.
|
|
744
|
+
"""
|
|
745
|
+
|
|
746
|
+
warnings.warn(
|
|
747
|
+
message=(
|
|
748
|
+
"The parameter parse_error_response is deprecated and will be removed in a future version. "
|
|
749
|
+
"Use capture_errors() to get the error details instead, e.g. errors.root_cause.server_message."
|
|
750
|
+
),
|
|
751
|
+
category=DeprecationWarning,
|
|
752
|
+
skip_file_prefixes=_PYXECM_PACKAGE_DIRS,
|
|
753
|
+
)
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
# end function definition
|
|
757
|
+
|
|
758
|
+
|
|
722
759
|
def request_error_severity(show_error: bool, show_warning: bool = True) -> ErrorSeverity:
|
|
723
760
|
"""Return the severity of a failed REST call that matches its log level.
|
|
724
761
|
|
|
@@ -705,18 +705,30 @@ class Data:
|
|
|
705
705
|
convert_dates: bool = False,
|
|
706
706
|
index_column: str | None = None,
|
|
707
707
|
compression: str | None = None,
|
|
708
|
+
replace: bool = False,
|
|
708
709
|
) -> bool:
|
|
709
710
|
"""Load JSON data into a Pandas data frame.
|
|
710
711
|
|
|
712
|
+
Without a schema (no `dtypes` given when the Data object was created), pandas infers the
|
|
713
|
+
data types.
|
|
714
|
+
|
|
715
|
+
With a schema, the JSON file is loaded with the schema (e.g. data saved with save_json_data()):
|
|
716
|
+
pandas does not infer the data types (it would e.g. turn the string "1e3" into the number
|
|
717
|
+
1000.0), and the schema is applied after loading (see apply_schema()): the columns of the
|
|
718
|
+
schema with their data types (also datetimes, if saved in ISO format) and the index
|
|
719
|
+
(`index_columns`). If the schema cannot be applied, the data frame is left unchanged.
|
|
720
|
+
|
|
711
721
|
Args:
|
|
712
722
|
json_path (str):
|
|
713
723
|
The path to the JSON file.
|
|
714
724
|
convert_dates (bool, optional):
|
|
715
725
|
Defines whether or not dates should be converted.
|
|
716
|
-
The default is False = dates are NOT converted.
|
|
726
|
+
The default is False = dates are NOT converted. With a schema, the datetime
|
|
727
|
+
columns of the schema are converted anyway.
|
|
717
728
|
index_column (str | None, optional):
|
|
718
729
|
The Name of the column (i.e. JSON data field) that should
|
|
719
730
|
become the index in the loaded data frame. Defaults to None.
|
|
731
|
+
Not to be combined with a schema - its index (`index_columns`) is applied.
|
|
720
732
|
compression (str | None, optional):
|
|
721
733
|
The compression of the JSON file that is to be decompressed:
|
|
722
734
|
* gzip (.gz)
|
|
@@ -725,6 +737,9 @@ class Data:
|
|
|
725
737
|
* xz (.xz)
|
|
726
738
|
The value for compression should not include the dot.
|
|
727
739
|
Default is None = no compression.
|
|
740
|
+
replace (bool, optional):
|
|
741
|
+
If True, the loaded data replaces the content of the data frame. If False (default),
|
|
742
|
+
it is appended to the data frame (with a new row index).
|
|
728
743
|
|
|
729
744
|
Returns:
|
|
730
745
|
bool: False in case an error occurred, True otherwise.
|
|
@@ -758,6 +773,8 @@ class Data:
|
|
|
758
773
|
path_or_buf=json_path,
|
|
759
774
|
convert_dates=convert_dates,
|
|
760
775
|
compression=compression,
|
|
776
|
+
# With a schema the data types are defined by it - pandas must not infer them:
|
|
777
|
+
dtype=not self._schema,
|
|
761
778
|
)
|
|
762
779
|
|
|
763
780
|
if index_column and index_column not in df.columns:
|
|
@@ -769,10 +786,18 @@ class Data:
|
|
|
769
786
|
|
|
770
787
|
if index_column:
|
|
771
788
|
df = df.set_index(keys=index_column)
|
|
772
|
-
|
|
789
|
+
previous_df = self._df
|
|
790
|
+
if replace or self._df is None:
|
|
773
791
|
self._df = df
|
|
774
792
|
else:
|
|
775
793
|
self._df = pd.concat([self._df, df], ignore_index=True)
|
|
794
|
+
if self._schema:
|
|
795
|
+
try:
|
|
796
|
+
self.apply_schema()
|
|
797
|
+
except Exception:
|
|
798
|
+
# apply_schema() has logged the error - keep the data frame as it was:
|
|
799
|
+
self._df = previous_df
|
|
800
|
+
raise
|
|
776
801
|
self.logger.info(
|
|
777
802
|
"After loading JSON file -> '%s', the data frame has %s row(s) and %s column(s)",
|
|
778
803
|
json_path,
|
|
@@ -824,9 +849,14 @@ class Data:
|
|
|
824
849
|
preserve_index: bool = False,
|
|
825
850
|
index_column: str = "index",
|
|
826
851
|
compression: str | None = None,
|
|
852
|
+
indent: int | None = 2,
|
|
827
853
|
) -> bool:
|
|
828
854
|
"""Save JSON data from data frame to file.
|
|
829
855
|
|
|
856
|
+
Datetimes are saved in ISO format (with millisecond precision). A data frame saved with
|
|
857
|
+
`orient="records"` is loaded again with its data types by load_json_data() of a Data object
|
|
858
|
+
with the same schema.
|
|
859
|
+
|
|
830
860
|
Args:
|
|
831
861
|
json_path (str):
|
|
832
862
|
The path to where the JSON file should be saved.
|
|
@@ -851,6 +881,9 @@ class Data:
|
|
|
851
881
|
* zip (.zip)
|
|
852
882
|
* xz (.xz)
|
|
853
883
|
Default is None = no compression.
|
|
884
|
+
indent (int | None, optional):
|
|
885
|
+
The number of spaces to indent the JSON. None writes compact JSON (smaller files).
|
|
886
|
+
Defaults to 2.
|
|
854
887
|
|
|
855
888
|
Returns:
|
|
856
889
|
bool:
|
|
@@ -874,8 +907,10 @@ class Data:
|
|
|
874
907
|
# Save data to JSON file
|
|
875
908
|
try:
|
|
876
909
|
if self._df is not None:
|
|
877
|
-
|
|
878
|
-
|
|
910
|
+
# A file name without directory is saved in the current directory:
|
|
911
|
+
json_directory = os.path.dirname(json_path)
|
|
912
|
+
if json_directory:
|
|
913
|
+
os.makedirs(json_directory, exist_ok=True)
|
|
879
914
|
|
|
880
915
|
# index parameter is only allowed if orient has one of the following values:
|
|
881
916
|
if orient in ("columns", "index", "table", "split"):
|
|
@@ -883,7 +918,7 @@ class Data:
|
|
|
883
918
|
path_or_buf=json_path,
|
|
884
919
|
index=preserve_index,
|
|
885
920
|
orient=orient,
|
|
886
|
-
indent=
|
|
921
|
+
indent=indent,
|
|
887
922
|
compression=compression,
|
|
888
923
|
date_format="iso",
|
|
889
924
|
)
|
|
@@ -899,7 +934,7 @@ class Data:
|
|
|
899
934
|
df_with_index.to_json(
|
|
900
935
|
path_or_buf=json_path,
|
|
901
936
|
orient=orient,
|
|
902
|
-
indent=
|
|
937
|
+
indent=indent,
|
|
903
938
|
compression=compression,
|
|
904
939
|
date_format="iso",
|
|
905
940
|
)
|
|
@@ -907,7 +942,7 @@ class Data:
|
|
|
907
942
|
self._df.to_json(
|
|
908
943
|
path_or_buf=json_path,
|
|
909
944
|
orient=orient,
|
|
910
|
-
indent=
|
|
945
|
+
indent=indent,
|
|
911
946
|
compression=compression,
|
|
912
947
|
date_format="iso",
|
|
913
948
|
)
|
|
@@ -1819,6 +1854,9 @@ class Data:
|
|
|
1819
1854
|
def deduplicate(self, unique_fields: list, inplace: bool = True) -> pd.DataFrame:
|
|
1820
1855
|
"""Remove duplicate rows that have all fields in unique_fields in common.
|
|
1821
1856
|
|
|
1857
|
+
The index defined for this Data object (`index_columns`) is kept - its rows are selected by it (e.g.
|
|
1858
|
+
with `loc[]`). Without one, the rows are numbered again (0, 1, 2, ...).
|
|
1859
|
+
|
|
1822
1860
|
Args:
|
|
1823
1861
|
unique_fields (list):
|
|
1824
1862
|
Defines the fields for which we want a unique combination for.
|
|
@@ -1834,11 +1872,13 @@ class Data:
|
|
|
1834
1872
|
|
|
1835
1873
|
if inplace:
|
|
1836
1874
|
self._df.drop_duplicates(subset=unique_fields, inplace=True)
|
|
1837
|
-
self.
|
|
1875
|
+
if not self._index_columns:
|
|
1876
|
+
self._df.reset_index(drop=True, inplace=True)
|
|
1838
1877
|
return self._df
|
|
1839
1878
|
else:
|
|
1840
1879
|
df = self._df.drop_duplicates(subset=unique_fields, inplace=False)
|
|
1841
|
-
|
|
1880
|
+
if not self._index_columns:
|
|
1881
|
+
df = df.reset_index(drop=True, inplace=False)
|
|
1842
1882
|
return df
|
|
1843
1883
|
|
|
1844
1884
|
# end method definition
|