diff --git a/cdci_data_analysis/analysis/parameters.py b/cdci_data_analysis/analysis/parameters.py index 03364984..ad84535f 100644 --- a/cdci_data_analysis/analysis/parameters.py +++ b/cdci_data_analysis/analysis/parameters.py @@ -210,6 +210,8 @@ def __init__(self, allowed_values=None, min_value = None, max_value = None, + + extra_metadata=None, **kwargs ): @@ -261,6 +263,7 @@ def __init__(self, self._max_value = max_value self._bound_units = self.units self.value = value + self.extra_metadata = extra_metadata self._arg_list = [self.name] @@ -463,6 +466,8 @@ def reprJSONifiable(self): restrictions['schema'] = self.schema if restrictions: reprjson[0]['restrictions'] = restrictions + if getattr(self, 'extra_metadata', None) is not None: + reprjson[0]['extra_metadata'] = self.extra_metadata if getattr(self, 'owl_uris', None): if isinstance(self.owl_uris, str): reprjson[0]['owl_uri'] = [ self.owl_uris ] @@ -487,6 +492,8 @@ def from_owl_uri(cls, **kwargs): from oda_api.ontology_helper import Ontology + metadata_keys = ['label', 'description', 'group'] + if ontology_path is not None and ontology_object is not None: raise RuntimeError("Both ontology_path and ontology_object parameters are set.") elif ontology_path is None and ontology_object is None: @@ -494,7 +501,7 @@ def from_owl_uri(cls, 'Trying to find parameter which have %s directly set. ' 'extra_ttl will be ignored ', owl_uri) parameter_hierarchy = [ owl_uri ] - par_format = par_unit = allowed_values = min_value = max_value = None + par_format = par_unit = allowed_values = min_value = max_value = label = description = group = None else: if ontology_path is not None: if isinstance(ontology_path, (str, os.PathLike)): @@ -514,13 +521,18 @@ def from_owl_uri(cls, par_unit = onto.get_parameter_unit(owl_uri) min_value, max_value = onto.get_limits(owl_uri) allowed_values = onto.get_allowed_values(owl_uri) - + label = onto.get_direct_annotation(owl_uri, "label") + description = onto.get_direct_annotation(owl_uri, "description") + group = onto.get_direct_annotation(owl_uri, "group") + for owl_superclass_uri in parameter_hierarchy: for python_subclass in subclasses_recursive(cls): logger.debug("searching for class with owl_uri=%s, found %s", owl_superclass_uri, python_subclass) if python_subclass.matches_owl_uri(owl_superclass_uri): logger.info("will construct %s by owl_uri %s", python_subclass, owl_superclass_uri) - call_kwargs = {} + call_kwargs = { + 'extra_metadata': {key: val for key, val in zip(metadata_keys, [label, description, group]) if + val is not None}} call_signature = signature(python_subclass) var_kw_signature = call_parameter.VAR_KEYWORD in [x.kind for x in call_signature.parameters.values()] @@ -566,7 +578,7 @@ def from_owl_uri(cls, class String(Parameter): owl_uris = ("http://www.w3.org/2001/XMLSchema#str", "http://odahub.io/ontology#String") - def __init__(self, value=None, name_format='str', name=None, allowed_values = None): + def __init__(self, value=None, name_format='str', name=None, allowed_values = None, extra_metadata = None): _allowed_units = ['str'] super().__init__(value=value, @@ -574,7 +586,8 @@ def __init__(self, value=None, name_format='str', name=None, allowed_values = No check_value=self.check_name_value, name=name, allowed_units=_allowed_units, - allowed_values=allowed_values) + allowed_values=allowed_values, + extra_metadata=extra_metadata) @staticmethod def check_name_value(value, units=None, name=None, par_format=None): @@ -671,7 +684,8 @@ def __init__(self, check_value=None, min_value= None, max_value = None, - units_name = None): + units_name = None, + extra_metadata=None): super().__init__(value=value, units=units, @@ -684,7 +698,8 @@ def __init__(self, allowed_units=allowed_units, min_value=min_value, max_value=max_value, - units_name = units_name) + units_name = units_name, + extra_metadata=extra_metadata) @@ -700,7 +715,8 @@ def __init__(self, max_value = None, units_name = None, default_units = None, - allowed_units = None): + allowed_units = None, + extra_metadata=None): super().__init__(value=value, units=units, @@ -712,7 +728,8 @@ def __init__(self, allowed_units=allowed_units, min_value = min_value, max_value = max_value, - units_name = units_name) + units_name = units_name, + extra_metadata=extra_metadata) def set_par_internal_value(self, value): if isinstance(value, float): @@ -725,13 +742,14 @@ class Time(Parameter): owl_uris = ("http://odahub.io/ontology#TimeInstant",) format_kw = 'T_format' - def __init__(self, value=None, T_format='isot', name=None, Time_format_name='T_format', par_default_format='isot'): + def __init__(self, value=None, T_format='isot', name=None, Time_format_name='T_format', par_default_format='isot', extra_metadata=None): super().__init__(value=value, par_format=T_format, par_format_name=Time_format_name, par_default_format=par_default_format, - name=name) + name=name, + extra_metadata=extra_metadata) def get_default_value(self): return self.get_value_in_default_format() @@ -776,7 +794,7 @@ class TimeDelta(Time): owl_uris = ("http://odahub.io/ontology#TimeDeltaIsDeprecated",) format_kw = 'delta_T_format' - def __init__(self, value=None, delta_T_format='sec', name=None, delta_T_format_name=None, par_default_format='sec'): + def __init__(self, value=None, delta_T_format='sec', name=None, delta_T_format_name=None, par_default_format='sec', extra_metadata=None): logging.warning(('TimeDelta parameter is deprecated. ' 'It derives from Time, which is confusing. ' 'Consider using TimeInterval parameter.')) @@ -784,7 +802,8 @@ def __init__(self, value=None, delta_T_format='sec', name=None, delta_T_format_n T_format=delta_T_format, Time_format_name=delta_T_format_name, par_default_format=par_default_format, - name=name) + name=name, + extra_metadata=extra_metadata) def get_value_in_format(self, units): return getattr(self._astropy_time_delta, units) @@ -816,7 +835,8 @@ def __init__(self, default_units='s', min_value=None, max_value=None, - units_name = None): + units_name = None, + extra_metadata=None): _allowed_units = ['s', 'minute', 'hour', 'day', 'year'] super().__init__(value=value, @@ -826,13 +846,14 @@ def __init__(self, min_value=min_value, max_value=max_value, units_name = units_name, - allowed_units=_allowed_units) + allowed_units=_allowed_units, + extra_metadata=extra_metadata) class InputProdList(Parameter): owl_uris = ('http://odahub.io/ontology#InputProdList',) # TODO removal of the leading underscore cannot be done for compatibility with the plugins - def __init__(self, value=None, _format='names_list', name: str = None): + def __init__(self, value=None, _format='names_list', name: str = None, extra_metadata=None): _allowed_units = ['names_list'] if value is None: @@ -842,7 +863,8 @@ def __init__(self, value=None, _format='names_list', name: str = None): par_format=_format, check_value=self.check_list_value, name=name, - allowed_units=_allowed_units) + allowed_units=_allowed_units, + extra_metadata=extra_metadata) @staticmethod def _split(str_list): @@ -903,7 +925,8 @@ def __init__(self, name=None, min_value = None, max_value = None, - units_name = None): + units_name = None, + extra_metadata=None): super().__init__(value=value, units=units, @@ -913,7 +936,8 @@ def __init__(self, allowed_units=None, min_value = min_value, max_value = max_value, - units_name = units_name) + units_name = units_name, + extra_metadata=extra_metadata) class Energy(Float): @@ -928,7 +952,8 @@ def __init__(self, check_value=None, min_value = None, max_value = None, - units_name = None): + units_name = None, + extra_metadata=None): _allowed_units = ['keV', 'eV', 'MeV', 'GeV', 'TeV', 'Hz', 'MHz', 'GHz'] @@ -940,7 +965,8 @@ def __init__(self, allowed_units=_allowed_units, min_value = min_value, max_value = max_value, - units_name = units_name) + units_name = units_name, + extra_metadata=extra_metadata) class SpectralBoundary(Energy): def __init__(self, @@ -951,7 +977,8 @@ def __init__(self, check_value=None, min_value=None, max_value=None, - units_name=None): + units_name=None, + extra_metadata=None): # retro-compatibility with integral plugin if check_value is None: @@ -964,7 +991,8 @@ def __init__(self, check_value=check_value, min_value=min_value, max_value=max_value, - units_name=units_name) + units_name=units_name, + extra_metadata=extra_metadata) @staticmethod def check_energy_value(value, units, name): @@ -973,7 +1001,7 @@ def check_energy_value(value, units, name): class DetectionThreshold(Float): owl_uris = ("http://odahub.io/ontology#DetectionThreshold",) - def __init__(self, value=None, units='sigma', name=None, min_value = None, max_value = None): + def __init__(self, value=None, units='sigma', name=None, min_value = None, max_value = None, extra_metadata=None): _allowed_units = ['sigma'] super().__init__(value=value, @@ -982,7 +1010,8 @@ def __init__(self, value=None, units='sigma', name=None, min_value = None, max_v name=name, allowed_units=_allowed_units, min_value = min_value, - max_value = max_value) + max_value = max_value, + extra_metadata=extra_metadata) # 'sigma' is not astropy unit, so need to override methods def get_value_in_units(self, units): @@ -995,24 +1024,26 @@ def set_par_internal_value(self, value): self._value = None class UserCatalog(Parameter): - def __init__(self, value=None, name_format='str', name=None): + def __init__(self, value=None, name_format='str', name=None, extra_metadata=None): _allowed_units = ['str'] super().__init__(value=value, par_format=name_format, check_value=None, name=name, - allowed_units=_allowed_units) + allowed_units=_allowed_units, + extra_metadata=extra_metadata) class Boolean(Parameter): owl_uris = ('http://www.w3.org/2001/XMLSchema#bool',"http://odahub.io/ontology#Boolean") - def __init__(self, value=None, name=None): + def __init__(self, value=None, name=None, extra_metadata=None): self._true_rep = ['True', 'true', 'yes', '1', True] self._false_rep = ['False', 'false', 'no', '0', False] super().__init__(value=value, name=name, - allowed_values=self._true_rep+self._false_rep + allowed_values=self._true_rep+self._false_rep, + extra_metadata = extra_metadata ) @property @@ -1031,7 +1062,7 @@ def value(self, v): class StructuredParameter(Parameter): owl_uris = ("http://odahub.io/ontology#StructuredParameter") - def __init__(self, value=None, name=None, schema={"oneOf": [{"type": "object"}, {"type": "array"}]}): + def __init__(self, value=None, name=None, schema={"oneOf": [{"type": "object"}, {"type": "array"}]}, extra_metadata=None): self.schema = schema @@ -1039,7 +1070,8 @@ def __init__(self, value=None, name=None, schema={"oneOf": [{"type": "object"}, logger.warning("Parameter %s: Schema is not defined, will allow any structure.", name) super().__init__(value=value, - name=name) + name=name, + extra_metadata=extra_metadata) def check_schema(self): if self.schema is not None: @@ -1065,7 +1097,7 @@ def get_default_value(self): class PhosphorosFiltersTable(StructuredParameter): owl_uris = ('http://odahub.io/ontology#PhosphorosFiltersTable') - def __init__(self, value=None, name=None): + def __init__(self, value=None, name=None, extra_metadata=None): # TODO: either list or the whole schema may be loaded from the external file, purely based on URI. # If there is no additional check, this would allow to avoid even having the class. @@ -1102,7 +1134,7 @@ def __init__(self, value=None, name=None): "required": ["filter", "flux", "flux_error"] } - super().__init__(value=value, name=name, schema=schema) + super().__init__(value=value, name=name, schema=schema, extra_metadata=extra_metadata) def additional_check(self): assert len(self._value['filter']) == len(self._value['flux']) == len(self._value['flux_error']) diff --git a/tests/test_parameters.py b/tests/test_parameters.py index 3eb47853..779e5beb 100644 --- a/tests/test_parameters.py +++ b/tests/test_parameters.py @@ -1,4 +1,5 @@ import pytest +import os from cdci_data_analysis.analysis.instrument import Instrument from cdci_data_analysis.analysis.queries import ( @@ -422,7 +423,7 @@ def test_boolean_parameter(inval, iswrong, expected): True, 100, Energy), ]) def test_parameter_class_from_owl_uri(uri, extra_ttl, use_ontology, value, param_type): - ontology_path = 'tests/oda-ontology.ttl' if use_ontology else None + ontology_path = os.path.join(os.path.dirname(__file__), 'oda-ontology.ttl') if use_ontology else None param = Parameter.from_owl_uri(uri, value=value, extra_ttl = extra_ttl,