From 4a9d1c5e3c231d3f14bf7e28b3f3106f7c6931bd Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 12:02:09 +0000 Subject: [PATCH 01/24] Removed default JSON files and submoduled JSON encoder class --- openflexure_microscope/api/app.py | 2 +- openflexure_microscope/captures/capture.py | 2 +- openflexure_microscope/config.py | 65 ++++--------------- openflexure_microscope/json.py | 31 +++++++++ .../microscope_configuration.default.json | 9 --- .../microscope_settings.default.json | 7 -- openflexure_microscope/paths.py | 12 ---- .../rescue/check_settings.py | 25 +++++-- 8 files changed, 65 insertions(+), 88 deletions(-) create mode 100644 openflexure_microscope/json.py delete mode 100644 openflexure_microscope/microscope_configuration.default.json delete mode 100644 openflexure_microscope/microscope_settings.default.json 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()) From 7c450b62143b5d4813639c79a9a68087182c38cc Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 12:03:34 +0000 Subject: [PATCH 02/24] Moved piexif into captures submodule --- openflexure_microscope/captures/capture.py | 4 ++-- .../{camera => captures}/piexif/LICENSE.txt | 0 .../{camera => captures}/piexif/__init__.py | 0 openflexure_microscope/{camera => captures}/piexif/_common.py | 0 openflexure_microscope/{camera => captures}/piexif/_dump.py | 0 .../{camera => captures}/piexif/_exceptions.py | 0 openflexure_microscope/{camera => captures}/piexif/_exif.py | 0 openflexure_microscope/{camera => captures}/piexif/_insert.py | 0 openflexure_microscope/{camera => captures}/piexif/_load.py | 0 openflexure_microscope/{camera => captures}/piexif/_remove.py | 0 .../{camera => captures}/piexif/_transplant.py | 0 openflexure_microscope/{camera => captures}/piexif/_webp.py | 0 openflexure_microscope/{camera => captures}/piexif/helper.py | 0 13 files changed, 2 insertions(+), 2 deletions(-) rename openflexure_microscope/{camera => captures}/piexif/LICENSE.txt (100%) rename openflexure_microscope/{camera => captures}/piexif/__init__.py (100%) rename openflexure_microscope/{camera => captures}/piexif/_common.py (100%) rename openflexure_microscope/{camera => captures}/piexif/_dump.py (100%) rename openflexure_microscope/{camera => captures}/piexif/_exceptions.py (100%) rename openflexure_microscope/{camera => captures}/piexif/_exif.py (100%) rename openflexure_microscope/{camera => captures}/piexif/_insert.py (100%) rename openflexure_microscope/{camera => captures}/piexif/_load.py (100%) rename openflexure_microscope/{camera => captures}/piexif/_remove.py (100%) rename openflexure_microscope/{camera => captures}/piexif/_transplant.py (100%) rename openflexure_microscope/{camera => captures}/piexif/_webp.py (100%) rename openflexure_microscope/{camera => captures}/piexif/helper.py (100%) diff --git a/openflexure_microscope/captures/capture.py b/openflexure_microscope/captures/capture.py index 1e97c686..be5dd401 100644 --- a/openflexure_microscope/captures/capture.py +++ b/openflexure_microscope/captures/capture.py @@ -10,8 +10,8 @@ from PIL import Image import dateutil.parser -from openflexure_microscope.camera import piexif -from openflexure_microscope.camera.piexif._exceptions import InvalidImageDataError +from openflexure_microscope.captures import piexif +from openflexure_microscope.captures.piexif import InvalidImageDataError from openflexure_microscope.json import JSONEncoder EXIF_FORMATS = ["JPG", "JPEG", "TIF", "TIFF"] diff --git a/openflexure_microscope/camera/piexif/LICENSE.txt b/openflexure_microscope/captures/piexif/LICENSE.txt similarity index 100% rename from openflexure_microscope/camera/piexif/LICENSE.txt rename to openflexure_microscope/captures/piexif/LICENSE.txt diff --git a/openflexure_microscope/camera/piexif/__init__.py b/openflexure_microscope/captures/piexif/__init__.py similarity index 100% rename from openflexure_microscope/camera/piexif/__init__.py rename to openflexure_microscope/captures/piexif/__init__.py diff --git a/openflexure_microscope/camera/piexif/_common.py b/openflexure_microscope/captures/piexif/_common.py similarity index 100% rename from openflexure_microscope/camera/piexif/_common.py rename to openflexure_microscope/captures/piexif/_common.py diff --git a/openflexure_microscope/camera/piexif/_dump.py b/openflexure_microscope/captures/piexif/_dump.py similarity index 100% rename from openflexure_microscope/camera/piexif/_dump.py rename to openflexure_microscope/captures/piexif/_dump.py diff --git a/openflexure_microscope/camera/piexif/_exceptions.py b/openflexure_microscope/captures/piexif/_exceptions.py similarity index 100% rename from openflexure_microscope/camera/piexif/_exceptions.py rename to openflexure_microscope/captures/piexif/_exceptions.py diff --git a/openflexure_microscope/camera/piexif/_exif.py b/openflexure_microscope/captures/piexif/_exif.py similarity index 100% rename from openflexure_microscope/camera/piexif/_exif.py rename to openflexure_microscope/captures/piexif/_exif.py diff --git a/openflexure_microscope/camera/piexif/_insert.py b/openflexure_microscope/captures/piexif/_insert.py similarity index 100% rename from openflexure_microscope/camera/piexif/_insert.py rename to openflexure_microscope/captures/piexif/_insert.py diff --git a/openflexure_microscope/camera/piexif/_load.py b/openflexure_microscope/captures/piexif/_load.py similarity index 100% rename from openflexure_microscope/camera/piexif/_load.py rename to openflexure_microscope/captures/piexif/_load.py diff --git a/openflexure_microscope/camera/piexif/_remove.py b/openflexure_microscope/captures/piexif/_remove.py similarity index 100% rename from openflexure_microscope/camera/piexif/_remove.py rename to openflexure_microscope/captures/piexif/_remove.py diff --git a/openflexure_microscope/camera/piexif/_transplant.py b/openflexure_microscope/captures/piexif/_transplant.py similarity index 100% rename from openflexure_microscope/camera/piexif/_transplant.py rename to openflexure_microscope/captures/piexif/_transplant.py diff --git a/openflexure_microscope/camera/piexif/_webp.py b/openflexure_microscope/captures/piexif/_webp.py similarity index 100% rename from openflexure_microscope/camera/piexif/_webp.py rename to openflexure_microscope/captures/piexif/_webp.py diff --git a/openflexure_microscope/camera/piexif/helper.py b/openflexure_microscope/captures/piexif/helper.py similarity index 100% rename from openflexure_microscope/camera/piexif/helper.py rename to openflexure_microscope/captures/piexif/helper.py From b350f62013dee746eaa6eed81746d7cc826842cd Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 13:50:48 +0000 Subject: [PATCH 03/24] Removed old JSONResponse class --- .../api/default_extensions/zip_builder.py | 11 +++-- .../api/utilities/__init__.py | 48 ------------------- .../api/v2/views/captures.py | 36 +++++--------- .../api/v2/views/instrument.py | 19 ++++---- openflexure_microscope/devel/__init__.py | 2 - 5 files changed, 26 insertions(+), 90 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/zip_builder.py b/openflexure_microscope/api/default_extensions/zip_builder.py index 42470901..5db25a70 100644 --- a/openflexure_microscope/api/default_extensions/zip_builder.py +++ b/openflexure_microscope/api/default_extensions/zip_builder.py @@ -11,8 +11,6 @@ from labthings.schema import Schema, pre_dump from labthings.utilities import description_from_view from labthings.views import ActionView, PropertyView, View -from openflexure_microscope.devel import JsonResponse, request - class ZipObjectSchema(Schema): id = fields.String() @@ -128,12 +126,15 @@ default_zip_manager = ZipManager() class ZipBuilderAPIView(ActionView): - def post(self): - ids = list(JsonResponse(request).json) + args = fields.List(fields.String(), required=True) + + def post(self, args): microscope = find_component("org.openflexure.microscope") # Return a handle on the autofocus task - return default_zip_manager.marshaled_build_zip_from_capture_ids(microscope, ids) + return default_zip_manager.marshaled_build_zip_from_capture_ids( + microscope, args + ) class ZipListAPIView(PropertyView): diff --git a/openflexure_microscope/api/utilities/__init__.py b/openflexure_microscope/api/utilities/__init__.py index 6e01e9dc..8f72a948 100644 --- a/openflexure_microscope/api/utilities/__init__.py +++ b/openflexure_microscope/api/utilities/__init__.py @@ -24,54 +24,6 @@ def blueprint_name_for_module(module_name, api_ver=2, suffix=""): return f"v{api_ver}_{bp_name}_blueprint{suffix}" -class JsonResponse: - def __init__(self, request): - """ - Object to wrap up simple functionality for parsing a JSON response. - """ - # Try to load as json - try: - self.json = ( - request.get_json() - ) #: dict: Dictionary representation of request JSON - # If malformed JSON is passed, make an empty dictionary - except BadRequest as e: - logging.error(e) - self.json = {} - - if self.json is None: - self.json = {} - - # Store raw response data - self.data = request.get_data() #: str: String representation of request data - - if self.data is None: - self.data = "" - - def param(self, key, default=None, convert=None): - """ - Check if a key exists in a JSON/dictionary payload, and returns it. - - Args: - key (str): JSON key to look for - default: Value to return if no matching key-value pair is found. - convert: Converter function. By passing a type, the value will be converted to that type. - **Use with caution!** - """ - # If no JSON payload exists, make an empty dictionary - if not self.json: - self.json = {} - - if key in self.json: - val = self.json[key] - else: - val = default - - if convert and (val is not None): - val = convert(val) - return val - - def gen(camera): """Video streaming generator function.""" while True: diff --git a/openflexure_microscope/api/v2/views/captures.py b/openflexure_microscope/api/v2/views/captures.py index 9272aafb..f9035498 100644 --- a/openflexure_microscope/api/v2/views/captures.py +++ b/openflexure_microscope/api/v2/views/captures.py @@ -2,12 +2,12 @@ import logging from flask import abort, redirect, request, send_file, url_for from labthings import Schema, fields, find_component -from labthings.marshalling import marshal_with +from labthings.marshalling import marshal_with, use_args from labthings.utilities import description_from_view from labthings.views import PropertyView, View from marshmallow import pre_dump -from openflexure_microscope.api.utilities import JsonResponse, get_bool +from openflexure_microscope.api.utilities import get_bool class InstrumentSchema(Schema): @@ -203,7 +203,8 @@ class CaptureTags(View): return capture_obj.tags - def put(self, id_): + @use_args(fields.List(fields.String(), required=True)) + def put(self, args, id_): """ Add tags to a single image capture """ @@ -213,17 +214,12 @@ class CaptureTags(View): if not capture_obj: return abort(404) # 404 Not Found - # TODO: Replace with normal Flask request JSON thing - data_dict = JsonResponse(request).json - - if type(data_dict) != list: - return abort(400) - - capture_obj.put_tags(data_dict) + capture_obj.put_tags(args) return capture_obj.tags - def delete(self, id_): + @use_args(fields.List(fields.String(), required=True)) + def delete(self, args, id_): """ Delete tags from a single image capture """ @@ -233,12 +229,7 @@ class CaptureTags(View): if not capture_obj: return abort(404) # 404 Not Found - data_dict = JsonResponse(request).json - - if type(data_dict) != list: - return abort(400) - - for tag in data_dict: + for tag in args: capture_obj.delete_tag(str(tag)) return capture_obj.tags @@ -259,7 +250,8 @@ class CaptureAnnotations(View): return capture_obj.annotations - def put(self, id_): + @use_args(fields.Dict()) + def put(self, args, id_): """ Update metadata for a single image capture """ @@ -269,12 +261,6 @@ class CaptureAnnotations(View): if not capture_obj: return abort(404) # 404 Not Found - data_dict = JsonResponse(request).json - logging.debug(data_dict) - - if type(data_dict) != dict: - return abort(400) - - capture_obj.put_annotations(data_dict) + capture_obj.put_annotations(args) return capture_obj.annotations diff --git a/openflexure_microscope/api/v2/views/instrument.py b/openflexure_microscope/api/v2/views/instrument.py index 000bfb6f..3056f800 100644 --- a/openflexure_microscope/api/v2/views/instrument.py +++ b/openflexure_microscope/api/v2/views/instrument.py @@ -1,12 +1,11 @@ import logging from flask import abort, request -from labthings import find_component +from labthings import find_component, fields +from labthings.marshalling import use_args from labthings.utilities import create_from_path, get_by_path, set_by_path from labthings.views import PropertyView, View -from openflexure_microscope.api.utilities import JsonResponse - class SettingsProperty(PropertyView): def get(self): @@ -16,17 +15,17 @@ class SettingsProperty(PropertyView): microscope = find_component("org.openflexure.microscope") return microscope.read_settings() - def put(self): + @use_args(fields.Dict()) + def put(self, args): """ Update current microscope settings, including camera and stage """ microscope = find_component("org.openflexure.microscope") - payload = JsonResponse(request) logging.debug("Updating settings from PUT request:") - logging.debug(payload.json) + logging.debug(args) - microscope.update_settings(payload.json) + microscope.update_settings(args) microscope.save_settings() return self.get() @@ -50,16 +49,16 @@ class NestedSettingsProperty(View): return value - def put(self, route): + @use_args(fields.Dict()) + def put(self, args, route): """ Update a nested section of the current microscope settings """ microscope = find_component("org.openflexure.microscope") keys = route.split("/") - payload = JsonResponse(request) dictionary = create_from_path(keys) - set_by_path(dictionary, keys, payload.json) + set_by_path(dictionary, keys, args) microscope.update_settings(dictionary) microscope.save_settings() diff --git a/openflexure_microscope/devel/__init__.py b/openflexure_microscope/devel/__init__.py index a8e5d39a..fe8d387b 100644 --- a/openflexure_microscope/devel/__init__.py +++ b/openflexure_microscope/devel/__init__.py @@ -13,8 +13,6 @@ from labthings import current_action as current_task from labthings import update_action_data as update_task_data from labthings import update_action_progress as update_task_progress -from openflexure_microscope.api.utilities import JsonResponse - __all__ = [ "current_task", "update_task_progress", From f4b123c237d0eab00476dd130f73858199b7af44 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 14:28:44 +0000 Subject: [PATCH 04/24] Style and linting --- .../api/v2/views/captures.py | 2 - .../api/v2/views/instrument.py | 2 +- openflexure_microscope/paths.py | 1 + .../rescue/check_settings.py | 45 +++++++++++-------- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/openflexure_microscope/api/v2/views/captures.py b/openflexure_microscope/api/v2/views/captures.py index f9035498..4b9347fd 100644 --- a/openflexure_microscope/api/v2/views/captures.py +++ b/openflexure_microscope/api/v2/views/captures.py @@ -1,5 +1,3 @@ -import logging - from flask import abort, redirect, request, send_file, url_for from labthings import Schema, fields, find_component from labthings.marshalling import marshal_with, use_args diff --git a/openflexure_microscope/api/v2/views/instrument.py b/openflexure_microscope/api/v2/views/instrument.py index 3056f800..95c237cd 100644 --- a/openflexure_microscope/api/v2/views/instrument.py +++ b/openflexure_microscope/api/v2/views/instrument.py @@ -1,6 +1,6 @@ import logging -from flask import abort, request +from flask import abort from labthings import find_component, fields from labthings.marshalling import use_args from labthings.utilities import create_from_path, get_by_path, set_by_path diff --git a/openflexure_microscope/paths.py b/openflexure_microscope/paths.py index 3442fd74..38998580 100644 --- a/openflexure_microscope/paths.py +++ b/openflexure_microscope/paths.py @@ -38,6 +38,7 @@ def logs_file_path(filename: str): os.makedirs(logs_dir) return os.path.join(logs_dir, filename) + # BASE PATHS if os.name == "nt": diff --git a/openflexure_microscope/rescue/check_settings.py b/openflexure_microscope/rescue/check_settings.py index 562f26c4..62c30be8 100644 --- a/openflexure_microscope/rescue/check_settings.py +++ b/openflexure_microscope/rescue/check_settings.py @@ -8,36 +8,41 @@ ERROR_SOURCES = [] def trace_config_exceptions(): error_sources = [] - from openflexure_microscope.paths import ( - CONFIGURATION_FILE_PATH, - SETTINGS_FILE_PATH, - ) + from openflexure_microscope.paths import CONFIGURATION_FILE_PATH, SETTINGS_FILE_PATH try: default_config = json.load(CONFIGURATION_FILE_PATH) if not default_config: - error_sources.append(ErrorSource( - "Configuration file is missing or empty. This may occur if the server has never been started." - )) + 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(ErrorSource( - f"Configuration file is malformed. You can reset to the default configuration by deleting {CONFIGURATION_FILE_PATH}." - )) + 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(ErrorSource( - "Settings file is missing or empty. This may occur if the server has never been started." - )) + 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(ErrorSource( - f"Settings file is malformed. You can reset to the default settings by deleting {SETTINGS_FILE_PATH}." - )) + error_sources.append( + ErrorSource( + f"Settings file is malformed. You can reset to the default settings by deleting {SETTINGS_FILE_PATH}." + ) + ) return error_sources @@ -48,9 +53,11 @@ def main(): try: from openflexure_microscope import config as _ except Exception as e: # pylint: disable=W0703 - error_sources.append(ErrorSource( - "Error importing configuration submodule. This could be an error in our code." - )) + 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()) From 5119e2c5446add6d0f58c4266d5b974b6f065828 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 14:30:54 +0000 Subject: [PATCH 05/24] Add pre-commit --- .pre-commit-config.yaml | 19 +++++ .pylintrc | 4 + poetry.lock | 166 +++++++++++++++++++++++++++++++++++++++- pyproject.toml | 7 +- 4 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 .pylintrc diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..a6643d24 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,19 @@ +repos: + - repo: https://github.com/psf/black + rev: 19.10b0 + hooks: + - id: black + - repo: https://github.com/timothycrosley/isort + rev: 5.4.2 + hooks: + - id: isort + additional_dependencies: [toml] + exclude: ^.*/?setup\.py$ + - repo: local + hooks: + - id: pylint + name: pylint + entry: poetry run pylint + files: "./openflexure_microscope/" + language: system + types: [python] diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..918a4af4 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,4 @@ +[MESSAGES CONTROL] + +disable=C0330,C0326,W1202,W0511,C,R +max-line-length = 88 \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index d3b58da7..57e04fc1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -128,6 +128,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "cfgv" +version = "3.2.0" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" + [[package]] name = "chardet" version = "3.0.4" @@ -164,6 +172,14 @@ python-versions = "*" doc = ["sphinx"] test = ["pytest", "coverage", "mock"] +[[package]] +name = "distlib" +version = "0.3.1" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "docutils" version = "0.16" @@ -183,6 +199,14 @@ python-versions = "*" [package.extras] tests = ["dill", "coverage", "coveralls", "mock", "nose"] +[[package]] +name = "filelock" +version = "3.0.12" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "flask" version = "1.1.2" @@ -222,6 +246,17 @@ category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "identify" +version = "1.5.9" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.extras] +license = ["editdistance"] + [[package]] name = "idna" version = "2.10" @@ -246,6 +281,35 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "importlib-metadata" +version = "2.0.0" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "rst.linker"] +testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] + +[[package]] +name = "importlib-resources" +version = "3.3.0" +description = "Read resources from Python packages" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" + +[package.dependencies] +zipp = {version = ">=0.4", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["sphinx", "rst.linker", "jaraco.packaging"] + [[package]] name = "isort" version = "5.6.4" @@ -336,6 +400,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "nodeenv" +version = "1.5.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "numpy" version = "1.19.2" @@ -391,6 +463,24 @@ category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "pre-commit" +version = "2.8.2" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-resources = {version = "*", markers = "python_version < \"3.7\""} +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + [[package]] name = "psutil" version = "5.7.3" @@ -698,6 +788,26 @@ brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +[[package]] +name = "virtualenv" +version = "20.1.0" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +appdirs = ">=1.4.3,<2" +distlib = ">=0.3.1,<1" +filelock = ">=3.0.0,<4" +importlib-metadata = {version = ">=0.12,<3", markers = "python_version < \"3.8\""} +importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] + [[package]] name = "webargs" version = "6.1.1" @@ -747,13 +857,25 @@ python-versions = "*" [package.dependencies] ifaddr = ">=0.1.7" +[[package]] +name = "zipp" +version = "3.4.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + [extras] rpi = ["RPi.GPIO"] [metadata] lock-version = "1.1" -python-versions = "^3.6" -content-hash = "fc03c76cedfd54d4d6be2ced9eb236dfdd1ffb986fdc731f96ee560053f7aa8a" +python-versions = "^3.6.1" +content-hash = "f1a0a5f3d5b31f79086350e20993ea51a5a0e13b084f1329b2594d40c8eae475" [metadata.files] alabaster = [ @@ -796,6 +918,10 @@ certifi = [ {file = "certifi-2020.11.8-py2.py3-none-any.whl", hash = "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd"}, {file = "certifi-2020.11.8.tar.gz", hash = "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"}, ] +cfgv = [ + {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, + {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, +] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, @@ -812,6 +938,10 @@ colorzero = [ {file = "colorzero-1.1-py2.py3-none-any.whl", hash = "sha256:e3c36d15b293de2b2f77ff54a5bd243fffac941ed0a5332d0697a6612a26a0a3"}, {file = "colorzero-1.1.tar.gz", hash = "sha256:acba47119b5d8555680d3cda9afe6ccc5481385ccc3c00084dd973f7aa184599"}, ] +distlib = [ + {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, + {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, +] docutils = [ {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, @@ -819,6 +949,10 @@ docutils = [ expiringdict = [ {file = "expiringdict-1.2.1.tar.gz", hash = "sha256:fe2ba427220425c3c8a3d29f6d2e2985bcee323f8bcd4021e68ebefbd90d8250"}, ] +filelock = [ + {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, + {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, +] flask = [ {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, @@ -830,6 +964,10 @@ flask-cors = [ future = [ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, ] +identify = [ + {file = "identify-1.5.9-py2.py3-none-any.whl", hash = "sha256:5dd84ac64a9a115b8e0b27d1756b244b882ad264c3c423f42af8235a6e71ca12"}, + {file = "identify-1.5.9.tar.gz", hash = "sha256:c9504ba6a043ee2db0a9d69e43246bc138034895f6338d5aed1b41e4a73b1513"}, +] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, @@ -842,6 +980,14 @@ imagesize = [ {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"}, {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, ] +importlib-metadata = [ + {file = "importlib_metadata-2.0.0-py2.py3-none-any.whl", hash = "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3"}, + {file = "importlib_metadata-2.0.0.tar.gz", hash = "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da"}, +] +importlib-resources = [ + {file = "importlib_resources-3.3.0-py2.py3-none-any.whl", hash = "sha256:a3d34a8464ce1d5d7c92b0ea4e921e696d86f2aa212e684451cb1482c8d84ed5"}, + {file = "importlib_resources-3.3.0.tar.gz", hash = "sha256:7b51f0106c8ec564b1bef3d9c588bc694ce2b92125bbb6278f4f2f5b54ec3592"}, +] isort = [ {file = "isort-5.6.4-py3-none-any.whl", hash = "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7"}, {file = "isort-5.6.4.tar.gz", hash = "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58"}, @@ -919,6 +1065,10 @@ mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] +nodeenv = [ + {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, + {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, +] numpy = [ {file = "numpy-1.19.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a"}, {file = "numpy-1.19.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd"}, @@ -1003,6 +1153,10 @@ pillow = [ {file = "Pillow-7.2.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d"}, {file = "Pillow-7.2.0.tar.gz", hash = "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626"}, ] +pre-commit = [ + {file = "pre_commit-2.8.2-py2.py3-none-any.whl", hash = "sha256:22e6aa3bd571debb01eb7d34483f11c01b65237be4eebbf30c3d4fb65762d315"}, + {file = "pre_commit-2.8.2.tar.gz", hash = "sha256:905ebc9b534b991baec87e934431f2d0606ba27f2b90f7f652985f5a5b8b6ae6"}, +] psutil = [ {file = "psutil-5.7.3-cp27-none-win32.whl", hash = "sha256:1cd6a0c9fb35ece2ccf2d1dd733c1e165b342604c67454fd56a4c12e0a106787"}, {file = "psutil-5.7.3-cp27-none-win_amd64.whl", hash = "sha256:e02c31b2990dcd2431f4524b93491941df39f99619b0d312dfe1d4d530b08b4b"}, @@ -1165,6 +1319,10 @@ urllib3 = [ {file = "urllib3-1.25.11-py2.py3-none-any.whl", hash = "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"}, {file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"}, ] +virtualenv = [ + {file = "virtualenv-20.1.0-py2.py3-none-any.whl", hash = "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2"}, + {file = "virtualenv-20.1.0.tar.gz", hash = "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"}, +] webargs = [ {file = "webargs-6.1.1-py2.py3-none-any.whl", hash = "sha256:2ead6ce38559152043ab4ada4d389aef6c804b0c169482e7c92b3f498f690b2c"}, {file = "webargs-6.1.1.tar.gz", hash = "sha256:412ecadd977afdea0ed6fa5f5b65ddd13a099269e622ec537f9c74c443ce4d0b"}, @@ -1180,3 +1338,7 @@ zeroconf = [ {file = "zeroconf-0.28.6-py3-none-any.whl", hash = "sha256:17ae1e1681091b91b0337517db222eae1807003154c01b0bd0ab99572dfeafd8"}, {file = "zeroconf-0.28.6.tar.gz", hash = "sha256:70f10f0f16e3a8c4eb5e1a106b812b8d052253041cf1ee1195933df706f5261c"}, ] +zipp = [ + {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, + {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, +] diff --git a/pyproject.toml b/pyproject.toml index a027c8c2..f73c45d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.6" +python = "^3.6.1" Flask = "^1.0" numpy = "1.19.2" # Exact version so we can guarantee a wheel Pillow = "^7.2.0" @@ -58,6 +58,7 @@ sphinxcontrib-httpdomain = "^1.7" rope = "^0.14.0" pylint = "^2.3" black = {version = "^18.3-alpha.0",allow-prereleases = true} +pre-commit = "^2.8.2" [tool.black] exclude = '(\.eggs|\.git|\.venv|node_modules/)' @@ -69,7 +70,3 @@ force_grid_wrap = 0 use_parentheses = true ensure_newline_before_comments = true line_length = 88 - -[tool.pylint.'MESSAGES CONTROL'] -disable = "C0330, C0326, W1202, W0511, C, R" -max-line-length = 88 \ No newline at end of file From 66448193526c53668423ad4f072342bdda5897fa Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 14:33:08 +0000 Subject: [PATCH 06/24] Formatting --- openflexure_microscope/api/app.py | 2 +- openflexure_microscope/api/v2/views/instrument.py | 2 +- openflexure_microscope/camera/base.py | 2 +- openflexure_microscope/captures/__init__.py | 2 +- openflexure_microscope/captures/capture.py | 2 +- openflexure_microscope/microscope.py | 2 +- openflexure_microscope/rescue/auto.py | 8 ++++---- openflexure_microscope/rescue/check_capture_reload.py | 2 +- openflexure_microscope/rescue/check_sangaboard.py | 4 ++-- openflexure_microscope/rescue/check_settings.py | 1 + openflexure_microscope/rescue/check_system.py | 3 ++- 11 files changed, 16 insertions(+), 14 deletions(-) diff --git a/openflexure_microscope/api/app.py b/openflexure_microscope/api/app.py index 2ca440bb..ebb77d19 100644 --- a/openflexure_microscope/api/app.py +++ b/openflexure_microscope/api/app.py @@ -24,8 +24,8 @@ import pkg_resources from flask import abort, send_file from flask_cors import CORS, cross_origin from labthings import create_app -from labthings.views import View from labthings.extensions import find_extensions +from labthings.views import View from openflexure_microscope.api.microscope import default_microscope as api_microscope from openflexure_microscope.api.utilities import init_default_extensions, list_routes diff --git a/openflexure_microscope/api/v2/views/instrument.py b/openflexure_microscope/api/v2/views/instrument.py index 95c237cd..c441240e 100644 --- a/openflexure_microscope/api/v2/views/instrument.py +++ b/openflexure_microscope/api/v2/views/instrument.py @@ -1,7 +1,7 @@ import logging from flask import abort -from labthings import find_component, fields +from labthings import fields, find_component from labthings.marshalling import use_args from labthings.utilities import create_from_path, get_by_path, set_by_path from labthings.views import PropertyView, View diff --git a/openflexure_microscope/camera/base.py b/openflexure_microscope/camera/base.py index de1530a3..4ef6d7b8 100644 --- a/openflexure_microscope/camera/base.py +++ b/openflexure_microscope/camera/base.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- +import io import logging import threading import time -import io from abc import ABCMeta, abstractmethod from collections import namedtuple diff --git a/openflexure_microscope/captures/__init__.py b/openflexure_microscope/captures/__init__.py index 4df0ac64..7d15ca40 100644 --- a/openflexure_microscope/captures/__init__.py +++ b/openflexure_microscope/captures/__init__.py @@ -1,3 +1,3 @@ from . import capture, capture_manager -from .capture import CaptureObject, THUMBNAIL_SIZE +from .capture import THUMBNAIL_SIZE, CaptureObject from .capture_manager import CaptureManager diff --git a/openflexure_microscope/captures/capture.py b/openflexure_microscope/captures/capture.py index be5dd401..8f75bf6d 100644 --- a/openflexure_microscope/captures/capture.py +++ b/openflexure_microscope/captures/capture.py @@ -6,9 +6,9 @@ import logging import os import uuid from collections import OrderedDict -from PIL import Image import dateutil.parser +from PIL import Image from openflexure_microscope.captures import piexif from openflexure_microscope.captures.piexif import InvalidImageDataError diff --git a/openflexure_microscope/microscope.py b/openflexure_microscope/microscope.py index 17d2be9f..b9b398e1 100644 --- a/openflexure_microscope/microscope.py +++ b/openflexure_microscope/microscope.py @@ -10,7 +10,7 @@ import pkg_resources from expiringdict import ExpiringDict from openflexure_microscope.camera.mock import MissingCamera -from openflexure_microscope.captures import CaptureManager, THUMBNAIL_SIZE +from openflexure_microscope.captures import THUMBNAIL_SIZE, CaptureManager from openflexure_microscope.stage.mock import MissingStage from openflexure_microscope.stage.sanga import SangaDeltaStage, SangaStage diff --git a/openflexure_microscope/rescue/auto.py b/openflexure_microscope/rescue/auto.py index 5bb95f22..bc4c52bc 100644 --- a/openflexure_microscope/rescue/auto.py +++ b/openflexure_microscope/rescue/auto.py @@ -1,10 +1,9 @@ import logging import os -import sys import platform -import pkg_resources +import sys -from .error_sources import bcolors +import pkg_resources from openflexure_microscope.paths import ( FALLBACK_OPENFLEXURE_VAR_PATH, @@ -13,11 +12,12 @@ from openflexure_microscope.paths import ( from . import ( check_capture_reload, - check_settings, check_picamera, check_sangaboard, + check_settings, check_system, ) +from .error_sources import bcolors # Paths for suggestions LOGS_PATHS = [ diff --git a/openflexure_microscope/rescue/check_capture_reload.py b/openflexure_microscope/rescue/check_capture_reload.py index 54459665..e50ae05b 100644 --- a/openflexure_microscope/rescue/check_capture_reload.py +++ b/openflexure_microscope/rescue/check_capture_reload.py @@ -1,9 +1,9 @@ import logging from openflexure_microscope.captures.capture import ( + EXIF_FORMATS, build_captures_from_exif, make_file_list, - EXIF_FORMATS, ) from openflexure_microscope.captures.capture_manager import BASE_CAPTURE_PATH from openflexure_microscope.config import user_settings diff --git a/openflexure_microscope/rescue/check_sangaboard.py b/openflexure_microscope/rescue/check_sangaboard.py index 8c7f6aa7..13b154b1 100644 --- a/openflexure_microscope/rescue/check_sangaboard.py +++ b/openflexure_microscope/rescue/check_sangaboard.py @@ -1,7 +1,7 @@ -from .error_sources import ErrorSource - from openflexure_microscope.config import user_configuration +from .error_sources import ErrorSource + def main(): error_sources = [] diff --git a/openflexure_microscope/rescue/check_settings.py b/openflexure_microscope/rescue/check_settings.py index 62c30be8..64fffb92 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 = [] diff --git a/openflexure_microscope/rescue/check_system.py b/openflexure_microscope/rescue/check_system.py index 1c3f31ee..880ba87c 100644 --- a/openflexure_microscope/rescue/check_system.py +++ b/openflexure_microscope/rescue/check_system.py @@ -1,5 +1,6 @@ -from urllib.request import urlopen from urllib.error import URLError +from urllib.request import urlopen + import psutil from .error_sources import WarningSource From 0647a039b020b050e3e028614adbeceb86222bec Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 14:35:17 +0000 Subject: [PATCH 07/24] Added note on pre-commit --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index e3327bce..670dd7d0 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,17 @@ This includes installing the server in a mode better suited for active developme * `poetry install` * `poetry run build_static` +### Pre-commit hooks + +We make use of pre-commit hooks to run code analysis before committing to the codebase. + +The simplest way to ensure this works is to install pre-commits into your global Python environment: + +* `pip3 install pre-commit` +* `pre-commit install` + +Alternatively, you can work within the Poetry project's virtual environment where pre-commit will already be installed as a developer dependency. + ### Node installation * Note, building the static interface will require a valid Node.js installation From 36e837d37453007f17dfb2642688077ab0c09070 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 14:37:18 +0000 Subject: [PATCH 08/24] Added note on manually running pre-commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 670dd7d0..faa41e9b 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ The simplest way to ensure this works is to install pre-commits into your global Alternatively, you can work within the Poetry project's virtual environment where pre-commit will already be installed as a developer dependency. +Finally, to run all analysis within installing pre-commits to Git, you can run `pre-commit run`. + ### Node installation * Note, building the static interface will require a valid Node.js installation From 9f5252194ab3c9097f86b66c248554a1d33e1bcf Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 15:12:17 +0000 Subject: [PATCH 09/24] Updated all log strings to new format --- .pre-commit-config.yaml | 1 - .pylintrc | 8 +- docs/source/conf.py | 2 +- .../example_extension/04_properties.py | 2 +- .../example_extension/05_actions.py | 2 +- .../example_extension/06_tasks_locks.py | 12 +- .../extensions/example_extension/07_ev_gui.py | 12 +- openflexure_microscope/api/app.py | 2 +- .../api/default_extensions/__init__.py | 2 +- .../api/default_extensions/autofocus.py | 7 +- .../api/default_extensions/autostorage.py | 6 +- .../recalibrate_utils.py | 19 +- .../api/default_extensions/scan.py | 16 +- .../api/default_extensions/zip_builder.py | 2 +- .../api/dev_extensions/tools.py | 2 +- .../api/utilities/__init__.py | 6 +- openflexure_microscope/api/utilities/gui.py | 2 +- .../api/v2/views/actions/stage.py | 4 +- openflexure_microscope/camera/base.py | 4 +- openflexure_microscope/camera/pi.py | 55 ++-- .../camera/set_picamera_gain.py | 13 +- openflexure_microscope/captures/capture.py | 32 +-- .../captures/capture_manager.py | 10 +- openflexure_microscope/config.py | 12 +- openflexure_microscope/microscope.py | 18 +- .../rescue/check_capture_reload.py | 2 +- .../rescue/monitor_timeout.py | 2 +- openflexure_microscope/stage/mock.py | 4 +- openflexure_microscope/stage/sanga.py | 10 +- openflexure_microscope/utilities.py | 2 +- tests/api_client.py | 101 ------- tests/test_api.py | 112 -------- tests/test_camera.py | 266 ------------------ tests/test_plugins.py | 44 --- tests/test_stage.py | 42 --- 35 files changed, 121 insertions(+), 715 deletions(-) delete mode 100644 tests/api_client.py delete mode 100644 tests/test_api.py delete mode 100644 tests/test_camera.py delete mode 100644 tests/test_plugins.py delete mode 100644 tests/test_stage.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a6643d24..7ff044af 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,5 @@ repos: - id: pylint name: pylint entry: poetry run pylint - files: "./openflexure_microscope/" language: system types: [python] diff --git a/.pylintrc b/.pylintrc index 918a4af4..7dc2c3fe 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,4 +1,8 @@ [MESSAGES CONTROL] -disable=C0330,C0326,W1202,W0511,C,R -max-line-length = 88 \ No newline at end of file +disable=fixme,C,R +max-line-length = 88 + +[LOGGING] + +logging-format-style=new \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index fe3f927f..4fd4b0c0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -38,7 +38,7 @@ sys.modules.update((mod_name, Mock()) for mod_name in mock_imports) # -- Project information ----------------------------------------------------- project = "OpenFlexure Microscope Software" -copyright = "2018, Bath Open Instrumentation Group" +copyright = "2018, Bath Open Instrumentation Group" # pylint: disable=redefined-builtin author = "Bath Open Instrumentation Group" # The short X.Y version diff --git a/docs/source/extensions/example_extension/04_properties.py b/docs/source/extensions/example_extension/04_properties.py index ec13cc3c..3bedbad0 100644 --- a/docs/source/extensions/example_extension/04_properties.py +++ b/docs/source/extensions/example_extension/04_properties.py @@ -1,6 +1,6 @@ from labthings import Schema, fields, find_component from labthings.extensions import BaseExtension -from labthings.views import PropertyView, View +from labthings.views import PropertyView ## Extension methods diff --git a/docs/source/extensions/example_extension/05_actions.py b/docs/source/extensions/example_extension/05_actions.py index 3980e531..52b1b024 100644 --- a/docs/source/extensions/example_extension/05_actions.py +++ b/docs/source/extensions/example_extension/05_actions.py @@ -3,7 +3,7 @@ import io # Used in our capture action from flask import send_file # Used to send images from our server from labthings import Schema, fields, find_component from labthings.extensions import BaseExtension -from labthings.views import ActionView, PropertyView, View +from labthings.views import ActionView, PropertyView ## Extension methods diff --git a/docs/source/extensions/example_extension/06_tasks_locks.py b/docs/source/extensions/example_extension/06_tasks_locks.py index e17ac971..710b4e73 100644 --- a/docs/source/extensions/example_extension/06_tasks_locks.py +++ b/docs/source/extensions/example_extension/06_tasks_locks.py @@ -1,16 +1,8 @@ -import io # Used in our capture action import time # Used in our timelapse function -from flask import send_file # Used to send images from our server -from labthings import ( - Schema, - current_action, - fields, - find_component, - update_action_progress, -) +from labthings import current_action, fields, find_component, update_action_progress from labthings.extensions import BaseExtension -from labthings.views import ActionView, PropertyView, View +from labthings.views import ActionView # Used in our timelapse function from openflexure_microscope.captures.capture_manager import generate_basename diff --git a/docs/source/extensions/example_extension/07_ev_gui.py b/docs/source/extensions/example_extension/07_ev_gui.py index ea2c9c9b..a71d9053 100644 --- a/docs/source/extensions/example_extension/07_ev_gui.py +++ b/docs/source/extensions/example_extension/07_ev_gui.py @@ -1,16 +1,8 @@ -import io # Used in our capture action import time # Used in our timelapse function -from flask import send_file # Used to send images from our server -from labthings import ( - Schema, - current_action, - fields, - find_component, - update_action_progress, -) +from labthings import current_action, fields, find_component, update_action_progress from labthings.extensions import BaseExtension -from labthings.views import ActionView, PropertyView, View +from labthings.views import ActionView # Used to convert our GUI dictionary into a complete eV extension GUI from openflexure_microscope.api.utilities.gui import build_gui diff --git a/openflexure_microscope/api/app.py b/openflexure_microscope/api/app.py index ebb77d19..998be592 100644 --- a/openflexure_microscope/api/app.py +++ b/openflexure_microscope/api/app.py @@ -72,7 +72,7 @@ root_log.addHandler(fh) access_log.addHandler(afh) # Log server paths being used -logging.info("Running with data path %s", OPENFLEXURE_VAR_PATH) +logging.info("Running with data path {}", OPENFLEXURE_VAR_PATH) logging.info("Creating app") # Create flask app diff --git a/openflexure_microscope/api/default_extensions/__init__.py b/openflexure_microscope/api/default_extensions/__init__.py index 561239af..e76733f9 100644 --- a/openflexure_microscope/api/default_extensions/__init__.py +++ b/openflexure_microscope/api/default_extensions/__init__.py @@ -10,7 +10,7 @@ def handle_extension_error(extension_name): yield except Exception: # pylint: disable=W0703 logging.error( - "Exception loading builtin extension %s: \n%s", + "Exception loading builtin extension {}: \n{}", extension_name, traceback.format_exc(), ) diff --git a/openflexure_microscope/api/default_extensions/autofocus.py b/openflexure_microscope/api/default_extensions/autofocus.py index 507a9fac..cc30a908 100644 --- a/openflexure_microscope/api/default_extensions/autofocus.py +++ b/openflexure_microscope/api/default_extensions/autofocus.py @@ -83,7 +83,7 @@ class JPEGSharpnessMonitor: raise e if stop < 1: stop = len(jpeg_times) - logging.debug("changing stop to {}".format(stop)) + logging.debug("changing stop to {}", (stop)) jpeg_times = jpeg_times[start:stop] jpeg_zs = np.interp(jpeg_times, stage_times, stage_zs) return jpeg_times, jpeg_zs, jpeg_sizes[start:stop] @@ -292,9 +292,8 @@ def fast_up_down_up_autofocus( logging.debug("Correction move") correction_move = best_z + target_z - jz[inow] logging.debug( - "Fast autofocus scan: correcting backlash by moving {} steps".format( - correction_move - ) + "Fast autofocus scan: correcting backlash by moving {} steps", + (correction_move), ) m.focus_rel(correction_move) return m.data_dict() diff --git a/openflexure_microscope/api/default_extensions/autostorage.py b/openflexure_microscope/api/default_extensions/autostorage.py index e54f3aea..8a7c4abe 100644 --- a/openflexure_microscope/api/default_extensions/autostorage.py +++ b/openflexure_microscope/api/default_extensions/autostorage.py @@ -95,10 +95,10 @@ class AutostorageExtension(BaseExtension): def on_microscope(self, microscope_obj): """Function to automatically call when the parent LabThing has a microscope attached.""" - logging.debug("Autostorage extension found microscope %s", microscope_obj) + logging.debug("Autostorage extension found microscope {}", microscope_obj) if hasattr(microscope_obj, "captures"): logging.debug( - "Autostorage extension bound to CaptureManager %s", self.capture_manager + "Autostorage extension bound to CaptureManager {}", self.capture_manager ) # Store a reference to the CaptureManager @@ -117,7 +117,7 @@ class AutostorageExtension(BaseExtension): # If preferred path does not exist, or cannot be written to if not (os.path.isdir(location) and check_rw(location)): logging.error( - "Preferred capture path %s is missing or cannot be written to. Restoring defaults.", + "Preferred capture path {} is missing or cannot be written to. Restoring defaults.", location, ) # Reset the storage location to default diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py index 28b04c02..6aa70937 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -63,17 +63,15 @@ def auto_expose_and_freeze_settings(camera): logging.info("Freezing the camera settings...") camera.shutter_speed = camera.exposure_speed - logging.info("Shutter speed = {}".format(camera.shutter_speed)) + logging.info("Shutter speed = {}", (camera.shutter_speed)) camera.exposure_mode = "off" logging.info("Auto exposure disabled") g = camera.awb_gains camera.awb_mode = "off" camera.awb_gains = g - logging.info("Auto white balance disabled, gains are {}".format(g)) + logging.info("Auto white balance disabled, gains are {}", (g)) logging.info( - "Analogue gain: {}, Digital gain: {}".format( - camera.analog_gain, camera.digital_gain - ) + "Analogue gain: {}, Digital gain: {}", camera.analog_gain, camera.digital_gain ) adjust_exposure_to_setpoint(camera, 215) @@ -100,7 +98,7 @@ def lst_from_channels(channels): # lst_resolution = list(np.ceil(full_resolution / 64.0).astype(int)) lst_resolution = [(r // 64) + 1 for r in full_resolution] # NB the size of the LST is 1/64th of the image, but rounded UP. - logging.info("Generating a lens shading table at {}x{}".format(*lst_resolution)) + logging.info("Generating a lens shading table at {}x{}", *lst_resolution) lens_shading = np.zeros([channels.shape[0]] + lst_resolution, dtype=np.float) for i in range(lens_shading.shape[0]): image_channel = channels[i, :, :] @@ -118,9 +116,12 @@ def lst_from_channels(channels): image_channel, [(0, lw * 32 - iw), (0, lh * 32 - ih)], mode="edge" ) # Pad image to the right and bottom logging.info( - "Channel shape: {}x{}, shading table shape: {}x{}, after padding {}".format( - iw, ih, lw * 32, lh * 32, padded_image_channel.shape - ) + "Channel shape: {}x{}, shading table shape: {}x{}, after padding {}", + iw, + ih, + lw * 32, + lh * 32, + padded_image_channel.shape, ) # Next, fill the shading table (except edge pixels). Please excuse the # for loop - I know it's not fast but this code needn't be! diff --git a/openflexure_microscope/api/default_extensions/scan.py b/openflexure_microscope/api/default_extensions/scan.py index 9a292f11..52b9e0ec 100644 --- a/openflexure_microscope/api/default_extensions/scan.py +++ b/openflexure_microscope/api/default_extensions/scan.py @@ -216,7 +216,7 @@ class ScanExtension(BaseExtension): for x_y in line: # Move to new grid position without changing z - logging.debug("Moving to step {}".format([x_y[0], x_y[1], next_z])) + logging.debug("Moving to step {}", ([x_y[0], x_y[1], next_z])) microscope.stage.move_abs([x_y[0], x_y[1], next_z]) # Refocus if autofocus_enabled: @@ -270,11 +270,11 @@ class ScanExtension(BaseExtension): # Make sure we use our current best estimate of focus (i.e. the current position) next point next_z = microscope.stage.position[2] - logging.debug("Returning to {}".format(initial_position)) + logging.debug("Returning to {}", (initial_position)) microscope.stage.move_abs(initial_position) end = time.time() - logging.info("Scan took %s seconds", end - start) + logging.info("Scan took {} seconds", end - start) def stack( self, @@ -298,17 +298,17 @@ class ScanExtension(BaseExtension): # Store initial position initial_position = microscope.stage.position - logging.debug("Starting z-stack from position %s", microscope.stage.position) + logging.debug("Starting z-stack from position {}", microscope.stage.position) with microscope.lock: # Move to center scan logging.debug("Moving to z-stack starting position") microscope.stage.move_rel([0, 0, int((-step_size * steps) / 2)]) - logging.debug("Starting scan from position %s", microscope.stage.position) + logging.debug("Starting scan from position {}", microscope.stage.position) for i in range(steps): time.sleep(0.1) - logging.debug("Capturing from position %s", microscope.stage.position) + logging.debug("Capturing from position {}", microscope.stage.position) self.capture( microscope, basename, @@ -328,10 +328,10 @@ class ScanExtension(BaseExtension): return if i != steps - 1: - logging.debug("Moving z by {}".format(step_size)) + logging.debug("Moving z by {}", (step_size)) microscope.stage.move_rel([0, 0, step_size]) if return_to_start: - logging.debug("Returning to {}".format(initial_position)) + logging.debug("Returning to {}", (initial_position)) microscope.stage.move_abs(initial_position) diff --git a/openflexure_microscope/api/default_extensions/zip_builder.py b/openflexure_microscope/api/default_extensions/zip_builder.py index 5db25a70..9ce774da 100644 --- a/openflexure_microscope/api/default_extensions/zip_builder.py +++ b/openflexure_microscope/api/default_extensions/zip_builder.py @@ -156,7 +156,7 @@ class ZipGetterAPIView(View): if not session_id in default_zip_manager.session_zips: return abort(404) # 404 Not Found - logging.info("Session ID: %s", session_id) + logging.info("Session ID: {}", session_id) return send_file( default_zip_manager.zip_fp_from_id(session_id).name, diff --git a/openflexure_microscope/api/dev_extensions/tools.py b/openflexure_microscope/api/dev_extensions/tools.py index ddbadfef..1f6d12cf 100644 --- a/openflexure_microscope/api/dev_extensions/tools.py +++ b/openflexure_microscope/api/dev_extensions/tools.py @@ -17,7 +17,7 @@ class SleepFor(ActionView): def post(self, args): sleep_time = args.get("time") - logging.info("Going to sleep for %s...", sleep_time) + logging.info("Going to sleep for {}...", sleep_time) start = time.time() time.sleep(sleep_time) end = time.time() diff --git a/openflexure_microscope/api/utilities/__init__.py b/openflexure_microscope/api/utilities/__init__.py index 8f72a948..333fa767 100644 --- a/openflexure_microscope/api/utilities/__init__.py +++ b/openflexure_microscope/api/utilities/__init__.py @@ -73,12 +73,10 @@ def init_default_extensions(extension_dir): default_ext_path = os.path.join(extension_dir, "defaults.py") if not os.path.isfile(default_ext_path): # If user extensions file doesn't exist - logging.warning( - "No extension file found at {}. Creating...".format(extension_dir) - ) + logging.warning("No extension file found at {}. Creating...", (extension_dir)) create_file(default_ext_path) - logging.info("Populating {}...".format(default_ext_path)) + logging.info("Populating {}...", (default_ext_path)) with open(default_ext_path, "w") as outfile: outfile.write(_DEFAULT_EXTENSION_INIT) diff --git a/openflexure_microscope/api/utilities/gui.py b/openflexure_microscope/api/utilities/gui.py index 1fc03c1f..5613ae69 100644 --- a/openflexure_microscope/api/utilities/gui.py +++ b/openflexure_microscope/api/utilities/gui.py @@ -27,7 +27,7 @@ def build_gui_from_dict(gui_description, extension_object): if "route" in form and form["route"] in ext_rules.keys(): form["route"] = ext_rules[form["route"]]["urls"][0] else: - logging.warning("No valid expandable route found for %s", form["route"]) + logging.warning("No valid expandable route found for {}", form["route"]) # Inject extension information api_gui["id"] = extension_object.name diff --git a/openflexure_microscope/api/v2/views/actions/stage.py b/openflexure_microscope/api/v2/views/actions/stage.py index cd989f5c..5c7cc416 100644 --- a/openflexure_microscope/api/v2/views/actions/stage.py +++ b/openflexure_microscope/api/v2/views/actions/stage.py @@ -25,11 +25,11 @@ class MoveStageAPI(ActionView): # Handle absolute positioning (calculate a relative move from current position and target) if (args.get("absolute")) and (microscope.stage): # Only if stage exists target_position = axes_to_array(args, ["x", "y", "z"]) - logging.debug("TARGET: {}".format(target_position)) + logging.debug("TARGET: {}", (target_position)) position = [ target_position[i] - microscope.stage.position[i] for i in range(3) ] - logging.debug("DELTA: {}".format(position)) + logging.debug("DELTA: {}", (position)) else: # Get coordinates from payload diff --git a/openflexure_microscope/camera/base.py b/openflexure_microscope/camera/base.py index 4ef6d7b8..d3bdbefa 100644 --- a/openflexure_microscope/camera/base.py +++ b/openflexure_microscope/camera/base.py @@ -113,10 +113,10 @@ class BaseCamera(metaclass=ABCMeta): def close(self): """Close the BaseCamera and all attached StreamObjects.""" - logging.info("Closing {}".format(self)) + logging.info("Closing {}", (self)) # Stop worker thread self.stop_worker() - logging.info("Closed {}".format(self)) + logging.info("Closed {}", (self)) # START AND STOP WORKER THREAD diff --git a/openflexure_microscope/camera/pi.py b/openflexure_microscope/camera/pi.py index 11dafab8..d86957d4 100644 --- a/openflexure_microscope/camera/pi.py +++ b/openflexure_microscope/camera/pi.py @@ -156,10 +156,10 @@ class PiCameraStreamer(BaseCamera): for key in PiCameraStreamer.picamera_settings_keys: try: value = getattr(self.camera, key) - logging.debug("Reading PiCamera().{}: {}".format(key, value)) + logging.debug("Reading PiCamera().{}: {}", key, value) conf_dict["picamera"][key] = value except AttributeError: - logging.debug("Unable to read PiCamera attribute {}".format(key)) + logging.debug("Unable to read PiCamera attribute {}", (key)) # Include a serialised lens shading table if ( @@ -243,26 +243,22 @@ class PiCameraStreamer(BaseCamera): # Set exposure mode if "exposure_mode" in settings_dict: logging.debug( - "Applying exposure_mode: {}".format(settings_dict["exposure_mode"]) + "Applying exposure_mode: {}", (settings_dict["exposure_mode"]) ) self.camera.exposure_mode = settings_dict["exposure_mode"] # Apply gains and let them settle if "analog_gain" in settings_dict: - logging.debug( - "Applying analog_gain: {}".format(settings_dict["analog_gain"]) - ) + logging.debug("Applying analog_gain: {}", (settings_dict["analog_gain"])) set_analog_gain(self.camera, float(settings_dict["analog_gain"])) if "digital_gain" in settings_dict: - logging.debug( - "Applying digital_gain: {}".format(settings_dict["digital_gain"]) - ) + logging.debug("Applying digital_gain: {}", (settings_dict["digital_gain"])) set_digital_gain(self.camera, float(settings_dict["digital_gain"])) # Apply shutter speed if "shutter_speed" in settings_dict: logging.debug( - "Applying shutter_speed: {}".format(settings_dict["shutter_speed"]) + "Applying shutter_speed: {}", (settings_dict["shutter_speed"]) ) self.camera.shutter_speed = int(settings_dict["shutter_speed"]) @@ -272,17 +268,17 @@ class PiCameraStreamer(BaseCamera): if "awb_gains" in settings_dict: logging.debug("Applying awb_mode: off") self.camera.awb_mode = "off" - logging.debug("Applying awb_gains: {}".format(settings_dict["awb_gains"])) + logging.debug("Applying awb_gains: {}", (settings_dict["awb_gains"])) self.camera.awb_gains = settings_dict["awb_gains"] elif "awb_mode" in settings_dict: - logging.debug("Applying awb_mode: {}".format(settings_dict["awb_mode"])) + logging.debug("Applying awb_mode: {}", (settings_dict["awb_mode"])) self.camera.awb_mode = settings_dict["awb_mode"] # Handle some properties that can be quickly applied batched_keys = ["framerate", "saturation"] for key in batched_keys: if (key in settings_dict) and hasattr(self.camera, key): - logging.debug("Applying {}: {}".format(key, settings_dict[key])) + logging.debug("Applying {}: {}", key, settings_dict[key]) setattr(self.camera, key, settings_dict[key]) # Final optional pause to settle @@ -307,7 +303,7 @@ class PiCameraStreamer(BaseCamera): for i in range(2): if np.abs(centre[i] - 0.5) + size / 2 > 0.5: centre[i] = 0.5 + (1.0 - size) / 2 * np.sign(centre[i] - 0.5) - logging.info("setting zoom, centre {}, size {}".format(centre, size)) + logging.info("setting zoom, centre {}, size {}", centre, size) new_fov = (centre[0] - size / 2, centre[1] - size / 2, size, size) self.camera.zoom = new_fov @@ -329,13 +325,12 @@ class PiCameraStreamer(BaseCamera): self.preview_active = True except picamerax.exc.PiCameraMMALError as e: logging.error( - "Suppressed a MMALError in start_preview. Exception: {}".format(e) + "Suppressed a MMALError in start_preview. Exception: {}", (e) ) except picamerax.exc.PiCameraValueError as e: logging.error( - "Suppressed a ValueError exception in start_preview. Exception: {}".format( - e - ) + "Suppressed a ValueError exception in start_preview. Exception: {}", + (e), ) def stop_preview(self): @@ -364,7 +359,7 @@ class PiCameraStreamer(BaseCamera): if not self.record_active: # Start the camera video recording on port 2 - logging.info("Recording to {}".format(output)) + logging.info("Recording to {}", (output)) self.camera.start_recording( output, @@ -406,19 +401,19 @@ class PiCameraStreamer(BaseCamera): """ for k in kwargs.keys(): logging.warning( - "Warning, kwarg %s is invalid for stop_stream_recording.", k + "Warning, kwarg {} is invalid for stop_stream_recording.", k ) with self.lock: # Stop the camera video recording on port 1 try: self.camera.stop_recording(splitter_port=splitter_port) except picamerax.exc.PiCameraNotRecording: - logging.info("Not recording on splitter_port {}".format(splitter_port)) + logging.info("Not recording on splitter_port {}", (splitter_port)) else: logging.info( - "Stopped MJPEG stream on port {1}. Switching to {0}.".format( - self.image_resolution, splitter_port - ) + "Stopped MJPEG stream on port {}. Switching to {}.", + splitter_port, + self.image_resolution, ) # Increase the resolution for taking an image @@ -436,7 +431,7 @@ class PiCameraStreamer(BaseCamera): """ for k in kwargs.keys(): logging.warning( - "Warning, kwarg %s is invalid for stop_stream_recording.", k + "Warning, kwarg {} is invalid for stop_stream_recording.", k ) with self.lock(timeout=None): # Reduce the resolution for video streaming @@ -470,9 +465,9 @@ class PiCameraStreamer(BaseCamera): ) else: logging.debug( - "Started MJPEG stream at {} on port {}".format( - self.stream_resolution, splitter_port - ) + "Started MJPEG stream at {} on port {}", + self.stream_resolution, + splitter_port, ) def capture( @@ -501,7 +496,7 @@ class PiCameraStreamer(BaseCamera): output_object (str/BytesIO): Target object. """ with self.lock: - logging.info("Capturing to {}".format(output)) + logging.info("Capturing to {}", (output)) # Set resolution and stop stream recording if necessary if not use_video_port: @@ -537,7 +532,7 @@ class PiCameraStreamer(BaseCamera): logging.debug("Creating PiRGBArray") with picamerax.array.PiRGBArray(self.camera) as output: - logging.info("Capturing to {}".format(output)) + logging.info("Capturing to {}", (output)) self.camera.capture(output, format="rgb", use_video_port=True) diff --git a/openflexure_microscope/camera/set_picamera_gain.py b/openflexure_microscope/camera/set_picamera_gain.py index d1d7175a..3e92b4b9 100644 --- a/openflexure_microscope/camera/set_picamera_gain.py +++ b/openflexure_microscope/camera/set_picamera_gain.py @@ -55,26 +55,17 @@ if __name__ == "__main__": # fix the shutter speed cam.shutter_speed = cam.exposure_speed - logging.info( - "Current a/d gains: {}, {}".format(cam.analog_gain, cam.digital_gain) - ) + logging.info("Current a/d gains: {}, {}", cam.analog_gain, cam.digital_gain) logging.info("Attempting to set analogue gain to 1") set_analog_gain(cam, 1) logging.info("Attempting to set digital gain to 1") set_digital_gain(cam, 1) - # The old code is left in here in case it is a useful example... - # ret = mmal.mmal_port_parameter_set_rational(cam._camera.control._port, - # MMAL_PARAMETER_DIGITAL_GAIN, - # to_rational(1)) - # print("Return code: {}".format(ret)) try: while True: logging.info( - "Current a/d gains: {}, {}".format( - cam.analog_gain, cam.digital_gain - ) + "Current a/d gains: {}, {}", cam.analog_gain, cam.digital_gain ) time.sleep(1) except KeyboardInterrupt: diff --git a/openflexure_microscope/captures/capture.py b/openflexure_microscope/captures/capture.py index 8f75bf6d..131f84ef 100644 --- a/openflexure_microscope/captures/capture.py +++ b/openflexure_microscope/captures/capture.py @@ -25,23 +25,23 @@ def make_file_list(directory, formats): glob.glob("{}/**/*.{}".format(directory, fmt.lower()), recursive=True) ) - logging.info("{} capture files found on disk".format(len(files))) + logging.info("{} capture files found on disk", (len(files))) return files def build_captures_from_exif(capture_path): - logging.debug("Reloading captures from {}...".format(capture_path)) + logging.debug("Reloading captures from {}...", (capture_path)) files = make_file_list(capture_path, EXIF_FORMATS) captures = OrderedDict() for f in files: - logging.debug("Reloading capture {}...".format(f)) + logging.debug("Reloading capture {}...", (f)) capture = capture_from_path(f) if capture: captures[capture.id] = capture - logging.info("{} capture files successfully reloaded".format(len(captures))) + logging.info("{} capture files successfully reloaded", (len(captures))) return captures @@ -58,7 +58,7 @@ def capture_from_path(path): capture.sync_basic_metadata() return capture except (InvalidImageDataError, json.decoder.JSONDecodeError): - logging.error("Invalid metadata at {}.".format(path)) + logging.error("Invalid metadata at {}.", (path)) return None @@ -75,7 +75,7 @@ class CaptureObject(object): # Store a nice ID self.id = uuid.uuid4() #: str: Unique capture ID - logging.debug("Created CaptureObject {}".format(self.id)) + logging.debug("Created CaptureObject {}", (self.id)) self.time = datetime.datetime.now() @@ -97,17 +97,17 @@ class CaptureObject(object): self.tags = [] def write(self, s): - logging.debug("Writing to %s", self) + logging.debug("Writing to {}", self) self.stream.write(s) def flush(self): - logging.info("Writing image data to disk %s", self.file) + logging.info("Writing image data to disk {}", self.file) with open(self.file, "wb") as outfile: outfile.write(self.stream.getbuffer()) self.stream.close() - logging.info("Writing metadata to disk %s", self.file) + logging.info("Writing metadata to disk {}", self.file) self._init_metadata() - logging.info("Finished writing to disk %s", self.file) + logging.info("Finished writing to disk {}", self.file) def open(self, mode): return open(self.file, mode) @@ -175,7 +175,7 @@ class CaptureObject(object): } def read_full_metadata(self): - logging.info("Reading full capture metadata from %s...", self.file) + logging.info("Reading full capture metadata from {}...", self.file) exif_dict = self._read_exif() return self._decode_usercomment(exif_dict) @@ -271,7 +271,7 @@ class CaptureObject(object): # Write new data to file EXIF, if supported if self.format.upper() in EXIF_FORMATS and self.exists: - logging.info("Writing Exif data to %s", self.file) + logging.info("Writing Exif data to {}", self.file) # Extract current Exif data exif_dict = self._read_exif() @@ -286,7 +286,7 @@ class CaptureObject(object): # Serialize metadata metadata_string = json.dumps(metadata_dict, cls=JSONEncoder) - logging.debug("Saving metadata string to file: %s", metadata_string) + logging.debug("Saving metadata string to file: {}", metadata_string) # Insert metadata into exif_dict exif_dict["Exif"][piexif.ExifIFD.UserComment] = metadata_string.encode() @@ -296,7 +296,7 @@ class CaptureObject(object): # Insert exif into file piexif.insert(exif_bytes, self.file) - logging.info("Finished writing Exif data to %s", self.file) + logging.info("Finished writing Exif data to {}", self.file) # PROPERTIES @@ -316,7 +316,7 @@ class CaptureObject(object): """ if self.exists: # If data file exists - logging.info("Opening from file {}".format(self.file)) + logging.info("Opening from file {}", (self.file)) with open(self.file, "rb") as f: d = io.BytesIO(f.read()) # Load bytes from file d.seek(0) # Rewind loaded bytestream @@ -364,7 +364,7 @@ class CaptureObject(object): """If the StreamObject has been saved, delete the file.""" if os.path.isfile(self.file): - logging.info("Deleting file {}".format(self.file)) + logging.info("Deleting file {}", (self.file)) os.remove(self.file) return True diff --git a/openflexure_microscope/captures/capture_manager.py b/openflexure_microscope/captures/capture_manager.py index e5e290eb..4b435016 100644 --- a/openflexure_microscope/captures/capture_manager.py +++ b/openflexure_microscope/captures/capture_manager.py @@ -61,7 +61,7 @@ class CaptureManager: self.close() def close(self): - logging.info("Closing {}".format(self)) + logging.info("Closing {}", (self)) # Close all StreamObjects for capture_list in [self.images.values(), self.videos.values()]: for stream_object in capture_list: @@ -75,9 +75,9 @@ class CaptureManager: """ if os.path.isdir(self.paths["temp"]): - logging.info("Clearing {}...".format(self.paths["temp"])) + logging.info("Clearing {}...", (self.paths["temp"])) shutil.rmtree(self.paths["temp"]) - logging.debug("Cleared {}.".format(self.paths["temp"])) + logging.debug("Cleared {}.", (self.paths["temp"])) def rebuild_captures(self): self.images = build_captures_from_exif(self.paths["default"]) @@ -158,7 +158,7 @@ class CaptureManager: # Update capture list capture_key = str(output.id) - logging.debug("Adding image %s with key %s", output, capture_key) + logging.debug("Adding image {} with key {}", output, capture_key) self.images[capture_key] = output @@ -205,7 +205,7 @@ class CaptureManager: # Update capture list capture_key = str(output.id) - logging.debug("Adding video %s with key %s", output, capture_key) + logging.debug("Adding video {} with key {}", output, capture_key) self.videos[capture_key] = output return output diff --git a/openflexure_microscope/config.py b/openflexure_microscope/config.py index f4ff2cdb..f13cab2c 100644 --- a/openflexure_microscope/config.py +++ b/openflexure_microscope/config.py @@ -86,7 +86,7 @@ def load_json_file(config_path) -> dict: """ config_path = os.path.expanduser(config_path) - logging.info("Loading {}...".format(config_path)) + logging.info("Loading {}...", config_path) with open(config_path) as config_file: try: @@ -109,7 +109,7 @@ def save_json_file(config_path: str, config_dict: dict): """ config_path = os.path.expanduser(config_path) - logging.info("Saving {}...".format(config_path)) + logging.info("Saving {}...", config_path) logging.debug(config_dict) with open(config_path, "w") as outfile: @@ -142,14 +142,14 @@ def initialise_file(config_path, populate: str = "{}\n"): """ config_path = os.path.expanduser(config_path) - logging.debug("Initialising {}".format(config_path)) - logging.debug("Exists: {}".format(os.path.exists(config_path))) + logging.debug("Initialising {}", (config_path)) + logging.debug("Exists: {}", (os.path.exists(config_path))) if not os.path.exists(config_path): # If user config file doesn't exist - logging.warning("No config file found at {}. Creating...".format(config_path)) + logging.warning("No config file found at {}. Creating...", (config_path)) create_file(config_path) - logging.info("Populating {}...".format(config_path)) + logging.info("Populating {}...", (config_path)) with open(config_path, "w") as outfile: outfile.write(populate) diff --git a/openflexure_microscope/microscope.py b/openflexure_microscope/microscope.py index b9b398e1..bfb22ae1 100644 --- a/openflexure_microscope/microscope.py +++ b/openflexure_microscope/microscope.py @@ -71,7 +71,7 @@ class Microscope: def close(self): """Shut down the microscope hardware.""" - logging.info("Closing {}".format(self)) + logging.info("Closing {}", (self)) if self.camera: try: self.camera.close() @@ -83,7 +83,7 @@ class Microscope: except TimeoutError as e: logging.error(e) self.captures.close() - logging.info("Closed {}".format(self)) + logging.info("Closed {}", (self)) def setup(self, configuration): """ @@ -196,7 +196,7 @@ class Microscope: Applies a settings dictionary to the microscope. Missing parameters will be left untouched. """ with self.lock: - logging.debug("Microscope: Applying settings: {}".format(settings)) + logging.debug("Microscope: Applying settings: {}", (settings)) # If attached to a camera if ("camera" in settings) and self.camera: @@ -329,14 +329,14 @@ class Microscope: # Load cached bits of metadata if cache_key: - logging.debug("Reading cached microscope metadata: %s", cache_key) + logging.debug("Reading cached microscope metadata: {}", cache_key) metadata = self.metadata_cache.get(cache_key, None) if not metadata: - logging.debug("Building and caching microscope metadata: %s", cache_key) + logging.debug("Building and caching microscope metadata: {}", cache_key) metadata = self.force_get_metadata() self.metadata_cache[cache_key] = metadata else: - logging.debug("Building microscope metadata: %s", cache_key) + logging.debug("Building microscope metadata: {}", cache_key) metadata = self.force_get_metadata() # Keys that should never be cached @@ -362,7 +362,7 @@ class Microscope: metadata: dict = None, cache_key: str = None, ): - logging.debug("Microscope capturing to %s", filename) + logging.debug("Microscope capturing to {}", filename) if not annotations: annotations = {} if not metadata: @@ -384,7 +384,7 @@ class Microscope: extras = {} if fmt == "jpeg": extras["thumbnail"] = (*THUMBNAIL_SIZE, 85) - logging.info("Starting microscope capture %s", output.file) + logging.info("Starting microscope capture {}", output.file) self.camera.capture( output, use_video_port=use_video_port, @@ -395,6 +395,6 @@ class Microscope: ) output.put_and_save(tags, annotations, full_metadata) - logging.debug("Finished capture to %s", output.file) + logging.debug("Finished capture to {}", output.file) return output diff --git a/openflexure_microscope/rescue/check_capture_reload.py b/openflexure_microscope/rescue/check_capture_reload.py index e50ae05b..843bc3c2 100644 --- a/openflexure_microscope/rescue/check_capture_reload.py +++ b/openflexure_microscope/rescue/check_capture_reload.py @@ -27,7 +27,7 @@ def main(): logging.info("Loading user settings...") settings = user_settings.load() cap_path = str(settings.get("captures", {}).get("paths", {}).get("default")) - logging.info("Capture path found: %s", cap_path) + logging.info("Capture path found: {}", cap_path) if not cap_path: logging.error( diff --git a/openflexure_microscope/rescue/monitor_timeout.py b/openflexure_microscope/rescue/monitor_timeout.py index 49af1169..879f6063 100644 --- a/openflexure_microscope/rescue/monitor_timeout.py +++ b/openflexure_microscope/rescue/monitor_timeout.py @@ -24,7 +24,7 @@ def launch_timeout_test_process(target, args=(), kwargs=None, timeout=10): # If thread is still active if p.is_alive(): logging.error( - "Function %s reached timeout after %s seconds. Terminating.", + "Function {} reached timeout after {} seconds. Terminating.", target, timeout, ) diff --git a/openflexure_microscope/stage/mock.py b/openflexure_microscope/stage/mock.py index 9e9eb20f..6d6dd856 100644 --- a/openflexure_microscope/stage/mock.py +++ b/openflexure_microscope/stage/mock.py @@ -80,13 +80,13 @@ class MissingStage(BaseStage): self._position = list(np.array(self._position) + np.array(initial_move)) logging.debug(np.array(self._position) + np.array(initial_move)) - logging.debug("New position: %s", self._position) + logging.debug("New position: {}", self._position) def move_abs(self, final, **kwargs): time.sleep(0.5) self._position = list(final) - logging.debug("New position: %s", self._position) + logging.debug("New position: {}", self._position) def zero_position(self): """Set the current position to zero""" diff --git a/openflexure_microscope/stage/sanga.py b/openflexure_microscope/stage/sanga.py index 549ab5b9..3389d83d 100644 --- a/openflexure_microscope/stage/sanga.py +++ b/openflexure_microscope/stage/sanga.py @@ -81,7 +81,7 @@ class SangaStage(BaseStage): @backlash.setter def backlash(self, blsh): - logging.debug("Setting backlash to {}".format(blsh)) + logging.debug("Setting backlash to {}", (blsh)) if blsh is None: self._backlash = None elif isinstance(blsh, Iterable): @@ -118,7 +118,7 @@ class SangaStage(BaseStage): backlash: (default: True) whether to correct for backlash. """ with self.lock: - logging.debug("Moving sangaboard by %s", displacement) + logging.debug("Moving sangaboard by {}", displacement) if not backlash or self.backlash is None: return self.board.move_rel(displacement, axis=axis) if axis is not None: @@ -158,7 +158,7 @@ class SangaStage(BaseStage): """Make an absolute move to a position """ with self.lock: - logging.debug("Moving sangaboard to %s", final) + logging.debug("Moving sangaboard to {}", final) self.board.move_abs(final, **kwargs) # Settle outside of the stage lock so that another move request # can just take over before settling @@ -262,7 +262,7 @@ class SangaDeltaStage(SangaStage): # Transform into delta coordinates displacement = np.dot(self.Tdv, displacement) - logging.debug("Delta displacement: {}".format(displacement)) + logging.debug("Delta displacement: {}", (displacement)) # Do the move SangaStage.move_rel(self, displacement, axis=None, backlash=backlash) @@ -274,7 +274,7 @@ class SangaDeltaStage(SangaStage): # Transform into delta coordinates final = np.dot(self.Tdv, final) - logging.debug("Delta final: {}".format(final)) + logging.debug("Delta final: {}", (final)) # Do the move SangaStage.move_abs(self, final, **kwargs) diff --git a/openflexure_microscope/utilities.py b/openflexure_microscope/utilities.py index e0756303..4ded53c7 100644 --- a/openflexure_microscope/utilities.py +++ b/openflexure_microscope/utilities.py @@ -22,7 +22,7 @@ class Timer(object): def __exit__(self, type_, value, traceback): self.end = time.time() - logging.debug("%s time: %s", self.name, self.end - self.start) + logging.debug("{} time: {}", self.name, self.end - self.start) def deserialise_array_b64(b64_string, dtype, shape): diff --git a/tests/api_client.py b/tests/api_client.py deleted file mode 100644 index 43ad4942..00000000 --- a/tests/api_client.py +++ /dev/null @@ -1,101 +0,0 @@ -from io import BytesIO - -import numpy as np -import requests -from PIL import Image - - -class APIconnection: - def __init__(self, host="localhost", port=5000, api_ver="v1"): - self.base = self.build_base(host=host, port=port, api_ver=api_ver) - - def build_base(self, host, port, api_ver): - return "http://{}:{}/api/{}".format(host, port, api_ver) - - def uri(self, suffix): - return self.base + suffix - - def get(self, route, json=True, timeout=5): - r = requests.get(self.uri(route), timeout=timeout) - if json: - return r.json() - else: - return r - - def post(self, route, json=None, timeout=5): - r = requests.post(self.uri(route), json=json, timeout=timeout) - return r.json() - - def delete(self, route, timeout=5): - r = requests.delete(self.uri(route), timeout=timeout) - return r.json() - - def set_overlay(self, message="", size=50): - json = {"text": message, "size": size} - return self.post("/camera/overlay", json=json) - - def get_overlay(self): - return self.get("/camera/overlay") - - def get_config(self): - return self.get("/config") - - def set_config(self, config_dict): - return self.post("/config", json=config_dict) - - def get_state(self): - return self.get("/state") - - def start_preview(self): - return self.post("/camera/preview/start") - - def stop_preview(self): - return self.post("/camera/preview/stop") - - def move_by(self, x=0, y=0, z=0): - json = {"x": x, "y": y, "z": z} - return self.post("/stage/position", json=json) - - def new_capture(self, use_video_port=True, keep_on_disk=False, resize=None): - json = {"keep_on_disk": keep_on_disk, "use_video_port": use_video_port} - - if resize: - json["size"] = {"width": resize[0], "height": resize[1]} - - return self.post("/camera/capture", json=json) - - def get_capture(self, capture_id): - uri_route = "/camera/capture/{}/download".format(capture_id) - r = self.get(uri_route, json=False) - img = Image.open(BytesIO(r.content)) - array = np.asarray(img, dtype=np.int32) - return array - - def del_capture(self, capture_id): - uri_route = "/camera/capture/{}".format(capture_id) - return self.delete(uri_route) - - def capture( - self, - use_video_port=True, - keep_on_disk=False, - delete_after_use=True, - resize=None, - ): - p = self.new_capture( - use_video_port=use_video_port, keep_on_disk=keep_on_disk, resize=resize - ) - capture_id = p["metadata"]["id"] - img_array = self.get_capture(capture_id) - - if delete_after_use: - self.del_capture(capture_id) - - return img_array - - def set_zoom(self, zoom_value=1.0): - json = {"zoom_value": zoom_value} - return self.post("/camera/zoom", json=json) - - def get_zoom(self): - return self.get("/camera/zoom") diff --git a/tests/test_api.py b/tests/test_api.py deleted file mode 100644 index c7b38e35..00000000 --- a/tests/test_api.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env python -import logging -import sys -import unittest - -import numpy as np -from api_client import APIconnection - -logging.basicConfig(stream=sys.stderr, level=logging.INFO) - - -class TestCapture(unittest.TestCase): - def test_capture_config(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - config = connection.get_config() - - expected_keys = ["image_resolution", "stream_resolution", "numpy_resolution"] - - for key in expected_keys: - self.assertTrue(key in config) - - def test_capture_videoport(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - resolution = connection.get_config()["stream_resolution"] - - for resize in [None, (640, 480)]: - - if resize: - resolution = resize - - capture_array = connection.capture( - use_video_port=True, - keep_on_disk=False, - delete_after_use=True, - resize=resize, - ) - - self.assertTrue(capture_array.shape == (resolution[1], resolution[0], 3)) - - def test_capture_full(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - resolution = connection.get_config()["image_resolution"] - - for resize in [None, (640, 480)]: - - if resize: - resolution = resize - - capture_array = connection.capture( - use_video_port=False, - keep_on_disk=False, - delete_after_use=True, - resize=resize, - ) - - self.assertTrue(capture_array.shape == (resolution[1], resolution[0], 3)) - - -class TestStage(unittest.TestCase): - def test_stage_config(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - config = connection.get_config() - - expected_keys = ["backlash"] - - for key in expected_keys: - self.assertTrue(key in config) - - def test_stage_state(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - state = connection.get_state() - - self.assertTrue("stage" in state) - - expected_keys = ["position"] - - for key in expected_keys: - self.assertTrue(key in state["stage"]) - - def test_stage_movement(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - - move_distance = 500 - for axis in range(3): - for direction in [1, -1]: - pos_i_dict = connection.get_state()["stage"]["position"] - pos_i = [pos_i_dict["x"], pos_i_dict["y"], pos_i_dict["z"]] - - move = [0, 0, 0] - move[axis] = move_distance * direction - - connection.move_by(*move) - - pos_f_dict = connection.get_state()["stage"]["position"] - pos_f = [pos_f_dict["x"], pos_f_dict["y"], pos_f_dict["z"]] - - diff = np.subtract(pos_f, pos_i) - logging.debug("{} > {}".format(pos_i, pos_f)) - - self.assertTrue(np.array_equal(diff, move)) - - -if __name__ == "__main__": - - suites = [ - unittest.TestLoader().loadTestsFromTestCase(TestCapture), - unittest.TestLoader().loadTestsFromTestCase(TestStage), - ] - - alltests = unittest.TestSuite(suites) - - result = unittest.TextTestRunner(verbosity=2).run(alltests) diff --git a/tests/test_camera.py b/tests/test_camera.py deleted file mode 100644 index 1d2723fb..00000000 --- a/tests/test_camera.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env python -import io -import logging -import os -import sys -import time -import unittest - -import numpy as np -from PIL import Image - -from openflexure_microscope.camera.pi import CaptureObject, PiCameraStreamer - -logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) - - -class TestCaptureMethods(unittest.TestCase): - def test_still_capture(self): - """Tests capturing still images to a BytesIO stream.""" - global camera - - for use_video_port in [True, False]: - for resize in [None, (640, 480)]: - - # Wait for camera - camera.wait_for_camera() - - # Capture to a context (auto-deletes files when done) - with camera.new_image() as output: - - camera.capture(output, use_video_port=use_video_port, resize=resize) - - # Ensure file deletion fails and returns False - self.assertFalse(output.delete_file()) - # Ensure capture not stored to file - self.assertFalse(os.path.isfile(output.file)) - - # BEFORE DELETE: Ensure StreamObject 'stream' has - # a valid BytesIO object and byte string - self.assertTrue(isinstance(output.data, io.IOBase)) - self.assertTrue(isinstance(output.binary, (bytes, bytearray))) - - # Save capture to file - output.save_file() - # Check file got saved - self.assertTrue(os.path.isfile(output.file)) - - # Delete file - output.delete_file() - # Check file got deleted - self.assertFalse(os.path.isfile(output.file)) - - # AFTER DELETE: Ensure StreamObject 'stream' has - # a valid BytesIO object and byte string - self.assertTrue(isinstance(output.data, io.IOBase)) - self.assertTrue(isinstance(output.binary, (bytes, bytearray))) - - # Create a PIL image from stream - image = Image.open(output.data) - - # Ensure a valid PIL image was created - self.assertTrue(isinstance(image, Image.Image)) - - # Calculate expected dimensions - if resize: - dims = resize - else: - if use_video_port: - dims = camera.stream_resolution - else: - dims = camera.image_resolution - - # Ensure PIL image size matches expected size - print(image.size, dims) - self.assertTrue(image.size == dims) - - def test_still_store(self): - """Tests capturing still images to a file on disk.""" - global camera - - for use_video_port in [True, False]: - for resize in [None, (640, 480)]: - # Wait for camera - camera.wait_for_camera() - - # Capture - output = camera.capture( - camera.new_image().file, - use_video_port=use_video_port, - resize=resize, - ) - - # Check file got saved - self.assertTrue(os.path.isfile(output.file)) - statinfo = os.stat(output.file) - self.assertTrue(statinfo.st_size > 0) - - # Ensure StreamObject 'stream' has - # a valid BytesIO object and byte string - self.assertTrue(isinstance(output.data, io.IOBase)) - self.assertTrue(isinstance(output.binary, (bytes, bytearray))) - - # Ensure file deletion completes and returns True - self.assertTrue(output.delete_file()) - - # Check file got deleted - self.assertFalse(os.path.isfile(output.file)) - - -class TestUnencodedMethods(unittest.TestCase): - def test_yuv_array(self): - """Tests capturing unencoded YUV data to a Numpy array.""" - global camera - - for use_video_port in [True, False]: - for resize in [None, (640, 480)]: - - print("{}, {}, {}".format(id, resize, use_video_port)) - - # Wait for camera - camera.wait_for_camera() - - # Capture RGB array - yuv = camera.yuv(use_video_port=use_video_port, resize=resize) - - # Ensure capture output is a valid numpy array - self.assertTrue(isinstance(yuv, np.ndarray)) - - # Calculate expected dimensions - if resize: - dims = resize - else: - if use_video_port: - dims = camera.stream_resolution - else: - dims = camera.numpy_resolution - - # Ensure array shape matches expected dimensions - self.assertTrue(yuv.shape == (dims[1], dims[0], 3)) - - def test_rgb_array(self): - """Tests capturing unencoded YUV/RGB data to a Numpy array.""" - global camera - - for use_video_port in [True, False]: - for resize in [None, (640, 480)]: - - print("{}, {}, {}".format(id, resize, use_video_port)) - - # Wait for camera - camera.wait_for_camera() - - # Capture RGB array - rgb = camera.array(use_video_port=use_video_port, resize=resize) - - # Ensure capture output is a valid numpy array - self.assertTrue(isinstance(rgb, np.ndarray)) - - # Calculate expected dimensions - if resize: - dims = resize - else: - if use_video_port: - dims = camera.stream_resolution - else: - dims = camera.numpy_resolution - - # Ensure array shape matches expected dimensions - self.assertTrue(rgb.shape == (dims[1], dims[0], 3)) - - -class TestRecordMethods(unittest.TestCase): - def test_video_record(self): - """Tests recording videos to BytesIO stream, and to file on disk.""" - global camera - - # Wait for camera - camera.wait_for_camera() - - with camera.new_video() as output: - - # Start recording - camera.start_recording(output) - - # Record for 2 seconds - time.sleep(2) - # Stop recording - camera.stop_recording() - - # Check stream - self.assertTrue(isinstance(output.data, io.IOBase)) - self.assertTrue(isinstance(output.binary, (bytes, bytearray))) - - # Check file - statinfo = os.stat(output.file) - self.assertTrue(statinfo.st_size > 0) - - # Log path - temp_path = output.file - - # Check file got deleted on __exit__ - self.assertFalse(os.path.isfile(temp_path)) - - time.sleep(0.25) - - def test_video_store(self): - """Tests recording videos to file on disk, without context manager.""" - global camera - - # Wait for camera - camera.wait_for_camera() - - # Start recording - output = camera.start_recording(camera.new_video()) - - # Record for 2 seconds - time.sleep(2) - # Stop recording - camera.stop_recording() - - # Check file - statinfo = os.stat(output.file) - self.assertTrue(statinfo.st_size > 0) - - # Ensure file deletion completes and returns True - self.assertTrue(output.delete_file()) - - # Check file got deleted - self.assertFalse(os.path.isfile(output.file)) - - -class TestThreadStarting(unittest.TestCase): - def test_restarting_stream(self): - """Tests that a capture call restarts the camera worker thread.""" - global camera - - # Wait for camera - camera.wait_for_camera() - - # Force-stop the camera worker thread (should return True) - self.assertTrue(camera.stop_worker()) - - # Check Pi camera has been disconnected - self.assertTrue(camera.camera) - - # Restart worker thread - self.assertTrue(camera.start_worker()) - - self.assertTrue(camera.camera) - self.assertTrue(camera.thread) - self.assertIsInstance(camera.stream, io.IOBase) - - -if __name__ == "__main__": - with PiCameraStreamer() as camera: - - suites = [ - unittest.TestLoader().loadTestsFromTestCase(TestCaptureMethods), - unittest.TestLoader().loadTestsFromTestCase(TestUnencodedMethods), - unittest.TestLoader().loadTestsFromTestCase(TestThreadStarting), - unittest.TestLoader().loadTestsFromTestCase(TestRecordMethods), - ] - - alltests = unittest.TestSuite(suites) - - result = unittest.TextTestRunner(verbosity=2).run(alltests) diff --git a/tests/test_plugins.py b/tests/test_plugins.py deleted file mode 100644 index 431e70a8..00000000 --- a/tests/test_plugins.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -import atexit -import logging -import sys -import unittest - -from openflexure_stage import OpenFlexureStage - -from openflexure_microscope import Microscope, config -from openflexure_microscope.camera.pi import PiCameraStreamer - -logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) - - -class TestPluginMethods(unittest.TestCase): - def test_plugin_load(self): - plugin_arr = microscope.plugins.plugins - - plugin_names = [plugin[0] for plugin in plugin_arr] - - self.assertTrue("testing" in plugin_names) - - def test_camera_access(self): - identify = microscope.plugins.testing.identify() - self.assertTrue(identify[0] is microscope.camera) - - def test_stage_access(self): - identify = microscope.plugins.testing.identify() - self.assertTrue(identify[1] is microscope.stage) - - -if __name__ == "__main__": - - with Microscope() as microscope: - - microscope.attach(PiCameraStreamer(), OpenFlexureStage()) - - microscope.plugins.attach("openflexure_microscope.plugins.testing:Plugin") - - suites = [unittest.TestLoader().loadTestsFromTestCase(TestPluginMethods)] - - alltests = unittest.TestSuite(suites) - - result = unittest.TextTestRunner(verbosity=2).run(alltests) diff --git a/tests/test_stage.py b/tests/test_stage.py deleted file mode 100644 index 90ee10d4..00000000 --- a/tests/test_stage.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python -import logging -import sys -import unittest - -import numpy as np -from openflexure_stage import OpenFlexureStage - -logging.basicConfig(stream=sys.stderr, level=logging.INFO) - - -class TestMicroscope(unittest.TestCase): - def test_movement(self): - move_distance = 500 - for axis in range(3): - for direction in [1, -1]: - pos_i = stage.position - logging.debug(pos_i) - - logging.info( - "Moving axis {} by {}".format(axis, move_distance * direction) - ) - move = [0, 0, 0] - move[axis] = move_distance * direction - - stage.move_rel(move) - - pos_f = stage.position - diff = np.subtract(pos_f, pos_i) - logging.debug("{} > {}".format(pos_i, pos_f)) - - self.assertTrue(np.array_equal(diff, move)) - - -if __name__ == "__main__": - with OpenFlexureStage("/dev/ttyUSB0") as stage: - - suites = [unittest.TestLoader().loadTestsFromTestCase(TestMicroscope)] - - alltests = unittest.TestSuite(suites) - - result = unittest.TextTestRunner(verbosity=2).run(alltests) From 00d30d8e8523e987276794e9fed5efcb129419fc Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 15:15:21 +0000 Subject: [PATCH 10/24] Removed pre-commit from Poetry dependencies --- README.md | 6 +- poetry.lock | 164 +------------------------------------------------ pyproject.toml | 1 - 3 files changed, 3 insertions(+), 168 deletions(-) diff --git a/README.md b/README.md index faa41e9b..55b83c66 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,12 @@ This includes installing the server in a mode better suited for active developme We make use of pre-commit hooks to run code analysis before committing to the codebase. -The simplest way to ensure this works is to install pre-commits into your global Python environment: +The simplest way to ensure this works is to install pre-commits into your current Python environment: * `pip3 install pre-commit` * `pre-commit install` -Alternatively, you can work within the Poetry project's virtual environment where pre-commit will already be installed as a developer dependency. - -Finally, to run all analysis within installing pre-commits to Git, you can run `pre-commit run`. +To run pre-commit analysis manually, you can run `pre-commit run`. ### Node installation diff --git a/poetry.lock b/poetry.lock index 57e04fc1..8415d386 100644 --- a/poetry.lock +++ b/poetry.lock @@ -128,14 +128,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "cfgv" -version = "3.2.0" -description = "Validate configuration and produce human readable error messages." -category = "dev" -optional = false -python-versions = ">=3.6.1" - [[package]] name = "chardet" version = "3.0.4" @@ -172,14 +164,6 @@ python-versions = "*" doc = ["sphinx"] test = ["pytest", "coverage", "mock"] -[[package]] -name = "distlib" -version = "0.3.1" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "docutils" version = "0.16" @@ -199,14 +183,6 @@ python-versions = "*" [package.extras] tests = ["dill", "coverage", "coveralls", "mock", "nose"] -[[package]] -name = "filelock" -version = "3.0.12" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "flask" version = "1.1.2" @@ -246,17 +222,6 @@ category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "identify" -version = "1.5.9" -description = "File identification library for Python" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" - -[package.extras] -license = ["editdistance"] - [[package]] name = "idna" version = "2.10" @@ -281,35 +246,6 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[[package]] -name = "importlib-metadata" -version = "2.0.0" -description = "Read metadata from Python packages" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] - -[[package]] -name = "importlib-resources" -version = "3.3.0" -description = "Read resources from Python packages" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" - -[package.dependencies] -zipp = {version = ">=0.4", markers = "python_version < \"3.8\""} - -[package.extras] -docs = ["sphinx", "rst.linker", "jaraco.packaging"] - [[package]] name = "isort" version = "5.6.4" @@ -400,14 +336,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "nodeenv" -version = "1.5.0" -description = "Node.js virtual environment builder" -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "numpy" version = "1.19.2" @@ -463,24 +391,6 @@ category = "main" optional = false python-versions = ">=3.5" -[[package]] -name = "pre-commit" -version = "2.8.2" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" -optional = false -python-versions = ">=3.6.1" - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -importlib-resources = {version = "*", markers = "python_version < \"3.7\""} -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -toml = "*" -virtualenv = ">=20.0.8" - [[package]] name = "psutil" version = "5.7.3" @@ -788,26 +698,6 @@ brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -[[package]] -name = "virtualenv" -version = "20.1.0" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" - -[package.dependencies] -appdirs = ">=1.4.3,<2" -distlib = ">=0.3.1,<1" -filelock = ">=3.0.0,<4" -importlib-metadata = {version = ">=0.12,<3", markers = "python_version < \"3.8\""} -importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} -six = ">=1.9.0,<2" - -[package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] - [[package]] name = "webargs" version = "6.1.1" @@ -857,25 +747,13 @@ python-versions = "*" [package.dependencies] ifaddr = ">=0.1.7" -[[package]] -name = "zipp" -version = "3.4.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] - [extras] rpi = ["RPi.GPIO"] [metadata] lock-version = "1.1" python-versions = "^3.6.1" -content-hash = "f1a0a5f3d5b31f79086350e20993ea51a5a0e13b084f1329b2594d40c8eae475" +content-hash = "747d39deb1de397ea0c74dfa25a565505195ece1cc4185834ada7d94f58bb5ab" [metadata.files] alabaster = [ @@ -918,10 +796,6 @@ certifi = [ {file = "certifi-2020.11.8-py2.py3-none-any.whl", hash = "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd"}, {file = "certifi-2020.11.8.tar.gz", hash = "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"}, ] -cfgv = [ - {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, - {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, -] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, @@ -938,10 +812,6 @@ colorzero = [ {file = "colorzero-1.1-py2.py3-none-any.whl", hash = "sha256:e3c36d15b293de2b2f77ff54a5bd243fffac941ed0a5332d0697a6612a26a0a3"}, {file = "colorzero-1.1.tar.gz", hash = "sha256:acba47119b5d8555680d3cda9afe6ccc5481385ccc3c00084dd973f7aa184599"}, ] -distlib = [ - {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, - {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, -] docutils = [ {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, @@ -949,10 +819,6 @@ docutils = [ expiringdict = [ {file = "expiringdict-1.2.1.tar.gz", hash = "sha256:fe2ba427220425c3c8a3d29f6d2e2985bcee323f8bcd4021e68ebefbd90d8250"}, ] -filelock = [ - {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, - {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, -] flask = [ {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, @@ -964,10 +830,6 @@ flask-cors = [ future = [ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, ] -identify = [ - {file = "identify-1.5.9-py2.py3-none-any.whl", hash = "sha256:5dd84ac64a9a115b8e0b27d1756b244b882ad264c3c423f42af8235a6e71ca12"}, - {file = "identify-1.5.9.tar.gz", hash = "sha256:c9504ba6a043ee2db0a9d69e43246bc138034895f6338d5aed1b41e4a73b1513"}, -] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, @@ -980,14 +842,6 @@ imagesize = [ {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"}, {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, ] -importlib-metadata = [ - {file = "importlib_metadata-2.0.0-py2.py3-none-any.whl", hash = "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3"}, - {file = "importlib_metadata-2.0.0.tar.gz", hash = "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da"}, -] -importlib-resources = [ - {file = "importlib_resources-3.3.0-py2.py3-none-any.whl", hash = "sha256:a3d34a8464ce1d5d7c92b0ea4e921e696d86f2aa212e684451cb1482c8d84ed5"}, - {file = "importlib_resources-3.3.0.tar.gz", hash = "sha256:7b51f0106c8ec564b1bef3d9c588bc694ce2b92125bbb6278f4f2f5b54ec3592"}, -] isort = [ {file = "isort-5.6.4-py3-none-any.whl", hash = "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7"}, {file = "isort-5.6.4.tar.gz", hash = "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58"}, @@ -1065,10 +919,6 @@ mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] -nodeenv = [ - {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, - {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, -] numpy = [ {file = "numpy-1.19.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a"}, {file = "numpy-1.19.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd"}, @@ -1153,10 +1003,6 @@ pillow = [ {file = "Pillow-7.2.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d"}, {file = "Pillow-7.2.0.tar.gz", hash = "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626"}, ] -pre-commit = [ - {file = "pre_commit-2.8.2-py2.py3-none-any.whl", hash = "sha256:22e6aa3bd571debb01eb7d34483f11c01b65237be4eebbf30c3d4fb65762d315"}, - {file = "pre_commit-2.8.2.tar.gz", hash = "sha256:905ebc9b534b991baec87e934431f2d0606ba27f2b90f7f652985f5a5b8b6ae6"}, -] psutil = [ {file = "psutil-5.7.3-cp27-none-win32.whl", hash = "sha256:1cd6a0c9fb35ece2ccf2d1dd733c1e165b342604c67454fd56a4c12e0a106787"}, {file = "psutil-5.7.3-cp27-none-win_amd64.whl", hash = "sha256:e02c31b2990dcd2431f4524b93491941df39f99619b0d312dfe1d4d530b08b4b"}, @@ -1319,10 +1165,6 @@ urllib3 = [ {file = "urllib3-1.25.11-py2.py3-none-any.whl", hash = "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"}, {file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"}, ] -virtualenv = [ - {file = "virtualenv-20.1.0-py2.py3-none-any.whl", hash = "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2"}, - {file = "virtualenv-20.1.0.tar.gz", hash = "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"}, -] webargs = [ {file = "webargs-6.1.1-py2.py3-none-any.whl", hash = "sha256:2ead6ce38559152043ab4ada4d389aef6c804b0c169482e7c92b3f498f690b2c"}, {file = "webargs-6.1.1.tar.gz", hash = "sha256:412ecadd977afdea0ed6fa5f5b65ddd13a099269e622ec537f9c74c443ce4d0b"}, @@ -1338,7 +1180,3 @@ zeroconf = [ {file = "zeroconf-0.28.6-py3-none-any.whl", hash = "sha256:17ae1e1681091b91b0337517db222eae1807003154c01b0bd0ab99572dfeafd8"}, {file = "zeroconf-0.28.6.tar.gz", hash = "sha256:70f10f0f16e3a8c4eb5e1a106b812b8d052253041cf1ee1195933df706f5261c"}, ] -zipp = [ - {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, - {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, -] diff --git a/pyproject.toml b/pyproject.toml index f73c45d7..7e8fd28e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,6 @@ sphinxcontrib-httpdomain = "^1.7" rope = "^0.14.0" pylint = "^2.3" black = {version = "^18.3-alpha.0",allow-prereleases = true} -pre-commit = "^2.8.2" [tool.black] exclude = '(\.eggs|\.git|\.venv|node_modules/)' From 1f0b90cff1d5814b28c7646410cf64886b313229 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 15:18:37 +0000 Subject: [PATCH 11/24] Added rescue to Poetry scripts --- openflexure_microscope/rescue/auto.py | 8 +++++--- pyproject.toml | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/openflexure_microscope/rescue/auto.py b/openflexure_microscope/rescue/auto.py index bc4c52bc..9c195113 100644 --- a/openflexure_microscope/rescue/auto.py +++ b/openflexure_microscope/rescue/auto.py @@ -46,9 +46,7 @@ else: logger.setLevel(logging.WARNING) -if __name__ == "__main__": - spoof = False - +def main(): print() print(bcolors.HEADER + "OpenFlexure Rescue" + bcolors.ENDC) print() @@ -88,3 +86,7 @@ if __name__ == "__main__": print() for err in error_sources: print(err.message) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 7e8fd28e..6087dfe0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ rpi = ["RPi.GPIO"] [tool.poetry.scripts] build_static = 'openflexure_microscope.install:build' +rescue = 'openflexure_microscope.rescue.auto:main' [tool.poetry.dev-dependencies] pynpm = "^0.1.2" From e25c23cf1c363282bb370fabaf41212ce5be29db Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 15:20:12 +0000 Subject: [PATCH 12/24] Removed unused files from root --- .gitattributes | 0 Dockerfile | 20 -------------------- 2 files changed, 20 deletions(-) delete mode 100644 .gitattributes delete mode 100644 Dockerfile diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index e69de29b..00000000 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index c9cbf4ad..00000000 --- a/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM python:3.6 - -ENV PYTHONUNBUFFERED=1 \ - POETRY_VERSION=1.0.3 \ - POETRY_HOME="/opt/poetry" \ - POETRY_VIRTUALENVS_IN_PROJECT=true \ - POETRY_NO_INTERACTION=1 - -ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH" - -WORKDIR /app - -RUN curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python - -COPY . ./ - -RUN poetry install --no-dev --no-interaction - -EXPOSE 5000 -CMD ["poetry", "run", "python", "-m", "openflexure_microscope.api.app"] From 6fb61e1e3f183c723d53b3ae38c4afccb4621643 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 15:45:58 +0000 Subject: [PATCH 13/24] Reverted logging style --- .pylintrc | 6 +-- openflexure_microscope/api/app.py | 2 +- .../api/default_extensions/__init__.py | 2 +- .../api/default_extensions/autofocus.py | 10 ++--- .../api/default_extensions/autostorage.py | 6 +-- .../recalibrate_utils.py | 10 ++--- .../api/default_extensions/scan.py | 16 ++++---- .../api/default_extensions/zip_builder.py | 2 +- .../api/dev_extensions/tools.py | 2 +- .../api/utilities/__init__.py | 4 +- openflexure_microscope/api/utilities/gui.py | 2 +- .../api/v2/views/actions/stage.py | 4 +- openflexure_microscope/camera/base.py | 4 +- openflexure_microscope/camera/pi.py | 40 +++++++++---------- .../camera/set_picamera_gain.py | 4 +- openflexure_microscope/captures/capture.py | 32 +++++++-------- .../captures/capture_manager.py | 10 ++--- openflexure_microscope/config.py | 12 +++--- openflexure_microscope/microscope.py | 18 ++++----- .../rescue/check_capture_reload.py | 2 +- .../rescue/monitor_timeout.py | 2 +- openflexure_microscope/stage/mock.py | 4 +- openflexure_microscope/stage/sanga.py | 10 ++--- openflexure_microscope/utilities.py | 2 +- 24 files changed, 100 insertions(+), 106 deletions(-) diff --git a/.pylintrc b/.pylintrc index 7dc2c3fe..9aa90314 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,8 +1,4 @@ [MESSAGES CONTROL] disable=fixme,C,R -max-line-length = 88 - -[LOGGING] - -logging-format-style=new \ No newline at end of file +max-line-length = 88 \ No newline at end of file diff --git a/openflexure_microscope/api/app.py b/openflexure_microscope/api/app.py index 998be592..ebb77d19 100644 --- a/openflexure_microscope/api/app.py +++ b/openflexure_microscope/api/app.py @@ -72,7 +72,7 @@ root_log.addHandler(fh) access_log.addHandler(afh) # Log server paths being used -logging.info("Running with data path {}", OPENFLEXURE_VAR_PATH) +logging.info("Running with data path %s", OPENFLEXURE_VAR_PATH) logging.info("Creating app") # Create flask app diff --git a/openflexure_microscope/api/default_extensions/__init__.py b/openflexure_microscope/api/default_extensions/__init__.py index e76733f9..561239af 100644 --- a/openflexure_microscope/api/default_extensions/__init__.py +++ b/openflexure_microscope/api/default_extensions/__init__.py @@ -10,7 +10,7 @@ def handle_extension_error(extension_name): yield except Exception: # pylint: disable=W0703 logging.error( - "Exception loading builtin extension {}: \n{}", + "Exception loading builtin extension %s: \n%s", extension_name, traceback.format_exc(), ) diff --git a/openflexure_microscope/api/default_extensions/autofocus.py b/openflexure_microscope/api/default_extensions/autofocus.py index cc30a908..a9e3f0e4 100644 --- a/openflexure_microscope/api/default_extensions/autofocus.py +++ b/openflexure_microscope/api/default_extensions/autofocus.py @@ -83,7 +83,7 @@ class JPEGSharpnessMonitor: raise e if stop < 1: stop = len(jpeg_times) - logging.debug("changing stop to {}", (stop)) + logging.debug("changing stop to %s", (stop)) jpeg_times = jpeg_times[start:stop] jpeg_zs = np.interp(jpeg_times, stage_times, stage_zs) return jpeg_times, jpeg_zs, jpeg_sizes[start:stop] @@ -258,13 +258,12 @@ def fast_up_down_up_autofocus( # Ensure the MJPEG stream has started microscope.camera.start_stream_recording() - df = dz # TODO: refactor so I actually use dz in the code below! logging.debug("Initial move") if initial_move_up: - m.focus_rel(df / 2) + m.focus_rel(dz / 2) # move down logging.debug("Move down") - i, z = m.focus_rel(-df) + i, z = m.focus_rel(-dz) # now inspect where the sharpest point is, and estimate the sharpness # (JPEG size) that we should find at the start of the Z stack _, jz, js = m.move_data(i) @@ -285,14 +284,13 @@ def fast_up_down_up_autofocus( inow = np.argmax( js < current_js ) # use the curve we recorded to estimate our position - # TODO: fancy interpolation stuff # So, the Z position corresponding to our current sharpness value is zs[inow] # That means we should move forwards, by best_z - zs[inow] logging.debug("Correction move") correction_move = best_z + target_z - jz[inow] logging.debug( - "Fast autofocus scan: correcting backlash by moving {} steps", + "Fast autofocus scan: correcting backlash by moving %s steps", (correction_move), ) m.focus_rel(correction_move) diff --git a/openflexure_microscope/api/default_extensions/autostorage.py b/openflexure_microscope/api/default_extensions/autostorage.py index 8a7c4abe..e54f3aea 100644 --- a/openflexure_microscope/api/default_extensions/autostorage.py +++ b/openflexure_microscope/api/default_extensions/autostorage.py @@ -95,10 +95,10 @@ class AutostorageExtension(BaseExtension): def on_microscope(self, microscope_obj): """Function to automatically call when the parent LabThing has a microscope attached.""" - logging.debug("Autostorage extension found microscope {}", microscope_obj) + logging.debug("Autostorage extension found microscope %s", microscope_obj) if hasattr(microscope_obj, "captures"): logging.debug( - "Autostorage extension bound to CaptureManager {}", self.capture_manager + "Autostorage extension bound to CaptureManager %s", self.capture_manager ) # Store a reference to the CaptureManager @@ -117,7 +117,7 @@ class AutostorageExtension(BaseExtension): # If preferred path does not exist, or cannot be written to if not (os.path.isdir(location) and check_rw(location)): logging.error( - "Preferred capture path {} is missing or cannot be written to. Restoring defaults.", + "Preferred capture path %s is missing or cannot be written to. Restoring defaults.", location, ) # Reset the storage location to default diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py index 6aa70937..fd18cf46 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -63,15 +63,15 @@ def auto_expose_and_freeze_settings(camera): logging.info("Freezing the camera settings...") camera.shutter_speed = camera.exposure_speed - logging.info("Shutter speed = {}", (camera.shutter_speed)) + logging.info("Shutter speed = %s", (camera.shutter_speed)) camera.exposure_mode = "off" logging.info("Auto exposure disabled") g = camera.awb_gains camera.awb_mode = "off" camera.awb_gains = g - logging.info("Auto white balance disabled, gains are {}", (g)) + logging.info("Auto white balance disabled, gains are %s", (g)) logging.info( - "Analogue gain: {}, Digital gain: {}", camera.analog_gain, camera.digital_gain + "Analogue gain: %s, Digital gain: %s", camera.analog_gain, camera.digital_gain ) adjust_exposure_to_setpoint(camera, 215) @@ -98,7 +98,7 @@ def lst_from_channels(channels): # lst_resolution = list(np.ceil(full_resolution / 64.0).astype(int)) lst_resolution = [(r // 64) + 1 for r in full_resolution] # NB the size of the LST is 1/64th of the image, but rounded UP. - logging.info("Generating a lens shading table at {}x{}", *lst_resolution) + logging.info("Generating a lens shading table at %sx%s", *lst_resolution) lens_shading = np.zeros([channels.shape[0]] + lst_resolution, dtype=np.float) for i in range(lens_shading.shape[0]): image_channel = channels[i, :, :] @@ -116,7 +116,7 @@ def lst_from_channels(channels): image_channel, [(0, lw * 32 - iw), (0, lh * 32 - ih)], mode="edge" ) # Pad image to the right and bottom logging.info( - "Channel shape: {}x{}, shading table shape: {}x{}, after padding {}", + "Channel shape: %sx%s, shading table shape: %sx%s, after padding %s", iw, ih, lw * 32, diff --git a/openflexure_microscope/api/default_extensions/scan.py b/openflexure_microscope/api/default_extensions/scan.py index 52b9e0ec..8eeb78bc 100644 --- a/openflexure_microscope/api/default_extensions/scan.py +++ b/openflexure_microscope/api/default_extensions/scan.py @@ -216,7 +216,7 @@ class ScanExtension(BaseExtension): for x_y in line: # Move to new grid position without changing z - logging.debug("Moving to step {}", ([x_y[0], x_y[1], next_z])) + logging.debug("Moving to step %s", ([x_y[0], x_y[1], next_z])) microscope.stage.move_abs([x_y[0], x_y[1], next_z]) # Refocus if autofocus_enabled: @@ -270,11 +270,11 @@ class ScanExtension(BaseExtension): # Make sure we use our current best estimate of focus (i.e. the current position) next point next_z = microscope.stage.position[2] - logging.debug("Returning to {}", (initial_position)) + logging.debug("Returning to %s", (initial_position)) microscope.stage.move_abs(initial_position) end = time.time() - logging.info("Scan took {} seconds", end - start) + logging.info("Scan took %s seconds", end - start) def stack( self, @@ -298,17 +298,17 @@ class ScanExtension(BaseExtension): # Store initial position initial_position = microscope.stage.position - logging.debug("Starting z-stack from position {}", microscope.stage.position) + logging.debug("Starting z-stack from position %s", microscope.stage.position) with microscope.lock: # Move to center scan logging.debug("Moving to z-stack starting position") microscope.stage.move_rel([0, 0, int((-step_size * steps) / 2)]) - logging.debug("Starting scan from position {}", microscope.stage.position) + logging.debug("Starting scan from position %s", microscope.stage.position) for i in range(steps): time.sleep(0.1) - logging.debug("Capturing from position {}", microscope.stage.position) + logging.debug("Capturing from position %s", microscope.stage.position) self.capture( microscope, basename, @@ -328,10 +328,10 @@ class ScanExtension(BaseExtension): return if i != steps - 1: - logging.debug("Moving z by {}", (step_size)) + logging.debug("Moving z by %s", (step_size)) microscope.stage.move_rel([0, 0, step_size]) if return_to_start: - logging.debug("Returning to {}", (initial_position)) + logging.debug("Returning to %s", (initial_position)) microscope.stage.move_abs(initial_position) diff --git a/openflexure_microscope/api/default_extensions/zip_builder.py b/openflexure_microscope/api/default_extensions/zip_builder.py index 9ce774da..5db25a70 100644 --- a/openflexure_microscope/api/default_extensions/zip_builder.py +++ b/openflexure_microscope/api/default_extensions/zip_builder.py @@ -156,7 +156,7 @@ class ZipGetterAPIView(View): if not session_id in default_zip_manager.session_zips: return abort(404) # 404 Not Found - logging.info("Session ID: {}", session_id) + logging.info("Session ID: %s", session_id) return send_file( default_zip_manager.zip_fp_from_id(session_id).name, diff --git a/openflexure_microscope/api/dev_extensions/tools.py b/openflexure_microscope/api/dev_extensions/tools.py index 1f6d12cf..ddbadfef 100644 --- a/openflexure_microscope/api/dev_extensions/tools.py +++ b/openflexure_microscope/api/dev_extensions/tools.py @@ -17,7 +17,7 @@ class SleepFor(ActionView): def post(self, args): sleep_time = args.get("time") - logging.info("Going to sleep for {}...", sleep_time) + logging.info("Going to sleep for %s...", sleep_time) start = time.time() time.sleep(sleep_time) end = time.time() diff --git a/openflexure_microscope/api/utilities/__init__.py b/openflexure_microscope/api/utilities/__init__.py index 333fa767..7c12b2a9 100644 --- a/openflexure_microscope/api/utilities/__init__.py +++ b/openflexure_microscope/api/utilities/__init__.py @@ -73,10 +73,10 @@ def init_default_extensions(extension_dir): default_ext_path = os.path.join(extension_dir, "defaults.py") if not os.path.isfile(default_ext_path): # If user extensions file doesn't exist - logging.warning("No extension file found at {}. Creating...", (extension_dir)) + logging.warning("No extension file found at %s. Creating...", (extension_dir)) create_file(default_ext_path) - logging.info("Populating {}...", (default_ext_path)) + logging.info("Populating %s...", (default_ext_path)) with open(default_ext_path, "w") as outfile: outfile.write(_DEFAULT_EXTENSION_INIT) diff --git a/openflexure_microscope/api/utilities/gui.py b/openflexure_microscope/api/utilities/gui.py index 5613ae69..1fc03c1f 100644 --- a/openflexure_microscope/api/utilities/gui.py +++ b/openflexure_microscope/api/utilities/gui.py @@ -27,7 +27,7 @@ def build_gui_from_dict(gui_description, extension_object): if "route" in form and form["route"] in ext_rules.keys(): form["route"] = ext_rules[form["route"]]["urls"][0] else: - logging.warning("No valid expandable route found for {}", form["route"]) + logging.warning("No valid expandable route found for %s", form["route"]) # Inject extension information api_gui["id"] = extension_object.name diff --git a/openflexure_microscope/api/v2/views/actions/stage.py b/openflexure_microscope/api/v2/views/actions/stage.py index 5c7cc416..80e2a037 100644 --- a/openflexure_microscope/api/v2/views/actions/stage.py +++ b/openflexure_microscope/api/v2/views/actions/stage.py @@ -25,11 +25,11 @@ class MoveStageAPI(ActionView): # Handle absolute positioning (calculate a relative move from current position and target) if (args.get("absolute")) and (microscope.stage): # Only if stage exists target_position = axes_to_array(args, ["x", "y", "z"]) - logging.debug("TARGET: {}", (target_position)) + logging.debug("TARGET: %s", (target_position)) position = [ target_position[i] - microscope.stage.position[i] for i in range(3) ] - logging.debug("DELTA: {}", (position)) + logging.debug("DELTA: %s", (position)) else: # Get coordinates from payload diff --git a/openflexure_microscope/camera/base.py b/openflexure_microscope/camera/base.py index d3bdbefa..dab2b06a 100644 --- a/openflexure_microscope/camera/base.py +++ b/openflexure_microscope/camera/base.py @@ -113,10 +113,10 @@ class BaseCamera(metaclass=ABCMeta): def close(self): """Close the BaseCamera and all attached StreamObjects.""" - logging.info("Closing {}", (self)) + logging.info("Closing %s", (self)) # Stop worker thread self.stop_worker() - logging.info("Closed {}", (self)) + logging.info("Closed %s", (self)) # START AND STOP WORKER THREAD diff --git a/openflexure_microscope/camera/pi.py b/openflexure_microscope/camera/pi.py index d86957d4..4d47e2de 100644 --- a/openflexure_microscope/camera/pi.py +++ b/openflexure_microscope/camera/pi.py @@ -156,10 +156,10 @@ class PiCameraStreamer(BaseCamera): for key in PiCameraStreamer.picamera_settings_keys: try: value = getattr(self.camera, key) - logging.debug("Reading PiCamera().{}: {}", key, value) + logging.debug("Reading PiCamera().%s: %s", key, value) conf_dict["picamera"][key] = value except AttributeError: - logging.debug("Unable to read PiCamera attribute {}", (key)) + logging.debug("Unable to read PiCamera attribute %s", (key)) # Include a serialised lens shading table if ( @@ -243,22 +243,22 @@ class PiCameraStreamer(BaseCamera): # Set exposure mode if "exposure_mode" in settings_dict: logging.debug( - "Applying exposure_mode: {}", (settings_dict["exposure_mode"]) + "Applying exposure_mode: %s", (settings_dict["exposure_mode"]) ) self.camera.exposure_mode = settings_dict["exposure_mode"] # Apply gains and let them settle if "analog_gain" in settings_dict: - logging.debug("Applying analog_gain: {}", (settings_dict["analog_gain"])) + logging.debug("Applying analog_gain: %s", (settings_dict["analog_gain"])) set_analog_gain(self.camera, float(settings_dict["analog_gain"])) if "digital_gain" in settings_dict: - logging.debug("Applying digital_gain: {}", (settings_dict["digital_gain"])) + logging.debug("Applying digital_gain: %s", (settings_dict["digital_gain"])) set_digital_gain(self.camera, float(settings_dict["digital_gain"])) # Apply shutter speed if "shutter_speed" in settings_dict: logging.debug( - "Applying shutter_speed: {}", (settings_dict["shutter_speed"]) + "Applying shutter_speed: %s", (settings_dict["shutter_speed"]) ) self.camera.shutter_speed = int(settings_dict["shutter_speed"]) @@ -268,17 +268,17 @@ class PiCameraStreamer(BaseCamera): if "awb_gains" in settings_dict: logging.debug("Applying awb_mode: off") self.camera.awb_mode = "off" - logging.debug("Applying awb_gains: {}", (settings_dict["awb_gains"])) + logging.debug("Applying awb_gains: %s", (settings_dict["awb_gains"])) self.camera.awb_gains = settings_dict["awb_gains"] elif "awb_mode" in settings_dict: - logging.debug("Applying awb_mode: {}", (settings_dict["awb_mode"])) + logging.debug("Applying awb_mode: %s", (settings_dict["awb_mode"])) self.camera.awb_mode = settings_dict["awb_mode"] # Handle some properties that can be quickly applied batched_keys = ["framerate", "saturation"] for key in batched_keys: if (key in settings_dict) and hasattr(self.camera, key): - logging.debug("Applying {}: {}", key, settings_dict[key]) + logging.debug("Applying %s: %s", key, settings_dict[key]) setattr(self.camera, key, settings_dict[key]) # Final optional pause to settle @@ -303,7 +303,7 @@ class PiCameraStreamer(BaseCamera): for i in range(2): if np.abs(centre[i] - 0.5) + size / 2 > 0.5: centre[i] = 0.5 + (1.0 - size) / 2 * np.sign(centre[i] - 0.5) - logging.info("setting zoom, centre {}, size {}", centre, size) + logging.info("setting zoom, centre %s, size %s", centre, size) new_fov = (centre[0] - size / 2, centre[1] - size / 2, size, size) self.camera.zoom = new_fov @@ -325,11 +325,11 @@ class PiCameraStreamer(BaseCamera): self.preview_active = True except picamerax.exc.PiCameraMMALError as e: logging.error( - "Suppressed a MMALError in start_preview. Exception: {}", (e) + "Suppressed a MMALError in start_preview. Exception: %s", (e) ) except picamerax.exc.PiCameraValueError as e: logging.error( - "Suppressed a ValueError exception in start_preview. Exception: {}", + "Suppressed a ValueError exception in start_preview. Exception: %s", (e), ) @@ -359,7 +359,7 @@ class PiCameraStreamer(BaseCamera): if not self.record_active: # Start the camera video recording on port 2 - logging.info("Recording to {}", (output)) + logging.info("Recording to %s", (output)) self.camera.start_recording( output, @@ -401,17 +401,17 @@ class PiCameraStreamer(BaseCamera): """ for k in kwargs.keys(): logging.warning( - "Warning, kwarg {} is invalid for stop_stream_recording.", k + "Warning, kwarg %s is invalid for stop_stream_recording.", k ) with self.lock: # Stop the camera video recording on port 1 try: self.camera.stop_recording(splitter_port=splitter_port) except picamerax.exc.PiCameraNotRecording: - logging.info("Not recording on splitter_port {}", (splitter_port)) + logging.info("Not recording on splitter_port %s", (splitter_port)) else: logging.info( - "Stopped MJPEG stream on port {}. Switching to {}.", + "Stopped MJPEG stream on port %s. Switching to %s.", splitter_port, self.image_resolution, ) @@ -431,7 +431,7 @@ class PiCameraStreamer(BaseCamera): """ for k in kwargs.keys(): logging.warning( - "Warning, kwarg {} is invalid for stop_stream_recording.", k + "Warning, kwarg %s is invalid for stop_stream_recording.", k ) with self.lock(timeout=None): # Reduce the resolution for video streaming @@ -465,7 +465,7 @@ class PiCameraStreamer(BaseCamera): ) else: logging.debug( - "Started MJPEG stream at {} on port {}", + "Started MJPEG stream at %s on port %s", self.stream_resolution, splitter_port, ) @@ -496,7 +496,7 @@ class PiCameraStreamer(BaseCamera): output_object (str/BytesIO): Target object. """ with self.lock: - logging.info("Capturing to {}", (output)) + logging.info("Capturing to %s", (output)) # Set resolution and stop stream recording if necessary if not use_video_port: @@ -532,7 +532,7 @@ class PiCameraStreamer(BaseCamera): logging.debug("Creating PiRGBArray") with picamerax.array.PiRGBArray(self.camera) as output: - logging.info("Capturing to {}", (output)) + logging.info("Capturing to %s", (output)) self.camera.capture(output, format="rgb", use_video_port=True) diff --git a/openflexure_microscope/camera/set_picamera_gain.py b/openflexure_microscope/camera/set_picamera_gain.py index 3e92b4b9..fc04f851 100644 --- a/openflexure_microscope/camera/set_picamera_gain.py +++ b/openflexure_microscope/camera/set_picamera_gain.py @@ -55,7 +55,7 @@ if __name__ == "__main__": # fix the shutter speed cam.shutter_speed = cam.exposure_speed - logging.info("Current a/d gains: {}, {}", cam.analog_gain, cam.digital_gain) + logging.info("Current a/d gains: %s, %s", cam.analog_gain, cam.digital_gain) logging.info("Attempting to set analogue gain to 1") set_analog_gain(cam, 1) @@ -65,7 +65,7 @@ if __name__ == "__main__": try: while True: logging.info( - "Current a/d gains: {}, {}", cam.analog_gain, cam.digital_gain + "Current a/d gains: %s, %s", cam.analog_gain, cam.digital_gain ) time.sleep(1) except KeyboardInterrupt: diff --git a/openflexure_microscope/captures/capture.py b/openflexure_microscope/captures/capture.py index 131f84ef..36d252b4 100644 --- a/openflexure_microscope/captures/capture.py +++ b/openflexure_microscope/captures/capture.py @@ -25,23 +25,23 @@ def make_file_list(directory, formats): glob.glob("{}/**/*.{}".format(directory, fmt.lower()), recursive=True) ) - logging.info("{} capture files found on disk", (len(files))) + logging.info("%s capture files found on disk", (len(files))) return files def build_captures_from_exif(capture_path): - logging.debug("Reloading captures from {}...", (capture_path)) + logging.debug("Reloading captures from %s...", (capture_path)) files = make_file_list(capture_path, EXIF_FORMATS) captures = OrderedDict() for f in files: - logging.debug("Reloading capture {}...", (f)) + logging.debug("Reloading capture %s...", (f)) capture = capture_from_path(f) if capture: captures[capture.id] = capture - logging.info("{} capture files successfully reloaded", (len(captures))) + logging.info("%s capture files successfully reloaded", (len(captures))) return captures @@ -58,7 +58,7 @@ def capture_from_path(path): capture.sync_basic_metadata() return capture except (InvalidImageDataError, json.decoder.JSONDecodeError): - logging.error("Invalid metadata at {}.", (path)) + logging.error("Invalid metadata at %s.", (path)) return None @@ -75,7 +75,7 @@ class CaptureObject(object): # Store a nice ID self.id = uuid.uuid4() #: str: Unique capture ID - logging.debug("Created CaptureObject {}", (self.id)) + logging.debug("Created CaptureObject %s", (self.id)) self.time = datetime.datetime.now() @@ -97,17 +97,17 @@ class CaptureObject(object): self.tags = [] def write(self, s): - logging.debug("Writing to {}", self) + logging.debug("Writing to %s", self) self.stream.write(s) def flush(self): - logging.info("Writing image data to disk {}", self.file) + logging.info("Writing image data to disk %s", self.file) with open(self.file, "wb") as outfile: outfile.write(self.stream.getbuffer()) self.stream.close() - logging.info("Writing metadata to disk {}", self.file) + logging.info("Writing metadata to disk %s", self.file) self._init_metadata() - logging.info("Finished writing to disk {}", self.file) + logging.info("Finished writing to disk %s", self.file) def open(self, mode): return open(self.file, mode) @@ -175,7 +175,7 @@ class CaptureObject(object): } def read_full_metadata(self): - logging.info("Reading full capture metadata from {}...", self.file) + logging.info("Reading full capture metadata from %s...", self.file) exif_dict = self._read_exif() return self._decode_usercomment(exif_dict) @@ -271,7 +271,7 @@ class CaptureObject(object): # Write new data to file EXIF, if supported if self.format.upper() in EXIF_FORMATS and self.exists: - logging.info("Writing Exif data to {}", self.file) + logging.info("Writing Exif data to %s", self.file) # Extract current Exif data exif_dict = self._read_exif() @@ -286,7 +286,7 @@ class CaptureObject(object): # Serialize metadata metadata_string = json.dumps(metadata_dict, cls=JSONEncoder) - logging.debug("Saving metadata string to file: {}", metadata_string) + logging.debug("Saving metadata string to file: %s", metadata_string) # Insert metadata into exif_dict exif_dict["Exif"][piexif.ExifIFD.UserComment] = metadata_string.encode() @@ -296,7 +296,7 @@ class CaptureObject(object): # Insert exif into file piexif.insert(exif_bytes, self.file) - logging.info("Finished writing Exif data to {}", self.file) + logging.info("Finished writing Exif data to %s", self.file) # PROPERTIES @@ -316,7 +316,7 @@ class CaptureObject(object): """ if self.exists: # If data file exists - logging.info("Opening from file {}", (self.file)) + logging.info("Opening from file %s", (self.file)) with open(self.file, "rb") as f: d = io.BytesIO(f.read()) # Load bytes from file d.seek(0) # Rewind loaded bytestream @@ -364,7 +364,7 @@ class CaptureObject(object): """If the StreamObject has been saved, delete the file.""" if os.path.isfile(self.file): - logging.info("Deleting file {}", (self.file)) + logging.info("Deleting file %s", (self.file)) os.remove(self.file) return True diff --git a/openflexure_microscope/captures/capture_manager.py b/openflexure_microscope/captures/capture_manager.py index 4b435016..98cc1dde 100644 --- a/openflexure_microscope/captures/capture_manager.py +++ b/openflexure_microscope/captures/capture_manager.py @@ -61,7 +61,7 @@ class CaptureManager: self.close() def close(self): - logging.info("Closing {}", (self)) + logging.info("Closing %s", (self)) # Close all StreamObjects for capture_list in [self.images.values(), self.videos.values()]: for stream_object in capture_list: @@ -75,9 +75,9 @@ class CaptureManager: """ if os.path.isdir(self.paths["temp"]): - logging.info("Clearing {}...", (self.paths["temp"])) + logging.info("Clearing %s...", (self.paths["temp"])) shutil.rmtree(self.paths["temp"]) - logging.debug("Cleared {}.", (self.paths["temp"])) + logging.debug("Cleared %s.", (self.paths["temp"])) def rebuild_captures(self): self.images = build_captures_from_exif(self.paths["default"]) @@ -158,7 +158,7 @@ class CaptureManager: # Update capture list capture_key = str(output.id) - logging.debug("Adding image {} with key {}", output, capture_key) + logging.debug("Adding image %s with key %s", output, capture_key) self.images[capture_key] = output @@ -205,7 +205,7 @@ class CaptureManager: # Update capture list capture_key = str(output.id) - logging.debug("Adding video {} with key {}", output, capture_key) + logging.debug("Adding video %s with key %s", output, capture_key) self.videos[capture_key] = output return output diff --git a/openflexure_microscope/config.py b/openflexure_microscope/config.py index f13cab2c..5788a385 100644 --- a/openflexure_microscope/config.py +++ b/openflexure_microscope/config.py @@ -86,7 +86,7 @@ def load_json_file(config_path) -> dict: """ config_path = os.path.expanduser(config_path) - logging.info("Loading {}...", config_path) + logging.info("Loading %s...", config_path) with open(config_path) as config_file: try: @@ -109,7 +109,7 @@ def save_json_file(config_path: str, config_dict: dict): """ config_path = os.path.expanduser(config_path) - logging.info("Saving {}...", config_path) + logging.info("Saving %s...", config_path) logging.debug(config_dict) with open(config_path, "w") as outfile: @@ -142,14 +142,14 @@ def initialise_file(config_path, populate: str = "{}\n"): """ config_path = os.path.expanduser(config_path) - logging.debug("Initialising {}", (config_path)) - logging.debug("Exists: {}", (os.path.exists(config_path))) + logging.debug("Initialising %s", (config_path)) + logging.debug("Exists: %s", (os.path.exists(config_path))) if not os.path.exists(config_path): # If user config file doesn't exist - logging.warning("No config file found at {}. Creating...", (config_path)) + logging.warning("No config file found at %s. Creating...", (config_path)) create_file(config_path) - logging.info("Populating {}...", (config_path)) + logging.info("Populating %s...", (config_path)) with open(config_path, "w") as outfile: outfile.write(populate) diff --git a/openflexure_microscope/microscope.py b/openflexure_microscope/microscope.py index bfb22ae1..46e63e28 100644 --- a/openflexure_microscope/microscope.py +++ b/openflexure_microscope/microscope.py @@ -71,7 +71,7 @@ class Microscope: def close(self): """Shut down the microscope hardware.""" - logging.info("Closing {}", (self)) + logging.info("Closing %s", (self)) if self.camera: try: self.camera.close() @@ -83,7 +83,7 @@ class Microscope: except TimeoutError as e: logging.error(e) self.captures.close() - logging.info("Closed {}", (self)) + logging.info("Closed %s", (self)) def setup(self, configuration): """ @@ -196,7 +196,7 @@ class Microscope: Applies a settings dictionary to the microscope. Missing parameters will be left untouched. """ with self.lock: - logging.debug("Microscope: Applying settings: {}", (settings)) + logging.debug("Microscope: Applying settings: %s", (settings)) # If attached to a camera if ("camera" in settings) and self.camera: @@ -329,14 +329,14 @@ class Microscope: # Load cached bits of metadata if cache_key: - logging.debug("Reading cached microscope metadata: {}", cache_key) + logging.debug("Reading cached microscope metadata: %s", cache_key) metadata = self.metadata_cache.get(cache_key, None) if not metadata: - logging.debug("Building and caching microscope metadata: {}", cache_key) + logging.debug("Building and caching microscope metadata: %s", cache_key) metadata = self.force_get_metadata() self.metadata_cache[cache_key] = metadata else: - logging.debug("Building microscope metadata: {}", cache_key) + logging.debug("Building microscope metadata: %s", cache_key) metadata = self.force_get_metadata() # Keys that should never be cached @@ -362,7 +362,7 @@ class Microscope: metadata: dict = None, cache_key: str = None, ): - logging.debug("Microscope capturing to {}", filename) + logging.debug("Microscope capturing to %s", filename) if not annotations: annotations = {} if not metadata: @@ -384,7 +384,7 @@ class Microscope: extras = {} if fmt == "jpeg": extras["thumbnail"] = (*THUMBNAIL_SIZE, 85) - logging.info("Starting microscope capture {}", output.file) + logging.info("Starting microscope capture %s", output.file) self.camera.capture( output, use_video_port=use_video_port, @@ -395,6 +395,6 @@ class Microscope: ) output.put_and_save(tags, annotations, full_metadata) - logging.debug("Finished capture to {}", output.file) + logging.debug("Finished capture to %s", output.file) return output diff --git a/openflexure_microscope/rescue/check_capture_reload.py b/openflexure_microscope/rescue/check_capture_reload.py index 843bc3c2..e50ae05b 100644 --- a/openflexure_microscope/rescue/check_capture_reload.py +++ b/openflexure_microscope/rescue/check_capture_reload.py @@ -27,7 +27,7 @@ def main(): logging.info("Loading user settings...") settings = user_settings.load() cap_path = str(settings.get("captures", {}).get("paths", {}).get("default")) - logging.info("Capture path found: {}", cap_path) + logging.info("Capture path found: %s", cap_path) if not cap_path: logging.error( diff --git a/openflexure_microscope/rescue/monitor_timeout.py b/openflexure_microscope/rescue/monitor_timeout.py index 879f6063..49af1169 100644 --- a/openflexure_microscope/rescue/monitor_timeout.py +++ b/openflexure_microscope/rescue/monitor_timeout.py @@ -24,7 +24,7 @@ def launch_timeout_test_process(target, args=(), kwargs=None, timeout=10): # If thread is still active if p.is_alive(): logging.error( - "Function {} reached timeout after {} seconds. Terminating.", + "Function %s reached timeout after %s seconds. Terminating.", target, timeout, ) diff --git a/openflexure_microscope/stage/mock.py b/openflexure_microscope/stage/mock.py index 6d6dd856..9e9eb20f 100644 --- a/openflexure_microscope/stage/mock.py +++ b/openflexure_microscope/stage/mock.py @@ -80,13 +80,13 @@ class MissingStage(BaseStage): self._position = list(np.array(self._position) + np.array(initial_move)) logging.debug(np.array(self._position) + np.array(initial_move)) - logging.debug("New position: {}", self._position) + logging.debug("New position: %s", self._position) def move_abs(self, final, **kwargs): time.sleep(0.5) self._position = list(final) - logging.debug("New position: {}", self._position) + logging.debug("New position: %s", self._position) def zero_position(self): """Set the current position to zero""" diff --git a/openflexure_microscope/stage/sanga.py b/openflexure_microscope/stage/sanga.py index 3389d83d..c3a6c939 100644 --- a/openflexure_microscope/stage/sanga.py +++ b/openflexure_microscope/stage/sanga.py @@ -81,7 +81,7 @@ class SangaStage(BaseStage): @backlash.setter def backlash(self, blsh): - logging.debug("Setting backlash to {}", (blsh)) + logging.debug("Setting backlash to %s", (blsh)) if blsh is None: self._backlash = None elif isinstance(blsh, Iterable): @@ -118,7 +118,7 @@ class SangaStage(BaseStage): backlash: (default: True) whether to correct for backlash. """ with self.lock: - logging.debug("Moving sangaboard by {}", displacement) + logging.debug("Moving sangaboard by %s", displacement) if not backlash or self.backlash is None: return self.board.move_rel(displacement, axis=axis) if axis is not None: @@ -158,7 +158,7 @@ class SangaStage(BaseStage): """Make an absolute move to a position """ with self.lock: - logging.debug("Moving sangaboard to {}", final) + logging.debug("Moving sangaboard to %s", final) self.board.move_abs(final, **kwargs) # Settle outside of the stage lock so that another move request # can just take over before settling @@ -262,7 +262,7 @@ class SangaDeltaStage(SangaStage): # Transform into delta coordinates displacement = np.dot(self.Tdv, displacement) - logging.debug("Delta displacement: {}", (displacement)) + logging.debug("Delta displacement: %s", (displacement)) # Do the move SangaStage.move_rel(self, displacement, axis=None, backlash=backlash) @@ -274,7 +274,7 @@ class SangaDeltaStage(SangaStage): # Transform into delta coordinates final = np.dot(self.Tdv, final) - logging.debug("Delta final: {}", (final)) + logging.debug("Delta final: %s", (final)) # Do the move SangaStage.move_abs(self, final, **kwargs) diff --git a/openflexure_microscope/utilities.py b/openflexure_microscope/utilities.py index 4ded53c7..e0756303 100644 --- a/openflexure_microscope/utilities.py +++ b/openflexure_microscope/utilities.py @@ -22,7 +22,7 @@ class Timer(object): def __exit__(self, type_, value, traceback): self.end = time.time() - logging.debug("{} time: {}", self.name, self.end - self.start) + logging.debug("%s time: %s", self.name, self.end - self.start) def deserialise_array_b64(b64_string, dtype, shape): From 738f527c7e1b64218188fde4f017dd351477c4c3 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 16:34:21 +0000 Subject: [PATCH 14/24] Updated to LabThings 1.1.3 --- poetry.lock | 34 +++++++++++++++++----------------- pyproject.toml | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8415d386..13771f48 100644 --- a/poetry.lock +++ b/poetry.lock @@ -79,7 +79,7 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (> [[package]] name = "babel" -version = "2.8.1" +version = "2.9.0" description = "Internationalization utilities" category = "dev" optional = false @@ -283,7 +283,7 @@ i18n = ["Babel (>=0.8)"] [[package]] name = "labthings" -version = "1.1.2" +version = "1.1.3" description = "Python implementation of LabThings, based on the Flask microframework" category = "main" optional = false @@ -483,7 +483,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "requests" -version = "2.24.0" +version = "2.25.0" description = "Python HTTP for Humans." category = "dev" optional = false @@ -493,7 +493,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" certifi = ">=2017.4.17" chardet = ">=3.0.2,<4" idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] @@ -557,7 +557,7 @@ python-versions = "*" [[package]] name = "sphinx" -version = "3.3.0" +version = "3.3.1" description = "Python documentation generator" category = "dev" optional = false @@ -687,7 +687,7 @@ python-versions = "*" [[package]] name = "urllib3" -version = "1.25.11" +version = "1.26.1" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false @@ -753,7 +753,7 @@ rpi = ["RPi.GPIO"] [metadata] lock-version = "1.1" python-versions = "^3.6.1" -content-hash = "747d39deb1de397ea0c74dfa25a565505195ece1cc4185834ada7d94f58bb5ab" +content-hash = "e0aad912d5cb388d3ff395dc8a517f701ba2cbe4c2169771080a889906e91b41" [metadata.files] alabaster = [ @@ -781,8 +781,8 @@ attrs = [ {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] babel = [ - {file = "Babel-2.8.1-py2.py3-none-any.whl", hash = "sha256:be432f50d6c38c705ea45a0c05a4bbb22a70742a93888fbae5e7948da1635d23"}, - {file = "Babel-2.8.1.tar.gz", hash = "sha256:820c195271534e8a86f564ba9ef2c207f356cfeb7e94d2bdc6b57897c4233837"}, + {file = "Babel-2.9.0-py2.py3-none-any.whl", hash = "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5"}, + {file = "Babel-2.9.0.tar.gz", hash = "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"}, ] black = [ {file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"}, @@ -855,8 +855,8 @@ jinja2 = [ {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, ] labthings = [ - {file = "labthings-1.1.2-py3-none-any.whl", hash = "sha256:1e17e20c3585dbb2293ca1e75c46c4b9a21ee3aa30b71a2d839e16f40c21f600"}, - {file = "labthings-1.1.2.tar.gz", hash = "sha256:0d456f11920c4cd84e94afae2e7f005c3ea65346da5523d51aeec17698808156"}, + {file = "labthings-1.1.3-py3-none-any.whl", hash = "sha256:51f0bc897fcbacbcbb911658d3daafd41a35373bca4ef2156423cdb728fa3d78"}, + {file = "labthings-1.1.3.tar.gz", hash = "sha256:3d68c638866f1c525a8b5285570c2d4599005d227b928e78088c0a29e6544007"}, ] lazy-object-proxy = [ {file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"}, @@ -1058,8 +1058,8 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, + {file = "requests-2.25.0-py2.py3-none-any.whl", hash = "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"}, + {file = "requests-2.25.0.tar.gz", hash = "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8"}, ] rope = [ {file = "rope-0.14.0-py2-none-any.whl", hash = "sha256:6b728fdc3e98a83446c27a91fc5d56808a004f8beab7a31ab1d7224cecc7d969"}, @@ -1103,8 +1103,8 @@ snowballstemmer = [ {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, ] sphinx = [ - {file = "Sphinx-3.3.0-py3-none-any.whl", hash = "sha256:3abdb2c57a65afaaa4f8573cbabd5465078eb6fd282c1e4f87f006875a7ec0c7"}, - {file = "Sphinx-3.3.0.tar.gz", hash = "sha256:1c21e7c5481a31b531e6cbf59c3292852ccde175b504b00ce2ff0b8f4adc3649"}, + {file = "Sphinx-3.3.1-py3-none-any.whl", hash = "sha256:d4e59ad4ea55efbb3c05cde3bfc83bfc14f0c95aa95c3d75346fcce186a47960"}, + {file = "Sphinx-3.3.1.tar.gz", hash = "sha256:1e8d592225447104d1172be415bc2972bd1357e3e12fdc76edf2261105db4300"}, ] sphinxcontrib-applehelp = [ {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, @@ -1162,8 +1162,8 @@ typed-ast = [ {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, ] urllib3 = [ - {file = "urllib3-1.25.11-py2.py3-none-any.whl", hash = "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"}, - {file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"}, + {file = "urllib3-1.26.1-py2.py3-none-any.whl", hash = "sha256:61ad24434555a42c0439770462df38b47d05d9e8e353d93ec3742900975e3e65"}, + {file = "urllib3-1.26.1.tar.gz", hash = "sha256:097116a6f16f13482d2a2e56792088b9b2920f4eb6b4f84a2c90555fb673db74"}, ] webargs = [ {file = "webargs-6.1.1-py2.py3-none-any.whl", hash = "sha256:2ead6ce38559152043ab4ada4d389aef6c804b0c169482e7c92b3f498f690b2c"}, diff --git a/pyproject.toml b/pyproject.toml index 6087dfe0..a40df870 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ expiringdict = "^1.2.1" camera-stage-mapping = "0.1.4" picamerax = ">=20.9.1" pyyaml = "^5.3.1" -labthings = "^1.1.2" +labthings = "^1.1.3" [tool.poetry.extras] rpi = ["RPi.GPIO"] From f681800e2697eecc14d3b73492e11be374abffda Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 16:46:07 +0000 Subject: [PATCH 15/24] Improved args schema for captures --- .../api/v2/views/actions/camera.py | 73 ++++++++----------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/openflexure_microscope/api/v2/views/actions/camera.py b/openflexure_microscope/api/v2/views/actions/camera.py index 02d9b229..c7b7dd43 100644 --- a/openflexure_microscope/api/v2/views/actions/camera.py +++ b/openflexure_microscope/api/v2/views/actions/camera.py @@ -1,33 +1,39 @@ import io import logging -from flask import abort, send_file -from labthings import fields, find_component +from flask import send_file +from labthings import Schema, fields, find_component from labthings.views import ActionView from openflexure_microscope.api.v2.views.captures import CaptureSchema +class CaptureResizeSchema(Schema): + width = fields.Integer(example=640, required=True) + height = fields.Integer(example=480, required=True) + + +class BasicCaptureArgs(Schema): + use_video_port = fields.Boolean(missing=False) + bayer = fields.Boolean( + missing=False, description="Include raw bayer data in capture" + ) + resize = fields.Nested(CaptureResizeSchema(), required=False) + + +class FullCaptureArgs(BasicCaptureArgs): + filename = fields.String(example="MyFileName") + temporary = fields.Boolean(missing=False, description="Delete capture on shutdown") + annotations = fields.Dict(missing={}, example={"Client": "SwaggerUI"}) + tags = fields.List(fields.String, missing=[], example=["docs"]) + + class CaptureAPI(ActionView): """ Create a new image capture. """ - args = { - "filename": fields.String(example="MyFileName"), - "temporary": fields.Boolean( - missing=False, description="Delete capture on shutdown" - ), - "use_video_port": fields.Boolean(missing=False), - "bayer": fields.Boolean( - missing=False, description="Store raw bayer data in file" - ), - "annotations": fields.Dict(missing={}, example={"Client": "SwaggerUI"}), - "tags": fields.List(fields.String, missing=[], example=["docs"]), - "resize": fields.Dict( - missing=None, example={"width": 640, "height": 480} - ), # TODO: Validate keys - } + args = FullCaptureArgs() schema = CaptureSchema() def post(self, args): @@ -38,13 +44,10 @@ class CaptureAPI(ActionView): resize = args.get("resize", None) if resize: - if ("width" in resize) and ("height" in resize): - resize = ( - int(resize["width"]), - int(resize["height"]), - ) # Convert dict to tuple - else: - abort(404) + resize = ( + int(resize["width"]), + int(resize["height"]), + ) # Convert dict to tuple # Explicitally acquire lock (prevents empty files being created if lock is unavailable) with microscope.camera.lock: @@ -64,16 +67,7 @@ class RAMCaptureAPI(ActionView): Take a non-persistant image capture. """ - args = { - "use_video_port": fields.Boolean(missing=True), - "bayer": fields.Boolean( - missing=False, description="Return with raw bayer data" - ), - "resize": fields.Dict( - missing=None, example={"width": 640, "height": 480} - ), # TODO: Validate keys - } - + args = BasicCaptureArgs() responses = {200: {"content_type": "image/jpeg"}} def post(self, args): @@ -84,13 +78,10 @@ class RAMCaptureAPI(ActionView): resize = args.get("resize", None) if resize: - if ("width" in resize) and ("height" in resize): - resize = ( - int(resize["width"]), - int(resize["height"]), - ) # Convert dict to tuple - else: - abort(404) + resize = ( + int(resize["width"]), + int(resize["height"]), + ) # Convert dict to tuple # Open a BytesIO stream to be destroyed once request has returned with microscope.camera.lock, io.BytesIO() as stream: From 3451b8a8a53efef1480b88a53c176eb1c700754b Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 16:56:28 +0000 Subject: [PATCH 16/24] Added full TileScanArgs schema --- .../api/default_extensions/scan.py | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/scan.py b/openflexure_microscope/api/default_extensions/scan.py index 8eeb78bc..544d8017 100644 --- a/openflexure_microscope/api/default_extensions/scan.py +++ b/openflexure_microscope/api/default_extensions/scan.py @@ -16,6 +16,7 @@ from labthings import ( from labthings.extensions import BaseExtension from labthings.views import ActionView +from openflexure_microscope.api.v2.views.actions.camera import FullCaptureArgs from openflexure_microscope.captures.capture_manager import generate_basename from openflexure_microscope.devel import abort @@ -338,24 +339,19 @@ class ScanExtension(BaseExtension): scan_extension_v2 = ScanExtension() +class TileScanArgs(FullCaptureArgs): + namemode = fields.String(missing="coordinates", example="coordinates") + grid = fields.List(fields.Integer, missing=[3, 3, 3], example=[3, 3, 3]) + style = fields.String(missing="raster") + autofocus_dz = fields.Integer(missing=50) + fast_autofocus = fields.Boolean(missing=False) + stride_size = fields.List( + fields.Integer, missing=[2000, 1500, 100], example=[2000, 1500, 100] + ) + + class TileScanAPI(ActionView): - args = { - "filename": fields.String(missing=None, example=None), - "namemode": fields.String(missing="coordinates", example="coordinates"), - "temporary": fields.Boolean(missing=False), - "stride_size": fields.List( - fields.Integer, missing=[2000, 1500, 100], example=[2000, 1500, 100] - ), - "grid": fields.List(fields.Integer, missing=[3, 3, 3], example=[3, 3, 3]), - "style": fields.String(missing="raster"), - "autofocus_dz": fields.Integer(missing=50), - "fast_autofocus": fields.Boolean(missing=False), - "use_video_port": fields.Boolean(missing=False), - "bayer": fields.Boolean(missing=False), - "annotations": fields.Dict(missing={}, example={"Foo": "Bar"}), - "tags": fields.List(fields.String, missing=[]), - "resize": fields.Dict(missing=None), # TODO: Validate keys - } + args = TileScanArgs() # Allow 10 seconds to stop upon DELETE request # Gives fast-autofocus time to finish if it's running From 1e273beff029ba5999277ed59e32c7ce579ccfa0 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 17:04:37 +0000 Subject: [PATCH 17/24] Warn about unused settings when updating --- openflexure_microscope/microscope.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/openflexure_microscope/microscope.py b/openflexure_microscope/microscope.py index 46e63e28..b8b543d0 100644 --- a/openflexure_microscope/microscope.py +++ b/openflexure_microscope/microscope.py @@ -200,28 +200,30 @@ class Microscope: # If attached to a camera if ("camera" in settings) and self.camera: - self.camera.update_settings(settings.get("camera", {})) + self.camera.update_settings(settings.pop("camera", {})) # If attached to a stage if ("stage" in settings) and self.stage: - self.stage.update_settings(settings.get("stage", {})) + self.stage.update_settings(settings.pop("stage", {})) # Capture manager - self.captures.update_settings(settings.get("captures", {})) + self.captures.update_settings(settings.pop("captures", {})) # Microscope settings if "id" in settings: - self.id = settings["id"] + self.id = settings.pop("id") if "name" in settings: - self.name = settings["name"] + self.name = settings.pop("name") if "fov" in settings: - self.fov = settings["fov"] + self.fov = settings.pop("fov") # Extension settings if "extensions" in settings: - self.extension_settings.update(settings["extensions"]) + self.extension_settings.update(settings.pop("extensions")) - # TODO: warn if there are settings that we silently ignore + # Warn about any superfluous keys + for key in settings.keys(): + logging.warning("Key %s is unused and was ignored", key) def read_settings(self, full: bool = True): """ From cb4afbac07618268ae3c7e14ae8d9f60c76ab7d0 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 17:05:57 +0000 Subject: [PATCH 18/24] Removed PyLint from pre-commit --- .pre-commit-config.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7ff044af..5d5425e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,10 +9,3 @@ repos: - id: isort additional_dependencies: [toml] exclude: ^.*/?setup\.py$ - - repo: local - hooks: - - id: pylint - name: pylint - entry: poetry run pylint - language: system - types: [python] From 527bfeb7ecd27a64875d7d4183b475feae860a2b Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 17:07:58 +0000 Subject: [PATCH 19/24] Moved PyLint config back to pyproject.toml --- .pylintrc | 4 ---- pyproject.toml | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 9aa90314..00000000 --- a/.pylintrc +++ /dev/null @@ -1,4 +0,0 @@ -[MESSAGES CONTROL] - -disable=fixme,C,R -max-line-length = 88 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index a40df870..836e1dfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,3 +70,7 @@ force_grid_wrap = 0 use_parentheses = true ensure_newline_before_comments = true line_length = 88 + +[tool.pylint.'MESSAGES CONTROL'] +disable = "fixme,C,R" +max-line-length = 88 From 84dcf4f4751e9d927617d9fe40744dfe22de5b6a Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Fri, 13 Nov 2020 11:12:39 +0000 Subject: [PATCH 20/24] Cleaned up Capture schemas --- .../api/v2/views/captures.py | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/openflexure_microscope/api/v2/views/captures.py b/openflexure_microscope/api/v2/views/captures.py index 4b9347fd..9de19506 100644 --- a/openflexure_microscope/api/v2/views/captures.py +++ b/openflexure_microscope/api/v2/views/captures.py @@ -7,6 +7,8 @@ from marshmallow import pre_dump from openflexure_microscope.api.utilities import get_bool +# SCHEMAS + class InstrumentSchema(Schema): id = fields.UUID() @@ -21,17 +23,25 @@ class ImageSchema(Schema): format = fields.String() name = fields.String() tags = fields.List(fields.String()) - annotations = fields.Dict() + annotations = fields.Dict(keys=fields.Str(), values=fields.Str()) class CaptureMetadataSchema(Schema): - experimenter = fields.Dict() # TODO: Make schema - experimenterGroup = fields.Dict() # TODO: Make schema - dataset = fields.Dict() # TODO: Make schema + # Full dataset dictionary will change depending on the type of + # dataset, so we can't make a specific schema in this case. + dataset = fields.Dict() + # Nested schema for Image data image = fields.Nested(ImageSchema()) + # Nested schema for instrument data instrument = fields.Nested(InstrumentSchema()) +class BasicDatasetSchema(Schema): + id = fields.UUID() + name = fields.String() + type = fields.String() + + class CaptureSchema(ImageSchema): """ Schema containing only basic attributes required @@ -39,10 +49,15 @@ class CaptureSchema(ImageSchema): are returned by using FullCaptureSchema """ - dataset = fields.Dict() # TODO: Make schema + # We need dataset information in the capture array + # so that client applications can sort data into folders + # without the server having to do a tonne of file IO + dataset = fields.Nested(BasicDatasetSchema()) file = fields.String( data_key="path", description="Path of file on microscope device" ) + # No need to make a schema for links as we only ever + # create the dictionary right here in `generate_links` links = fields.Dict() @pre_dump @@ -102,6 +117,9 @@ class FullCaptureSchema(CaptureSchema): metadata = fields.Nested(CaptureMetadataSchema()) +# VIEWS + + class CaptureList(PropertyView): tags = ["captures"] schema = CaptureSchema(many=True) From 004ba0b06ef0bcdffae68662307ca81c1822150f Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Fri, 13 Nov 2020 11:23:11 +0000 Subject: [PATCH 21/24] Removed duplicated code --- .../captures/capture_manager.py | 66 ++++++++----------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/openflexure_microscope/captures/capture_manager.py b/openflexure_microscope/captures/capture_manager.py index 98cc1dde..d636f7a4 100644 --- a/openflexure_microscope/captures/capture_manager.py +++ b/openflexure_microscope/captures/capture_manager.py @@ -118,25 +118,7 @@ class CaptureManager: # CREATING NEW CAPTURES - def new_image( - self, - temporary: bool = True, - filename: str = None, - folder: str = "", - fmt: str = "jpeg", - ): - - """ - Create a new image capture object. - - Args: - temporary (bool): Should the data be deleted after session ends. - Creating the capture with a content manager sets this to true. - filename (str): Name of the stored file. Defaults to timestamp. - folder (str): Name of the folder in which to store the capture. - fmt (str): Format of the capture. - """ - + def _new_output(self, temporary, filename, folder, fmt): # Generate file name if not filename: filename = generate_numbered_basename(self.images.values()) @@ -156,10 +138,32 @@ class CaptureManager: if temporary: output.put_tags(["temporary"]) + return output + + def new_image( + self, + temporary: bool = True, + filename: str = None, + folder: str = "", + fmt: str = "jpeg", + ): + + """ + Create a new image capture object. + + Args: + temporary (bool): Should the data be deleted after session ends. + Creating the capture with a content manager sets this to true. + filename (str): Name of the stored file. Defaults to timestamp. + folder (str): Name of the folder in which to store the capture. + fmt (str): Format of the capture. + """ + # Create a new output object + output = self._new_output(temporary, filename, folder, fmt) + # Update capture list capture_key = str(output.id) logging.debug("Adding image %s with key %s", output, capture_key) - self.images[capture_key] = output return output @@ -182,26 +186,8 @@ class CaptureManager: folder (str): Name of the folder in which to store the capture. fmt (str): Format of the capture. """ - # TODO: Remove the redundancy here - - # Generate file name - if not filename: - filename = generate_numbered_basename(self.videos.values()) - logging.debug(filename) - filename = "{}.{}".format(filename, fmt) - - # Generate folder - base_folder = self.paths["temp"] if temporary else self.paths["default"] - folder = os.path.join(base_folder, folder) - - # Generate file path - filepath = os.path.join(folder, filename) - - # Create capture object - output = CaptureObject(filepath=filepath) - # Insert a temporary tag if temporary - if temporary: - output.put_tags(["temporary"]) + # Create a new output object + output = self._new_output(temporary, filename, folder, fmt) # Update capture list capture_key = str(output.id) From c0f9f34256aabcd74ed9b7cc212cb0c40a9ec807 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Fri, 13 Nov 2020 11:27:54 +0000 Subject: [PATCH 22/24] Removed now-redundant TODO comments --- openflexure_microscope/api/static/src/App.vue | 3 --- .../api/static/src/components/pluginComponents/JsonForm.vue | 1 - openflexure_microscope/api/v2/views/actions/camera.py | 3 --- openflexure_microscope/api/v2/views/actions/stage.py | 2 -- 4 files changed, 9 deletions(-) diff --git a/openflexure_microscope/api/static/src/App.vue b/openflexure_microscope/api/static/src/App.vue index 09ccbb4f..320b0384 100644 --- a/openflexure_microscope/api/static/src/App.vue +++ b/openflexure_microscope/api/static/src/App.vue @@ -184,7 +184,6 @@ export default { this.checkConnection(); // Handle guided tour // If the user has already completed or skipped the guided tour - // TODO: Only run this if connected to the API var completedTour = this.getLocalStorageObj("completedTour") || false; if (!completedTour) { this.$tours["guidedTour"].start(); @@ -207,8 +206,6 @@ export default { ); // Keyboard shortcuts - - // TODO: Shortcut guide Mousetrap.bind("?", () => { console.log(this.keyboardManual); this.toggleModalElement(this.$refs["keyboardManualModal"]); // Calls the mixin diff --git a/openflexure_microscope/api/static/src/components/pluginComponents/JsonForm.vue b/openflexure_microscope/api/static/src/components/pluginComponents/JsonForm.vue index 70262bd8..d53e9eb1 100644 --- a/openflexure_microscope/api/static/src/components/pluginComponents/JsonForm.vue +++ b/openflexure_microscope/api/static/src/components/pluginComponents/JsonForm.vue @@ -131,7 +131,6 @@ export default { }, submitApiUri: function() { - // TODO: This could probably be handled more explicitally return this.pluginApiUri + this.route; } }, diff --git a/openflexure_microscope/api/v2/views/actions/camera.py b/openflexure_microscope/api/v2/views/actions/camera.py index c7b7dd43..7c1f1fee 100644 --- a/openflexure_microscope/api/v2/views/actions/camera.py +++ b/openflexure_microscope/api/v2/views/actions/camera.py @@ -124,8 +124,6 @@ class GPUPreviewStartAPI(ActionView): window = [int(w) for w in window] microscope.camera.start_preview(fullscreen=fullscreen, window=window) - - # TODO: Make schema for microscope state return microscope.state @@ -136,5 +134,4 @@ class GPUPreviewStopAPI(ActionView): """ microscope = find_component("org.openflexure.microscope") microscope.camera.stop_preview() - # TODO: Make schema for microscope state return microscope.state diff --git a/openflexure_microscope/api/v2/views/actions/stage.py b/openflexure_microscope/api/v2/views/actions/stage.py index 80e2a037..53e0b0b1 100644 --- a/openflexure_microscope/api/v2/views/actions/stage.py +++ b/openflexure_microscope/api/v2/views/actions/stage.py @@ -45,7 +45,6 @@ class MoveStageAPI(ActionView): else: logging.warning("Unable to move. No stage found.") - # TODO: Make schema for microscope state return microscope.state["stage"]["position"] @@ -60,5 +59,4 @@ class ZeroStageAPI(ActionView): with microscope.stage.lock(timeout=1): microscope.stage.zero_position() - # TODO: Make schema for microscope state return microscope.state["stage"] From c6458046ec2a2bced91520f6a2c11ac319fd87a1 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Fri, 13 Nov 2020 12:34:08 +0000 Subject: [PATCH 23/24] Data-driven tab layout --- .../api/static/src/components/appContent.vue | 247 ++++++++---------- 1 file changed, 109 insertions(+), 138 deletions(-) diff --git a/openflexure_microscope/api/static/src/components/appContent.vue b/openflexure_microscope/api/static/src/components/appContent.vue index 51e3cd0f..c5e0919d 100644 --- a/openflexure_microscope/api/static/src/components/appContent.vue +++ b/openflexure_microscope/api/static/src/components/appContent.vue @@ -16,63 +16,27 @@ id="switcher-left" class="uk-flex uk-flex-column uk-padding-remove uk-width-auto uk-height-1-1 uk-text-center" > - - visibility - - - - photo_library - - -
- - - gamepad - - - camera_alt - - - @@ -252,14 +162,69 @@ export default { return { plugins: [], currentTab: "view", - unwatchStoreFunction: null + unwatchStoreFunction: null, + topTabs: [ + { + id: "view", + icon: "visibility", + component: viewContent + }, + { + id: "gallery", + icon: "photo_library", + component: galleryContent, + divide: true // Add a divider after this tab icon + }, + { + id: "navigate", + icon: "gamepad", + component: navigateContent + }, + { + id: "capture", + icon: "camera_alt", + component: captureContent, + divide: true // Add a divider after this tab icon + } + ], + bottomTabs: [ + { + id: "settings", + icon: "settings", + component: settingsContent, + class: "uk-margin-auto-top" + }, + { + id: "logging", + icon: "assignment_late", + component: loggingContent + }, + { + id: "about", + icon: "info", + component: aboutContent + } + ] }; }, computed: { + enabledTopTabs: function() { + var enabledTabs = this.topTabs; + if (this.$store.state.globalSettings.IHIEnabled) { + enabledTabs.push({ + id: "slidescan", + icon: "settings_overscan", + component: slideScanContent + }); + } + return enabledTabs; + }, + pluginsUri: function() { return `${this.$store.getters.baseUri}/api/v2/extensions`; }, + pluginsGuiList: function() { // List of plugin GUIs, obtained from this.plugins values console.log("Recalculating plugins"); @@ -271,15 +236,21 @@ export default { } return pluginGuis; }, + tabOrder: function() { - // TODO: There must be a better way to do this, somehow reading the order of the HTML elements? - var ind = ["view", "gallery", "navigate", "capture", "settings"]; + var ind = []; + for (const tab of this.enabledTopTabs) { + ind.push(tab.id); + } for (const plugin of this.pluginsGuiList) { ind.push(plugin.id); } - ind.push("about"); + for (const tab of this.bottomTabs) { + ind.push(tab.id); + } return ind; }, + currentTabIndex: function() { return this.tabOrder.indexOf(this.currentTab); } From c48c6d859d399aa65f6782dc852aeea6678e37d0 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Fri, 13 Nov 2020 12:35:12 +0000 Subject: [PATCH 24/24] Extra comments --- .../api/static/src/components/appContent.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openflexure_microscope/api/static/src/components/appContent.vue b/openflexure_microscope/api/static/src/components/appContent.vue index c5e0919d..6410db42 100644 --- a/openflexure_microscope/api/static/src/components/appContent.vue +++ b/openflexure_microscope/api/static/src/components/appContent.vue @@ -77,6 +77,7 @@ id="container-left" class="uk-padding-remove uk-height-1-1 uk-width-expand" > + + +