diff --git a/.coveragerc b/.coveragerc index c27fe2cf1..b49310851 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,2 +1,2 @@ [run] -omit = *HardwareRepository/* \ No newline at end of file +omit = *HardwareRepository/* diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..463e9a49d --- /dev/null +++ b/.flake8 @@ -0,0 +1,12 @@ +[flake8] +ignore = E203, E266, E501, W503, F403, F401 +max-line-length = 88 +max-complexity = 18 +select = B,C,E,F,W,T4,B9 +exclude = + .git, + __pycache__, + docs/conf.py, + build, + dist, + test, diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 6758e002f..ffdd1495e 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -1,4 +1,4 @@ -name: Build and test +name: Lint and test on: [push, pull_request] @@ -8,7 +8,7 @@ jobs: strategy: max-parallel: 5 matrix: - python-version: ["3.10"] #, "3.9", "3.10"] + python-version: ["3.10"] #, "3.9", "3.10"] # Skip `pull_request` runs on local PRs for which `push` runs are already triggered if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository @@ -35,26 +35,23 @@ jobs: uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - - name: Add conda to system path - run: | - # $CONDA is an environment variable pointing to the root of the miniconda directory - echo $CONDA/bin >> $GITHUB_PATH - - name: Install ldap dependencies + + - name: Install LDAP dependencies run: sudo apt-get -y install libsasl2-dev libldap2-dev libssl-dev - - name: Install dependencies - run: | - conda install -c conda-forge mamba - mamba env update --file conda-environment.yml --name base - pip install -e . - - name: Lint with flake8 - run: | - #conda install flake8 - # stop the build if there are Python syntax errors or undefined names - #flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - #flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Set up Conda environment + uses: mamba-org/setup-micromamba@v1 + with: + micromamba-version: "latest" + environment-file: conda-environment.yml + cache-environment: true + post-cleanup: "all" + + - name: Install MXCuBE + run: "${MAMBA_EXE} run --name mxcubeweb poetry install" + + - name: Linting & Code Quality + run: "${MAMBA_EXE} run --name mxcubeweb poetry run pre-commit run --all-files" + - name: Test with pytest - run: | - conda install pytest - conda install pytest-cov - pytest + run: "${MAMBA_EXE} run --name mxcubeweb poetry run pytest" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..d24317f32 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,57 @@ +default_language_version: + python: python3.10 +repos: + - repo: https://github.com/python-poetry/poetry + rev: 1.5.0 + hooks: + - id: poetry-check + language_version: python3.10 + # - id: poetry-lock + # language_version: python3.10 + - repo: https://github.com/myint/autoflake + rev: v1.6.0 + hooks: + - id: autoflake + name: Autoflake + args: + - --expand-star-imports + - --ignore-init-module-imports + - --in-place + - --remove-duplicate-keys + - --ignore-pass-after-docstring + - repo: https://github.com/psf/black + rev: 22.8.0 + hooks: + - id: black + name: Black + language_version: python3.10 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-case-conflict + - id: check-merge-conflict + # exclude files where underlines are not distinguishable from merge conflicts + exclude: /README\.rst$|^docs/.*\.rst$ + - id: check-symlinks + - id: check-xml + - id: check-yaml + exclude: ^.drone\.yml|meta.yaml + - id: mixed-line-ending + args: ["--fix=lf"] + # - repo: https://github.com/PyCQA/isort + # rev: 5.10.1 + # hooks: + # - id: isort + # name: Import Sort + # args: + # - --settings=. + - repo: https://github.com/PyCQA/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + name: Flake8 +# Not using bugbear for now +# additional_dependencies: ["flake8-bugbear==22.9.11"] + exclude: (ui/|test/) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc91f2312..a5e1bfc54 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ Pull requests (PR's) are used to submitt new code to the repository, it helps de ```bash git remote add upstream git@github.com:mxcube/mxcubecore.git ``` - + A branching model based on the popular [gitlfow model](https://nvie.com/posts/a-successful-git-branching-model/) is used inorder to be able to provide versioned releases and at the same time continue seperate development. The stable releases are kept on the [**master**](https://github.com/mxcube/mxcube3/tree/master) branch and the development takes place on [**develop**](https://github.com/mxcube/mxcube3/tree/develop). This means that all pull requests should be made against the [**develop**](https://github.com/mxcube/mxcube3/tree/develop) branch. The work on the **develop** branch is performed by simply creating a branch for the work to be done and then making a PR according to the description below. @@ -34,7 +34,7 @@ This means that all pull requests should be made against the [**develop**](https git checkout develop git rebase upstream/develop ``` - + * If you already are working on the **develop** branch and tracking the official repository, simply: ```bash git pull --rebase develop @@ -47,19 +47,19 @@ We **recommend to always rebase your local changes instead of merging them**, gi #### Preparing a new commit * First, make sure that you are working with the latest changes from develop -```bash +```bash git checkout develop` git pull --rebase develop ``` * Create a new branch, its recommended to use a meaningfull name. for instance [initials]-[fix/feature]-[some name] i.e mo-feature-gizmo1 `git checkout -b mo-feature-gizmo1` * If the pull request is associated with an issue then reference the issue in the name. For example: - `git checkout -b issue_100` + `git checkout -b issue_100` * Edit necessary files, delete existing or add a new file. * Add files to the staging area: `git add ChangedFile1 ChangedFile2` * Save your new commit to the local repository: - `git commit` + `git commit` * Commit command will open a text editor: * In the first line write a short commit summary (max 50 characters. It will appear as a title of PR. * Add an empty line. @@ -81,16 +81,16 @@ git pull --rebase develop * The changes made in the PR are assumed to be tested by the author * All the assigned reviewers of a PR have to review the PR before it can be merged. * A PR that has no reviewer assigned can be reviewed by anyone. -* The author of the PR is free to merge the PR once its been reviewed and all pending comments/discussions are solved +* The author of the PR is free to merge the PR once its been reviewed and all pending comments/discussions are solved -### Versioning +### Versioning Versioning is partly automated by GitHub actions and [Poetry](https://python-poetry.org/)) and based on the gitflow braching model: - Each new feature is implemented in a `feature branch`, branching from the `develop branch`. -- The minor version is bumped automatically by the CI workflow when a PR is merged on develop +- The minor version is bumped automatically by the CI workflow when a PR is merged on develop -- The merge of a `feature branch` is made via PR to the `develop branch`. The author of +- The merge of a `feature branch` is made via PR to the `develop branch`. The author of the PR must solve any conflicts with the latest development version before the merge. - When decided, a release branch is created from the development branch and becomes @@ -98,8 +98,8 @@ Versioning is partly automated by GitHub actions and [Poetry](https://python-poe - Once the code can be released, the release branch is merged to the `master branch` and also to the `develop branch`. - -- If a bug is found in a released version, a `hotfix branch` is created with the + +- If a bug is found in a released version, a `hotfix branch` is created with the necessary changes and applied to the `main branch` and the corresponding commits are also cherry-picked to the development branch. @@ -108,8 +108,8 @@ The exact routine is described more preceisly in [MEP01](https://github.com/mxcu ### Coding convention and style guidelines #### Units -Functions returning a value representing a physical quantity should in general be assoicated with -a unit. It has been agreed that the following units should, where applicable, be used across the +Functions returning a value representing a physical quantity should in general be assoicated with +a unit. It has been agreed that the following units should, where applicable, be used across the code base * mm (millimeter) for translative motors and sizes @@ -122,7 +122,7 @@ code base * Datetime YYYY-MM-DD HH:MM:SS(.ff) ,possibly with hundreds of seconds (ff), and with 24 hour clock. #### Type hints -We strongly encourage the usage of type hints +We strongly encourage the usage of type hints #### Naming convention @@ -138,13 +138,13 @@ We strongly encourage the usage of type hints * names of maps are plural or contain 'map', 'dict', 'data', or an internel '2', like 'name2state' * variables should distinguish between objects (e.g. 'motor') and their names or string representations (e.g. 'motor_name')) * Booleans can be indcated by participles (e.g. 'enabled', 'tunable') or an 'is_' prefix. We should use positive rather than negative expressions (e.g. 'enabled' rather than 'disabled') - + #### Properties v. functions * You should prefer functions ('get_', 'set_', 'update_') when attributes are mutable and changing the value requires moving hardware or is slow or has side effects, or where you (might) need additional parameters like swithces or timeout values. * For Boolean states prefer e.g. set_enabled (True/False) rather than separate enable()/disable() functions. * You should prefer properties for simple properties or states of objects (e.g. 'name', 'user_name', 'tolerance'). Contained HardwareObjects also use properties - - + + #### Style guidlines It is very important to write a clean and readable code. Therefore we follow the [PEP8 guidlines](https://www.python.org/dev/peps/pep-0008/). Minimal required guidlines are: diff --git a/conda-environment.yml b/conda-environment.yml index 3164b2990..1651cd06f 100644 --- a/conda-environment.yml +++ b/conda-environment.yml @@ -5,8 +5,7 @@ dependencies: - python >=3.8,<3.11 - openldap - python-ldap=3.4.0 - - pip - poetry - nodejs - redis-server - - pnpm \ No newline at end of file + - pnpm diff --git a/conda-install.sh b/conda-install.sh index ab7b5f425..c7ff8d3ee 100755 --- a/conda-install.sh +++ b/conda-install.sh @@ -27,7 +27,7 @@ mxcube_download() { echo >&2 'You need git, curl, or wget to install MXCuBE' exit 1 fi - + command git clone "$(mxcube_web_source)" "$(mxcube_install_dir)" || { echo >&2 'Failed to clone mxcube-3 repo. Please report this !' exit 2 @@ -41,7 +41,7 @@ mxcube_download() { command cd "$(mxcubecore_install_dir)" command pip install -e . command cd .. - + command cd "$(mxcube_install_dir)" } diff --git a/debian-install.sh b/debian-install.sh index 88f62cfa4..bab27c28d 100755 --- a/debian-install.sh +++ b/debian-install.sh @@ -19,7 +19,7 @@ mxcube_download() { echo >&2 'You need git, curl, or wget to install MXCuBE' exit 1 fi - + command git clone "$(mxcube_source)" "$(mxcube_install_dir)" || { echo >&2 'Failed to clone mxcube-3 repo. Please report this !' exit 2 @@ -44,7 +44,7 @@ install_debian_deps() { command sudo apt-get install v4l2loopback-dkms v4l2loopback-utils #ffmpeg - command sudo apt-get install ffmpeg + command sudo apt-get install ffmpeg } install_python_deps() { diff --git a/docker/README.md b/docker/README.md index 5399965de..364419353 100644 --- a/docker/README.md +++ b/docker/README.md @@ -66,4 +66,4 @@ Point the browser to localhost:8090 to start using MXCuBE3 The test credentials are: username: idtest0 -password: 000 \ No newline at end of file +password: 000 diff --git a/mxcube3/__init__.py b/mxcube3/__init__.py index b6749db36..a332f77ad 100644 --- a/mxcube3/__init__.py +++ b/mxcube3/__init__.py @@ -1,20 +1,20 @@ -import argparse -import mock -import os -import redis -import sys -import traceback - - from gevent import monkey monkey.patch_all(thread=False) -from mxcube3.server import Server as server -from mxcube3.app import MXCUBEApplication as mxcube -from mxcube3.config import Config +# Disabling E402 (module level import not at top of file) +# for the lines below as we are monkey patching +import argparse # noqa: E402 +import mock # noqa: E402 +import os # noqa: E402 +import redis # noqa: E402 +import sys # noqa: E402 +import traceback # noqa: E402 -from mxcubecore import HardwareRepository as HWR +from mxcube3.server import Server as server # noqa: E402 +from mxcube3.app import MXCUBEApplication as mxcube # noqa: E402 +from mxcube3.config import Config # noqa: E402 +from mxcubecore import HardwareRepository as HWR # noqa: E402 sys.modules["Qub"] = mock.Mock() sys.modules["Qub.CTools"] = mock.Mock() @@ -66,7 +66,11 @@ def parse_args(argv): "-el", "--enabled-loggers", dest="enabled_logger_list", - help="Which loggers to use, default is to use all loggers ([exception_logger, hwr_logger, mx3_hwr_logger, user_logger, queue_logger])", + help=( + "Which loggers to use, default is to use all loggers" + " ([exception_logger, hwr_logger, mx3_hwr_logger," + " user_logger, queue_logger])" + ), default=[ "exception_logger", "hwr_logger", diff --git a/mxcube3/app.py b/mxcube3/app.py index 385f7f0d4..1c48a814d 100644 --- a/mxcube3/app.py +++ b/mxcube3/app.py @@ -11,7 +11,6 @@ import time from pathlib import Path -from urllib.parse import urlparse from logging import StreamHandler from logging.handlers import TimedRotatingFileHandler @@ -21,7 +20,9 @@ from mxcubecore.utils.conversion import make_table from mxcube3.logging_handler import MX3LoggingHandler -from mxcube3.core.util.adapterutils import get_adapter_cls_from_hardware_object +from mxcube3.core.util.adapterutils import ( + get_adapter_cls_from_hardware_object, +) from mxcube3.core.adapter.adapter_base import AdapterBase from mxcube3.core.components.component_base import import_component from mxcube3.core.components.lims import Lims @@ -86,7 +87,9 @@ def init(app): :return: None """ - from mxcube3.core.adapter.beamline_adapter import BeamlineAdapter + from mxcube3.core.adapter.beamline_adapter import ( + BeamlineAdapter, + ) fname = os.path.dirname(__file__) HWR.add_hardware_objects_dirs([os.path.join(fname, "HardwareObjects")]) @@ -257,7 +260,13 @@ def __init__(self): @staticmethod def init( - server, allow_remote, ra_timeout, log_fpath, log_level, enabled_logger_list, cfg + server, + allow_remote, + ra_timeout, + log_fpath, + log_level, + enabled_logger_list, + cfg, ): """ Initializes application wide variables, sample video stream, and applies @@ -439,7 +448,10 @@ def init_state_storage(): def get_ui_properties(): # Add type information to each component retrieved from the beamline adapter # (either via config or via mxcubecore.beamline) - for _item_name, item_data in MXCUBEApplication.CONFIG.app.ui_properties.items(): + for ( + _item_name, + item_data, + ) in MXCUBEApplication.CONFIG.app.ui_properties.items(): for component_data in item_data.components: try: mxcore = MXCUBEApplication.mxcubecore @@ -447,8 +459,15 @@ def get_ui_properties(): adapter_cls_name = type(adapter).__name__ value_type = adapter.adapter_type except AttributeError: - msg = f"{component_data.attribute} not accessible via Beamline object. " - msg += f"Verify that beamline.{component_data.attribute} is valid and/or " + msg = ( + f"{component_data.attribute} not accessible" + " via Beamline object. " + ) + msg += ( + "Verify that" + f" beamline.{component_data.attribute} is" + " valid and/or " + ) msg += f"{component_data.attribute} accessible via get_role " msg += "check ui.yaml configuration file. " msg += "(attribute will NOT be avilable in UI)" @@ -466,7 +485,10 @@ def get_ui_properties(): return { key: value.dict() - for (key, value) in MXCUBEApplication.CONFIG.app.ui_properties.items() + for ( + key, + value, + ) in MXCUBEApplication.CONFIG.app.ui_properties.items() } @staticmethod diff --git a/mxcube3/config.py b/mxcube3/config.py index ad34a1e83..acc74cc98 100644 --- a/mxcube3/config.py +++ b/mxcube3/config.py @@ -16,7 +16,6 @@ class ConfigLoader: @staticmethod def load(path: str, schema: BaseModel, filetype="yaml"): - with open(os.path.join(path)) as f: config = ruamel.yaml.load(f.read(), ruamel.yaml.RoundTripLoader) try: diff --git a/mxcube3/core/adapter/adapter_base.py b/mxcube3/core/adapter/adapter_base.py index 72d43a5a7..ceece1e3d 100644 --- a/mxcube3/core/adapter/adapter_base.py +++ b/mxcube3/core/adapter/adapter_base.py @@ -8,7 +8,9 @@ from typing import Any -from mxcube3.core.util.adapterutils import get_adapter_cls_from_hardware_object +from mxcube3.core.util.adapterutils import ( + get_adapter_cls_from_hardware_object, +) from mxcube3.core.models.adaptermodels import HOModel, HOActuatorModel @@ -89,7 +91,7 @@ def _command_success(self, t): try: model["return"].validate({"return": value}) - except pydantic.ValidationError as ex: + except pydantic.ValidationError: attr_name = t.call_args["cmd_name"] logging.getLogger("MX3.HWR").exception( f"Return value of {self._name}.{attr_name} is of wrong type" @@ -232,7 +234,9 @@ def attributes(self): model["return"].validate({"return": value}) except pydantic.ValidationError: logging.getLogger("MX3.HWR").exception( - f"Return value of {self._name}.{attribute_name} is of wrong type" + "Return value of" + f" {self._name}.{attribute_name} is of wrong" + " type" ) _attributes[attribute_name] = {} else: @@ -425,7 +429,8 @@ def _dict_repr(self): f"Could not get dictionary representation of {self._ho.name()}" ) logging.getLogger("MX3.HWR").error( - f"Check status of {self._ho.name()}, object is offline, in fault or returns unexpected value !" + f"Check status of {self._ho.name()}, object is" + " offline, in fault or returns unexpected value !" ) self._available = False diff --git a/mxcube3/core/adapter/beam_adapter.py b/mxcube3/core/adapter/beam_adapter.py index 6f119fb1a..5e36a1e27 100644 --- a/mxcube3/core/adapter/beam_adapter.py +++ b/mxcube3/core/adapter/beam_adapter.py @@ -1,6 +1,9 @@ from mxcube3.core.adapter.adapter_base import ActuatorAdapterBase from mxcube3.core.util.adapterutils import export -from mxcube3.core.models.adaptermodels import HOBeamModel, HOBeamValueModel +from mxcube3.core.models.adaptermodels import ( + HOBeamModel, + HOBeamValueModel, +) class BeamAdapter(ActuatorAdapterBase): @@ -29,7 +32,12 @@ def _get_aperture(self) -> tuple: def _get_value(self) -> HOBeamValueModel: beam_ho = self._ho - beam_info_dict = {"position": [], "shape": "", "size_x": 0, "size_y": 0} + beam_info_dict = { + "position": [], + "shape": "", + "size_x": 0, + "size_y": 0, + } sx, sy, shape, _label = beam_ho.get_value() if beam_ho is not None: @@ -45,7 +53,10 @@ def _get_value(self) -> HOBeamValueModel: aperture_list, current_aperture = self._get_aperture() beam_info_dict.update( - {"apertureList": aperture_list, "currentAperture": current_aperture} + { + "apertureList": aperture_list, + "currentAperture": current_aperture, + } ) return HOBeamValueModel(**{"value": beam_info_dict}) diff --git a/mxcube3/core/adapter/beamline_adapter.py b/mxcube3/core/adapter/beamline_adapter.py index e5f55e3e5..5da3d62ed 100644 --- a/mxcube3/core/adapter/beamline_adapter.py +++ b/mxcube3/core/adapter/beamline_adapter.py @@ -39,14 +39,10 @@ def __init__(self, beamline_hwobj, app): ) def wf_parameters_needed(self, params): - self.app.server.emit( - "workflowParametersDialog", params, namespace="/hwr" - ) + self.app.server.emit("workflowParametersDialog", params, namespace="/hwr") def gphl_wf_parameters_needed(self, params): - self.app.server.emit( - "gphlWorkflowParametersDialog", params, namespace="/hwr" - ) + self.app.server.emit("gphlWorkflowParametersDialog", params, namespace="/hwr") def get_object(self, name): return self.get_attr_from_path(name) diff --git a/mxcube3/core/adapter/data_publisher_adapter.py b/mxcube3/core/adapter/data_publisher_adapter.py index 15866e3c5..a6b647012 100644 --- a/mxcube3/core/adapter/data_publisher_adapter.py +++ b/mxcube3/core/adapter/data_publisher_adapter.py @@ -1,5 +1,4 @@ import logging -import datetime from mxcubecore.BaseHardwareObjects import HardwareObjectState @@ -16,7 +15,7 @@ def __init__(self, ho, *args, **kwargs): """ super(DataPublisherAdapter, self).__init__(ho, *args, **kwargs) self._all_data_list = [] - self._current_data_list= [] + self._current_data_list = [] self._current_info = {} try: @@ -31,7 +30,9 @@ def __init__(self, ho, *args, **kwargs): def _new_data_handler(self, data): self._current_data_list.append(data["data"]) - self.emit_ho_attribute_changed("current_data", [data["data"]], operation="UPDATE") + self.emit_ho_attribute_changed( + "current_data", [data["data"]], operation="UPDATE" + ) def _start_handler(self, data): self._current_info = data diff --git a/mxcube3/core/adapter/machine_info_adapter.py b/mxcube3/core/adapter/machine_info_adapter.py index 79be847ba..3309601ee 100644 --- a/mxcube3/core/adapter/machine_info_adapter.py +++ b/mxcube3/core/adapter/machine_info_adapter.py @@ -43,9 +43,7 @@ def get_message(self): def get_current(self): try: - current = "{:.1f} mA".format( - round(float(self._ho.get_current()), 1) - ) + current = "{:.1f} mA".format(round(float(self._ho.get_current()), 1)) except (TypeError, AttributeError): current = "-1" diff --git a/mxcube3/core/adapter/wavelength_adapter.py b/mxcube3/core/adapter/wavelength_adapter.py index 4c058db16..3162fd961 100644 --- a/mxcube3/core/adapter/wavelength_adapter.py +++ b/mxcube3/core/adapter/wavelength_adapter.py @@ -59,8 +59,8 @@ def _get_value(self) -> FloatValueModel: """ try: return FloatValueModel(**{"value": self._ho.get_wavelength()}) - except (AttributeError, TypeError): - raise ValueError("Could not get value") + except (AttributeError, TypeError) as ex: + raise ValueError("Could not get value") from ex def state(self): """ @@ -87,8 +87,8 @@ def limits(self): """ try: return self._ho.get_wavelength_limits() - except (AttributeError, TypeError): - raise ValueError("Could not get limits") + except (AttributeError, TypeError) as ex: + raise ValueError("Could not get limits") from ex def read_only(self): """ diff --git a/mxcube3/core/components/beamline.py b/mxcube3/core/components/beamline.py index 3784ceeed..edb43e4d3 100644 --- a/mxcube3/core/components/beamline.py +++ b/mxcube3/core/components/beamline.py @@ -28,7 +28,6 @@ def init_signals(self): msg += "signals" logging.getLogger("MX3.HWR").exception(msg) try: - actions = HWR.beamline.beamline_actions if actions is not None: cmds = ( @@ -36,10 +35,19 @@ def init_signals(self): + HWR.beamline.beamline_actions.get_annotated_commands() ) for cmd in cmds: - cmd.connect("commandBeginWaitReply", signals.beamline_action_start) - cmd.connect("commandReplyArrived", signals.beamline_action_done) + cmd.connect( + "commandBeginWaitReply", + signals.beamline_action_start, + ) + cmd.connect( + "commandReplyArrived", + signals.beamline_action_done, + ) cmd.connect("commandReady", signals.beamline_action_done) - cmd.connect("commandFailed", signals.beamline_action_failed) + cmd.connect( + "commandFailed", + signals.beamline_action_failed, + ) else: logging.getLogger("MX3.HWR").error( "beamline_actions hardware object is not defined" @@ -270,18 +278,20 @@ def beamline_run_action(self, name, params): try: cmd.emit("commandBeginWaitReply", name) logging.getLogger("user_level_log").info( - "Starting %s(%s)", cmd.name(), ", ".join(map(str, params)) + "Starting %s(%s)", + cmd.name(), + ", ".join(map(str, params)), ) cmd(*params) - except Exception: + except Exception as ex: err = str(sys.exc_info()[1]) - raise Exception(str(err)) + raise Exception(str(err)) from ex # Annotated command try: HWR.beamline.beamline_actions.execute_command(name, params) - except Exception: + except Exception as ex: msg = "Action cannot run: command '%s' does not exist" % name - raise Exception(msg) + raise Exception(msg) from ex def get_beam_info(self): """ @@ -292,7 +302,12 @@ def get_beam_info(self): :rtype: dict """ beam = HWR.beamline.beam - beam_info_dict = {"position": [], "shape": "", "size_x": 0, "size_y": 0} + beam_info_dict = { + "position": [], + "shape": "", + "size_x": 0, + "size_y": 0, + } sx, sy, shape, _label = beam.get_value() if beam is not None: @@ -308,7 +323,10 @@ def get_beam_info(self): aperture_list, current_aperture = self.get_aperture() beam_info_dict.update( - {"apertureList": aperture_list, "currentAperture": current_aperture} + { + "apertureList": aperture_list, + "currentAperture": current_aperture, + } ) return beam_info_dict @@ -352,7 +370,6 @@ def diffractometer_get_info(self): return ret def get_detector_info(self): - try: filetype = HWR.beamline.detector.get_property("file_suffix") except Exception: diff --git a/mxcube3/core/components/gphl_workflow.py b/mxcube3/core/components/gphl_workflow.py index a013f0080..61f4246fd 100644 --- a/mxcube3/core/components/gphl_workflow.py +++ b/mxcube3/core/components/gphl_workflow.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import base64 import copy @@ -14,8 +13,9 @@ def __init__(self, app, config): def get_available_workflows(self): return { - "workflows": - copy.deepcopy(HWR.beamline.gphl_workflow.get_available_workflows()) + "workflows": copy.deepcopy( + HWR.beamline.gphl_workflow.get_available_workflows() + ) } def submit_parameters(self, params): @@ -24,7 +24,11 @@ def submit_parameters(self, params): def test_workflow_dialog(self, wf): dialog = { "properties": { - "name": {"title": "Task name", "type": "string", "minLength": 2}, + "name": { + "title": "Task name", + "type": "string", + "minLength": 2, + }, "description": { "title": "Description", "type": "string", diff --git a/mxcube3/core/components/lims.py b/mxcube3/core/components/lims.py index ce9d4612b..1e619dd49 100644 --- a/mxcube3/core/components/lims.py +++ b/mxcube3/core/components/lims.py @@ -2,7 +2,6 @@ import sys import logging import copy -import os import io import math import re @@ -238,14 +237,18 @@ def lims_login(self, loginID, password, create_session): session["proposal_list"].append(dummy) login_res["proposalList"] = session["proposal_list"] - login_res["status"] = {"code": "ok", "msg": "Successful login"} + login_res["status"] = { + "code": "ok", + "msg": "Successful login", + } else: try: login_res = HWR.beamline.lims.login( loginID, password, create_session=create_session ) proposal = HWR.beamline.lims.get_proposal( - login_res["Proposal"]["code"], login_res["Proposal"]["number"] + login_res["Proposal"]["code"], + login_res["Proposal"]["number"], ) except Exception: @@ -342,9 +345,6 @@ def select_proposal(self, proposal): "[LIMS] Error creating data directories, %s" % sys.exc_info()[1] ) - # Get all the files in the root data dir for this user - root_path = HWR.beamline.session.get_base_image_directory() - # save selected proposal in users db current_user.selected_proposal = proposal self.app.usermanager.update_user(current_user) @@ -434,7 +434,8 @@ def synch_with_lims(self): ] + ":%02d" % int(sample_info["sampleLocation"]) except Exception: logging.getLogger("MX3.HWR").info( - "[LIMS] Could not parse sample loaction from LIMS, (perhaps not set ?)" + "[LIMS] Could not parse sample loaction from" + " LIMS, (perhaps not set ?)" ) else: sample_info["lims_location"] = lims_location diff --git a/mxcube3/core/components/queue.py b/mxcube3/core/components/queue.py index 30cf6a76f..adc47a373 100644 --- a/mxcube3/core/components/queue.py +++ b/mxcube3/core/components/queue.py @@ -6,9 +6,6 @@ import itertools import logging import re -import json - -from typing import Any from mock import Mock @@ -62,7 +59,7 @@ def build_prefix_path_dict(self, path_list): '[QUEUE] Warning, failed to interpret path: "%s", please check path' % path ) - path, run_number, image_number = (path, 0, 0) + path, run_number = (path, 0) if path in prefix_path_dict: prefix_path_dict[path] = max(prefix_path_dict[path], run_number) else: @@ -98,7 +95,7 @@ def node_index(self, node): # RootNode nothing to return if isinstance(node, qmo.RootNode): - sample, idx = None, None + sample = None # For samples simply return the sampleID elif isinstance(node, qmo.Sample): sample = node.loc_str @@ -496,7 +493,10 @@ def _handle_wf(self, sample_node, node, include_lims_data): def _handle_xrf(self, sample_node, node): queueID = node._node_id enabled, state = self.get_node_state(queueID) - parameters = {"countTime": node.count_time, "shape": node.shape} + parameters = { + "countTime": node.count_time, + "shape": node.shape, + } parameters.update(node.path_template.as_dict()) parameters["path"] = parameters["directory"] @@ -532,7 +532,11 @@ def _handle_xrf(self, sample_node, node): def _handle_energy_scan(self, sample_node, node): queueID = node._node_id enabled, state = self.get_node_state(queueID) - parameters = {"element": node.element_symbol, "edge": node.edge, "shape": -1} + parameters = { + "element": node.element_symbol, + "edge": node.edge, + "shape": -1, + } parameters.update(node.path_template.as_dict()) parameters["path"] = parameters["directory"] @@ -643,7 +647,10 @@ def _handle_interleaved(self, sample_node, node): res = { "label": "Interleaved", "type": "Interleaved", - "parameters": {"wedges": wedges, "swNumImages": node.interleave_num_images}, + "parameters": { + "wedges": wedges, + "swNumImages": node.interleave_num_images, + }, "checked": node.is_enabled(), "sampleID": sample_node.loc_str, "sampleQueueID": sample_node._node_id, @@ -1138,8 +1145,13 @@ def set_dc_params(self, model, entry, task_data, sample_model): acq2.acquisition_parameters.centred_position = cpos2 elif params.get("mesh", False): grid = HWR.beamline.sample_view.get_shape(params["shape"]) - acq.acquisition_parameters.mesh_range = (grid.width, grid.height) - mesh_center = HWR.beamline.default_acquisition_parameters["mesh"].get("mesh_center", "top-left") + acq.acquisition_parameters.mesh_range = ( + grid.width, + grid.height, + ) + mesh_center = HWR.beamline.default_acquisition_parameters["mesh"].get( + "mesh_center", "top-left" + ) if mesh_center == "top-left": acq.acquisition_parameters.centred_position = ( grid.get_centred_positions()[0] @@ -1220,13 +1232,15 @@ def set_wf_params(self, model, entry, task_data, sample_model): ) full_path = os.path.join( - HWR.beamline.session.get_base_image_directory(), params.get("subdir", "") + HWR.beamline.session.get_base_image_directory(), + params.get("subdir", ""), ) model.path_template.directory = full_path process_path = os.path.join( - HWR.beamline.session.get_base_process_directory(), params.get("subdir", "") + HWR.beamline.session.get_base_process_directory(), + params.get("subdir", ""), ) model.path_template.process_directory = process_path @@ -1245,7 +1259,10 @@ def set_wf_params(self, model, entry, task_data, sample_model): beamline_params["shape"] = params["shape"] params_list = list( - map(str, list(itertools.chain(*iter(beamline_params.items())))) + map( + str, + list(itertools.chain(*iter(beamline_params.items()))), + ) ) params_list.insert(0, params["wfpath"]) params_list.insert(0, "modelpath") @@ -1266,13 +1283,18 @@ def set_char_params(self, model, entry, task_data, sample_model): """ params = task_data["parameters"] self.set_dc_params( - model.reference_image_collection, entry, task_data, sample_model + model.reference_image_collection, + entry, + task_data, + sample_model, ) try: - params["strategy_complexity"] = ["SINGLE", "FEW", "MANY"].index( - params["strategy_complexity"] - ) + params["strategy_complexity"] = [ + "SINGLE", + "FEW", + "MANY", + ].index(params["strategy_complexity"]) except ValueError: params["strategy_complexity"] = 0 @@ -1629,7 +1651,10 @@ def add_workflow(self, node_id, task): if task["parameters"]["wfpath"] == "Gphl": wf_model, dc_entry = self._create_gphl_wf(task) self.set_gphl_wf_params( - wf_model, dc_entry, task, parent_model.get_sample_node() + wf_model, + dc_entry, + task, + parent_model.get_sample_node(), ) else: wf_model, dc_entry = self._create_wf(task) @@ -1861,7 +1886,10 @@ def queue_model_diff_plan_available(self, char, collection_list): task = self._handle_dc(sample, collection) task.update( - {"isDiffractionPlan": True, "originID": origin_model._node_id} + { + "isDiffractionPlan": True, + "originID": origin_model._node_id, + } ) cols.append(task) @@ -1948,7 +1976,9 @@ def init_signals(self, queue): from mxcube3.routes import signals HWR.beamline.collect.connect( - HWR.beamline.collect, "collectStarted", signals.collect_started + HWR.beamline.collect, + "collectStarted", + signals.collect_started, ) HWR.beamline.collect.connect( HWR.beamline.collect, @@ -1961,7 +1991,9 @@ def init_signals(self, queue): signals.collect_oscillation_failed, ) HWR.beamline.collect.connect( - HWR.beamline.collect, "collectImageTaken", signals.collect_image_taken + HWR.beamline.collect, + "collectImageTaken", + signals.collect_image_taken, ) HWR.beamline.collect.connect( @@ -1973,7 +2005,9 @@ def init_signals(self, queue): queue.connect(queue, "child_added", self.queue_model_child_added) queue.connect( - queue, "diff_plan_available", self.queue_model_diff_plan_available + queue, + "diff_plan_available", + self.queue_model_diff_plan_available, ) HWR.beamline.queue_manager.connect( @@ -1981,7 +2015,8 @@ def init_signals(self, queue): ) HWR.beamline.queue_manager.connect( - "queue_execution_finished", signals.queue_execution_finished + "queue_execution_finished", + signals.queue_execution_finished, ) HWR.beamline.queue_manager.connect( @@ -1993,25 +2028,30 @@ def init_signals(self, queue): ) HWR.beamline.queue_manager.connect( - "queue_entry_execute_finished", signals.queue_execution_entry_finished + "queue_entry_execute_finished", + signals.queue_execution_entry_finished, ) HWR.beamline.queue_manager.connect( - "queue_entry_execute_started", signals.queue_execution_entry_started + "queue_entry_execute_started", + signals.queue_execution_entry_started, ) HWR.beamline.queue_manager.connect("collectEnded", signals.collect_ended) HWR.beamline.queue_manager.connect( - "queue_interleaved_started", signals.queue_interleaved_started + "queue_interleaved_started", + signals.queue_interleaved_started, ) HWR.beamline.queue_manager.connect( - "queue_interleaved_finished", signals.queue_interleaved_finished + "queue_interleaved_finished", + signals.queue_interleaved_finished, ) HWR.beamline.queue_manager.connect( - "queue_interleaved_sw_done", signals.queue_interleaved_sw_done + "queue_interleaved_sw_done", + signals.queue_interleaved_sw_done, ) HWR.beamline.queue_manager.connect( @@ -2304,7 +2344,11 @@ def add_centring(self, _id, params): logging.getLogger("MX3.HWR").info("[QUEUE] centring added to sample") - return {"QueueId": new_node, "Type": "Centring", "Params": params} + return { + "QueueId": new_node, + "Type": "Centring", + "Params": params, + } def update_dependent_field(self, task_name, data): try: @@ -2339,7 +2383,7 @@ def get_default_task_parameters(self, task_name): try: ui_schema = data_model.ui_schema() if data_model else {} - except: + except Exception: ui_schema = {} if schema: diff --git a/mxcube3/core/components/samplechanger.py b/mxcube3/core/components/samplechanger.py index d6b525a3f..6ec814737 100644 --- a/mxcube3/core/components/samplechanger.py +++ b/mxcube3/core/components/samplechanger.py @@ -224,7 +224,7 @@ def mount_sample_clean_up(self, sample): except Exception as ex: logging.getLogger("MX3.HWR").exception("[SC] sample could not be mounted") - raise RuntimeError(str(ex)) + raise RuntimeError(str(ex)) from ex else: # Clean up if the new sample was mounted or the current sample was # unmounted and the new one, for some reason, failed to mount @@ -300,7 +300,10 @@ def get_capacity(self): num_samples = 0 for basket in baskets: num_samples += basket.get_number_of_samples() - res = {"num_baskets": len(baskets), "num_samples": num_samples} + res = { + "num_baskets": len(baskets), + "num_samples": num_samples, + } return res def get_maintenance_cmds(self): @@ -343,7 +346,10 @@ def get_initial_state(self): "state": state, "loaded_sample": loaded_sample, "contents": contents, - "global_state": {"global_state": global_state, "commands_state": cmdstate}, + "global_state": { + "global_state": global_state, + "commands_state": cmdstate, + }, "cmds": {"cmds": cmds}, "msg": msg, "plate_mode": HWR.beamline.diffractometer.in_plate_mode(), @@ -380,7 +386,8 @@ def sync_with_crims(self): return {"xtal_list": xtal_list} -def queue_mount_sample(view, data_model, centring_done_cb, async_result): +# Disabling C901 function is too complex (19) +def queue_mount_sample(view, data_model, centring_done_cb, async_result): # noqa: C901 from mxcube3.routes import signals from mxcube3.app import MXCUBEApplication as mxcube @@ -418,7 +425,10 @@ def queue_mount_sample(view, data_model, centring_done_cb, async_result): sample = {"location": element, "sampleID": element} mxcube.sample_changer.mount_sample_clean_up(sample) else: - sample = {"location": data_model.loc_str, "sampleID": data_model.loc_str} + sample = { + "location": data_model.loc_str, + "sampleID": data_model.loc_str, + } try: res = mxcube.sample_changer.mount_sample_clean_up(sample) @@ -428,11 +438,12 @@ def queue_mount_sample(view, data_model, centring_done_cb, async_result): "Sample loading res: %s" % str(res) ) - if res == False: + # We need to investigte if the comment below is still valid + if not res == False: # noqa: E712 # WARNING: explicit test of False return value. - # This is to preserve backward compatibility (load_sample was supposed to return None); - # if sample could not be loaded, but no exception is raised, let's skip - # the sample + # This is to preserve backward compatibility (load_sample was + # supposed to return None); if sample could not be loaded, but + # no exception is raised, let's skip the sample raise queue_entry.QueueSkippEntryException( "Sample changer could not load sample", "" @@ -503,7 +514,8 @@ def queue_mount_sample(view, data_model, centring_done_cb, async_result): == queue_entry.CENTRING_METHOD.FULLY_AUTOMATIC ): raise queue_entry.QueueSkippEntryException( - "Could not center sample, skipping", "" + "Could not center sample, skipping", + "", ) else: raise RuntimeError("Could not center sample") diff --git a/mxcube3/core/components/sampleview.py b/mxcube3/core/components/sampleview.py index 0c6c88090..320c397df 100644 --- a/mxcube3/core/components/sampleview.py +++ b/mxcube3/core/components/sampleview.py @@ -17,7 +17,9 @@ from mxcubecore.queue_entry.base_queue_entry import CENTRING_METHOD from mxcubecore.BaseHardwareObjects import HardwareObjectState -from mxcubecore.HardwareObjects.abstract.AbstractNState import AbstractNState +from mxcubecore.HardwareObjects.abstract.AbstractNState import ( + AbstractNState, +) from mxcubecore import HardwareRepository as HWR @@ -91,14 +93,16 @@ def frames(): while True: self._new_frame.wait() yield ( - b"--frame\r\n" - b"--!>\nContent-type: image/jpeg\n\n" + self._sample_image + b"\r\n" + b"--frame\r\n--!>\nContent-type: image/jpeg\n\n" + + self._sample_image + + b"\r\n" ) self._client_connected() response = Response( - frames(), mimetype='multipart/x-mixed-replace; boundary="!>"' + frames(), + mimetype='multipart/x-mixed-replace; boundary="!>"', ) # keep track of when client stops reading the stream response.call_on_close(self._client_disconnected) @@ -115,7 +119,9 @@ def __init__(self, app, config): self.http_streamer = HttpStreamer() enable_snapshots( - HWR.beamline.collect, HWR.beamline.diffractometer, HWR.beamline.sample_view + HWR.beamline.collect, + HWR.beamline.diffractometer, + HWR.beamline.sample_view, ) HWR.beamline.sample_view.connect("shapesChanged", self._emit_shapes_updated) @@ -128,7 +134,9 @@ def __init__(self, app, config): def _zoom_changed(self, *args, **kwargs): ppm = HWR.beamline.diffractometer.get_pixels_per_mm() self.app.server.emit( - "update_pixels_per_mm", {"pixelsPerMm": ppm}, namespace="/hwr" + "update_pixels_per_mm", + {"pixelsPerMm": ppm}, + namespace="/hwr", ) def _emit_shapes_updated(self): @@ -373,7 +381,10 @@ def move_zoom_motor(self, pos): return ( "motor is already moving", 406, - {"Content-Type": "application/json", "msg": "zoom already moving"}, + { + "Content-Type": "application/json", + "msg": "zoom already moving", + }, ) if isinstance(zoom_motor, AbstractNState): @@ -487,12 +498,7 @@ def move_to_beam(self, x, y): msg = "Moving point x: %s, y: %s to beam" % (x, y) logging.getLogger("user_level_log").info(msg) - if getattr(HWR.beamline.diffractometer, "move_to_beam") is None: - # v > 2.2, or perhaps start_move_to_beam? - HWR.beamline.diffractometer.move_to_beam(x, y) - else: - # v <= 2.1 - HWR.beamline.diffractometer.move_to_beam(x, y) + HWR.beamline.diffractometer.move_to_beam(x, y) def set_centring_method(self, method): if method == CENTRING_METHOD.LOOP: @@ -605,11 +611,12 @@ def take_snapshots(self, snapshots=None, _do_take_snapshot=_do_take_snapshot): ) _do_take_snapshot(snapshot_filename) # diffractometer.save_snapshot(snapshot_filename) - except Exception: + except Exception as ex: sys.excepthook(*sys.exc_info()) raise RuntimeError( - "Could not take snapshot '%s'", snapshot_filename - ) + "Could not take snapshot '%s'", + snapshot_filename, + ) from ex if number_of_snapshots > 1: move_omega_relative(90) diff --git a/mxcube3/core/components/user/database.py b/mxcube3/core/components/user/database.py index 7934d99cf..db46dfedc 100644 --- a/mxcube3/core/components/user/database.py +++ b/mxcube3/core/components/user/database.py @@ -30,10 +30,17 @@ class UserDatastore(SQLAlchemySessionUserDatastore): :param role_model: See :ref:`Models `. """ - def __init__(self, *args, message_model=typing.Type["Message"], **kwargs): + # pyflakes are a bit picky with F821 (Undefined name), + # not even sure "Message" should ba considered as an undefined name + def __init__( + self, + *args, + message_model=typing.Type["Message"], # noqa: F821 + **kwargs, + ): SQLAlchemySessionUserDatastore.__init__(self, *args, **kwargs) self._message_model = message_model - self._messages_users_model = typing.Type["MessagesUsers"] + self._messages_users_model = typing.Type["MessagesUsers"] # noqa: F821 def create_message(self, message): return self.put( diff --git a/mxcube3/core/components/user/usermanager.py b/mxcube3/core/components/user/usermanager.py index 24b554bde..12f9f346a 100644 --- a/mxcube3/core/components/user/usermanager.py +++ b/mxcube3/core/components/user/usermanager.py @@ -1,7 +1,6 @@ import logging import json import uuid -import time import datetime import flask @@ -149,7 +148,7 @@ def login(self, login_id: str, password: str): except Exception: raise else: - if not "sid" in flask.session: + if "sid" not in flask.session: flask.session["sid"] = str(uuid.uuid4()) # Making sure that the session of any in active users are invalideted @@ -381,7 +380,9 @@ def _login(self, login_id: str, password: str): raise Exception("You are already logged in") else: raise Exception( - "Login rejected, you are already logged in somewhere else\nand Another user is already logged in" + "Login rejected, you are already logged in" + " somewhere else\nand Another user is already" + " logged in" ) # Only allow in-house log-in from local host diff --git a/mxcube3/core/components/workflow.py b/mxcube3/core/components/workflow.py index 615ace300..5f1239684 100644 --- a/mxcube3/core/components/workflow.py +++ b/mxcube3/core/components/workflow.py @@ -44,7 +44,11 @@ def get_mesh_result(self, gid, _type="heatmap"): def test_workflow_dialog(self, wf): dialog = { "properties": { - "name": {"title": "Task name", "type": "string", "minLength": 2}, + "name": { + "title": "Task name", + "type": "string", + "minLength": 2, + }, "description": { "title": "Description", "type": "string", diff --git a/mxcube3/core/models/adaptermodels.py b/mxcube3/core/models/adaptermodels.py index cf050d22c..9cc36c040 100644 --- a/mxcube3/core/models/adaptermodels.py +++ b/mxcube3/core/models/adaptermodels.py @@ -10,13 +10,16 @@ class HOModel(BaseModel): type: str = Field("", description="type of data the object contains") available: bool = Field(True, description="True if the object avilable/enabled") readonly: bool = Field( - True, description="True if the object can only be read (not manipluated)" + True, + description="True if the object can only be read (not manipluated)", ) attributes: dict = Field({}, description="Data attributes") commands: Union[dict, list] = Field({}, description="Available methods") + # pyflakes are a bit picky with F821 (Undefined name), + # not even sure "forbid" should ba considered as an undefined name class Config: - extra: "forbid" + extra: "forbid" # noqa: F821 class HOActuatorModel(HOModel): @@ -43,14 +46,16 @@ class HOBeamRawValueModel(BaseModel): position: Tuple[float, float] = Field((0, 0), description="Beam position on OAV") shape: str = Field("ellipse", descrption="Beam shape") size_x: float = Field( - 0.01, description="Current aperture x size (width) in millimieters" + 0.01, + description="Current aperture x size (width) in millimieters", ) size_y: float = Field( - 0.01, description="Current aperture y size (height) in millimieters" + 0.01, + description="Current aperture y size (height) in millimieters", ) class Config: - extra: "forbid" + extra: "forbid" # noqa: F821 class HOBeamModel(HOActuatorModel): diff --git a/mxcube3/core/models/configmodels.py b/mxcube3/core/models/configmodels.py index 57aae86b1..fab697c1c 100644 --- a/mxcube3/core/models/configmodels.py +++ b/mxcube3/core/models/configmodels.py @@ -6,7 +6,8 @@ class FlaskConfigModel(BaseModel): SECRET_KEY: str = Field( - b"o`\xb5\xa5\xc2\x8c\xb2\x8c-?\xe0,/i#c", description="Flask secret key" + b"o`\xb5\xa5\xc2\x8c\xb2\x8c-?\xe0,/i#c", + description="Flask secret key", ) SESSION_TYPE: str = Field("redis", description="Flask session type") SESSION_KEY_PREFIX: str = Field("mxcube:session:", description="Session prefix") @@ -24,7 +25,8 @@ class FlaskConfigModel(BaseModel): # ADHOC for flask to generate a certifcate, # NONE for no SSL CERT: str = Field( - "NONE", description="One of the strings ['SIGNED', 'ADHOC', NONE]" + "NONE", + description="One of the strings ['SIGNED', 'ADHOC', NONE]", ) @@ -65,7 +67,8 @@ class UserManagerConfigModel(BaseModel): "UserManager", description="UserManager class", alias="class" ) inhouse_is_staff: bool = Field( - True, description="Treat users defined as inhouse in session.xml as staff" + True, + description="Treat users defined as inhouse in session.xml as staff", ) users: List[UserManagerUserConfigModel] @@ -82,7 +85,8 @@ class MXCUBEAppConfigModel(BaseModel): # URL from which the client retreives the video stream (often different from # local host when running behind proxy) VIDEO_STREAM_URL: str = Field( - "", description="Video stream URL, URL used by client to get video stream" + "", + description="Video stream URL, URL used by client to get video stream", ) # Port from which the video_stream process (https://github.com/mxcube/video-streamer) @@ -90,7 +94,9 @@ class MXCUBEAppConfigModel(BaseModel): VIDEO_STREAM_PORT: str = Field("", description="Video stream PORT") USE_EXTERNAL_STREAMER: bool = Field( False, - description="True to use video stream produced by external software, false otherwise", + description=( + "True to use video stream produced by external software, false otherwise" + ), ) mode: ModeEnum = Field(ModeEnum.OSC, description="MXCuBE mode SSX or OSC") usermanager: UserManagerConfigModel diff --git a/mxcube3/core/models/usermodels.py b/mxcube3/core/models/usermodels.py index b71cac570..caa582e59 100644 --- a/mxcube3/core/models/usermodels.py +++ b/mxcube3/core/models/usermodels.py @@ -70,10 +70,14 @@ class User(Base, UserMixin): limsdata = Column(JSON, unique=False) last_request_timestamp = Column(DateTime()) roles = relationship( - "Role", secondary="roles_users", backref=backref("users", lazy="dynamic") + "Role", + secondary="roles_users", + backref=backref("users", lazy="dynamic"), ) messages = relationship( - "Message", secondary="messages_users", backref=backref("users", lazy="dynamic") + "Message", + secondary="messages_users", + backref=backref("users", lazy="dynamic"), ) def has_roles(self, *args): diff --git a/mxcube3/core/util/adapterutils.py b/mxcube3/core/util/adapterutils.py index 9de5caeb8..a8e504652 100644 --- a/mxcube3/core/util/adapterutils.py +++ b/mxcube3/core/util/adapterutils.py @@ -17,9 +17,12 @@ def get_adapter_cls_from_hardware_object(ho): AbstractMotor, ) - from mxcubecore.HardwareObjects import MiniDiff, GenericDiffractometer - - # This needs to be a direct import of DataPublisher otherwise the + from mxcubecore.HardwareObjects import ( + MiniDiff, + GenericDiffractometer, + ) + + # This needs to be a direct import of DataPublisher otherwise the # is instance check below fails due to different "import paths" It # inly works because mxcubecore adds mxcubecore.HardwareObjects to # sys path in __init__.py @@ -28,11 +31,17 @@ def get_adapter_cls_from_hardware_object(ho): from mxcube3.core.adapter.actuator_adapter import ActuatorAdapter from mxcube3.core.adapter.motor_adapter import MotorAdapter from mxcube3.core.adapter.detector_adapter import DetectorAdapter - from mxcube3.core.adapter.machine_info_adapter import MachineInfoAdapter + from mxcube3.core.adapter.machine_info_adapter import ( + MachineInfoAdapter, + ) from mxcube3.core.adapter.beam_adapter import BeamAdapter - from mxcube3.core.adapter.data_publisher_adapter import DataPublisherAdapter + from mxcube3.core.adapter.data_publisher_adapter import ( + DataPublisherAdapter, + ) from mxcube3.core.adapter.energy_adapter import EnergyAdapter - from mxcube3.core.adapter.diffractometer_adapter import DiffractometerAdapter + from mxcube3.core.adapter.diffractometer_adapter import ( + DiffractometerAdapter, + ) from mxcube3.core.adapter.nstate_adapter import NStateAdapter if isinstance(ho, AbstractNState.AbstractNState) or isinstance( diff --git a/mxcube3/core/util/convertutils.py b/mxcube3/core/util/convertutils.py index baf8052cc..388b79cc0 100644 --- a/mxcube3/core/util/convertutils.py +++ b/mxcube3/core/util/convertutils.py @@ -13,12 +13,15 @@ def convert_to_dict(ispyb_object): val = convert_to_dict(val) elif isinstance(val, list): val = [ - convert_to_dict(x) if not isinstance(x, dict) else x for x in val + (convert_to_dict(x) if not isinstance(x, dict) else x) for x in val ] elif isinstance(val, dict): val = dict( [ - (k, convert_to_dict(x) if not isinstance(x, dict) else x) + ( + k, + (convert_to_dict(x) if not isinstance(x, dict) else x), + ) for k, x in val.items() ] ) diff --git a/mxcube3/core/util/networkutils.py b/mxcube3/core/util/networkutils.py index ca2ab2769..b9d611222 100644 --- a/mxcube3/core/util/networkutils.py +++ b/mxcube3/core/util/networkutils.py @@ -19,10 +19,11 @@ def RateLimited(maxPerSecond): minInterval = 1.0 / float(maxPerSecond) lastTimeCalled = {} + def decorate(func): def rateLimitedFunction(*args, **kargs): if type(args[0]) is dict: - key = args[0].get('Signal') + key = args[0].get("Signal") else: key = args[0] elapsed = time.time() - lastTimeCalled.get(key, 0) @@ -142,13 +143,13 @@ def send_feedback(sender_data): if not bl_name: try: bl_name = os.environ["BEAMLINENAME"].lower() - except (KeyError): + except KeyError: bl_name = "unknown-beamline" if not local_user: try: local_user = os.environ["USER"].lower() - except (KeyError): + except KeyError: local_user = "unknown_user" _from = HWR.beamline.session.get_property("from_email", "") @@ -162,7 +163,11 @@ def send_feedback(sender_data): # Sender information provided by user _sender = sender_data.get("sender", "") to = HWR.beamline.session.get_property("feedback_email", "") + ",%s" % _sender - subject = "[MX3 FEEDBACK] %s (%s) on %s" % (local_user, _sender, bl_name) + subject = "[MX3 FEEDBACK] %s (%s) on %s" % ( + local_user, + _sender, + bl_name, + ) content = sender_data.get("content", "") send_mail(_from, to, subject, content) diff --git a/mxcube3/routes/beamline.py b/mxcube3/routes/beamline.py index bfdf52153..2d1700469 100644 --- a/mxcube3/routes/beamline.py +++ b/mxcube3/routes/beamline.py @@ -116,19 +116,36 @@ def add_adapter_routes(app, server, bp): get_type_hint = typing.get_type_hints(adapter._get_value) if "return" in get_type_hint: - create_get_route(app, server, bp, adapter, "_get_value", "value") + create_get_route( + app, + server, + bp, + adapter, + "_get_value", + "value", + ) # Map all other functions starting with prefix get_ or set_ and # flagged with the @export for attr in dir(adapter): if attr.startswith("get"): create_get_route( - app, server, bp, adapter, attr, attr.replace("get_", "") + app, + server, + bp, + adapter, + attr, + attr.replace("get_", ""), ) if attr.startswith("set"): create_set_route( - app, server, bp, adapter, attr, attr.replace("set_", "") + app, + server, + bp, + adapter, + attr, + attr.replace("set_", ""), ) exported_methods = adapter._exported_methods() diff --git a/mxcube3/routes/detector.py b/mxcube3/routes/detector.py index a8e61d738..fe9015584 100644 --- a/mxcube3/routes/detector.py +++ b/mxcube3/routes/detector.py @@ -23,7 +23,8 @@ def get_detector_info(): @server.restrict def display_image(): res = app.beamline.display_image( - request.args.get("path", None), request.args.get("img_num", None) + request.args.get("path", None), + request.args.get("img_num", None), ) return jsonify(res) diff --git a/mxcube3/routes/diffractometer.py b/mxcube3/routes/diffractometer.py index 705c35f73..2b44a8c27 100644 --- a/mxcube3/routes/diffractometer.py +++ b/mxcube3/routes/diffractometer.py @@ -105,7 +105,12 @@ def get_aperture(): aperture_list, current_aperture = app.beamline.get_aperture() - ret.update({"apertureList": aperture_list, "currentAperture": current_aperture}) + ret.update( + { + "apertureList": aperture_list, + "currentAperture": current_aperture, + } + ) resp = jsonify(ret) resp.status_code = 200 diff --git a/mxcube3/routes/gphl_workflow.py b/mxcube3/routes/gphl_workflow.py index c301e3ebd..721926c85 100644 --- a/mxcube3/routes/gphl_workflow.py +++ b/mxcube3/routes/gphl_workflow.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import io from flask import Blueprint, Response, jsonify, request, send_file diff --git a/mxcube3/routes/lims.py b/mxcube3/routes/lims.py index de8516a4f..ce9f109f7 100644 --- a/mxcube3/routes/lims.py +++ b/mxcube3/routes/lims.py @@ -3,7 +3,14 @@ from os.path import isfile, join import logging -from flask import Blueprint, jsonify, Response, send_file, request, render_template +from flask import ( + Blueprint, + jsonify, + Response, + send_file, + request, + render_template, +) from mxcubecore.model import queue_model_objects as qmo from mxcubecore import HardwareRepository as HWR @@ -11,7 +18,8 @@ from . import signals -def init_route(app, server, url_prefix): +# Disabling C901 function is too complex (19) +def init_route(app, server, url_prefix): # noqa: C901 bp = Blueprint("lims", __name__, url_prefix=url_prefix) @bp.route("/synch_samples", methods=["GET"]) @@ -23,7 +31,10 @@ def proposal_samples(): res = ( "Could not synchronize with LIMS", 409, - {"Content-Type": "application/json", "message": str(ex)}, + { + "Content-Type": "application/json", + "message": str(ex), + }, ) return res diff --git a/mxcube3/routes/login.py b/mxcube3/routes/login.py index d9c0e557f..7ddd1d818 100644 --- a/mxcube3/routes/login.py +++ b/mxcube3/routes/login.py @@ -1,7 +1,13 @@ import logging -from datetime import timedelta -from flask import Blueprint, request, jsonify, make_response, redirect, session +from flask import ( + Blueprint, + request, + jsonify, + make_response, + redirect, + session, +) from mxcube3.core.util import networkutils from flask_login import current_user @@ -41,7 +47,10 @@ def login(): try: res = jsonify(app.usermanager.login(login_id, password)) except Exception as ex: - msg = "[LOGIN] User %s could not login (%s)" % (login_id, str(ex)) + msg = "[LOGIN] User %s could not login (%s)" % ( + login_id, + str(ex), + ) logging.getLogger("MX3.HWR").exception("") logging.getLogger("MX3.HWR").info(msg) res = deny_access("Could not authenticate") diff --git a/mxcube3/routes/main.py b/mxcube3/routes/main.py index 5cd8fafd4..eaa012885 100644 --- a/mxcube3/routes/main.py +++ b/mxcube3/routes/main.py @@ -9,8 +9,10 @@ from spectree import Response from mxcube3 import version -from mxcube3.core.models.configmodels import UIPropertiesListModel from mxcube3.core.models.generic import AppSettingsModel +from mxcube3.core.models.configmodels import ( + UIPropertiesListModel, +) def init_route(app, server, url_prefix): @@ -43,7 +45,12 @@ def get_ui_properties(): @server.restrict @server.validate(resp=Response(HTTP_200=AppSettingsModel)) def mxcube_mode(): - return jsonify({"mode": app.CONFIG.app.mode, "version": version.__version__}) + return jsonify( + { + "mode": app.CONFIG.app.mode, + "version": version.__version__, + } + ) @server.flask.before_request def before_request(): diff --git a/mxcube3/routes/queue.py b/mxcube3/routes/queue.py index c7515c0ed..640ed8b6c 100644 --- a/mxcube3/routes/queue.py +++ b/mxcube3/routes/queue.py @@ -7,7 +7,8 @@ from mxcube3.core.models.generic import SimpleNameValue -def init_route(app, server, url_prefix): +# Disabling C901 function is too complex (19) +def init_route(app, server, url_prefix): # noqa: C901 bp = Blueprint("queue", __name__, url_prefix=url_prefix) @bp.route("/start", methods=["PUT"]) @@ -361,7 +362,8 @@ def set_autoadd(): @server.require_control @server.restrict @server.validate( - json=SimpleNameValue, resp=spectree.Response("HTTP_409", "HTTP_200") + json=SimpleNameValue, + resp=spectree.Response("HTTP_409", "HTTP_200"), ) def set_setting(): result = app.queue.set_setting(SimpleNameValue(**request.json)) diff --git a/mxcube3/routes/ra.py b/mxcube3/routes/ra.py index 584128c21..18e21907a 100644 --- a/mxcube3/routes/ra.py +++ b/mxcube3/routes/ra.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from datetime import datetime import gevent from flask import ( @@ -17,7 +16,8 @@ DISCONNECT_HANDLED = True -def init_route(app, server, url_prefix): +# Disabling C901 function is too complex (19) +def init_route(app, server, url_prefix): # noqa: C901 bp = Blueprint("remote_access", __name__, url_prefix=url_prefix) @bp.route("/request_control", methods=["POST"]) @@ -33,7 +33,8 @@ def handle_timeout_gives_control(sid, timeout=30): # Pass control to user if still waiting if current_user.requests_control: toggle_operator( - current_user.username, "Timeout expired, you have control" + current_user.username, + "Timeout expired, you have control", ) data = request.get_json() @@ -47,7 +48,11 @@ def handle_timeout_gives_control(sid, timeout=30): current_user.requests_control = data["control"] server.user_datastore.commit() - gevent.spawn(handle_timeout_gives_control, current_user.username, timeout=10) + gevent.spawn( + handle_timeout_gives_control, + current_user.username, + timeout=10, + ) app.usermanager.emit_observers_changed() @@ -107,8 +112,16 @@ def toggle_operator(username, message): app.usermanager.update_user(oldop) app.usermanager.update_user(newop) - join_room("observers", sid=oldop.socketio_session_id, namespace="/ui_state") - leave_room("observers", sid=newop.socketio_session_id, namespace="/ui_state") + join_room( + "observers", + sid=oldop.socketio_session_id, + namespace="/ui_state", + ) + leave_room( + "observers", + sid=newop.socketio_session_id, + namespace="/ui_state", + ) app.usermanager.emit_observers_changed(message) @@ -117,7 +130,10 @@ def remain_observer(user, message): observer["message"] = message server.emit( - "setObserver", observer, room=user.socketio_session_id, namespace="/hwr" + "setObserver", + observer, + room=user.socketio_session_id, + namespace="/hwr", ) @bp.route("/", methods=["GET"]) @@ -138,7 +154,7 @@ def allow_remote(): """ """ allow = request.get_json().get("allow") - if app.ALLOW_REMOTE and allow == False: + if app.ALLOW_REMOTE and not allow: server.emit("forceSignoutObservers", {}, namespace="/hwr") app.ALLOW_REMOTE = allow diff --git a/mxcube3/routes/samplecentring.py b/mxcube3/routes/samplecentring.py index 5a1b9c24a..689f57157 100644 --- a/mxcube3/routes/samplecentring.py +++ b/mxcube3/routes/samplecentring.py @@ -6,7 +6,8 @@ from mxcubecore import HardwareRepository as HWR -def init_route(app, server, url_prefix): +# Disabling C901 function is too complex (19) +def init_route(app, server, url_prefix): # noqa: C901 bp = Blueprint("sampleview", __name__, url_prefix=url_prefix) @bp.route("/camera/subscribe", methods=["GET"]) diff --git a/mxcube3/routes/samplechanger.py b/mxcube3/routes/samplechanger.py index a0d28068f..576f65c5c 100644 --- a/mxcube3/routes/samplechanger.py +++ b/mxcube3/routes/samplechanger.py @@ -3,7 +3,8 @@ from mxcubecore import HardwareRepository as HWR -def init_route(app, server, url_prefix): +# Disabling C901 function is too complex (19) +def init_route(app, server, url_prefix): # noqa: C901 bp = Blueprint("sample_changer", __name__, url_prefix=url_prefix) @bp.route("/samples_list", methods=["GET"]) @@ -61,7 +62,10 @@ def unmount_current(): res = ( "Cannot unload sample", 409, - {"Content-Type": "application/json", "message": str(ex)}, + { + "Content-Type": "application/json", + "message": str(ex), + }, ) return jsonify(res) @@ -77,7 +81,10 @@ def mount_sample(): resp = ( "Cannot load sample", 409, - {"Content-Type": "application/json", "message": str(ex)}, + { + "Content-Type": "application/json", + "message": str(ex), + }, ) return resp @@ -94,7 +101,10 @@ def unmount_sample(): return ( "Cannot unload sample", 409, - {"Content-Type": "application/json", "message": str(ex)}, + { + "Content-Type": "application/json", + "message": str(ex), + }, ) return resp diff --git a/mxcube3/routes/signals.py b/mxcube3/routes/signals.py index 61f30b1db..f99a23ff7 100644 --- a/mxcube3/routes/signals.py +++ b/mxcube3/routes/signals.py @@ -6,10 +6,18 @@ from flask import Response -from mxcubecore.HardwareObjects.abstract.AbstractSampleChanger import SampleChangerState +from mxcubecore.HardwareObjects.abstract.AbstractSampleChanger import ( + SampleChangerState, +) from mxcube3.core.adapter.beamline_adapter import BeamlineAdapter -from mxcube3.core.components.queue import READY, RUNNING, FAILED, COLLECTED, WARNING +from mxcube3.core.components.queue import ( + READY, + RUNNING, + FAILED, + COLLECTED, + WARNING, +) from mxcubecore.model import queue_model_objects as qmo from mxcubecore import queue_entry as qe @@ -34,7 +42,12 @@ def last_queue_node(): return res -beam_signals = ["beamPosChanged", "beamInfoChanged", "valueChanged", "stateChanged"] +beam_signals = [ + "beamPosChanged", + "beamInfoChanged", + "valueChanged", + "stateChanged", +] centringSignals = [ "centringInvalid", @@ -135,7 +148,10 @@ def is_collision_safe(*args): new_state = args[0] # we are only interested when it becames true if new_state: - msg = {"signal": "isCollisionSafe", "message": "Sample moved to safe area"} + msg = { + "signal": "isCollisionSafe", + "message": "Sample moved to safe area", + } server.emit("sc", msg, namespace="/hwr") @@ -295,7 +311,10 @@ def queue_execution_entry_finished(entry, message): def queue_toggle_sample(entry): if isinstance(entry, qe.SampleQueueEntry): - msg = {"Signal": "DisableSample", "sampleID": entry.get_data_model().loc_str} + msg = { + "Signal": "DisableSample", + "sampleID": entry.get_data_model().loc_str, + } server.emit("queue", msg, namespace="/hwr") @@ -318,16 +337,25 @@ def queue_execution_finished(entry, queue_state=None): def queue_execution_stopped(*args): - msg = {"Signal": "QueueStopped", "Message": "Queue execution stopped"} + msg = { + "Signal": "QueueStopped", + "Message": "Queue execution stopped", + } server.emit("queue", msg, namespace="/hwr") def queue_execution_paused(state): if state: - msg = {"Signal": "QueuePaused", "Message": "Queue execution paused"} + msg = { + "Signal": "QueuePaused", + "Message": "Queue execution paused", + } else: - msg = {"Signal": "QueueRunning", "Message": "Queue execution paused"} + msg = { + "Signal": "QueueRunning", + "Message": "Queue execution paused", + } server.emit("queue", msg, namespace="/hwr") @@ -391,7 +419,12 @@ def _emit_progress(msg): def collect_oscillation_failed( - owner=None, status=FAILED, state=None, lims_id="", osc_id=None, params=None + owner=None, + status=FAILED, + state=None, + lims_id="", + osc_id=None, + params=None, ): node = last_queue_node() @@ -472,7 +505,6 @@ def collect_started(*args, **kwargs): node = last_queue_node() if not mxcube.queue.is_interleaved(node["node"]): - msg = { "Signal": kwargs["signal"], "Message": task_signals[kwargs["signal"]], @@ -496,7 +528,11 @@ def grid_result_available(shape): def energy_scan_finished(pk, ip, rm, sample): - server.emit("energy_scan_result", {"pk": pk, "ip": ip, "rm": rm}, namespace="/hwr") + server.emit( + "energy_scan_result", + {"pk": pk, "ip": ip, "rm": rm}, + namespace="/hwr", + ) def queue_interleaved_started(): @@ -587,7 +623,6 @@ def motor_position_callback(movable): def beam_changed(*args, **kwargs): - beam_info = HWR.beamline.beam if beam_info is None: @@ -595,7 +630,12 @@ def beam_changed(*args, **kwargs): # TODO fix error return Response(status=409) - beam_info_dict = {"position": [], "shape": "", "size_x": 0, "size_y": 0} + beam_info_dict = { + "position": [], + "shape": "", + "size_x": 0, + "size_y": 0, + } _beam = beam_info.get_value() beam_info_dict.update( { diff --git a/mxcube3/routes/workflow.py b/mxcube3/routes/workflow.py index deea88b51..87166ce43 100644 --- a/mxcube3/routes/workflow.py +++ b/mxcube3/routes/workflow.py @@ -23,7 +23,8 @@ def submit_parameters(): # @server.restrict def get_grid_data(gid, t, rand): res = send_file( - io.BytesIO(app.workflow.get_mesh_result(gid, t)), mimetype="image/png" + io.BytesIO(app.workflow.get_mesh_result(gid, t)), + mimetype="image/png", ) return res diff --git a/mxcube3/server.py b/mxcube3/server.py index d167526ab..7463e329f 100644 --- a/mxcube3/server.py +++ b/mxcube3/server.py @@ -3,7 +3,6 @@ import signal import atexit import os -import time import werkzeug import gevent @@ -17,7 +16,10 @@ from spectree import SpecTree from mxcube3.core.util import networkutils -from mxcube3.core.components.user.database import init_db, UserDatastore +from mxcube3.core.components.user.database import ( + init_db, + UserDatastore, +) from mxcube3.core.models.usermodels import User, Role, Message @@ -81,7 +83,8 @@ def init(cmdline_options, cfg, mxcube): Server.db_session = db_session Server.flask_socketio = SocketIO( - manage_session=False, cors_allowed_origins=cfg.flask.ALLOWED_CORS_ORIGINS + manage_session=False, + cors_allowed_origins=cfg.flask.ALLOWED_CORS_ORIGINS, ) Server.flask_socketio.init_app(Server.flask) @@ -121,22 +124,40 @@ def _register_route(init_blueprint_fn, app, url_prefix, tag=None): @staticmethod def register_routes(mxcube): - from mxcube3.routes.beamline import init_route as init_beamline_route - from mxcube3.routes.detector import init_route as init_detector_route + from mxcube3.routes.beamline import ( + init_route as init_beamline_route, + ) + from mxcube3.routes.detector import ( + init_route as init_detector_route, + ) from mxcube3.routes.diffractometer import ( init_route as init_diffractometer_route, ) from mxcube3.routes.lims import init_route as init_lims_route from mxcube3.routes.log import init_route as init_log_route - from mxcube3.routes.login import init_route as init_login_route + from mxcube3.routes.login import ( + init_route as init_login_route, + ) from mxcube3.routes.main import init_route as init_main_route - from mxcube3.routes.mockups import init_route as init_mockups_route - from mxcube3.routes.queue import init_route as init_queue_route + from mxcube3.routes.mockups import ( + init_route as init_mockups_route, + ) + from mxcube3.routes.queue import ( + init_route as init_queue_route, + ) from mxcube3.routes.ra import init_route as init_ra_route - from mxcube3.routes.samplecentring import init_route as init_sampleview_route - from mxcube3.routes.samplechanger import init_route as init_samplechanger_route - from mxcube3.routes.workflow import init_route as init_workflow_route - from mxcube3.routes.gphl_workflow import init_route as init_gphl_workflow_route + from mxcube3.routes.samplecentring import ( + init_route as init_sampleview_route, + ) + from mxcube3.routes.samplechanger import ( + init_route as init_samplechanger_route, + ) + from mxcube3.routes.workflow import ( + init_route as init_workflow_route, + ) + from mxcube3.routes.gphl_workflow import ( + init_route as init_gphl_workflow_route, + ) url_root_prefix = "/mxcube/api/v0.1" @@ -149,7 +170,9 @@ def register_routes(mxcube): ) Server._register_route( - init_diffractometer_route, mxcube, f"{url_root_prefix}/diffractometer" + init_diffractometer_route, + mxcube, + f"{url_root_prefix}/diffractometer", ) Server._register_route(init_lims_route, mxcube, f"{url_root_prefix}/lims") @@ -167,11 +190,15 @@ def register_routes(mxcube): Server._register_route(init_ra_route, mxcube, f"{url_root_prefix}/ra") Server._register_route( - init_sampleview_route, mxcube, f"{url_root_prefix}/sampleview" + init_sampleview_route, + mxcube, + f"{url_root_prefix}/sampleview", ) Server._register_route( - init_samplechanger_route, mxcube, f"{url_root_prefix}/sample_changer" + init_samplechanger_route, + mxcube, + f"{url_root_prefix}/sample_changer", ) Server._register_route( @@ -179,7 +206,9 @@ def register_routes(mxcube): ) Server._register_route( - init_gphl_workflow_route, mxcube, f"{url_root_prefix}/gphl_workflow" + init_gphl_workflow_route, + mxcube, + f"{url_root_prefix}/gphl_workflow", ) Server.security = flask_security.Security(Server.flask, Server.user_datastore) @@ -203,7 +232,10 @@ def run(cfg): if ssl_context: Server.flask_socketio.run( - Server.flask, ssl_context=ssl_context, host="0.0.0.0", port=8081 + Server.flask, + ssl_context=ssl_context, + host="0.0.0.0", + port=8081, ) else: Server.flask_socketio.run(Server.flask, host="0.0.0.0", port=8081) diff --git a/mxcube3/video/websocket-relay.js b/mxcube3/video/websocket-relay.js index 1cb47f156..e8fe55ccc 100644 --- a/mxcube3/video/websocket-relay.js +++ b/mxcube3/video/websocket-relay.js @@ -27,7 +27,7 @@ socketServer.connectionCount = 0; socketServer.on('connection', function(socket, upgradeReq) { socketServer.connectionCount++; console.log( - 'New WebSocket Connection: ', + 'New WebSocket Connection: ', (upgradeReq || socket.upgradeReq).socket.remoteAddress, (upgradeReq || socket.upgradeReq).headers['user-agent'], '('+socketServer.connectionCount+' total)' @@ -61,7 +61,7 @@ var streamServer = http.createServer( function(request, response) { response.connection.setTimeout(0); console.log( - 'Stream Connected: ' + + 'Stream Connected: ' + request.socket.remoteAddress + ':' + request.socket.remotePort ); diff --git a/mxcubeweb-server b/mxcubeweb-server index 3c2cedb1b..17368ae31 100755 --- a/mxcubeweb-server +++ b/mxcubeweb-server @@ -1,7 +1,5 @@ #!/usr/bin/env python -import sys -import os from mxcube3 import main diff --git a/pyproject.toml b/pyproject.toml index 4a7a27796..3f269bdae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,6 @@ bcrypt = "^4.0.1" [tool.poetry.dev-dependencies] myst-parser = "^2.0.0" -pylint = [ { version = "==2.13.9", python = "<=3.7.1" }, { version = "2.15.3", python = ">=3.8" } ] pre-commit = "2.20.0" pytest = "7.1.3" pytest-cov = "4.0.0" @@ -56,6 +55,7 @@ sphinxcontrib-httpdomain = "^1.8.1" [tool.black] line-length = 88 +preview = true include = '\.pyi?$' exclude = ''' /( @@ -68,6 +68,9 @@ exclude = ''' | buck-out | build | dist + | test + | ui + | docs )/ ''' diff --git a/pytest.ini b/pytest.ini index d39dc706e..c86d7aad8 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,4 +3,4 @@ addopts = --cov=./ testpaths = test norecursedirs = - HardwareRepository \ No newline at end of file + HardwareRepository diff --git a/test/.babelrc b/test/.babelrc index e0de09a72..65836a67b 100644 --- a/test/.babelrc +++ b/test/.babelrc @@ -1,3 +1,3 @@ { "presets": ["es2015-loose", "stage-0", "react"] -} \ No newline at end of file +} diff --git a/test/HardwareObjectsMockup.xml/beam_info.xml b/test/HardwareObjectsMockup.xml/beam_info.xml index 28b029090..e4d952619 100644 --- a/test/HardwareObjectsMockup.xml/beam_info.xml +++ b/test/HardwareObjectsMockup.xml/beam_info.xml @@ -2,4 +2,4 @@ (12.0, 5.0) - \ No newline at end of file + diff --git a/test/HardwareObjectsMockup.xml/chip_definition.json b/test/HardwareObjectsMockup.xml/chip_definition.json index 7f0c190e8..148ae9cb4 100644 --- a/test/HardwareObjectsMockup.xml/chip_definition.json +++ b/test/HardwareObjectsMockup.xml/chip_definition.json @@ -152,4 +152,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/HardwareObjectsMockup.xml/diffractometer_beamline_action.xml b/test/HardwareObjectsMockup.xml/diffractometer_beamline_action.xml index 5d2eb52cd..76351d81b 100644 --- a/test/HardwareObjectsMockup.xml/diffractometer_beamline_action.xml +++ b/test/HardwareObjectsMockup.xml/diffractometer_beamline_action.xml @@ -1,3 +1,3 @@ - \ No newline at end of file + diff --git a/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/filenametemplates.xml b/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/filenametemplates.xml index f2e9cb3a2..c78854f92 100644 --- a/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/filenametemplates.xml +++ b/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/filenametemplates.xml @@ -158,7 +158,7 @@ Element names are tied to the beamline or workflow and cannot be modified beam_setting_index unsignedInt - + _ @@ -167,7 +167,7 @@ Element names are tied to the beamline or workflow and cannot be modified run_number unsignedInt - + _ @@ -213,7 +213,7 @@ Element names are tied to the beamline or workflow and cannot be modified beam_setting_index unsignedInt - + inverse_beam_component_sign string @@ -227,7 +227,7 @@ Element names are tied to the beamline or workflow and cannot be modified run_number unsignedInt - + _ @@ -279,7 +279,7 @@ Element names are tied to the beamline or workflow and cannot be modified run_number unsignedInt - + _ @@ -381,6 +381,6 @@ Element names are tied to the beamline or workflow and cannot be modified Beamline - + diff --git a/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/instrumentation.nml b/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/instrumentation.nml index 0cd7b8774..80dd02f03 100644 --- a/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/instrumentation.nml +++ b/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/instrumentation.nml @@ -378,4 +378,3 @@ SEG_ORG_X=0.0000000 SEG_ORG_Y=0.0000000 SEG_COORD=0.0000000 / - diff --git a/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/strategylib.nml b/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/strategylib.nml index 519015b1f..cac8d6ecb 100644 --- a/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/strategylib.nml +++ b/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/strategylib.nml @@ -609,4 +609,3 @@ beam_setting_id= 'dcalbset' start_deg= 240 length_deg= 60 / - diff --git a/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/transcal_2stage.json b/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/transcal_2stage.json index 37c92bde5..86b2a2d2d 100644 --- a/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/transcal_2stage.json +++ b/test/HardwareObjectsMockup.xml/gphl/config_embl_hh_p14/transcal_2stage.json @@ -1,5 +1,5 @@ [ - { + { "#": "Settings (omega, kappa, phi) for running TransCal on a mini-Kappa (short protocol, without SOC)", "settings": [ 0, 0, 0, @@ -15,10 +15,10 @@ "noRefineSOC": true, "useRecen": false }, - + { "#": "Settings(omega, kappa, phi) for running TransCal on a mini-Kappa (medium-length protocol)", - + "settings": [ 0.00, 0.00, -90.00, 0.00, 0.00, -45.00, diff --git a/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/filenametemplates.xml b/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/filenametemplates.xml index f22d9b8a2..720a9afa4 100644 --- a/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/filenametemplates.xml +++ b/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/filenametemplates.xml @@ -158,7 +158,7 @@ Element names are tied to the beamline or workflow and cannot be modified beam_setting_index unsignedInt - + _ @@ -167,7 +167,7 @@ Element names are tied to the beamline or workflow and cannot be modified run_number unsignedInt - + _ @@ -213,7 +213,7 @@ Element names are tied to the beamline or workflow and cannot be modified beam_setting_index unsignedInt - + inverse_beam_component_sign string @@ -227,7 +227,7 @@ Element names are tied to the beamline or workflow and cannot be modified run_number unsignedInt - + _ @@ -279,7 +279,7 @@ Element names are tied to the beamline or workflow and cannot be modified run_number unsignedInt - + _ @@ -381,6 +381,6 @@ Element names are tied to the beamline or workflow and cannot be modified Beamline - + diff --git a/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/instrumentation.nml b/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/instrumentation.nml index 157c5e1f2..b14f4084d 100644 --- a/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/instrumentation.nml +++ b/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/instrumentation.nml @@ -93,4 +93,3 @@ gonio_centring_axis_names= 'phiy', 'sampx', 'sampy' ! Must match detector type ! Otherwise DO NOT EDIT from this point of the file onwards / - diff --git a/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/strategylib.nml b/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/strategylib.nml index be983965b..507733032 100644 --- a/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/strategylib.nml +++ b/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/strategylib.nml @@ -608,4 +608,3 @@ beam_setting_id= 'dcalbset' start_deg= 240 length_deg= 60 / - diff --git a/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/transcal_2stage.json b/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/transcal_2stage.json index 37c92bde5..86b2a2d2d 100644 --- a/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/transcal_2stage.json +++ b/test/HardwareObjectsMockup.xml/gphl/config_esrf_id30b/transcal_2stage.json @@ -1,5 +1,5 @@ [ - { + { "#": "Settings (omega, kappa, phi) for running TransCal on a mini-Kappa (short protocol, without SOC)", "settings": [ 0, 0, 0, @@ -15,10 +15,10 @@ "noRefineSOC": true, "useRecen": false }, - + { "#": "Settings(omega, kappa, phi) for running TransCal on a mini-Kappa (medium-length protocol)", - + "settings": [ 0.00, 0.00, -90.00, 0.00, 0.00, -45.00, diff --git a/test/HardwareObjectsMockup.xml/gphl/gphl-setup.yml b/test/HardwareObjectsMockup.xml/gphl/gphl-setup.yml index 5e0195dc4..97bc23321 100644 --- a/test/HardwareObjectsMockup.xml/gphl/gphl-setup.yml +++ b/test/HardwareObjectsMockup.xml/gphl/gphl-setup.yml @@ -30,14 +30,14 @@ gphl_persistname: persistence #Hosts and ports for py4j java-python gateway, all OPTIONAL #If not set will use py4j default values, which are as given below #NB python_host is set automatically to localhost or socket.gethostname() -#NB java_host is not needed as all communication is effectively one-way +#NB java_host is not needed as all communication is effectively one-way #connection_parameters: # python_port: 25334 # java_port: 25333 #NB Non-absolute file names are interpreted relative to one of the #HardwareRepository directories on the lookup path - + software_paths: # Mandatory. Directory with workflow config input, e.g. instrumentation.nml gphl_beamline_config: gphl/config_esrf_id30b diff --git a/test/HardwareObjectsMockup.xml/gphl/gphl-workflow.yml b/test/HardwareObjectsMockup.xml/gphl/gphl-workflow.yml index dc7996103..9d2c45f58 100644 --- a/test/HardwareObjectsMockup.xml/gphl/gphl-workflow.yml +++ b/test/HardwareObjectsMockup.xml/gphl/gphl-workflow.yml @@ -30,7 +30,7 @@ settings: # - TEST_Characterisation_12_4 # - TEST_Characterisation_12_5 - # How to set starting value for beam energy. Values are: + # How to set starting value for beam energy. Values are: # 'configured': set value from calibration and characterisation strategy file # 'current': use current value starting_beamline_energy: current @@ -120,7 +120,7 @@ settings: # Switch to pass log to stdout. Defaults to True # co.gphl.wf.logStdout: false - + # Properties applied after the java command - syntax as above--> # Note that irrelevant properties are simply ignored.--> workflow_properties: @@ -152,7 +152,7 @@ settings: co.gphl.wf.simcal_predict.mosaicity: 0.2 # # Testing ONLY: # co.gphl.wf.run_number: 7 - + # Workflows, The options in the top elements are updated with the options # in the individual type, and passed as options to teh workflow application # The following options are set elsewhere and can *not* be set here diff --git a/test/HardwareObjectsMockup.xml/gphl/scripts/simcal b/test/HardwareObjectsMockup.xml/gphl/scripts/simcal index ceca38353..7265bca99 100755 --- a/test/HardwareObjectsMockup.xml/gphl/scripts/simcal +++ b/test/HardwareObjectsMockup.xml/gphl/scripts/simcal @@ -8,4 +8,3 @@ runit () #runit exec `dirname $0`/echo_limits.sh runit exec ${GPHL_INSTALLATION}/simcal $@ - diff --git a/test/HardwareObjectsMockup.xml/gphl/test_samples/germanate/sample.hkli b/test/HardwareObjectsMockup.xml/gphl/test_samples/germanate/sample.hkli index eaf1f1b83..4298b41ba 100644 --- a/test/HardwareObjectsMockup.xml/gphl/test_samples/germanate/sample.hkli +++ b/test/HardwareObjectsMockup.xml/gphl/test_samples/germanate/sample.hkli @@ -529629,4 +529629,4 @@ 64 2 2 1089.0000 64 3 -1 36141.1000 64 3 1 36141.1000 - 64 4 0 14596.5000 \ No newline at end of file + 64 4 0 14596.5000 diff --git a/test/HardwareObjectsMockup.xml/gphl/test_samples/thermolysin/sample.hkli b/test/HardwareObjectsMockup.xml/gphl/test_samples/thermolysin/sample.hkli index cf9d713f6..ce4fe6ad9 100644 --- a/test/HardwareObjectsMockup.xml/gphl/test_samples/thermolysin/sample.hkli +++ b/test/HardwareObjectsMockup.xml/gphl/test_samples/thermolysin/sample.hkli @@ -1073755,4 +1073755,4 @@ 47 -16 -6 17728.0960 47 -16 -5 19378.3690 47 -16 5 19378.3690 - 47 -16 6 17728.0960 \ No newline at end of file + 47 -16 6 17728.0960 diff --git a/test/HardwareObjectsMockup.xml/ldapconnection.xml b/test/HardwareObjectsMockup.xml/ldapconnection.xml index 4aa047b0e..8dc1cda32 100644 --- a/test/HardwareObjectsMockup.xml/ldapconnection.xml +++ b/test/HardwareObjectsMockup.xml/ldapconnection.xml @@ -9,4 +9,4 @@ ldap.esrf.fr 389 ---> \ No newline at end of file +--> diff --git a/test/HardwareObjectsMockup.xml/lims.xml b/test/HardwareObjectsMockup.xml/lims.xml index e12b50c48..4997c2df2 100644 --- a/test/HardwareObjectsMockup.xml/lims.xml +++ b/test/HardwareObjectsMockup.xml/lims.xml @@ -11,4 +11,4 @@ proposal - \ No newline at end of file + diff --git a/test/HardwareObjectsMockup.xml/lims_rest.xml b/test/HardwareObjectsMockup.xml/lims_rest.xml index 49dbf3d46..ee1f73465 100644 --- a/test/HardwareObjectsMockup.xml/lims_rest.xml +++ b/test/HardwareObjectsMockup.xml/lims_rest.xml @@ -13,4 +13,4 @@ - \ No newline at end of file + diff --git a/test/HardwareObjectsMockup.xml/machine_info.xml b/test/HardwareObjectsMockup.xml/machine_info.xml index 779f0f3a0..801168566 100644 --- a/test/HardwareObjectsMockup.xml/machine_info.xml +++ b/test/HardwareObjectsMockup.xml/machine_info.xml @@ -1,3 +1,2 @@ - diff --git a/test/HardwareObjectsMockup.xml/mxcollect.xml b/test/HardwareObjectsMockup.xml/mxcollect.xml index 4107ad731..4f42a576d 100644 --- a/test/HardwareObjectsMockup.xml/mxcollect.xml +++ b/test/HardwareObjectsMockup.xml/mxcollect.xml @@ -6,7 +6,7 @@ - + 0 diff --git a/test/HardwareObjectsMockup.xml/mxcube-web/server.yaml b/test/HardwareObjectsMockup.xml/mxcube-web/server.yaml index 6c55d2fcc..0c2275c25 100644 --- a/test/HardwareObjectsMockup.xml/mxcube-web/server.yaml +++ b/test/HardwareObjectsMockup.xml/mxcube-web/server.yaml @@ -10,14 +10,14 @@ server: ALLOWED_CORS_ORIGINS: - "http://localhost:8081" - - "http://localhost:3000" - - "ws://localhost:8000" + - "http://localhost:3000" + - "ws://localhost:8000" mxcube: USE_EXTERNAL_STREAMER: True VIDEO_FORMAT: MPEG1 VIDEO_STREAM_URL: "ws://localhost:8000/ws" - + # At which port to stream from VIDEO_STREAM_PORT: 8000 # Mode, SSX-CHIP, SSX-INJECTOR, OSC diff --git a/test/HardwareObjectsMockup.xml/mxcube-web/ui.yaml b/test/HardwareObjectsMockup.xml/mxcube-web/ui.yaml index 9d805003b..5d3d79ec4 100644 --- a/test/HardwareObjectsMockup.xml/mxcube-web/ui.yaml +++ b/test/HardwareObjectsMockup.xml/mxcube-web/ui.yaml @@ -14,21 +14,21 @@ sample_view: role: kappa step: 0.1 precision: 1 - suffix: "°" + suffix: "°" - label: Kappa Phi attribute: diffractometer.kappa_phi role: kappa_phi step: 0.1 precision: 1 - suffix: ° + suffix: ° - label: X attribute: diffractometer.phix role: phix step: 0.1 precision: 3 - suffix: mm + suffix: mm - label: Y attribute: diffractometer.phiy @@ -98,7 +98,7 @@ beamline_setup: - label: Fast Shutter attribute: fast_shutter - - + - label: Safety shutter attribute: safety_shutter - @@ -126,7 +126,7 @@ beamline_setup: label: Cryo attribute: cryo precision: 2 - suffix: k + suffix: k - label: Wavelength attribute: energy.wavelength @@ -138,7 +138,7 @@ beamline_setup: attribute: detector.detector_distance step: 0.1 precision: 1 - suffix: mm + suffix: mm - label: Flux attribute: flux diff --git a/test/HardwareObjectsMockup.xml/oxford_chip_def.json b/test/HardwareObjectsMockup.xml/oxford_chip_def.json index bec1ebeb7..f51a52a7f 100644 --- a/test/HardwareObjectsMockup.xml/oxford_chip_def.json +++ b/test/HardwareObjectsMockup.xml/oxford_chip_def.json @@ -62,4 +62,4 @@ 0 ] } -} \ No newline at end of file +} diff --git a/test/HardwareObjectsMockup.xml/plate_manipulator.xml b/test/HardwareObjectsMockup.xml/plate_manipulator.xml index 6efa5b844..0e357a52a 100644 --- a/test/HardwareObjectsMockup.xml/plate_manipulator.xml +++ b/test/HardwareObjectsMockup.xml/plate_manipulator.xml @@ -2,7 +2,7 @@ - Sample Changer + Sample Changer - diff --git a/test/HardwareObjectsMockup.xml/queue_model.xml b/test/HardwareObjectsMockup.xml/queue_model.xml index a94201519..de365c254 100644 --- a/test/HardwareObjectsMockup.xml/queue_model.xml +++ b/test/HardwareObjectsMockup.xml/queue_model.xml @@ -1,4 +1,3 @@ - + - diff --git a/test/HardwareObjectsMockup.xml/session.xml b/test/HardwareObjectsMockup.xml/session.xml index c7edf3b6b..0327f6fa6 100644 --- a/test/HardwareObjectsMockup.xml/session.xml +++ b/test/HardwareObjectsMockup.xml/session.xml @@ -1,5 +1,5 @@ - + ESRF mxcube3test mxcube3test @@ -62,4 +62,3 @@ test@... - diff --git a/test/HardwareObjectsMockup.xml/slits.xml b/test/HardwareObjectsMockup.xml/slits.xml index 099243bf1..76f89883b 100644 --- a/test/HardwareObjectsMockup.xml/slits.xml +++ b/test/HardwareObjectsMockup.xml/slits.xml @@ -10,5 +10,5 @@ 0.005 0.300 0.0005 - - \ No newline at end of file + + diff --git a/test/HardwareObjectsMockup.xml/transmission.xml b/test/HardwareObjectsMockup.xml/transmission.xml index a9b451e67..89cd5f340 100644 --- a/test/HardwareObjectsMockup.xml/transmission.xml +++ b/test/HardwareObjectsMockup.xml/transmission.xml @@ -1,4 +1,4 @@ transmission 10 - \ No newline at end of file + diff --git a/test/HardwareObjectsMockup.xml/udiff_frontlightswitch.xml b/test/HardwareObjectsMockup.xml/udiff_frontlightswitch.xml index 705487ce5..efcc47861 100644 --- a/test/HardwareObjectsMockup.xml/udiff_frontlightswitch.xml +++ b/test/HardwareObjectsMockup.xml/udiff_frontlightswitch.xml @@ -2,7 +2,7 @@ frontlight_switch wid30bmd:9001 FrontLightIsOn - False + False {"IN": True, "OUT": False} True diff --git a/test/HardwareObjectsMockup.xml/xml_rpc_server.xml b/test/HardwareObjectsMockup.xml/xml_rpc_server.xml index c0c5a16cf..428f8a7ea 100644 --- a/test/HardwareObjectsMockup.xml/xml_rpc_server.xml +++ b/test/HardwareObjectsMockup.xml/xml_rpc_server.xml @@ -2,7 +2,7 @@ True - + 7171 diff --git a/test/fixture.py b/test/fixture.py index 666c2eb58..87b83a5f1 100644 --- a/test/fixture.py +++ b/test/fixture.py @@ -99,7 +99,8 @@ def client(): @pytest.fixture def add_sample(client): - """Fixture to add a sample to the queue, since it is required for alot of test cases.""" + """Fixture to add a sample to the queue, since it is required for alot of test cases. + """ resp = client.post( "/mxcube/api/v0.1/queue", data=json.dumps([test_sample_1]), @@ -120,7 +121,8 @@ def add_sample(client): @pytest.fixture def add_task(client): - """Fixture to add a task to the sample in the queue queue, since it is required for alot of test cases.""" + """Fixture to add a task to the sample in the queue queue, since it is required for alot of test cases. + """ resp = client.get("/mxcube/api/v0.1/queue") assert resp.status_code == 200 and json.loads(resp.data).get("1:05") diff --git a/test/test_authn.py b/test/test_authn.py index b704a21f2..a7a217bdc 100644 --- a/test/test_authn.py +++ b/test/test_authn.py @@ -4,7 +4,6 @@ """Authentication tests.""" -import datetime import os import time diff --git a/test/test_queue_routes.py b/test/test_queue_routes.py index d81786216..01a1266ba 100644 --- a/test/test_queue_routes.py +++ b/test/test_queue_routes.py @@ -153,7 +153,8 @@ def test_queue_abort(client): def test_queue_clear(client): - """Test if we can clear the queue. A sample and a task are added by fixtures and then cleared.""" + """Test if we can clear the queue. A sample and a task are added by fixtures and then cleared. + """ resp = client.put("/mxcube/api/v0.1/queue/clear") assert resp.status_code == 200 @@ -210,7 +211,8 @@ def test_queue_enable_item(client): def test_queue_swap_task_item(client): - """Test if we can swap tasks in a sample in queue. Two tasks are added with a different param and then swaped and tested""" + """Test if we can swap tasks in a sample in queue. Two tasks are added with a different param and then swaped and tested + """ resp = client.get("/mxcube/api/v0.1/queue/") assert ( resp.status_code == 200 and len(json.loads(resp.data).get("1:05")["tasks"]) == 1 diff --git a/test/test_samplechanger_routes.py b/test/test_samplechanger_routes.py index d45c89c21..6045a29a7 100644 --- a/test/test_samplechanger_routes.py +++ b/test/test_samplechanger_routes.py @@ -1,5 +1,4 @@ import json -import random # Python 2 and 3 compatibility try: diff --git a/ui/src/components/BeamlineCamera/picture_in_picture.svg b/ui/src/components/BeamlineCamera/picture_in_picture.svg index c56135cbb..6b254a1c3 100644 --- a/ui/src/components/BeamlineCamera/picture_in_picture.svg +++ b/ui/src/components/BeamlineCamera/picture_in_picture.svg @@ -1 +1 @@ - \ No newline at end of file +