Commit 354524de authored by Joel Collins's avatar Joel Collins
Browse files

Jsonify lens-shading as lists

parent 756f18d8
Loading
Loading
Loading
Loading
+30 −51
Original line number Diff line number Diff line
@@ -4,8 +4,10 @@ import errno
import logging
import shutil
import copy
import numpy as np
from fractions import Fraction
from collections import abc

from .utilities import recursively_apply

"""
Attributes:
@@ -18,7 +20,7 @@ Attributes:
# HANDLE THE DEFAULT CONFIGURATION FILE

HERE = os.path.abspath(os.path.dirname(__file__))
DEFAULT_CONFIG_PATH = os.path.join(HERE, 'microscope_settings.default.yaml')
DEFAULT_CONFIG_PATH = os.path.join(HERE, "microscope_settings.default.yaml")

USER_CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".openflexure")
USER_CONFIG_FILE = os.path.join(USER_CONFIG_DIR, "microscope_settings.yaml")
@@ -26,7 +28,7 @@ USER_CONFIG_FILE = os.path.join(USER_CONFIG_DIR, "microscope_settings.yaml")
USER_CONFIG_FILE_OLD = os.path.join(USER_CONFIG_DIR, "microscoperc.yaml")

# Load the default config
with open(DEFAULT_CONFIG_PATH, 'r') as default_rc:
with open(DEFAULT_CONFIG_PATH, "r") as default_rc:
    DEFAULT_CONFIG = default_rc.read()

# HANDLE CUSTOM YAML PARSING
@@ -36,76 +38,51 @@ def construct_yaml_bool(self, node):
    YAML otherwise caused grief for picamera properties where 'off' is a valid string value.
    """
    override_bool_values = {
            'yes':      True,
            'no':       False,
            'true':     True,
            'false':    False,
            'on':       'on',
            'off':      'off',
        "yes": True,
        "no": False,
        "true": True,
        "false": False,
        "on": "on",
        "off": "off",
    }
    value = self.construct_scalar(node)
    return override_bool_values[value.lower()]


yaml.Loader.add_constructor(u'tag:yaml.org,2002:bool', construct_yaml_bool)
yaml.Loader.add_constructor(u"tag:yaml.org,2002:bool", construct_yaml_bool)


# HANDLE CONVERTING ARBITRARY CONFIG TO JSON-SAFE JSON


def convert_type_to_json_safe(v):
    """Make an individual attribute JSON-safe"""
    if isinstance(v, Fraction):
        return float(v)
    elif isinstance(v, np.ndarray):
        return v.tolist()
    else:
        return v


def recursively_apply(data, func):
    """
    Recursively apply a function to a dictionary, list, array, or tuple

    Args:
        data: Input iterable data
        func: Function to apply to all non-iterable values
    """
    # If the object is a dictionary
    if isinstance(data, abc.Mapping):
        return {key: recursively_apply(val, func) for key, val in data.items()}
    # If the object is iterable but NOT a dictionary or a string
    elif (isinstance(data, abc.Iterable) and
          not isinstance(data, abc.Mapping) and
          not isinstance(data, str)):
        return [recursively_apply(x, func) for x in data]
    # if the object is neither a map nor iterable
    else:
        return func(data)


def settings_to_json(data: dict, clean_keys=True):
def settings_to_json(data: dict):
    """
    Make a copy of an input dictionary that's safe for JSON return

    Args:
        data: Input dictionary
        clean_keys: Modify any keys unsuitable for JSON return
    """

    # Do not overwrite original data dictionary
    d = copy.deepcopy(data)

    # If we're cleaning up unsuitable keys
    if clean_keys:
        # Convert lens_shading_table to a bool
        if 'picamera_settings' in d['camera_settings'] and 'lens_shading_table' in d['camera_settings']['picamera_settings']:
            logging.debug("Bool-ifying lens_shading_table")
            if d['camera_settings']['picamera_settings']['lens_shading_table'] is not None:
                d['camera_settings']['picamera_settings']['lens_shading_table'] = True

    return recursively_apply(d, convert_type_to_json_safe)


# HANDLE BASIC LOADING AND SAVING OF YAML FILES


def load_yaml_file(config_path) -> dict:
    """
    Open a .yaml config file
@@ -138,7 +115,7 @@ def save_yaml_file(config_path: str, config_dict: dict, safe: bool = False):
    logging.info("Saving {}...".format(config_path))
    logging.debug(config_dict)

    with open(config_path, 'w') as outfile:
    with open(config_path, "w") as outfile:
        if not safe:
            yaml.dump(config_dict, outfile)
        else:
@@ -173,7 +150,7 @@ def initialise_file(config_path, populate: str = ""):
        create_file(config_path)

        logging.info("Populating {}...".format(config_path))
        with open(config_path, 'w') as outfile:
        with open(config_path, "w") as outfile:
            outfile.write(populate)


@@ -186,12 +163,13 @@ class OpenflexureSettingsFile:
        config_path (str): Path to the config YAML file (None falls back to default location)
        expand (bool): Expand paths to valid auxillary config files.
    """

    def __init__(self, config_path: str = None, expand: bool = True):
        global DEFAULT_CONFIG, USER_CONFIG_FILE

        self.expandable_keys = {
            'camera_settings': None,
            'stage_settings': None
            "camera_settings": None,
            "stage_settings": None,
        }  #: Dictionary of keys that can be passed as a file path string and expanded automatically

        # Set arguments
@@ -255,8 +233,7 @@ class OpenflexureSettingsFile:
        # For each value in the raw loaded config
        for key, value in config_dict.items():
            # If it's a valid expandable parameter
            if (key in self.expandable_keys and
                    type(value) is str):
            if key in self.expandable_keys and type(value) is str:

                logging.debug("Expanding {}".format(value))

@@ -282,9 +259,11 @@ class OpenflexureSettingsFile:
        # For each value in the expanded config
        for key, value in config_dict.items():
            # If it's a valid expandable parameter
            if (key in self.expandable_keys and
                    self.expandable_keys[key] is not None and
                    type(value) is dict):
            if (
                key in self.expandable_keys
                and self.expandable_keys[key] is not None
                and type(value) is dict
            ):

                logging.debug("Saving to {}".format(self.expandable_keys[key]))
                # Create the file if it doesn't exist