testsuit 0.1.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.
- testsuit/__init__.py +15 -0
- testsuit/cli/__init__.py +0 -0
- testsuit/cli/apply_clock_correction.py +167 -0
- testsuit/cli/data2db.py +82 -0
- testsuit/cli/data2h5.py +168 -0
- testsuit/cli/datapack.py +46 -0
- testsuit/cli/evalfile.py +88 -0
- testsuit/cli/exploit_runner.py +122 -0
- testsuit/cli/extract_data_any.py +307 -0
- testsuit/cli/extract_data_flags.py +249 -0
- testsuit/cli/mxp.py +75 -0
- testsuit/cli/notebooks.py +42 -0
- testsuit/datatools/DataFileMgrs/AFileMgr.py +443 -0
- testsuit/datatools/DataFileMgrs/CsvFileMgr.py +671 -0
- testsuit/datatools/DataFileMgrs/DxdFileMgr.py +183 -0
- testsuit/datatools/DataFileMgrs/FolderParamMgr.py +553 -0
- testsuit/datatools/DataFileMgrs/H5FileMgr.py +644 -0
- testsuit/datatools/DataFileMgrs/InfluxDbFileMgr.py +838 -0
- testsuit/datatools/DataFileMgrs/MdfFileMgr.py +175 -0
- testsuit/datatools/DataFileMgrs/TdmsFileMgr.py +177 -0
- testsuit/datatools/DataFileMgrs/UdbfFileMgr.py +204 -0
- testsuit/datatools/DataFileMgrs/__init__.py +0 -0
- testsuit/datatools/DataFileMgrs/formats.py +262 -0
- testsuit/datatools/DataframeToHdf5.py +139 -0
- testsuit/datatools/__init__.py +1 -0
- testsuit/datatools/data2db.py +977 -0
- testsuit/datatools/datapack/ADataImporter.py +52 -0
- testsuit/datatools/datapack/GitImporter.py +72 -0
- testsuit/datatools/datapack/__init__.py +0 -0
- testsuit/datatools/datapack/datapack_tools.py +565 -0
- testsuit/datatools/datapack/evalfiles/evalfile.py +328 -0
- testsuit/datatools/datapack/evalfiles/evalincludes.py +343 -0
- testsuit/datatools/datapack/evalfiles/evalkeys.py +309 -0
- testsuit/datatools/datapack/evalfiles/evalpath.py +373 -0
- testsuit/datatools/datapack/evalfiles/libdictionary.py +255 -0
- testsuit/datatools/datapack/importers.py +75 -0
- testsuit/datatools/datapack/processors/basickeys.py +62 -0
- testsuit/datatools/datapack/processors/filterkeys.py +33 -0
- testsuit/datatools/datapack/processors/finalize.py +42 -0
- testsuit/datatools/datapack/processors/replace.py +53 -0
- testsuit/datatools/datatoolbox.py +496 -0
- testsuit/datatools/h5diff.py +154 -0
- testsuit/datatools/plotHelpers.py +973 -0
- testsuit/exploit/mexploit/__init__.py +0 -0
- testsuit/exploit/mexploit/conftest.py +461 -0
- testsuit/exploit/mexploit/conftest_scn_template.py +25 -0
- testsuit/exploit/mexploit/criteria.py +1202 -0
- testsuit/exploit/mexploit/helpers.py +509 -0
- testsuit/exploit/mexploit/mexploit.py +229 -0
- testsuit/exploit/mexploit/registry.py +98 -0
- testsuit/exploit/runner/__init__.py +0 -0
- testsuit/exploit/runner/exploit_runner.py +483 -0
- testsuit/exploit/runner/report.py +153 -0
- testsuit/exploit/runner/report_html.py +1688 -0
- testsuit/exploit/runner/runProcess.py +241 -0
- testsuit/exploit/runner/utils.py +333 -0
- testsuit/jupytertools/FileDialog.py +38 -0
- testsuit/jupytertools/JupyterGui.py +404 -0
- testsuit/jupytertools/MexploitJupyterGui.py +297 -0
- testsuit/jupytertools/PlotJupyterGui.py +537 -0
- testsuit/jupytertools/__init__.py +6 -0
- testsuit/jupytertools/h5ConvertJupyterGui.py +235 -0
- testsuit/jupytertools/install.py +56 -0
- testsuit/jupytertools/notebooks/__init__.py +1 -0
- testsuit/jupytertools/notebooks/data_plot.ipynb +28 -0
- testsuit/jupytertools/notebooks/h5_convert.ipynb +28 -0
- testsuit/jupytertools/notebooks/mexploit.ipynb +29 -0
- testsuit/misc/MonitorProgress.py +457 -0
- testsuit/misc/__init__.py +0 -0
- testsuit/misc/files.py +99 -0
- testsuit/misc/logger.py +120 -0
- testsuit/plugins.py +77 -0
- testsuit-0.1.1.dist-info/METADATA +165 -0
- testsuit-0.1.1.dist-info/RECORD +78 -0
- testsuit-0.1.1.dist-info/WHEEL +5 -0
- testsuit-0.1.1.dist-info/entry_points.txt +11 -0
- testsuit-0.1.1.dist-info/licenses/LICENSE +201 -0
- testsuit-0.1.1.dist-info/top_level.txt +1 -0
testsuit/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Toolbox for preparing and analysing test data.
|
|
2
|
+
|
|
3
|
+
Subpackages:
|
|
4
|
+
datatools load data files into pandas, convert to HDF5, push to a DB, datapacks
|
|
5
|
+
exploit mexploit YAML scenario checks and the exploit runner
|
|
6
|
+
jupytertools ipywidgets GUIs for notebooks
|
|
7
|
+
misc logging, progress reporting, file helpers
|
|
8
|
+
cli command-line entry points (see [project.scripts] in pyproject.toml)
|
|
9
|
+
"""
|
|
10
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
__version__ = version("testsuit")
|
|
14
|
+
except PackageNotFoundError: # running from a source tree without install
|
|
15
|
+
__version__ = "0+unknown"
|
testsuit/cli/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import argparse
|
|
4
|
+
import concurrent.futures
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from testsuit.datatools.datatoolbox import loadDataframeFromFile
|
|
9
|
+
from testsuit.datatools.DataFileMgrs.AFileMgr import enable_pandas_display_helpers
|
|
10
|
+
from testsuit.datatools.DataFileMgrs.FolderParamMgr import FolderParamMgr
|
|
11
|
+
from testsuit.datatools.DataframeToHdf5 import DataframeToHdf5
|
|
12
|
+
|
|
13
|
+
from testsuit.misc.logger import get_logger
|
|
14
|
+
from testsuit.misc.logger import create_logger
|
|
15
|
+
from testsuit.misc.MonitorProgress import MonitorProgress,consoleRichProgressCb
|
|
16
|
+
|
|
17
|
+
CORRECTED_CLOCK_FILE_SUFFIX="clock_corrected"
|
|
18
|
+
|
|
19
|
+
NB_WORKERS=8
|
|
20
|
+
|
|
21
|
+
def _process_file_with_clock_correction(fpath, clock_correction_file, shiftDateSec, shiftDateRegex, monitorProgress, deleteIfExists=True):
|
|
22
|
+
"""Worker function for multithreaded clock correction processing."""
|
|
23
|
+
monitorProgress.msg(msg=f"applying dates correction on file: {fpath}")
|
|
24
|
+
|
|
25
|
+
if clock_correction_file:
|
|
26
|
+
mgr = FolderParamMgr([fpath, clock_correction_file], shiftDateSec=shiftDateSec, shiftDateRegex=shiftDateRegex, shiftDateInverted=True,continueOnError=True)
|
|
27
|
+
else:
|
|
28
|
+
mgr = FolderParamMgr([fpath], shiftDateSec=shiftDateSec, shiftDateRegex=shiftDateRegex, shiftDateInverted=True,continueOnError=True)
|
|
29
|
+
try:
|
|
30
|
+
dfs = mgr.findParams(os.path.basename(fpath) + "::", monitorProgress=monitorProgress)#,silent=True)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
import traceback
|
|
33
|
+
traceback.print_exc()
|
|
34
|
+
monitorProgress.msg(msg=f"could not apply clock correction to {fpath} : " + str(e), msgSeverity="error")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
comment = {}
|
|
38
|
+
comment["Comments"] = "Extracted from original data"
|
|
39
|
+
|
|
40
|
+
if len(dfs) == 0:
|
|
41
|
+
monitorProgress.msg(msg=f"No param in {os.path.basename(fpath)}", msgSeverity="warning")
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
targetFile = dfs[0].origin.replace(".h5", f".{CORRECTED_CLOCK_FILE_SUFFIX}.h5")
|
|
45
|
+
if deleteIfExists and os.path.exists(targetFile):
|
|
46
|
+
os.remove(targetFile)
|
|
47
|
+
|
|
48
|
+
h5GroupName = os.path.basename(dfs[0].origin)
|
|
49
|
+
for df in dfs:
|
|
50
|
+
if "clock corrected" in df.attrs:
|
|
51
|
+
DataframeToHdf5(targetFile, h5GroupName, df, comment=comment, chunk_size=True)
|
|
52
|
+
|
|
53
|
+
monitorProgress.msg(msg=f"written {targetFile}")
|
|
54
|
+
|
|
55
|
+
def apply_clock_correction(target_folder,
|
|
56
|
+
shiftDateSec, shiftDateRegex, shiftDateSrcFile, shiftDateInverted=False,
|
|
57
|
+
monitorProgress=None,deleteIfExist=True):
|
|
58
|
+
"""
|
|
59
|
+
Apply given clock correction (either constant litteral or a param name to be loaded.
|
|
60
|
+
Actual processing is planned to be applicable on-the-fly, and thus is implemented in
|
|
61
|
+
FolderParamMgr::findParams() and AFileMgr::finalizeParam() methods."""
|
|
62
|
+
|
|
63
|
+
monitorProgress.msg(msg=[f"correcting clock for {shiftDateRegex}",
|
|
64
|
+
f"clock correction from {shiftDateSec} in {shiftDateSrcFile} "])
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# try to interpret provided shitDateSec as a float litteral, if not, it is considered as a param name
|
|
68
|
+
try: shiftDateSec=float(shiftDateSec)
|
|
69
|
+
except (TypeError, ValueError): pass
|
|
70
|
+
|
|
71
|
+
clock_correction_file=None
|
|
72
|
+
|
|
73
|
+
totalSteps=3 if isinstance(shiftDateSec,str) else 2
|
|
74
|
+
|
|
75
|
+
monitorProgress.set_total_items(totalSteps)
|
|
76
|
+
subFindMatchingDfmP = monitorProgress.child("find matching params")
|
|
77
|
+
|
|
78
|
+
# early check of availability of provided shitDateSec param name
|
|
79
|
+
# if not str, it is a float litteral constant correction value
|
|
80
|
+
if isinstance(shiftDateSec,str):
|
|
81
|
+
|
|
82
|
+
clock_correction_file = shiftDateSrcFile
|
|
83
|
+
if not os.path.exists(clock_correction_file) and not clock_correction_file.startswith(os.sep):
|
|
84
|
+
clock_correction_file = os.path.join(target_folder, shiftDateSrcFile)
|
|
85
|
+
monitorProgress.msg(msg=f"Using file {clock_correction_file} to get clock correction info '{shiftDateSec}'.",
|
|
86
|
+
msgSeverity="info")
|
|
87
|
+
if not os.path.exists(clock_correction_file):
|
|
88
|
+
monitorProgress.msg(msg=f"File {clock_correction_file} not reachable. Cannot apply clock correction '{shiftDateSec}'.",
|
|
89
|
+
msgSeverity="error")
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
shiftdatefilemgr = FolderParamMgr(clock_correction_file)
|
|
93
|
+
shiftDateMatchingParams = shiftdatefilemgr.findParams(shiftDateSec, dryRun=True, silent=True, monitorProgress=subFindMatchingDfmP)
|
|
94
|
+
shiftDateParamNames = [df.name for df in shiftDateMatchingParams]
|
|
95
|
+
if len(shiftDateParamNames) == 0:
|
|
96
|
+
monitorProgress.msg(msg=f"No param matching shiftDateSec='{shiftDateSec}' not found in {clock_correction_file}.",
|
|
97
|
+
msgSeverity="error")
|
|
98
|
+
return False
|
|
99
|
+
if len(shiftDateParamNames) > 1:
|
|
100
|
+
monitorProgress.msg(msg=f"more than one param matching shiftDateSec='{shiftDateSec}' in {clock_correction_file}: {shiftDateParamNames}",
|
|
101
|
+
msgSeverity="error")
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
subLoadMatchingDfmP = monitorProgress.child("load matching params")
|
|
105
|
+
|
|
106
|
+
# list all params matching given regex, to identify corresponding files
|
|
107
|
+
matching_dfs = loadDataframeFromFile(sourceFolderOrFile=target_folder, paramRegexes=shiftDateRegex,
|
|
108
|
+
indices=None, dryRun=True, silent=True, monitorProgress=subLoadMatchingDfmP)
|
|
109
|
+
if len(matching_dfs) == 0:
|
|
110
|
+
monitorProgress.msg(msg=["found no data needing clock correction", f"pattern: {shiftDateRegex}"], msgSeverity="warning")
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
files_to_process = list(set(df.origin for df in matching_dfs if (df.origin and CORRECTED_CLOCK_FILE_SUFFIX not in df.origin)))
|
|
114
|
+
subApplyClockmP = monitorProgress.child("apply clock correction",len(files_to_process))
|
|
115
|
+
|
|
116
|
+
# Parallelize file processing with multithreading
|
|
117
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=NB_WORKERS) as executor:
|
|
118
|
+
futures = {
|
|
119
|
+
executor.submit(
|
|
120
|
+
_process_file_with_clock_correction,
|
|
121
|
+
fpath,
|
|
122
|
+
clock_correction_file,
|
|
123
|
+
shiftDateSec,
|
|
124
|
+
shiftDateRegex,
|
|
125
|
+
subApplyClockmP.child(fpath),
|
|
126
|
+
deleteIfExist
|
|
127
|
+
): fpath
|
|
128
|
+
for fpath in files_to_process
|
|
129
|
+
}
|
|
130
|
+
for future in concurrent.futures.as_completed(futures):
|
|
131
|
+
fpath = futures[future]
|
|
132
|
+
try:
|
|
133
|
+
future.result()
|
|
134
|
+
except Exception as e:
|
|
135
|
+
monitorProgress.msg(msg=f"unexpected error processing {fpath}: {e}", msgSeverity="error")
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def main():
|
|
141
|
+
enable_pandas_display_helpers()
|
|
142
|
+
parser = argparse.ArgumentParser(
|
|
143
|
+
description="Apply clock correction to extracted data files."
|
|
144
|
+
)
|
|
145
|
+
parser.add_argument('target_folder', help="Path to the folder containing the extracted data files")
|
|
146
|
+
parser.add_argument('--shiftDateSec', help="Float value, or name of param containing clock diff to apply at each timestamp.", default="/ParamClockRef$")
|
|
147
|
+
parser.add_argument('--shiftDateRegex', help="Filter which params/files to apply date shift", default="ref_.*::")
|
|
148
|
+
parser.add_argument('--shiftDateInverted', action='store_true', default=False, help="Inverse logic if your clock diff is based on the other clock")
|
|
149
|
+
parser.add_argument('--update', action='store_true', default=False, help="Update existing file if any, rather than deleting it. (useful to add manually a new param in a file)")
|
|
150
|
+
parser.add_argument('--shiftDateSrcFile', help="Name of file containing the shiftDateSec if it is a param name", default="ref_data.h5")
|
|
151
|
+
|
|
152
|
+
args = parser.parse_args()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
create_logger("apply_clock_correction")
|
|
156
|
+
|
|
157
|
+
monitorProgress=MonitorProgress(name="apply_clock_correction",progressCb=consoleRichProgressCb)
|
|
158
|
+
|
|
159
|
+
rst = apply_clock_correction(args.target_folder, args.shiftDateSec, args.shiftDateRegex, args.shiftDateSrcFile,
|
|
160
|
+
shiftDateInverted=args.shiftDateInverted,monitorProgress=monitorProgress, deleteIfExist=not args.update)
|
|
161
|
+
if not rst:
|
|
162
|
+
get_logger().error(f"failed to fix clock based on {args.shiftDateSec}.")
|
|
163
|
+
sys.exit(1)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
if __name__ == "__main__":
|
|
167
|
+
main()
|
testsuit/cli/data2db.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
|
|
2
|
+
import argparse,os,sys,json,logging
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from testsuit.datatools.datatoolbox import SUPPORTED_DATAFILE_EXTENSIONS
|
|
6
|
+
from testsuit.plugins import load_plugins
|
|
7
|
+
from testsuit.datatools.DataFileMgrs.AFileMgr import enable_pandas_display_helpers
|
|
8
|
+
from testsuit.misc.MonitorProgress import MonitorProgress,consoleRichProgressCb
|
|
9
|
+
from testsuit.datatools.data2db import create_data2db
|
|
10
|
+
from testsuit.misc.logger import create_logger,get_logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
create_logger("data2db")
|
|
14
|
+
|
|
15
|
+
## check if the given file is accessible
|
|
16
|
+
def isInputReadable(f):
|
|
17
|
+
if not os.access(f,os.R_OK):
|
|
18
|
+
get_logger().error(f"{f} does not exist or is not reachable")
|
|
19
|
+
sys.exit(1)
|
|
20
|
+
|
|
21
|
+
return f
|
|
22
|
+
|
|
23
|
+
# override the parsing error message using logger
|
|
24
|
+
class HelpParser(argparse.ArgumentParser):
|
|
25
|
+
def error(self, message):
|
|
26
|
+
get_logger().error("Input Arguments Error : "+message)
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
|
|
29
|
+
## the main function
|
|
30
|
+
def main():
|
|
31
|
+
# external formats (entry points, TESTSUIT_PLUGINS) show up in --extensions help
|
|
32
|
+
load_plugins()
|
|
33
|
+
enable_pandas_display_helpers()
|
|
34
|
+
parser = HelpParser(description=
|
|
35
|
+
"""Extract given parameters from data files or DB.
|
|
36
|
+
|
|
37
|
+
To extract into a db, you must provide a json file as follow:
|
|
38
|
+
{
|
|
39
|
+
"params" : ["No2"],
|
|
40
|
+
"indices" : [],
|
|
41
|
+
"db" : {
|
|
42
|
+
"dbtype" : "influxdb",
|
|
43
|
+
"url" : "http://influxdb:8086",
|
|
44
|
+
"org" : "_sandbox_",
|
|
45
|
+
"bucket" : "testBucket",
|
|
46
|
+
"measurement" :"testMeas",
|
|
47
|
+
"rename_params_re" : ".*([^/]+)$",
|
|
48
|
+
"dateUnit" : "s"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Return 1 if something went wrong, 0 otherwise
|
|
53
|
+
""",
|
|
54
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
|
55
|
+
parser.add_argument('sourceFolderOrFile', metavar="FolderOrFile", help="source folder or file where to scan data files",type=isInputReadable)
|
|
56
|
+
parser.add_argument('confJson', help="Conf for DB parameters. See up there of details about expected contents",type=isInputReadable)
|
|
57
|
+
parser.add_argument('-t','--token',help="DB password or token")
|
|
58
|
+
parser.add_argument("--test",action='store_true', default=False, help="Dry-run: does not actually inject data")
|
|
59
|
+
parser.add_argument('-d',"--debug",action='store_true', default=False, help="Show debug messages")
|
|
60
|
+
parser.add_argument('--extensions',type=lambda s: s.split(","),metavar='ext1,ext2,...',
|
|
61
|
+
help="File extensions to use as input (default: "+",".join(SUPPORTED_DATAFILE_EXTENSIONS)+")")
|
|
62
|
+
args = parser.parse_args()
|
|
63
|
+
|
|
64
|
+
if args.debug:
|
|
65
|
+
get_logger().setLevel(logging.DEBUG)
|
|
66
|
+
|
|
67
|
+
conf=None
|
|
68
|
+
with open(args.confJson) as f:
|
|
69
|
+
conf=json.load(f)
|
|
70
|
+
|
|
71
|
+
monitorProgress=MonitorProgress(progressCb=consoleRichProgressCb,name="data2db")
|
|
72
|
+
dbHandler = create_data2db(conf["db"]["dbtype"])
|
|
73
|
+
rst = dbHandler.data2db(args.sourceFolderOrFile,conf,args.extensions,token=args.token,dryRun=args.test,monitorProgress=monitorProgress,silent=True)
|
|
74
|
+
|
|
75
|
+
if rst == True:
|
|
76
|
+
sys.exit(0)
|
|
77
|
+
else:
|
|
78
|
+
sys.exit(1)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
main()
|
testsuit/cli/data2h5.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
|
|
2
|
+
import argparse
|
|
3
|
+
import sys,os,logging
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from testsuit.misc.logger import create_logger
|
|
7
|
+
from testsuit.datatools.DataFileMgrs.AFileMgr import enable_pandas_display_helpers, pretty_str
|
|
8
|
+
from testsuit.misc.logger import get_logger
|
|
9
|
+
from testsuit.misc.MonitorProgress import MonitorProgress,consoleRichProgressCb,consoleSilentProgressCb
|
|
10
|
+
|
|
11
|
+
from testsuit.datatools.datatoolbox import SUPPORTED_DATAFILE_EXTENSIONS
|
|
12
|
+
from testsuit.plugins import load_plugins
|
|
13
|
+
from testsuit.datatools.datatoolbox import loadDataframeFromFile,getDateParser
|
|
14
|
+
from testsuit.datatools.DataframeToHdf5 import DataframeToHdf5
|
|
15
|
+
|
|
16
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
17
|
+
sys.stderr.reconfigure(encoding='utf-8')
|
|
18
|
+
|
|
19
|
+
create_logger("data2h5")
|
|
20
|
+
|
|
21
|
+
## check if the given file is accessible
|
|
22
|
+
def isInputReadable(f):
|
|
23
|
+
if not os.access(f,os.R_OK):
|
|
24
|
+
get_logger().error(f"{f} does not exist or is not reachable")
|
|
25
|
+
sys.exit(1)
|
|
26
|
+
|
|
27
|
+
return f
|
|
28
|
+
|
|
29
|
+
# override the parsing error message using logger
|
|
30
|
+
class HelpParser(argparse.ArgumentParser):
|
|
31
|
+
def error(self, message):
|
|
32
|
+
get_logger().error("Input Arguments Error : "+message)
|
|
33
|
+
sys.exit(1)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def extract_to_hdf5(h5fileName,paramsDfList):
|
|
37
|
+
|
|
38
|
+
for df in paramsDfList:
|
|
39
|
+
get_logger().info("extracting params "+str(df.columns)+"")
|
|
40
|
+
DataframeToHdf5(h5fileName,os.path.basename(df.origin),df,comment="",chunk_size=True)
|
|
41
|
+
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
def data2h5(sourceFolderOrFile,paramRegexes,indices=None,targetFile=None,extensions=None,
|
|
45
|
+
excludeParamsRegex=None, minDate=None, maxDate=None, mergeParams=True,
|
|
46
|
+
shiftDateSec=None,shiftDateRegex=None, shiftDateInverted=None,
|
|
47
|
+
listOnly=False, silent=False,
|
|
48
|
+
monitorProgress=None):
|
|
49
|
+
"""Extract data into H5 file.
|
|
50
|
+
See __main__ args list for parameters list and usage."""
|
|
51
|
+
|
|
52
|
+
minDateSec=None
|
|
53
|
+
maxDateSec=None
|
|
54
|
+
if minDate:
|
|
55
|
+
convFunc=getDateParser(minDate)
|
|
56
|
+
minDateSec=convFunc(minDate).timestamp() if convFunc else minDateSec
|
|
57
|
+
if maxDate:
|
|
58
|
+
convFunc=getDateParser(maxDate)
|
|
59
|
+
maxDateSec=convFunc(maxDate).timestamp() if convFunc else maxDateSec
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def cbPrintName(df):
|
|
63
|
+
print(df.name)
|
|
64
|
+
|
|
65
|
+
def cbSaveDfAsH5File(df,monitorProgress=None):
|
|
66
|
+
|
|
67
|
+
if df is None:
|
|
68
|
+
get_logger().warning(f"skipping H5 writing of None param from {targetFile}")
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
monitorProgress.set_total_items(1)
|
|
72
|
+
monitorProgress.msg("writing H5 data for "+str(df.name)+"")
|
|
73
|
+
|
|
74
|
+
h5GroupName=os.path.basename(df.origin)
|
|
75
|
+
if len(h5GroupName)==0:
|
|
76
|
+
h5GroupName=os.path.basename(os.path.dirname(df.origin))
|
|
77
|
+
comment={}
|
|
78
|
+
comment["Comments"]="Generated by data2h5"
|
|
79
|
+
if minDate: comment["Min Date"]=minDate
|
|
80
|
+
if maxDate: comment["Max Date"]=maxDate
|
|
81
|
+
try:
|
|
82
|
+
DataframeToHdf5(targetFile,h5GroupName,df,comment=comment,chunk_size=True)
|
|
83
|
+
except Exception as e:
|
|
84
|
+
monitorProgress.msg(msg=f"while writing file '{targetFile}' for {df.name} : {str(e)}",msgSeverity="error")
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
monitorProgress.msg(msg=f"generated/updated file '{targetFile}' for {df.name}")
|
|
88
|
+
|
|
89
|
+
monitorProgress.complete_n(1)
|
|
90
|
+
return True
|
|
91
|
+
|
|
92
|
+
def cbprintDf(df,name=None,origin=None):
|
|
93
|
+
if df is not None:
|
|
94
|
+
print(pretty_str(df))
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
cbToUse = None
|
|
99
|
+
if listOnly:
|
|
100
|
+
cbToUse=cbPrintName
|
|
101
|
+
elif targetFile is None:
|
|
102
|
+
cbToUse=cbprintDf
|
|
103
|
+
else:
|
|
104
|
+
cbToUse=cbSaveDfAsH5File
|
|
105
|
+
|
|
106
|
+
dfListRst=loadDataframeFromFile(sourceFolderOrFile,paramRegexes,indices,extensions,
|
|
107
|
+
minDateSec=minDateSec,maxDateSec=maxDateSec,
|
|
108
|
+
excludeParamsRegex=excludeParamsRegex,mergeParams=mergeParams,
|
|
109
|
+
shiftDateSec=shiftDateSec,shiftDateRegex=shiftDateRegex,
|
|
110
|
+
shiftDateInverted=shiftDateInverted,
|
|
111
|
+
dryRun=False,monitorProgress=monitorProgress,
|
|
112
|
+
silent=silent,callback=cbToUse)
|
|
113
|
+
|
|
114
|
+
if len(dfListRst)==0:
|
|
115
|
+
return False, dfListRst
|
|
116
|
+
|
|
117
|
+
return True, dfListRst
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
## the main function
|
|
121
|
+
def main():
|
|
122
|
+
# external formats (entry points, TESTSUIT_PLUGINS) show up in --extensions help
|
|
123
|
+
load_plugins()
|
|
124
|
+
enable_pandas_display_helpers()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
parser = HelpParser(description=
|
|
128
|
+
"""Extract given parameters into an HDF5 file.
|
|
129
|
+
If target file is not provided, simply list matching fields found.
|
|
130
|
+
If params list is empty, list all param names found.
|
|
131
|
+
|
|
132
|
+
Return 1 if something went wrong, 0 otherwise
|
|
133
|
+
""",
|
|
134
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
|
135
|
+
parser.add_argument('sourceFolderOrFile', metavar="FolderOrFile", help="source folder or file where to scan data files",type=isInputReadable)
|
|
136
|
+
parser.add_argument('target', nargs='?',metavar="Target", help="Target of params contents: xxx.h5")
|
|
137
|
+
parser.add_argument('-l','--list',action='store_true', default=False, help="list all params found in given datasources" )
|
|
138
|
+
parser.add_argument('-p','--params',metavar='param1,param2,...', help="list of params (regexes) to extract into and HDF5 file. You can use python regex. Ex: 'param_02,my_.*4$'",default=".*")
|
|
139
|
+
parser.add_argument('--indices',metavar='paramIdx1,paramIdx2,...', help="use provided params as indices, 'auto' for trying to automatically identify one. Ex: 'param_02_timestamp,auto'")
|
|
140
|
+
parser.add_argument('-d',"--debug",action='store_true', default=False, help="Show debug messages")
|
|
141
|
+
parser.add_argument('-s',"--silent",action='store_true', default=False, help="Only show relevant output messages, no progress.")
|
|
142
|
+
parser.add_argument('--extensions',type=lambda s: s.split(","),metavar='ext1,ext2,...',
|
|
143
|
+
help="File extensions to use as input (default: "+",".join(SUPPORTED_DATAFILE_EXTENSIONS)+")")
|
|
144
|
+
parser.add_argument('--minDate', help="Minimal date of data to record in file")
|
|
145
|
+
parser.add_argument('--maxDate', help="Maximal date of data to record in file")
|
|
146
|
+
|
|
147
|
+
args = parser.parse_args()
|
|
148
|
+
|
|
149
|
+
if args.debug:
|
|
150
|
+
get_logger().setLevel(logging.DEBUG)
|
|
151
|
+
|
|
152
|
+
get_logger().debug(args.target)
|
|
153
|
+
|
|
154
|
+
monitorProgress=MonitorProgress(progressCb=consoleSilentProgressCb if args.silent else consoleRichProgressCb,
|
|
155
|
+
name="data2h5")
|
|
156
|
+
|
|
157
|
+
rst, dfList = data2h5(args.sourceFolderOrFile,args.params,args.indices,args.target,args.extensions,
|
|
158
|
+
args.minDate,args.maxDate,listOnly=args.list,silent=args.silent,
|
|
159
|
+
monitorProgress=monitorProgress)
|
|
160
|
+
|
|
161
|
+
if rst == True:
|
|
162
|
+
sys.exit(0)
|
|
163
|
+
else:
|
|
164
|
+
sys.exit(1)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
main()
|
testsuit/cli/datapack.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
|
|
2
|
+
import argparse
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from colorama import Fore, Style
|
|
7
|
+
|
|
8
|
+
from testsuit import misc
|
|
9
|
+
from testsuit.datatools.datapack.datapack_tools import datapack
|
|
10
|
+
|
|
11
|
+
## check if the given file is accessible
|
|
12
|
+
def _isInputReadable(f):
|
|
13
|
+
if not os.access(misc.files.expandPath(f),os.R_OK):
|
|
14
|
+
raise argparse.ArgumentTypeError(f"{misc.files.expandPath(f)} does not exist or is not reachable")
|
|
15
|
+
return f
|
|
16
|
+
|
|
17
|
+
# override the parsing error message using logger
|
|
18
|
+
class _HelpParser(argparse.ArgumentParser):
|
|
19
|
+
def error(self, message):
|
|
20
|
+
print(Fore.RED+"ERROR: Input Arguments Error : "+message+Style.RESET_ALL)
|
|
21
|
+
sys.exit(1)
|
|
22
|
+
|
|
23
|
+
## the main function
|
|
24
|
+
def main():
|
|
25
|
+
parser = _HelpParser(description=
|
|
26
|
+
"""Generate datapack from given test definition file.
|
|
27
|
+
|
|
28
|
+
Dictionaries listed in test definition are applied to the test definition file itself and also to pointed datapack config.
|
|
29
|
+
Dictionaries listed in datapack config are applied to datapack config itself, but *not* to test definition.
|
|
30
|
+
|
|
31
|
+
Return 1 if something went wrong, 0 otherwise
|
|
32
|
+
""",
|
|
33
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
|
34
|
+
parser.add_argument('testdef_file',nargs='?',default="datapack.xml",help="the YAML file containing datapack definition",type=_isInputReadable)
|
|
35
|
+
parser.add_argument('--nocheck',action='store_true',help="Ignore warnings and consistency checks (git tags etc.) for test procedure and Setup folders.")
|
|
36
|
+
args = parser.parse_args()
|
|
37
|
+
|
|
38
|
+
success = datapack(misc.files.expandPath(args.testdef_file),args.nocheck)
|
|
39
|
+
if success:
|
|
40
|
+
sys.exit(0)
|
|
41
|
+
else:
|
|
42
|
+
sys.exit(1)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
main()
|
testsuit/cli/evalfile.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
## @package evalfile
|
|
4
|
+
# Evaluate given file both for includes and keys replacement, based on given dictionaries.
|
|
5
|
+
# This script is based on 'evalkeys.py' and 'evalincludes.py' algorithms.
|
|
6
|
+
#
|
|
7
|
+
# Performs several replacement and includes as long as some replacements are done, which means that key value can be a reference to another key,
|
|
8
|
+
# and include can contain a key value.
|
|
9
|
+
#
|
|
10
|
+
# Result lines are displayed on STDOUT.
|
|
11
|
+
#
|
|
12
|
+
#
|
|
13
|
+
# USAGE: evalfile.py [-h] targetfile dico [dico ...]
|
|
14
|
+
#
|
|
15
|
+
#
|
|
16
|
+
import sys,os
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import os.path
|
|
22
|
+
|
|
23
|
+
from testsuit.datatools.datapack.evalfiles.evalfile import evalfile,finalizeLines
|
|
24
|
+
from testsuit.datatools.datapack.evalfiles import evalkeys
|
|
25
|
+
from testsuit.datatools.datapack.evalfiles import evalincludes
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## check if the given file is accessible
|
|
29
|
+
def _isInputReadable(f):
|
|
30
|
+
if not os.access(f,os.R_OK):
|
|
31
|
+
raise argparse.ArgumentTypeError(f"{f} does not exist or is not readable")
|
|
32
|
+
return f
|
|
33
|
+
|
|
34
|
+
# override the parsing error message using logger
|
|
35
|
+
class _HelpParser(argparse.ArgumentParser):
|
|
36
|
+
def error(self, message):
|
|
37
|
+
print("Input Arguments Error : "+message)
|
|
38
|
+
sys.exit(1)
|
|
39
|
+
|
|
40
|
+
## the main function
|
|
41
|
+
def main():
|
|
42
|
+
parser = _HelpParser(description=
|
|
43
|
+
"""Evaluate given file for includes and keys replacement, based on given dictionnaries.
|
|
44
|
+
This script is based on 'evalkeys' and 'evalincludes' algorithms.
|
|
45
|
+
|
|
46
|
+
Performs several processing cycles as long as some replacements are done.
|
|
47
|
+
This means that key value can be a reference to another key, and an include can contain a key value.
|
|
48
|
+
|
|
49
|
+
When option '-o' is used, generates also an HTML view of performed key replacements and a '.<file>.keys' file containing the list of used keys and their provenance.
|
|
50
|
+
Otherwise result lines are displayed on STDOUT by default.
|
|
51
|
+
|
|
52
|
+
Result is displayed in <stdout>.
|
|
53
|
+
|
|
54
|
+
Return:
|
|
55
|
+
3 a problem occured during processing
|
|
56
|
+
2 if circular inclusion or key reference detected,
|
|
57
|
+
1 if unreacheable included file or key referene (and no circular include or key ref. detected)
|
|
58
|
+
0 if okay""",
|
|
59
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
|
60
|
+
parser.add_argument('targetfile',help="the text file to be processed",type=_isInputReadable,metavar="targetfile")
|
|
61
|
+
parser.add_argument('dico',nargs='+', help="dictionary file(s) to be used for keys replacement, most important one first",type=_isInputReadable)
|
|
62
|
+
parser.add_argument('--output', '-o',help="output in the given file, generating also the '.<file>.html' and '.<file>.keys' associated files")
|
|
63
|
+
parser.add_argument('--partial','-p',action='store_true',help="Partial replace : ignore unknown keys, process only defined ones")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
args = parser.parse_args()
|
|
67
|
+
|
|
68
|
+
lines, usedkeys=evalfile(args.targetfile, args.dico, args.partial)
|
|
69
|
+
if not lines :
|
|
70
|
+
sys.exit(3)
|
|
71
|
+
|
|
72
|
+
finalizeLines(lines, usedkeys, args.partial, args.output)
|
|
73
|
+
|
|
74
|
+
if args.output is not None:
|
|
75
|
+
print("generated "+str(args.output)+" and associated 'keys' and 'html' files.")
|
|
76
|
+
|
|
77
|
+
if evalkeys.nbInfinateRecursion>0 or evalincludes.nbCircularRecursions>0:
|
|
78
|
+
sys.exit(2)
|
|
79
|
+
if evalincludes.nbNotFoundIncludes>0:
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
if not args.partial and evalkeys.nbUndefined>0:
|
|
82
|
+
sys.exit(1)
|
|
83
|
+
|
|
84
|
+
sys.exit(0)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
main()
|