diff --git a/openflexure_microscope/api/app.py b/openflexure_microscope/api/app.py index 26d7f5c1..2ca440bb 100644 --- a/openflexure_microscope/api/app.py +++ b/openflexure_microscope/api/app.py @@ -30,7 +30,7 @@ from labthings.extensions import find_extensions from openflexure_microscope.api.microscope import default_microscope as api_microscope from openflexure_microscope.api.utilities import init_default_extensions, list_routes from openflexure_microscope.api.v2 import views -from openflexure_microscope.config import JSONEncoder +from openflexure_microscope.json import JSONEncoder from openflexure_microscope.paths import ( OPENFLEXURE_EXTENSIONS_PATH, OPENFLEXURE_VAR_PATH, diff --git a/openflexure_microscope/captures/capture.py b/openflexure_microscope/captures/capture.py index fcddc98f..1e97c686 100644 --- a/openflexure_microscope/captures/capture.py +++ b/openflexure_microscope/captures/capture.py @@ -12,7 +12,7 @@ import dateutil.parser from openflexure_microscope.camera import piexif from openflexure_microscope.camera.piexif._exceptions import InvalidImageDataError -from openflexure_microscope.config import JSONEncoder +from openflexure_microscope.json import JSONEncoder EXIF_FORMATS = ["JPG", "JPEG", "TIF", "TIFF"] THUMBNAIL_SIZE = (200, 150) diff --git a/openflexure_microscope/config.py b/openflexure_microscope/config.py index cfca29f3..f4ff2cdb 100644 --- a/openflexure_microscope/config.py +++ b/openflexure_microscope/config.py @@ -3,18 +3,9 @@ import json import logging import os import shutil -from fractions import Fraction -from uuid import UUID -import numpy as np -from labthings.json import LabThingsJSONEncoder - -from .paths import ( - CONFIGURATION_FILE_PATH, - DEFAULT_CONFIGURATION_FILE_PATH, - DEFAULT_SETTINGS_FILE_PATH, - SETTINGS_FILE_PATH, -) +from .json import JSONEncoder +from .paths import CONFIGURATION_FILE_PATH, SETTINGS_FILE_PATH class OpenflexureSettingsFile: @@ -33,7 +24,12 @@ class OpenflexureSettingsFile: self.path = path # Initialise basic config file with defaults if it doesn't exist - initialise_file(self.path, populate=defaults) + initialise_file( + self.path, + # Populate with default dictionary, or empty JSON if empty + populate=json.dumps(defaults, cls=JSONEncoder, indent=2, sort_keys=True) + or "{}\n", + ) def load(self) -> dict: """ @@ -78,32 +74,6 @@ class OpenflexureSettingsFile: return settings -class JSONEncoder(LabThingsJSONEncoder): - """ - A custom JSON encoder, with type conversions for PiCamera fractions, Numpy integers, and Numpy arrays - """ - - def default(self, o): - if isinstance(o, UUID): - return str(o) - # PiCamera fractions - elif isinstance(o, Fraction): - return float(o) - # Numpy integers - elif isinstance(o, np.integer): - return int(o) - # Numpy arrays - elif isinstance(o, np.ndarray): - return o.tolist() - # UUIDs - elif isinstance(o, UUID): - return str(o) - else: - # call base class implementation which takes care of - # raising exceptions for unsupported types - return LabThingsJSONEncoder.default(self, o) - - # HANDLE BASIC LOADING AND SAVING OF SETTINGS FILES @@ -184,21 +154,14 @@ def initialise_file(config_path, populate: str = "{}\n"): outfile.write(populate) -# Load the default settings -with open(DEFAULT_SETTINGS_FILE_PATH, "r") as default_settings: - DEFAULT_SETTINGS = default_settings.read() - #: Default user settings object -user_settings = OpenflexureSettingsFile( - path=SETTINGS_FILE_PATH, defaults=DEFAULT_SETTINGS -) - - -# Load the default configuration -with open(DEFAULT_CONFIGURATION_FILE_PATH, "r") as default_configuration: - DEFAULT_CONFIGURATION = default_configuration.read() +user_settings = OpenflexureSettingsFile(path=SETTINGS_FILE_PATH) #: Default user settings object user_configuration = OpenflexureSettingsFile( - path=CONFIGURATION_FILE_PATH, defaults=DEFAULT_CONFIGURATION + path=CONFIGURATION_FILE_PATH, + defaults={ + "camera": {"type": "PiCamera"}, + "stage": {"type": "SangaStage", "port": None}, + }, ) diff --git a/openflexure_microscope/json.py b/openflexure_microscope/json.py new file mode 100644 index 00000000..37b0d7d8 --- /dev/null +++ b/openflexure_microscope/json.py @@ -0,0 +1,31 @@ +from fractions import Fraction +from uuid import UUID + +import numpy as np +from labthings.json import LabThingsJSONEncoder + + +class JSONEncoder(LabThingsJSONEncoder): + """ + A custom JSON encoder, with type conversions for PiCamera fractions, Numpy integers, and Numpy arrays + """ + + def default(self, o): + if isinstance(o, UUID): + return str(o) + # PiCamera fractions + elif isinstance(o, Fraction): + return float(o) + # Numpy integers + elif isinstance(o, np.integer): + return int(o) + # Numpy arrays + elif isinstance(o, np.ndarray): + return o.tolist() + # UUIDs + elif isinstance(o, UUID): + return str(o) + else: + # call base class implementation which takes care of + # raising exceptions for unsupported types + return LabThingsJSONEncoder.default(self, o) diff --git a/openflexure_microscope/microscope_configuration.default.json b/openflexure_microscope/microscope_configuration.default.json deleted file mode 100644 index b9cbfdce..00000000 --- a/openflexure_microscope/microscope_configuration.default.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "camera": { - "type": "PiCamera" - }, - "stage": { - "type": "SangaStage", - "port": null - } -} \ No newline at end of file diff --git a/openflexure_microscope/microscope_settings.default.json b/openflexure_microscope/microscope_settings.default.json deleted file mode 100644 index 5c408d2e..00000000 --- a/openflexure_microscope/microscope_settings.default.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fov": [ - 4100, - 3146 - ], - "jpeg_quality": 100 -} \ No newline at end of file diff --git a/openflexure_microscope/paths.py b/openflexure_microscope/paths.py index d497873b..3442fd74 100644 --- a/openflexure_microscope/paths.py +++ b/openflexure_microscope/paths.py @@ -38,18 +38,6 @@ def logs_file_path(filename: str): os.makedirs(logs_dir) return os.path.join(logs_dir, filename) - -# HANDLE DEFAULTS FILES STORED IN THIS APPLICATION - -HERE = os.path.abspath(os.path.dirname(__file__)) - -#: Path of default (first-run) microscope settings -DEFAULT_SETTINGS_FILE_PATH = os.path.join(HERE, "microscope_settings.default.json") -#: Path of default (first-run) microscope configuration -DEFAULT_CONFIGURATION_FILE_PATH = os.path.join( - HERE, "microscope_configuration.default.json" -) - # BASE PATHS if os.name == "nt": diff --git a/openflexure_microscope/rescue/check_settings.py b/openflexure_microscope/rescue/check_settings.py index 1c28c9fd..562f26c4 100644 --- a/openflexure_microscope/rescue/check_settings.py +++ b/openflexure_microscope/rescue/check_settings.py @@ -1,5 +1,6 @@ import json import logging +from .error_sources import ErrorSource ERROR_SOURCES = [] @@ -8,27 +9,35 @@ def trace_config_exceptions(): error_sources = [] from openflexure_microscope.paths import ( - DEFAULT_CONFIGURATION_FILE_PATH, + CONFIGURATION_FILE_PATH, SETTINGS_FILE_PATH, ) try: - default_config = json.load(DEFAULT_CONFIGURATION_FILE_PATH) + default_config = json.load(CONFIGURATION_FILE_PATH) if not default_config: - error_sources.append("default_config_empty") + error_sources.append(ErrorSource( + "Configuration file is missing or empty. This may occur if the server has never been started." + )) except Exception as e: # pylint: disable=W0703 logging.error("Error parsing config:") logging.error(e) - error_sources.append("default_config_error") + error_sources.append(ErrorSource( + f"Configuration file is malformed. You can reset to the default configuration by deleting {CONFIGURATION_FILE_PATH}." + )) try: default_settings = json.load(SETTINGS_FILE_PATH) if not default_settings: - error_sources.append("default_settings_empty") + error_sources.append(ErrorSource( + "Settings file is missing or empty. This may occur if the server has never been started." + )) except Exception as e: # pylint: disable=W0703 logging.error("Error parsing settings:") logging.error(e) - error_sources.append("default_settings_error") + error_sources.append(ErrorSource( + f"Settings file is malformed. You can reset to the default settings by deleting {SETTINGS_FILE_PATH}." + )) return error_sources @@ -39,7 +48,9 @@ def main(): try: from openflexure_microscope import config as _ except Exception as e: # pylint: disable=W0703 - error_sources.append("config_settings_import_error") + error_sources.append(ErrorSource( + "Error importing configuration submodule. This could be an error in our code." + )) logging.error("Error importing config:") logging.error(e) error_sources.extend(trace_config_exceptions())