diff --git a/webservice/webservice.py b/webservice/webservice.py index fac0bf0f600ce72edf447f4463f0db0ae4e209b9..6973a578e97853b85466017f99325986b3fe473b 100644 --- a/webservice/webservice.py +++ b/webservice/webservice.py @@ -1,5 +1,3 @@ -import time - import argparse import ast import asyncio @@ -15,9 +13,10 @@ import re import shlex import sqlite3 import sys +import time import urllib.parse from asyncio import get_event_loop, shield -from datetime import datetime, timezone, timedelta +from datetime import datetime, timedelta, timezone from getpass import getuser from pathlib import Path from typing import Any, Dict, List, Optional, Tuple, Union @@ -37,6 +36,8 @@ except ImportError: from config import webservice as config from messages import MDC, Errors, MigrationError, Success +from cal_tools.calcat_interface2 import get_client + def init_job_db(config): """ Initialize the sqlite job database @@ -333,6 +334,70 @@ class time_db_transaction: return False +def get_number_of_runs(karabo_id, operation_mode): + """ + Get number of expected run for a given detector and operation mode. + + Args: + karabo_id (str): Detector identifier for the detector. + operation_mode (str): Operation mode identifier. + + Returns: + int: Number of runs for operation mode. + """ + client = get_client() + detector_id = client.detector_by_identifier(karabo_id)['id'] + + pdus = client.get( + 'physical_detector_units/get_all_by_detector?' + f'detector_id={detector_id}') + first_pdu_id = pdus[0]['detector_type']['id'] + operation_modes = client.get( + 'operation_modes/get_all_by_detector_type?' + f'detector_type_id={first_pdu_id}') + + number_of_runs = next( + ( + mode['number_of_runs'] for mode in operation_modes + if mode['identifier'] == operation_mode), + None) + if number_of_runs is None: + raise ValueError( + f"{karabo_id} doesn't have this operation mode {operation_mode}.") + return number_of_runs + + +def decide_run_parameters(runs, expected_number_of_runs): + """Decide the run parameters to pass for the xfel-calibrate CLI. + Based on number of runs and expected run numbers create a dict + with the correct run parameters expected by the notebook. + + Args: + runs (list): List of runs. + expected_number_of_runs (int): Expected run numbers. + + Raises: + ValueError: If provided runs more don't match any calibration. + + Returns: + dict: Mapping for run notebook parameters names and values. + """ + + runs_dict = {} + if expected_number_of_runs == 3: + if len(runs) == 1: + return {'run-high': runs[0], 'run-med': '0', 'run-low': '0'} + else: # len(runs) == 3 + return { + 'run-high': runs[0], 'run-med': runs[1], 'run-low': runs[2]} + elif expected_number_of_runs == 2 and len(runs) == 2: + return {'run-high': runs[0], 'run-low': runs[1]} + elif len(runs) == 1: # single run operation modes. + return {'run': runs[0]} + else: + raise ValueError(f"Unexpected number of runs: {runs}.") + + async def run_action(job_db, cmd, mode, proposal, run, exec_id) -> str: """Run action command (CORRECT or DARK). @@ -1286,36 +1351,6 @@ class ActionsServer: ) return - # Notebooks require one or three runs, depending on the - # detector type and operation mode. - triple = any( - det in karabo_id for det in self.config['dark']['three-run-detectors']) # noqa - double = any( - det in karabo_id for det in self.config['dark']['two-run-detectors']) - - # This fails silently if a flag is set but detector is not - # configured for the corresponding operation mode. - # e.g. (triple = False) but the underlying notebook - # still expects run-high/run-med/run-low. - if triple and len(runs) == 1: - runs_dict = { - 'run-high': runs[0], - 'run-med': '0', - 'run-low': '0'} - elif triple and len(runs) == 3: - runs_dict = { - 'run-high': runs[0], - 'run-med': runs[1], - 'run-low': runs[2]} - elif double and len(runs) == 2: - runs_dict = { - 'run-high': runs[0], - 'run-med': runs[1]} - elif len(runs) == 1: # single - runs_dict = {'run': runs[0]} - else: - raise ValueError(f"Unexpected number of runs: {runs}.") - # We assume that MyMDC does not allow dark request if the data # is not migrated, thus skipping some validation here. thisconf = copy.copy(data_conf[karabo_id]) @@ -1333,6 +1368,9 @@ class ActionsServer: thisconf['karabo-da'] = karabo_das thisconf['operation-mode'] = operation_mode + expected_number_of_runs = get_number_of_runs(karabo_id, operation_mode) + runs_dict = decide_run_parameters(runs, expected_number_of_runs) + thisconf.update(runs_dict) detectors = {karabo_id: thisconf}