diff --git a/acestatpy/Testing/ACEstatTest.py b/acestatpy/Testing/ACEstatTest.py index 050b9ae..f511fc8 100644 --- a/acestatpy/Testing/ACEstatTest.py +++ b/acestatpy/Testing/ACEstatTest.py @@ -21,220 +21,251 @@ import csv from datetime import datetime from os import path as _path, makedirs +from pathvalidate import sanitize_filename from re import sub from time import time from itertools import zip_longest # Local from . import Tests + class ACEstatTest(object): - id = None - parameters = None - results = None - __startTime = None - __errors = None - __duration = None - - def __init__(self, id, parameters): - self.id = id - if not isinstance(parameters, TestParameters): - self.parameters = TestParameters(id, parameters) - else: - self.parameters = parameters - self.results = TestResults(id) - self.__errors = [] - self.__startTime = time() - - @property - def info(self): - return Tests[self.id] - - @property - def startTime(self): - return self.__startTime - - def updateStartTime(self): - self.__startTime = time() - - @property - def errors(self): - return self.__errors - - def estimateDuration(self): - if not self.info.timing: - return None - if self.__duration is not None: - return self.__duration - def repVars(m): - return self.parameters[m.group(1)] - try: - self.__duration = eval(sub(r"\[(\w+)\]", repVars, self.info.timing)) - except: - return None - return self.__duration - - def estimateRemaining(self): - dur = self.estimateDuration() - if dur is None: - return None - return max(0, dur - (time() - self.__startTime)) - - def addError(self, err): - self.__errors.append(err) - - def export(self, path=None): - dname = "{0}_{1}.csv".format(datetime.fromtimestamp(self.__startTime).strftime('%Y%m%d-%H%M%S'), self.info.name.replace(' ', '-')) - if not path: - path = _path.join(".", dname) - elif _path.isdir(path): - path = _path.join(path, dname) + id = None + parameters = None + results = None + __startTime = None + __errors = None + __duration = None + + def __init__(self, id, parameters): + self.id = id + if not isinstance(parameters, TestParameters): + self.parameters = TestParameters(id, parameters) + else: + self.parameters = parameters + self.results = TestResults(id) + self.__errors = [] + self.__startTime = time() + + @property + def info(self): + return Tests[self.id] + + @property + def startTime(self): + return self.__startTime + + def updateStartTime(self): + self.__startTime = time() + + @property + def errors(self): + return self.__errors + + def estimateDuration(self): + if not self.info.timing: + return None + if self.__duration is not None: + return self.__duration + + def repVars(m): + return self.parameters[m.group(1)] + try: + self.__duration = eval(sub(r"\[(\w+)\]", repVars, self.info.timing)) + except: + return None + return self.__duration + + def estimateRemaining(self): + dur = self.estimateDuration() + if dur is None: + return None + return max(0, dur - (time() - self.__startTime)) + + def addError(self, err): + self.__errors.append(err) + + def generateFilename(self): + return sanitize_filename("{0}_{1}.csv".format(datetime.fromtimestamp(self.__startTime).strftime( + '%Y%m%d-%H%M%S'), self.info.name.replace(' ', '-'))) + + def export(self, path=None): + dname = self.generateFilename() + if not path: + path = _path.join(".", dname) + elif _path.isdir(path): + path = _path.join(path, dname) + else: + parts = _path.split(path) + if parts[0] and not _path.isdir(parts[0]): + makedirs(parts[0], exist_ok=True) + if not parts[1]: + path = _path.join(path, dname) + if not path.endswith(".csv"): + path = f"{path}.csv" + return self.__export(path) + + def __export(self, path): + with open(path, "w", newline='\n', encoding='utf-8') as f: + writer = csv.writer(f) + # Write parameters if they exist + data = [] + for p in self.parameters: + param = self.info.parameters[p] + col = "{0}".format(param.name) + if hasattr(param, "units"): + col += " ({0})".format(param.units) + if self.info.parameters[p].type == "select": + val = param.options[self.parameters[p]] else: - parts = _path.split(path) - if parts[0] and not _path.isdir(parts[0]): - makedirs(parts[0], exist_ok=True) - if not parts[1]: - path = _path.join(path, dname) - if not path.endswith(".csv"): - path = f"{path}.csv" - return self.__export(path) - - def __export(self, path): - with open(path, "w", newline='\n', encoding='utf-8') as f: - writer = csv.writer(f) - # Write parameters if they exist - data = [] - for p in self.parameters: - param = self.info.parameters[p] - col = "{0}".format(param.name) - if hasattr(param, "units"): - col += " ({0})".format(param.units) - if self.info.parameters[p].type == "select": - val = param.options[self.parameters[p]] - else: - val = self.parameters[p] - if not isinstance(val, list): - val = [val] - data.append([col, val]) - if len(data): - writer.writerow([c[0] for c in data]) - for values in zip_longest(*[d[1] for d in data]): - writer.writerow(values) - writer.writerow([]) - # Write outputs - for o in self.info.outputs: - data = [] - for field in self.info.outputs[o].fields: - label = getattr(field, "label", "") - col = "{0}.{1}".format(o, label) - if hasattr(field, "units"): - col += " ({0})".format(field.units) - val = self.results.get(o, {}).get(label, []) - if not isinstance(val, list): - val = [val] - data.append([col, val]) - writer.writerow([c[0] for c in data]) - for values in zip_longest(*[d[1] for d in data]): - writer.writerow(values) - writer.writerow([]) - return path + val = self.parameters[p] + if not isinstance(val, list): + val = [val] + data.append([col, val]) + if len(data): + writer.writerow([c[0] for c in data]) + for values in zip_longest(*[d[1] for d in data]): + writer.writerow(values) + writer.writerow([]) + # Write outputs + for o in self.info.outputs: + data = [] + for field in self.info.outputs[o].fields: + label = getattr(field, "label", "") + col = "{0}.{1}".format(o, label) + if hasattr(field, "units"): + col += " ({0})".format(field.units) + val = self.results.get(o, {}).get(label, []) + if not isinstance(val, list): + val = [val] + data.append([col, val]) + writer.writerow([c[0] for c in data]) + for values in zip_longest(*[d[1] for d in data]): + writer.writerow(values) + writer.writerow([]) + return path class ParameterError(Exception): - def __init__(self, id, message="Invalid parameter"): - self.id = id - self.message = message - super().__init__(self.message) + def __init__(self, id, message="Invalid parameter"): + self.id = id + self.message = message + super().__init__(self.message) class TestParameters(object): - def __init__(self, id, parameters=None): - # Validate parameters - if id not in Tests: - raise ParameterError(id, "Invalid test ID") - elif not isinstance(parameters, dict): - raise Exception("parameters must be instance of dict") - for item in Tests.get(id).parameters.items(): - if not parameters.get(item[0], False): - raise ParameterError(id, "Missing parameter") - elif item[1].type == "select": - if not parameters[item[0]] in item[1].options: - raise ParameterError(item[0], "Invalid select option") - elif item[1].type == "int": - try: - val = int(parameters.get(item[0])) - if val < item[1].min or val > item[1].max: - raise ParameterError(item[0], "Value outside valid range") - except ParameterError as e: - raise e - except Exception as e: - raise ParameterError(item[0], "Expected int") - - self.id = id - self.__parameters = parameters - - def info(self, key=None): - params = Tests.get(self.id).parameters - if key is None: - return params - elif key not in params: - raise Exception(f"{key} not in parameters") - return params[key] - - def get(self, key, *args, **kwargs): - if "default" in kwargs: - return self.__parameters.get(key, kwargs.get("default")) - elif len(args): - return self.__parameters.get(key, args[0]) - return self.__parameters.get(key) - - def getFormatted(self, key): - val = self.get(key) - param = Tests.get(self.id).parameters.get(key) - negative = False - if getattr(param, "signed", False): - negative = int(val) < 0 - val = str(abs(int(val))) - if hasattr(param, "lpad"): - val = val.rjust(param.lpad.width, param.lpad.char) - if hasattr(param, "rpad"): - val = val.ljust(param.rpad.width, param.rpad.char) - if getattr(param, "signed", False): - if negative: - val = f"-{val}" - else: - val = f"+{val}" - return val - - def __contains__(self, key): - return key in self.__parameters - - def __getitem__(self, key): - return self.__parameters.get(key, None) - - def __iter__(self): - return self.__parameters.__iter__() - - def export(self, path): - Tests.saveCustomPreset(self.id, self.__parameters, path) + def __init__(self, id, parameters=None): + # Validate parameters + if id not in Tests: + raise ParameterError(id, "Invalid test ID") + elif not isinstance(parameters, dict): + raise Exception("parameters must be instance of dict") + for item in Tests.get(id).parameters.items(): + if item[1].type == "static": + parameters[item[0]] = item[1].value + continue + elif not parameters.get(item[0], False): + raise ParameterError(id, f"Missing parameter {item[0]}") + elif item[1].type == "select": + if not parameters[item[0]] in item[1].options: + raise ParameterError(item[0], "Invalid select option") + continue + elif item[1].type == "int": + try: + val = int(parameters.get(item[0])) + except Exception as e: + raise ParameterError(item[0], "Expected int") + elif item[1].type == "float": + try: + val = float(parameters.get(item[0])) + except Exception as e: + raise ParameterError(item[0], "Expected float") + elif item[1].type == "number": + try: + val = float(parameters.get(item[0])) + if ((item[1].float_min and val < item[1].float_min) or + (item[1].float_max and val > item[1].float_max)): + parameters[item[0]] = f"{int(val)}" + val = float(int(val)) + except Exception as e: + raise ParameterError(item[0], "Expected number") + if val < item[1].min or val > item[1].max: + raise ParameterError(item[0], "Value outside valid range") + + self.id = id + self.__parameters = parameters + + def info(self, key=None): + params = Tests.get(self.id).parameters + if key is None: + return params + elif key not in params: + raise Exception(f"{key} not in parameters") + return params[key] + + def get(self, key, *args, **kwargs): + if "default" in kwargs: + return self.__parameters.get(key, kwargs.get("default")) + elif len(args): + return self.__parameters.get(key, args[0]) + return self.__parameters.get(key) + + def getFormatted(self, key): + val = self.get(key) + param = Tests.get(self.id).parameters.get(key) + negative = False + dec = "" + if getattr(param, "signed", False): + negative = float(val) < 0 + if param.type == "float": + val = f"{abs(float(val)):.{param.precision}f}" + val, dec = val.split(".") + dec = "." + dec + elif param.type == "int": + val = str(abs(int(val))) + if hasattr(param, "lpad"): + width = param.lpad.width + val = val.rjust(width, param.lpad.char) + if hasattr(param, "rpad"): + width = parame.rpad.width + val = val.ljust(width, param.rpad.char) + if getattr(param, "signed", False): + if negative: + val = f"-{val}" + else: + val = f"+{val}" + return val + dec + + def __contains__(self, key): + return key in self.__parameters + + def __getitem__(self, key): + return self.__parameters.get(key, None) + + def __iter__(self): + return self.__parameters.__iter__() + + def export(self, path): + Tests.saveCustomPreset(self.id, self.__parameters, path) class TestResults(object): - def __init__(self, id): - if id not in Tests: - raise Exception("Invalid test ID") - self.__results = {} + def __init__(self, id): + if id not in Tests: + raise Exception("Invalid test ID") + self.__results = {} - def get(self, key, default=None): - return self.__results.get(key, default) + def get(self, key, default=None): + return self.__results.get(key, default) - def __contains__(self, key): - return key in self.__results + def __contains__(self, key): + return key in self.__results - def __getitem__(self, key): - return self.__results.get(key, None) + def __getitem__(self, key): + return self.__results.get(key, None) - def __setitem__(self, key, value): - self.__results[key] = value + def __setitem__(self, key, value): + self.__results[key] = value - def __iter__(self): - return self.__results.__iter__() + def __iter__(self): + return self.__results.__iter__() diff --git a/acestatpy/Testing/TestParser.py b/acestatpy/Testing/TestParser.py index ecc55e0..8a6a843 100644 --- a/acestatpy/Testing/TestParser.py +++ b/acestatpy/Testing/TestParser.py @@ -50,286 +50,309 @@ class _Tests(OrderedDict): - # __definitions = None - __version = None - - def __init__(self, fpath=None): - if fpath is None: - fpath = resource_stream(FILENAME) - self.importTests(fpath, "") - - @property - def VERSION(self): - return self.__version - - def loadCustomPreset(self, fpath): - with open(fpath, "r") as presetFile: - data = load(presetFile) - preset = Object() - preset.name = f"*{splitext(basename(fpath))[0]}" - preset.parameters = data["parameters"] - if data["id"] not in self: - raise Exception(f"No test with ID: {data['id']}.") - self[data["id"]].presets[preset.name] = preset - - def saveCustomPreset(self, id, params, fpath): - if not fpath.endswith(".json"): - fpath = f"{fpath}.json" - with open(fpath, 'w') as exportFile: - dump({ - "id": id, - "parameters": params - }, exportFile) - self.loadCustomPreset(fpath) - - def importTests(self, fpath, prefix="*"): - tests, version = parseTests(fpath) - if self.__version is not None and version != self.__version: - self.clear() - self.__version = version - for t in tests: - t.name = f"{prefix}{t.name}" - self[t.id] = t - - def copyDefaultTests(self, fpath): - # Write the packed XML file to disk - if not fpath.endswith(".xml"): - fpath = f"{fpath}.xml" - with open(fpath, "wb") as xml: - xml.write(resource_stream(FILENAME).read()) + # __definitions = None + __version = None + + def __init__(self, fpath=None): + if fpath is None: + fpath = resource_stream(FILENAME) + self.importTests(fpath, "") + + @property + def VERSION(self): + return self.__version + + def loadCustomPreset(self, fpath): + with open(fpath, "r") as presetFile: + data = load(presetFile) + preset = Object() + preset.name = f"*{splitext(basename(fpath))[0]}" + preset.parameters = data["parameters"] + if data["id"] not in self: + raise Exception(f"No test with ID: {data['id']}.") + self[data["id"]].presets[preset.name] = preset + + def saveCustomPreset(self, id, params, fpath): + if not fpath.endswith(".json"): + fpath = f"{fpath}.json" + with open(fpath, 'w') as exportFile: + dump({ + "id": id, + "parameters": params + }, exportFile) + self.loadCustomPreset(fpath) + + def importTests(self, fpath, prefix="*"): + tests, version = parseTests(fpath) + if self.__version is not None and version != self.__version: + self.clear() + self.__version = version + for t in tests: + t.name = f"{prefix}{t.name}" + self[t.id] = t + + def copyDefaultTests(self, fpath): + # Write the packed XML file to disk + if not fpath.endswith(".xml"): + fpath = f"{fpath}.xml" + with open(fpath, "wb") as xml: + xml.write(resource_stream(FILENAME).read()) class Iterator: - def __init__(self, iterator): - self.iterator = iterator - self.current = None + def __init__(self, iterator): + self.iterator = iterator + self.current = None - def __next__(self): - try: - self.current = next(self.iterator) - except StopIteration: - self.current = None - finally: - return self.current + def __next__(self): + try: + self.current = next(self.iterator) + except StopIteration: + self.current = None + finally: + return self.current class VersionError(Exception): - def __init__(self, version): - if not version: - version = "old" - self.version = version - self.message = "Expected version {0}, found {1}".format( - EXPECTED_VERSION, version) - super().__init__(self.message) + def __init__(self, version): + if not version: + version = "old" + self.version = version + self.message = "Expected version {0}, found {1}".format( + EXPECTED_VERSION, version) + super().__init__(self.message) def parseParameter(context): - event, xml = context.current - param = Object() - param.name = xml.get("name") - param.id = xml.get("id") - param.type = xml.get("type") - if param.type == "select": - param.options = OrderedDict() - elif param.type == "int": - param.signed = bool(xml.get("signed", False)) - if xml.get("min", False): - param.min = int(xml.get("min")) - if xml.get("max", False): - param.max = int(xml.get("max")) - if xml.get("lpad", False): - lpad = xml.get("lpad").split("|") - param.lpad = Object() - param.lpad.char = lpad[1] - param.lpad.width = int(lpad[0]) - if xml.get("rpad", False): - rpad = xml.get("rpad").split("|") - param.rpad = Object() - param.rpad.char = rpad[1] - param.rpad.width = int(rpad[0]) - if xml.get("units", False): - param.units = xml.get("units") - while not (event == "end" and xml.tag == "parameter"): - if event == "end" and xml.tag == "option": - if xml.get("value", False): - param.options[xml.get("value")] = xml.text - else: - val = xml.text - param.options[val] = val - event, xml = next(context) - return param + event, xml = context.current + param = Object() + param.name = xml.get("name") + param.id = xml.get("id") + param.type = xml.get("type") + + if param.type == "select": + param.options = OrderedDict() + elif param.type == "int": + param.signed = bool(xml.get("signed", False)) + if xml.get("min", False): + param.min = int(xml.get("min")) + if xml.get("max", False): + param.max = int(xml.get("max")) + elif param.type == "float": + param.signed = bool(xml.get("signed", False)) + param.precision = int(xml.get("precision", 1)) # default 1 + if xml.get("min", False): + param.min = float(xml.get("min")) + if xml.get("max", False): + param.max = float(xml.get("max")) + elif param.type == "number": + param.signed = bool(xml.get("signed", False)) + param.precision = int(xml.get("precision", 1)) # default 1 + if xml.get("min", False): + param.min = float(xml.get("min")) + if xml.get("max", False): + param.max = float(xml.get("max")) + if xml.get("float-min", False): + param.float_min = float(xml.get("float-min")) + if xml.get("float-max", False): + param.float_max = float(xml.get("float-max")) + elif param.type == "static": + param.value = xml.get("value") + + if xml.get("lpad", False): + lpad = xml.get("lpad").split("|") + param.lpad = Object() + param.lpad.char = lpad[1] + param.lpad.width = int(lpad[0]) + if xml.get("rpad", False): + rpad = xml.get("rpad").split("|") + param.rpad = Object() + param.rpad.char = rpad[1] + param.rpad.width = int(rpad[0]) + + if xml.get("units", False): + param.units = xml.get("units") + while not (event == "end" and xml.tag == "parameter"): + if event == "end" and param.type == "select" and xml.tag == "option": + if xml.get("value", False): + param.options[xml.get("value")] = xml.text + else: + val = xml.text + param.options[val] = val + event, xml = next(context) + return param def parseParameters(context): - event, xml = context.current - # Use an array because order matters - parameters = [] - parameters = OrderedDict() - while not (event == "end" and xml.tag == "parameters"): - if event == "start" and xml.tag == "parameter": - parameters[xml.get("id")] = parseParameter(context) - event, xml = next(context) - return parameters + event, xml = context.current + # Use an array because order matters + parameters = [] + parameters = OrderedDict() + while not (event == "end" and xml.tag == "parameters"): + if event == "start" and xml.tag == "parameter": + parameters[xml.get("id")] = parseParameter(context) + event, xml = next(context) + return parameters def parseOutput(context): - event, xml = context.current - output = Object() - output.type = "field" - output.fields = [] - if xml.get("type", False): - output.type = xml.get("type") - if output.type == "list" and xml.get("separator", False): - output.separator = xml.get("separator") - elif output.type == "matrix": - if xml.get("col-separator", False): - output.col_separator = xml.get("col-separator") - if xml.get("row-separator", False): - output.row_separator = xml.get("row-separator") - while not (event == "end" and xml.tag == "output"): - if event == "start" and xml.tag == "field": - field = Object() - field.label = xml.get("label") - field.type = xml.get("type") - if xml.get("units", False): - field.units = xml.get("units") - output.fields.append(field) - event, xml = next(context) - return output + event, xml = context.current + output = Object() + output.type = "field" + output.fields = [] + if xml.get("type", False): + output.type = xml.get("type") + if output.type == "list" and xml.get("separator", False): + output.separator = xml.get("separator") + elif output.type == "matrix": + if xml.get("col-separator", False): + output.col_separator = xml.get("col-separator") + if xml.get("row-separator", False): + output.row_separator = xml.get("row-separator") + while not (event == "end" and xml.tag == "output"): + if event == "start" and xml.tag == "field": + field = Object() + field.label = xml.get("label") + field.type = xml.get("type") + if xml.get("units", False): + field.units = xml.get("units") + output.fields.append(field) + event, xml = next(context) + return output def parseOutputs(context): - event, xml = context.current - outputs = OrderedDict() - while not (event == "end" and xml.tag == "outputs"): - if event == "start" and xml.tag == "output": - outputs[xml.get("id")] = parseOutput(context) - event, xml = next(context) - return outputs + event, xml = context.current + outputs = OrderedDict() + while not (event == "end" and xml.tag == "outputs"): + if event == "start" and xml.tag == "output": + outputs[xml.get("id")] = parseOutput(context) + event, xml = next(context) + return outputs def parseSeries(context): - event, xml = context.current - series = Object() - series.name = xml.get("name") - while not (event == "end" and xml.tag == "series"): - if event == "end": - axis = Object() - axis.output = xml.get("output") - axis.field = xml.get("field") - setattr(series, xml.tag, axis) - event, xml = next(context) - return series + event, xml = context.current + series = Object() + series.name = xml.get("name") + while not (event == "end" and xml.tag == "series"): + if event == "end": + axis = Object() + axis.output = xml.get("output") + axis.field = xml.get("field") + setattr(series, xml.tag, axis) + event, xml = next(context) + return series def parsePlot(context): - event, xml = context.current - plot = Object() - plot.title = xml.get("title") - plot.x_label = xml.get("x-label") - plot.y_label = xml.get("y-label") - plot.series = [] - while not (event == "end" and xml.tag == "plot"): - if event == "start" and xml.tag == "series": - plot.series.append(parseSeries(context)) - event, xml = next(context) - return plot + event, xml = context.current + plot = Object() + plot.title = xml.get("title") + plot.x_label = xml.get("x-label") + plot.y_label = xml.get("y-label") + plot.series = [] + while not (event == "end" and xml.tag == "plot"): + if event == "start" and xml.tag == "series": + plot.series.append(parseSeries(context)) + event, xml = next(context) + return plot def parsePlots(context): - event, xml = context.current - plots = OrderedDict() - while not (event == "end" and xml.tag == "plots"): - if event == "start" and xml.tag == "plot": - plot = parsePlot(context) - plots[plot.title] = plot - event, xml = next(context) - return plots + event, xml = context.current + plots = OrderedDict() + while not (event == "end" and xml.tag == "plots"): + if event == "start" and xml.tag == "plot": + plot = parsePlot(context) + plots[plot.title] = plot + event, xml = next(context) + return plots def parsePreset(context): - event, xml = context.current - preset = Object() - preset.name = xml.get("name") - parameters = {} - while not (event == "end" and xml.tag == "preset"): - if event == "start" and xml.tag == "parameter": - parameters[xml.get("id")] = xml.get("value") - event, xml = next(context) - preset.parameters = parameters - return preset + event, xml = context.current + preset = Object() + preset.name = xml.get("name") + parameters = {} + while not (event == "end" and xml.tag == "preset"): + if event == "start" and xml.tag == "parameter": + parameters[xml.get("id")] = xml.get("value") + event, xml = next(context) + preset.parameters = parameters + return preset def parsePresets(context): - event, xml = context.current - presets = OrderedDict() - while not (event == "end" and xml.tag == "presets"): - if event == "start" and xml.tag == "preset": - preset = parsePreset(context) - presets[preset.name] = preset - event, xml = next(context) - return presets + event, xml = context.current + presets = OrderedDict() + while not (event == "end" and xml.tag == "presets"): + if event == "start" and xml.tag == "preset": + preset = parsePreset(context) + presets[preset.name] = preset + event, xml = next(context) + return presets def parseTest(context): - event, xml = context.current - test = Object() - test.name = xml.get("name") - test.description = "" - test.value = xml.get("value") - test.id = xml.get("id") - test.timing = None - test.parameters = [] - test.outputs = {} - test.plots = {} - test.presets = [] - while not (event == "end" and xml.tag == "test"): - if event == "start": - if xml.tag == "parameters": - test.parameters = parseParameters(context) - elif xml.tag == "outputs": - test.outputs = parseOutputs(context) - elif xml.tag == "plots": - test.plots = parsePlots(context) - elif xml.tag == "presets": - test.presets = parsePresets(context) - elif xml.tag == "timing": - test.timing = xml.get("equation") - elif event == "end": - if xml.tag == "description": - test.description = xml.text or "" - event, xml = next(context) - xml.clear() - return test + event, xml = context.current + test = Object() + test.name = xml.get("name") + test.description = "" + test.value = xml.get("value") + test.id = xml.get("id") + test.timing = None + test.parameters = [] + test.outputs = {} + test.plots = {} + test.presets = [] + while not (event == "end" and xml.tag == "test"): + if event == "start": + if xml.tag == "parameters": + test.parameters = parseParameters(context) + elif xml.tag == "outputs": + test.outputs = parseOutputs(context) + elif xml.tag == "plots": + test.plots = parsePlots(context) + elif xml.tag == "presets": + test.presets = parsePresets(context) + elif xml.tag == "timing": + test.timing = xml.get("equation") + elif event == "end": + if xml.tag == "description": + test.description = xml.text or "" + event, xml = next(context) + xml.clear() + return test def parseTests(xmlDoc): - results = [] - version = None - # results = _Tests() - context = iterparse(xmlDoc, events=("start", "end")) - - # turn it into an iterator - context = Iterator(context) - - # get the root element - event, xml = next(context) - root = xml.tag - - while not (event == "end" and xml.tag == root): - if event == "start" and xml.tag == "techniques": - version = xml.get("version") - elif event == "start" and xml.tag == "technique": - technique = xml.get("name") - while not (event == "end" and xml.tag == "technique"): - if event == "start" and xml.tag == "test": - test = parseTest(context) - test.technique = technique - results.append(test) - event, xml = next(context) - xml.clear() + results = [] + version = None + # results = _Tests() + context = iterparse(xmlDoc, events=("start", "end")) + + # turn it into an iterator + context = Iterator(context) + + # get the root element + event, xml = next(context) + root = xml.tag + + while not (event == "end" and xml.tag == root): + if event == "start" and xml.tag == "techniques": + version = xml.get("version") + elif event == "start" and xml.tag == "technique": + technique = xml.get("name") + while not (event == "end" and xml.tag == "technique"): + if event == "start" and xml.tag == "test" and xml.get("enabled", "true").lower() == "true": + test = parseTest(context) + test.technique = technique + results.append(test) event, xml = next(context) - return results, version + xml.clear() + event, xml = next(context) + return results, version Tests = _Tests() diff --git a/acestatpy/resources/Tests.xml b/acestatpy/resources/Tests.xml index 0d2c402..e28755a 100644 --- a/acestatpy/resources/Tests.xml +++ b/acestatpy/resources/Tests.xml @@ -1,232 +1,419 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/requirements.txt b/requirements.txt index 9155361..ab71d23 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ PyDispatcher==2.0.5 pyserial==3.5 +pathvalidate==3.0.0 ## Android requirements: # usbserial4a==0.3.0 # usb4a==0.2.0 diff --git a/setup.py b/setup.py index d41558a..16c7a88 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages -VERSION = '2022.4.28.1' +VERSION = '2023.7.5.1' DESCRIPTION = 'A Python library used for communicating with the ACEStat.' setup( @@ -15,7 +15,8 @@ python_requires=">=3.6,<4", install_requires=[ "PyDispatcher==2.0.5", - "pyserial==3.5" + "pyserial==3.5", + "pathvalidate==3.0.0" ], packages=find_packages(), url="",