Calibration Catalog (CalCat)

Besides the standard RESTful APIs for CRUD (+ list/search) operations the CalCat exposes the following special APIs (per model):

Table of contents

Operation Modes

get_all_by_detector_type

HTTP Method: GET

HTTP Path: /api/operation_modes/get_all_by_detector_type_id

Input parameters:

name type passed via required default example
detector_type_id integer QueryString yes - detector_type_id=10

Output:

List of operation modes defined for the requested detector type, where flg_available is set to true ordered by position ascending (position is a field in the relation (many to many) between operation modes and detector type).

Internals:

  • scope by_detector_type(detector_type_id) of the OperationMode model

Detectors

get_all_by_instrument

HTTP Method: GET

HTTP Path: /api/detectors/get_all_by_instrument

Input parameters:

name type passed via required default example
instrument_id integer QueryString yes - instrument_id=3

Output:

List of detectors belonging to the requested instrument (note that the relation is many to many: an instrument can have many detectors and a detector can belong to more than one instrument).

If the given instrument does not exists (wrong instrument_id) or exists but it is not associated to any detector, the HTTP response code will be in both cases a 404.

Internals:

  • retrieve instrument by id
  • direct use of has_many through relation between the Instrument and the Detector models

Physical Detector Units (PDUs)

get_all_by_detector

HTTP Method: GET

HTTP Path: /physical_detector_units/get_all_by_detector

Input parameters:

name type passed via required default example
detector_id integer QueryString yes - detector_id=10
pdu_snapshot_at datetime QueryString no - pdu_snapshot_at=20210526T123000+0200

Output:

List of PDUs belonging to the requested detector.

If the pdu_snapshot_at parameter is passed, the list of PDUs belonging to the detector at the pdu_snapshot_at moment in time will be returned.

If the given detector does not exists (wrong detector_id) or exists but does not have any associated PDU (or did not have at the pdu_snapshot_at moment), the HTTP response code will be in both cases a 404.

The parameter snapshot_at (in place of pdu_snapshot_at) is still accepted for compatibility.

Internals:

  • find the detector
  • get_pdus_by_detector(pdu_snapshot_at) method of the Detector model

Conditions

The following methods are exposed under the Conditions APIs, that is, under the base path: /api/conditions/

get_possible_conditions

A calibration condition is defined as a set of physical conditions each represented by some characteristic (e.g., a physical quantity), called parameter, the value of that characteristic, the physical unit of that value (where applicable), and allowable limits of variation for that value.

Parameters of type text (flg_text == true) also exist: in such case the limits of variations do not apply; the search on possible conditions for those parameters is limited to values which starts with a certain string.

This API searches for calibration conditions matching a list of parameters whose values are within certain boundaries (or text value starts with a certain string for text type parameters).

HTTP Method: POST | GET

HTTP Path: /api/conditions/get_possible_conditions

Input parameters:

name type passed via required default example
condition JSON Request body yes - read below

For backward compatibility reasons this API allows the list of parameters and values in a JSON request's body with an HTTP GET request: this is a non standard usage of HTTP protocol.

A typical example of a request body to retrieve conditions using this API is the following:

{"condition":{
  "event_at": "20210526T123000+0200",
  "parameters_conditions_attributes":[
    {
      "parameter_id":"7", 
      "value":"352.0"
    }, 
    {
      "parameter_id":"1", 
      "value":"300.0"
    }, 
    {
      "parameter_id":"13", 
      "value":"512.0"
    },
    {
      "parameter_id":"14", 
      "value":"128.0"
    },
    {
      "parameter_id":"17", 
      "value":"1.1"
    },
    {
      "parameter_id":"4",
      "value":"0.0"
    },
    {
      "parameter_id":"12",
      "value":"4.99006796365e-313"
    }
  ]
  }
}

Note that the key event_at is optional, while the parameters_conditions_attributes is mandatory.

The event_at parameter, if missing, is set as default to the current datetime.

For each of the the parameters_conditions_attributes:

  • the key parameter_id is mandatory.
  • the key value is not mandatory and it would be assumed equal to 0.0 as default if not present for float type parameters. However, such value will result in an error for logarithmic parameters, while, on the other side, for text type parameters (where the flg_txt is set to true) it would have the effect of not filtering at all, being valid for any value of the parameter in the DB (i.e. every text starts with an empty string).

Note that in the example above all values are passed in the JSON body as strings but, for float parameters (where the flg_txt is set to false), the value is automatically converted to float. For float parameters, float or integer values can be passed directly in the JSON.

As an alternative to parameter_id, the parameter parameter_name can be used. In such case, the search of the ID will be performed immediately, with exact match of the name (case insensitive, though).

In case a parameter with the requested name is not returned a 404 error will be triggered.

In the example above, for instance, using parameter_name instead of parameter_id, will yield the following request body:

{"condition":{
  "event_at": "20210526T123000+0200",
  "parameters_conditions_attributes":[
    {
      "parameter_name":"Memory cells", 
      "value":"352.0"
    }, 
    {
      "parameter_name":"Sensor Bias Voltage", 
      "value":"300.0"
    }, 
    {
      "parameter_name":"Pixels X", 
      "value":"512.0"
    },
    {
      "parameter_name":"Pixels Y", 
      "value":"128.0"
    },
    {
      "parameter_name":"Acquisition rate", 
      "value":"1.1"
    },
    {
      "parameter_name":"Gain Setting",
      "value":"0.0"
    },
    {
      "parameter_name":"Detector UUID",
      "value":"4.99006796365e-313"
    }
  ]
  }
}

Remarks

The python calibration client, due to how the HTTP request's body JSON object is built internally in the client, will inject additional keys in the JSON, like, e.g. lower_deviation_value, flg_available and so on: some of those are actually required by other APIs (e.g. when injecting the conditions) but not for this particular API, where those additional keys will be simply ignored.

For the same reason, the python calibration client will inject the values of parameter_id and value as strings, despite they should normally be integers (the parameter_id values) and floats (the value values): this is also how they are stored in the database. For text parameters (flg_text set to true) having value passed as a string is needed, but actually, if a numeric value would be passed instead, this would be automatically converted to a string.

Output:

A list of conditions which respect the parameters within certain boundaries (read further for more details).

Each of the retrieved conditions MUST be identified by the entire set of parameters (conditions are defined also by the number of parameter conditions which identifies them: a subset of parameter conditions, even when matching a condition, would not be sufficient to retrieve it).

The conditions in the list are returned ordered by date of creation the nearest to the optional event_at parameter.

The criteria to identify the requested calibration conditions are the following:

  • the calibration condition is identified by that exact number of parameters
  • the calibration condition has the flag flg_available set to true
  • for each of the requested parameter conditions:
    • the parameter id matches
    • if the parameter is of type text (flg_text == true)
      • the stored value in the DB starts with the value parameter
    • if the parameter is not text and not logarithmic (flg_logarithmic == false)
      • as a lower bound, the value parameter...
        • if cal_min_value is defined
          • is greater than cal_min_value (stored in the parameter condition)
        • if cal_min_value is NOT defined (set to NULL) and parameters.def_lower_deviation_value is defined (parameters.def_lower_deviation_value is a default for the parameter)
          • is greater than value - parameters.def_lower_deviation_value (where value refers to the value stored in the parameter condition)
        • if both cal_min_value and parameters.def_lower_deviation_value are NOT defined (set to NULL):
          • in such case any value would match
      • as an upper bound, the value parameter...
        • if cal_max_value is defined
          • is less than cal_max_value (stored in the parameter condition)
        • if cal_max_value is NOT defined (set to NULL) and parameters.def_upper_deviation_value is defined (parameters.def_upper_deviation_value is a default for the parameter)
          • is less than value + parameters.def_upper_deviation_value (where value refers to the value stored in the parameter condition)
        • if both cal_max_value and parameters.def_upper_deviation_value are NOT defined (set to NULL):
          • in such case any value would match
    • if the parameter is logarithmic (flg_logarithmic == true)
      • almost identical considerations apply, except that in case of cal_min_value set to NULL (cal_max_value respectively) the fallback parameters.def_lower_deviation_value (parameters.def_upper_deviation_value respectively) is considered as the exponent of the power in base 10.

Important: at the moment of writing this documentation (November 2022) there are almost NO parameter conditions registered in the production database with cal_min_value or cal_max_value set to NULL, so, in practice, this API will find those conditions whose parameter conditions have the requested values between cal_min_value and cal_max_value.

Internals:

  • conditions_ids = get_expected_conditions(params, false):
    • get_expected_conditions_int(params, false): loop over parameters
    • for each parameter condition: call get_match_param_cond_ids(param_cond, false, num_params, filter_condition_ids)
      • get the parameter by parameter_id
      • call ParametersCondition.get_possible_conditions(parameter, parameter_val, num_params, filter_condition_ids)
      • apply different rules depending on nature of the parameter ( flg_text, flg_logarithmic)
        • ParametersCondition.get_possible_conditions_txt(parameter, value, num_parameters, filter_condition_ids)
        • ParametersCondition.get_possible_conditions_log(parameter, value, num_parameters, filter_condition_ids)
        • ParametersCondition.get_possible_conditions_non_log(parameter, value, num_parameters, filter_condition_ids)
      • intersection of conditionids is performed at each step, as the `filtercondition_ids` is refined at each step and passed along
  • possible_conditions_by_closest_created_at(condtions_ids, event_at): all intersected conditions are retrieved (by id) and ordered by creation date the nearest to the event_at parameter

get_expected_condition

For a definition of what a calibration condition is, please refer to the previous api get_possible_conditions.

HTTP Method: POST | GET

HTTP Path: /api/conditions/get_expected_condition

Input parameters:

name type passed via required default example
condition JSON Request body yes - read below

As the previous API for backward compatibility reasons this one also allows the list of parameters and values in a JSON request's body with an HTTP GET request.

A typical example of a request body to retrieve conditions using this API is the following:

{
  "condition":{
    "parameters_conditions_attributes":[
      {
        "parameter_id":"7", 
        "value":"352.0", 
        "lower_deviation_value":"0.0", 
        "upper_deviation_value":"0.0"
      }, 
      {
        "parameter_id":"1", 
        "value":"300.0", 
        "lower_deviation_value":"5.0", 
        "upper_deviation_value":"5.0"
      }, 
      {
        "parameter_id":"13", 
        "value":"512.0", 
        "lower_deviation_value":"0.0", 
        "upper_deviation_value":"0.0"
      },
      {
        "parameter_id":"14", 
        "value":"128.0", 
        "lower_deviation_value":"0.0", 
        "upper_deviation_value":"0.0"
      },
      {
        "parameter_id":"17", 
        "value":"1.1", 
        "lower_deviation_value":"0.0",
        "upper_deviation_value":"0.0"
      },
      {
        "parameter_id":"4",
        "value":"0.0",
        "lower_deviation_value":"0.0",
        "upper_deviation_value":"0.0"
      },
      {
        "parameter_id":"30",
        "value":"This is a text parameter"
      },        
      {
        "parameter_id":"12",
        "value":"4.99006796365e-313",
        "lower_deviation_value":"0.0",
        "upper_deviation_value":"0.0"
      }
    ]
  }
}

Note that in comparison with the previous API this one does not accept an event_at parameter and additionally, for each parameter condition, all keys (parameter_id, value, lower_deviation_value and upper_deviation_value) are required, except for text type parameters, where lower_deviation_value and upper_deviation_value are not required or, if passed, are simply ignored.

As for the previous API, also in this case parameter_name can be used in place of parameter_id, producing the same results in case of non existing parameter with the requested name.

Output:

The most recently created condition which respects the following criteria:

  • the calibration condition is identified by that exact number of parameters
  • the calibration condition has the flag flg_available set to true
  • for each of the requested parameter conditions
    • if parameter is of type float (logarithmic or not):
      • exact match between value, lower_deviation_value and upper_deviation_value requested parameters and database content
    • if parameter is of text type:
      • exact match of value and database text_value content

Internals:

  • conditions_ids = get_expected_conditions(params, true):
    • get_expected_conditions_int(params, true): loop over parameters
    • for each parameter condition: call get_match_param_cond_ids(param_cond, true, num_params, filter_condition_ids)
      • get the parameter by parameter_id
      • call ParametersCondition.get_exact_conditions(parameter.id, parameter_val, parameter_lower_dev_val, parameter_upper_dev_val, num_params, filter_condition_ids)
    • intersection of conditionids is performed at each step, as the `filtercondition_ids` is refined at each step and passed along
  • existing_conditions(condition_ids): returns Condition.where(id: condition_ids).order(created_at: :desc).first (so only one: the most recently created condition)

set_expected_conditions

This API is used to create a new condition in the database or return the already existing one which already matches the same set of parameter conditions.

HTTP Method: POST

HTTP Path: /api/conditions/set_possible_conditions

Input parameters:

name type passed via required default example
condition JSON Request body yes - read below

Similar considerations as for the previous API apply here, the major difference in the input JSON is that additional optional parameters (JSON "keys") can be sent. In particular, under the main "condition" key the following additional keys/parameters can also be specified:

  • name (default: unix timestamp of current datetime)
  • flg_available (default: true)
  • description (default: NULL)

And under each of the parameter conditions attributes the following additional keys/parameters can be specified:

  • flg_available (default: true)
  • description (default: NULL)

Again, also in this API parameter_name can be used in place of parameter_id.

Output:

The newly created condition (HTTP response status will be 201 Created in such case) or the condition which already matches perfectly the set of parameter condition as for the criteria of the get_expected_condition API (HTTP response status will be 200 OK). Please note: in the latter case the aforementioned optional parameters are simply ignored (the way to update the name, description and flg_available of a condition, if needed, are the standard RESTful APIs)

Internals:

  • performs the same operations as the previous API and if any condition is found will return that condition
  • if nothing is found call create_condition_int(condition_params) which would create the condition and nested attributes (parameters conditions)

Calibration Constants (CCs)

These methods are actually exposed under the Calibrations (aka 'constant types') APIs, that is, under the base path: /api/calibrations/:id/

get_calibration_constant

HTTP Method: GET

HTTP Path: /api/calibrations/:id/get_calibration_constant

Input parameters

name type passed via required default example
:id integer Path yes - /1
detector_type_id integer QueryString yes - detector_type_id=10
condition_id integer QueryString yes - condition_id=20

Output:

One single calibration constant matching the criteria (constant_id = :id, detector type and condition) where the flg_available is set to true. In case multiple calibration constants should match the criteria (same calibration, aka constant type, same detector type and same condition) only the first orderd by creation date descending (i.e., the most recent) is returned.

Internals

  • scope search_calibration_constant of CalibrationConstant model, returns first record

get_all_calibration_constants

HTTP Method: GET

HTTP Path: /api/calibrations/:id/get_all_calibration_constants

Input parameters

name type passed via required default example
:id integer Path yes - /1
detector_type_id integer QueryString yes - detector_type_id=10
condition_ids array of integers QueryString yes - condition_ids=[1,2,3]

Output:

List of calibration constants matching the criteria (constant_id = :id, detector type and condition) where the flg_available is set to true, ordered by creation date descending.

Internals

  • scope search_calibration_constant of CalibrationConstant model

Calibration Constant Versions (CCVs)

The following methods (with the exception of the last one) are actually exposed under the Calibration Constants APIs, that is, under the base path: /api/calibration_constants/:id/

get_version

HTTP Method: GET

HTTP Path: /api/calibration_constants/:id/get_version

Input parameters:

name type passed via required default example
:id integer Path yes - /1
physical_detector_unit_id integer QueryString yes - physical_detector_unit_id=10
event_at datetime QueryString no current datetime - 1 second¹ event_at=20210526T123000+0200
snapshot_at datetime QueryString no - snapshot_at=20210526T123000+0200

Output:

One single Calibration Constant Version matching the criteria (CC and PDU by respective requested ids), with the flag flg_deployed set to true, where the param event_at is in the range of validity of the CCV (see "§ Notes / Range of validity of Calibration Constant Versions" for more details). If the snapshot_at parameter is passed, the CCVs taken into account would be the ones which had the flag flg_deployed set to true in the database at the snapshot_at moment in time. If more than one CCV should be "valid" at the event_at moment, the one with the most recent begin_at would be returned.

(1) The one second difference explained:

CCVs may be defined with end_validity_at set to NULL. In such case (see "§ Notes / Range of validity of Calibration Constant Versions" for more details) the end date would be considered the date of now, but with seconds resolution. Since the comparison for the validity is for an end_validity_at strictly greater than the event_at, removing one second guarantees we find such CCV.

Internals:

  • cc = CalibrationConstant.find(): retrieve a single calibration constant
  • cc.get_deployed_version: returns one calibration constant version matching the criteria with event_at in the range of validity of the CCV

get_closest_version

HTTP Method: GET

HTTP Path: /api/calibration_constants/:id/get_closest_version

Input parameters:

name type passed via required default example
:id² integer Path yes - /1
calibration_constant_ids array of integers QueryString yes - calibration_constant_ids=[1,2,3]
physical_detector_unit_id integer QueryString yes - physical_detector_unit_id=10
event_at datetime QueryString no current datetime - 1 second¹ event_at=20210526T123000+0200
snapshot_at datetime QueryString no - snapshot_at=20210526T123000+0200

(2) Note that the id parameter is there for compatibility and historical reason. It is actually not used to filter the results but it must correspond to the id of an existing constant: it is advisable to pass the first id of the calibration_constant_ids list.

Output:

One single Calibration Constant Version matching the criteria (multiple CCs and single PDU by respective requested ids), with the flag flg_deployed set to true, where the begin_at is the closest to the event_at parameter (it may return a CCV which has a validity period in the future of the event_at). If the snapshot_at parameter is passed, the CCVs taken into account would be the ones which had the flag flg_deployed set to true in the database at the snapshot_at moment in time.

Internals:

  • get_all_versions_ordered_by_closest(cc_ids, pdu_id, snap, event):
    • find CCs by cc_ids
    • loop over the CCs
    • collect CCVs ids of those CCs (here snapshot_at is taken into account)
  • returns one CCV (where begin_at is the closest to event_at)

get_prior_in_time_version

HTTP Method: GET

HTTP Path: /api/calibration_constants/:id/get_prior_in_time_version

Input parameters:

name type passed via required default example
:id² integer Path yes - /1
calibration_constant_ids array of integers QueryString yes - calibration_constant_ids=[1,2,3]
physical_detector_unit_id integer QueryString yes - physical_detector_unit_id=10
event_at datetime QueryString no current datetime - 1 second¹ event_at=20210526T123000+0200
snapshot_at datetime QueryString no - snapshot_at=20210526T123000+0200

(2) Note that the id parameter is there for compatibility and historical reason. It is actually not used to filter the results but it must correspond to the id of an existing constant: it is advisable to pass the first id of the calibration_constant_ids list.

Output:

One single Calibration Constant Version matching the criteria (multiple CCs and single PDU by respective requested ids), with the flag flg_deployed set to true, where the begin_at is the closest to the event_at parameter BUT BEFORE the event_at. If the snapshot_at parameter is passed, the CCVs taken into account would be the ones which had the flag flg_deployed set to true in the database at the snapshot_at moment in time.

Internals:

  • get_prior_in_time_version_render(cc_ids, pdu_id, snap, event):
    • find CCs by cc_ids
    • loop over the CCs
    • collect CCVs of those CCs (here snapshot_at is taken into account)
    • filter CCVs so that event_at is in their range of validity
    • reorder filtered CCVs by begin_at ascending
    • returns the last CCV of so re-ordered CCVs

get_all_versions

HTTP Method: GET

HTTP Path: /api/calibration_constants/:id/get_all_versions

Input parameters:

name type passed via required default example
:id² integer Path yes - /1
calibration_constant_ids array of integers QueryString yes - calibration_constant_ids=[1,2,3]
physical_detector_unit_id integer QueryString yes - physical_detector_unit_id=10
event_at datetime QueryString no current datetime - 1 second¹ event_at=20210526T123000+0200
snapshot_at datetime QueryString no - snapshot_at=20210526T123000+0200

(2) Note that the :id parameter is there for compatibility and historical reason. It is actually not used to filter the results but it must correspond to the id of an existing constant: it is advisable to pass the first id of the calibration_constant_ids list.

Output:

List of all Calibration Constant Versions matching the criteria (multiple CCs and single PDU by respective requested ids), with the flag flg_deployed set to true, ordered by begin_at closer to event_at parameter. If the snapshot_at parameter is passed, the CCVs taken into account would be the ones having the flg_deployed set to true in the database at the snapshot_at moment in time.

Internals:

  • get_all_versions_ordered_by_closest(cc_ids, pdu_id, snap, event):
    • find CCs by cc_ids
    • loop over the CCs
    • collect CCVs ids of those CCs (here snapshot_at is taken into account)
  • returns all those CCVs (ordered by begin_at closer to event_at)

get_closest_version_by_detector

HTTP Method: GET

HTTP Path: /api/calibration_constants/:id/get_closest_version_by_detector

Input parameters:

name type passed via required default example
:id² integer Path yes - /1
calibration_constant_ids array of integers QueryString yes - calibration_constant_ids=[1,2,3]
detector_id integer QueryString yes - detector_id=28
karabo_da string QueryString no - karabo_da=KARABODA02
event_at datetime QueryString no current datetime - 1 second¹ event_at=20210526T123000+0200
snapshot_at datetime QueryString no - snapshot_at=20210526T123000+0200

(2) Note that the :id parameter is there for compatibility and historical reason. It is actually not used to filter the results but it must correspond to the id of an existing constant: it is advisable to pass the first id of the calibration_constant_ids list.

Output:

List of all Calibration Constant Versions (one per PDU) matching the criteria (multiple CCs and PDUs belonging to detector, if karabo_da is specified will select only one matching PDU), with the flag flg_deployed set to true, ordered by begin_at closer to event_at parameter. If the snapshot_at parameter is passed:

  • the PDUs taken into account would be the ones belonging to the detector at the snapshot_at moment in time
  • the CCVs taken into account would be the ones having the flg_deployed set to true in the database at the snapshot_at moment in time

Internals:

  • find the detector
  • find the PDUs (could be just one if karabo_da is specified)
  • get_all_versions_by_pdu(cc_ids, pdus, snap, event):
    • loop over the PDUs and append the result of first get_all_versions_ordered_by_closest(cc_ids, pdu_id, snap, event)
    • this is a nested loop (PDUs x CCs, so is pretty inefficient when each CCs is bound to a single PDU!)
    • finally returns one CCV per PDU which has the closest begin_at to event_at

get_by_detector_conditions

Differently from the previous APIs, this one is exposed under the Calibration Constant Versions APIs that is, under the base path: /api/calibration_constant_versions/

This is a non-standard API which tries to retrieve the calibration constant versions by detector identifier and by a limited set of parameters conditions which does not include the PDUs' UUID, but assumes that they have been injected with it. This API combines strategies from the previous APIs and assumes a specific situation in the CalCat DB with conditions bound to the PDUs via the UUID parameter (parameter_id = 12).

HTTP Method: POST | GET

HTTP Path: /api/calibration_constant_versions/get_by_detector_conditions

Input parameters:

name type passed via required default example
detector_identifier string QueryString yes - detector_identifier=SPB_DET_AGIPD1M-1
calibration_id integer (or list of integers) QueryString no - calibration_id=2 (calibration_id=[2,11,13])
karabo_da string QueryString no - karabo_da=AGIPD03
parameters_conditions_attributes JSON Request body yes - read below
event_at datetime QueryString no current datetime - 1 second¹ event_at=20210526T123000+0200
pdu_snapshot_at datetime QueryString no - pdu_snapshot_at=20210526T105000+0200

Similarly to other APIs involving the conditions (e.g. get_possible_conditions), for backward compatibility reasons, this one allows the list of parameters and values in a JSON request's body with an HTTP GET request.

A typical example of a request body to retrieve CCVs using this API is the following:

{
  "parameters_conditions_attributes":[
    {
      "parameter_id":"7", 
      "value":"352.0"
    }, 
    {
      "parameter_id":"1", 
      "value":"300.0"
    }, 
    {
      "parameter_id":"13", 
      "value":"512.0"
    },
    {
      "parameter_id":"14", 
      "value":"128.0"
    },
    {
      "parameter_id":"17", 
      "value":"1.1"
    },
    {
      "parameter_id":"4",
      "value":"0.0"
    }
  ]
}

Please note that the parameter identified by parameter_id 12 (Detector UUID) MUST NOT be included for this API to work as expected, as the filtering by PDU is internally executed after retrieving from the database all possible conditions which respect the remaining parameters conditions.

As for the getpossibleconditions API, also in this case parameter_name can be used in place of parameter_id.

Output:

List of all Calibration Constant Versions (one per PDU) matching the criteria: PDUs belonging to detector specified by detector_identifier parameter, Calibration (aka constant type) matching the passed parameter (calibration_id), Calibration Constant matching the specified conditions, with the flag flg_deployed set to true, ordered by begin_at closer to event_at parameter.

If the pdu_snapshot_at parameter is passed, the PDUs taken into account would be the ones belonging to the detector at the pdu_snapshot_at moment in time

If the given detector does not exists (wrong detector_identifier) or exists but does not have any associated PDU (or did not have at the snapshot_at moment), the HTTP response code will be in both cases a 404.

In case the detector and associated PDU exist but no CCVs could be retrieved (either because of non matching conditions or constant type), the HTTP response would be a 200 but the result would consist of an empty list.

Either a single calibration_id, a list of calibration_id(s) or an empty calibration_id are accepted: if multiple ids are passed, then the API will loop over them and retrieve one CCV per PDU per constant type (calibrationid = constant type). If an empty `calibrationid` is passed then the API will loop over all possible constant types.

Internals:

  • Get the detector: det = Detector.find_by_identifier(detector_identifier)

  • Get PDUs at pdu_snapshot_at: pdus = det.get_pdus_by_detector(pdu_snapshot_at) or pdus = [det.get_pdu_by_detector_and_karabo_da(karabo_da, pdu_snapshot_at)].reject { |pdu| pdu.nil? } (if karabo_da parameter is passed)

  • Get all conditions with no PDU information: get_expected_conditions_int(parameters_conditions, false, num_params+1)

  • For each PDU:

    • For each constanttype (calibrationid):
    • find the specific conditions for the PDU among the conditions found at previous step: ParametersCondition.where(parameter_id: 12, value: pdu.float_uuid).where(condition_id: condition_ids)
    • find CCs by conditions_ids, PDU.detector_type_id, calibration_id (we'll have a CC per PDU): CalibrationConstant.search_calibration_constant
    • get the first CCV from list of CCVs ordered by begin_at closest at event_at: get_all_versions_ordered_by_closest(calibration_constants.collect(&:id), pdu.id, nil, event_at)

curl request example (single constant_type, all PDUs):

curl -s -X GET -H 'Accept: application/json; version=2' \
-H "X-USER-EMAIL: $user_email" -H "Authorization: Bearer $token" \
-H "Content-type: application/json" \
"https://in.xfel.eu/calibration/api/calibration_constant_versions/get_by_detector_conditions?detector_identifier=SPB_DET_AGIPD1M-1&calibration_id=2" \
-d '{"parameters_conditions_attributes":[{"parameter_id":"7","value":"352.0"},{"parameter_id":"1","value":"300.0"},{"parameter_id":"13","value":"512.0"},{"parameter_id":"14","value":"128.0"},{"parameter_id":"17","value":"1.1"},{"parameter_id":"4","value":"0.0"}]}' \
 | jq .

curl request example (multiple constant_types, one PDU):

curl -s -X GET -H 'Accept: application/json; version=2' \
-H "X-USER-EMAIL: $user_email" -H "Authorization: Bearer $token" \
-H "Content-type: application/json" \
"https://in.xfel.eu/calibration/api/calibration_constant_versions/get_by_detector_conditions?detector_identifier=SPB_DET_AGIPD1M-1&karabo_da=AGIPD12&calibration_id=%5B2%2C11%5D" \
-d '{"parameters_conditions_attributes":[{"parameter_id":"7","value":"352.0"},{"parameter_id":"1","value":"300.0"},{"parameter_id":"13","value":"512.0"},{"parameter_id":"14","value":"128.0"},{"parameter_id":"17","value":"1.1"},{"parameter_id":"4","value":"0.0"}]}' \
 | jq .

Notes:

Serialization of Calibration Constant Versions (example):

{
  "id": 81729,
  "name": "20210716_162211_sIdx=0",
  "file_name": "cal.1626452528.8961353.h5",
  "path_to_file": "xfel/cal/agipd-type/agipd_siv1_agipdv11_m441/",
  "data_set_name": "/AGIPD_SIV1_AGIPDV11_M441/ThresholdsDark/0",
  "flg_deployed": true,
  "flg_good_quality": true,
  "begin_validity_at": "2021-07-16T17:46:36.000+02:00",
  "end_validity_at": null,
  "begin_at": "2021-07-16T17:46:36.000+02:00",
  "start_idx": 0,
  "end_idx": 0,
  "raw_data_location": "proposal:p900214 runs:9 8 7",
  "report_id": 594,
  "description": "",
  "calibration_constant": {
    "id": 11042,
    "name": "AGIPD-Type_ThresholdsDark_AGIPD DefXphmDHCWAd61WU1/N5h3qA==\n",
    "flg_auto_approve": true,
    "flg_available": true,
    "description": "Per-pixel (per-memory cell) thresholds from dark runs",
    "detector_type_id": 2,
    "calibration_id": 11,
    "condition_id": 3286,
    "created_at": "2021-07-16T18:22:10.000+02:00"
  },
  "physical_detector_unit": {
    "id": 106,
    "physical_name": "AGIPD_SIV1_AGIPDV11_M441",
    "karabo_da": "AGIPD04",
    "virtual_device_name": "Q2M1",
    "uuid": 303002300000,
    "float_uuid": 1.49703027041e-312,
    "detector_type_id": 2,
    "detector_id": 4,
    "flg_available": true,
    "description": null
  }
}

Range of validity of Calibration Constant Versions:

Each Calibration Constant Version has the following date fields:

  • begin_at
  • begin_validity_at
  • end_validity_at

At the moment of writing this documentation (December 2022) the field begin_validity_at is actually NOT used in CalCat. The validity period of each CCV is normally calculated as the period between begin_at and end_validity_at. However, the end_validity_at can be NULL: in such case the validity period of each CCV is calculated as the period between the currently examined CCV begin_at and the begin_at of the following CCV (in the list of CCVs belonging to the same PDU and CC ordered by begin_at). The last CCV in the list, having a NULL end_validity_at, would be considered be valid until the current datetime (now), however with the caveat that this calculated datetime has resolution of seconds.

TODO: Complete this section with more explanation / examples