import argparse import re from collections import namedtuple from pathlib import Path TIME_UNIT_FACTORS = { "s": 1, "m": 60, "h": 60 * 60, } ParsedDuration = namedtuple("ParsedDuration", ["value", "unit", "seconds"]) Settings = namedtuple( "Settings", ["duration", "interval", "directory", "port"] ) def parse_duration(raw_value, default_unit="m"): """' parses a duration like 10sec, 5min or 2h :params raw_value: string value to parse :params default_unit: default unit to use, if no (known) unit is present :returns: ParsedTime named tuple or None """ match = re.match(r"(?P\d+)(?P.*)", raw_value) if not match: return None value = int(match["value"]) unit = _normalize_time_unit(match["unit"], default_unit=default_unit) factor = TIME_UNIT_FACTORS[unit] return ParsedDuration(value, unit, value * factor) def parse_cli_arguments(): """ parses command line interface arguments """ parser = argparse.ArgumentParser( description="Make time series measurements with a Sartorius scale.", epilog=( "Times can be specified as 10s, 10m, or 10h" "for seconds, minutes or hours respectively." "A relative directory path starts at the Desktop." ), ) parser.add_argument("port", nargs="?", default="COM4", metavar="COMPORT") parser.add_argument("-d", "--duration", nargs="?", default="30m") parser.add_argument("-i", "--interval", nargs="?", default="10s") parser.add_argument("-o", "--output", nargs="?", metavar="DIRECTORY") raw_arguments = parser.parse_args() return _normalize_cli_arguments(raw_arguments) def parse_gui_arguments(): from gooey import GooeyParser """ parses command line interface arguments """ parser = GooeyParser( description="Make time series measurements with a Sartorius scale.", epilog=( "Times can be specified as 10s, 10m, or 10h" "for seconds, minutes or hours respectively." "A relative directory path starts at the Desktop." ), ) parser.add_argument("port", nargs="?", default="COM4", metavar="COMPORT", help="Serial Port that connects the Scale, defaults to 'COM4'") parser.add_argument("-d", "--duration", nargs="?", default="30m", help="Measurement duration, e.g. 10s, 30m, 2h") parser.add_argument("-i", "--interval", nargs="?", default="10s", help="Measurement interval, e.g. 5s, 1m, 1h") parser.add_argument("-o", "--output", nargs="?", metavar="DIRECTORY", widget='DirChooser', help="Select Output Directory, defaults to 'Desktop'") raw_arguments = parser.parse_args() return _normalize_cli_arguments(raw_arguments) # helper functions def _normalize_time_unit(raw_unit, default_unit): """ checks if a known time unit is present, else uses the default unit """ known_units = "hms" found = [unit for unit in known_units if unit in raw_unit.lower()] unit = found[0] if found else default_unit return unit def _normalize_cli_arguments(raw_arguments): """ transforms cli arguments into a suitable form """ # time related stuff duration = parse_duration(raw_arguments.duration, default_unit="m") interval = parse_duration(raw_arguments.interval, default_unit="s") # directory stuff dir_path = _check_output_directory_path(raw_arguments.output) return Settings(duration, interval, dir_path, raw_arguments.port) def _check_output_directory_path(raw_path): """ returns the absolue path of the output directory The desktop path (~/Desktop) is considered the default directory. If a relative path is provided, it is considered relative to the desktop. """ # the desktop path is the default output directory out_path = desktop_path = Path.home() / "Desktop" # if a relative path is provided, make it relative to the desktop if raw_path: out_path = Path(raw_path) if not out_path.is_absolute(): out_path = desktop_path / out_path out_path = out_path.resolve() # if the provided path is something strange like an existing file, # use the default path (Desktop) if out_path.exists() and not out_path.is_dir(): out_path = desktop_path return out_path