diff --git a/source/incqueryserver-jupyter/iqs_jupyter/analysis_extensions.py b/source/incqueryserver-jupyter/iqs_jupyter/analysis_extensions.py index a55a1c2..0dbc669 100644 --- a/source/incqueryserver-jupyter/iqs_jupyter/analysis_extensions.py +++ b/source/incqueryserver-jupyter/iqs_jupyter/analysis_extensions.py @@ -18,29 +18,37 @@ ''' import html +import json +from typing import Union import iqs_client +import iqs_jupyter.config_defaults as defaults +import iqs_jupyter.tool_extension_point as ext_point +from IPython.core.display import display +from ipywidgets import widgets +from iqs_client import AnalysisConfigurationIdentifier, AnalysisApi +from iqs_jupyter import IQSClient +from iqs_jupyter.core_extensions import validation_color_scale from iqs_jupyter.helpers import cell_to_html as _cell_to_html from iqs_jupyter.helpers import dict_to_element as _dict_to_element -from iqs_jupyter.core_extensions import validation_color_scale -def _monkey_patch_analysis_results_repr_html(self : iqs_client.AnalysisResults): +def _monkey_patch_analysis_results_repr_html(self: iqs_client.AnalysisResults): escaped_id = html.escape(self.configuration.configuration_id) header_report = '''

Results for model analysis "{}"

Analysis configuration id: {} (see hover for details) '''.format( html.escape(self.configuration.configuration_name), - html.escape(self.to_str()), + html.escape(self.to_str()), escaped_id ) - + kpi_rows = "\n".join([ "{}{}".format( - html.escape(kpi.name), + html.escape(kpi.name), html.escape(str(kpi.value)) - ) + ) for kpi in self.kpi_values ]) kpi_report = ''' @@ -59,15 +67,15 @@ def _monkey_patch_analysis_results_repr_html(self : iqs_client.AnalysisResults): '''.format( kpi_rows ) - + toc_rows = "\n".join([ '''
  • {} by rule "{}"
  • '''.format( _marker_count_report(self.analysis_results[result_index]), - #escaped_id, str(result_index), + # escaped_id, str(result_index), html.escape(self.analysis_results[result_index].configuration_rule.name) - ) + ) for result_index in range(len(self.analysis_results)) ]) toc_report = ''' @@ -79,7 +87,6 @@ def _monkey_patch_analysis_results_repr_html(self : iqs_client.AnalysisResults): toc_rows ) - individual_result_tables = "\n".join([ '''
    @@ -87,10 +94,10 @@ def _monkey_patch_analysis_results_repr_html(self : iqs_client.AnalysisResults):
    '''.format( self.analysis_results[result_index]._repr_html_("h4") - ) + ) for result_index in range(len(self.analysis_results)) ]) - + style = "" return ''' {} @@ -103,7 +110,7 @@ def _monkey_patch_analysis_results_repr_html(self : iqs_client.AnalysisResults): '''.format(header_report, style, kpi_report, toc_report, individual_result_tables) -def _marker_count_report(analysis_result : iqs_client.AnalysisResult) -> str: +def _marker_count_report(analysis_result: iqs_client.AnalysisResult) -> str: marker_color = validation_color_scale.get(analysis_result.configuration_rule.severity.lower(), None) marker_style_string = 'style="background-color:{}; color: bisque;"'.format(marker_color) if marker_color else "" return '{} {} marker{}'.format( @@ -113,12 +120,14 @@ def _marker_count_report(analysis_result : iqs_client.AnalysisResult) -> str: "s" if 1 != len(analysis_result.matches) else "" ) -def _monkey_patch_analysis_result_repr_html(self : iqs_client.AnalysisResult, heading_lvl: str = 'h4', anchor : str = None): + +def _monkey_patch_analysis_result_repr_html(self: iqs_client.AnalysisResult, heading_lvl: str = 'h4', + anchor: str = None): anchor_tag = ''.format(anchor) if anchor else "" anchor_href = '# '.format(anchor) if anchor else "" subheader_report = '{}{} via "{}" (see hover for details)'.format( anchor_href, - html.escape(self.to_str()), + html.escape(self.to_str()), _marker_count_report(self), html.escape(self.configuration_rule.query_fqn) ) @@ -135,21 +144,21 @@ def _monkey_patch_analysis_result_repr_html(self : iqs_client.AnalysisResult, he ) if not self.matches: return header_report - + # we can assume at least one match pattern_params = [ argument_value.parameter - for argument_value in self.matches[0].matching_elements + for argument_value in self.matches[0].matching_elements ] - + param_headers = " ".join(["{} ".format(html.escape(param)) for param in pattern_params]) match_rows = "\n".join([ "{}{}{}\n".format(row_index, self.matches[row_index].message, " ".join([ - " {}".format(_cell_to_html(_dict_to_element(argument_value.value))) + " {}".format(_cell_to_html(_dict_to_element(argument_value.value))) for argument_value in self.matches[row_index].matching_elements ])) for row_index in range(len(self.matches)) ]) - + style = "" return ''' {} @@ -171,9 +180,61 @@ def _monkey_patch_analysis_result_repr_html(self : iqs_client.AnalysisResult, he '''.format(header_report, style, param_headers, match_rows) +class ConfigSelector: + + def __init__( + self, + iqs: IQSClient, + initial_config: Union[AnalysisConfigurationIdentifier, str] = defaults.default_auto_display, + auto_display=defaults.default_auto_display): + self.dropdown = widgets.Dropdown(description='') + self.box = widgets.HBox([widgets.Label(value="Configuration"), self.dropdown]) + config_list = iqs.analysis.list_model_analysis_configurations() + configs = config_list.model_analysis_configuration_identifiers + options = [("---- Select model analysis configuration", None)] + for config in configs: + additional = (f"{config.configuration_name} (ID: {config.configuration_id})", config) + options.append(additional) + self.dropdown.options = options + if initial_config: + if isinstance(initial_config, str): + for config in configs: + if config.configuration_id == initial_config: + self.dropdown.value = config + elif isinstance(initial_config, AnalysisConfigurationIdentifier): + self.dropdown.value = initial_config + if auto_display: + display(self.box) + + def value(self) -> AnalysisConfigurationIdentifier: + return self.dropdown.value + + def _repr_html_(self): + display(self.box) + + +def _get_analysis_configuration_selector_widget( + self: ext_point.IQSJupyterTools, + initial_config: Union[AnalysisConfigurationIdentifier, str] = None, + auto_display=True): + return ConfigSelector(self._iqs, initial_config, auto_display) + + +def _register_or_update_configuration(self: AnalysisApi, path: str): + with open(path, "r") as file: + conf = json.load(file) + on_server = self.list_model_analysis_configurations().model_analysis_configuration_identifiers + for old in on_server: + if old.configuration_name == conf["name"]: + self.delete_model_analysis_configuration(analysis_configuration_identifier=old) + self.register_model_analysis_configuration(analysis_configuration=conf) + def _do_monkey_patching(): iqs_client.AnalysisResults._repr_html_ = _monkey_patch_analysis_results_repr_html iqs_client.AnalysisResult._repr_html_ = _monkey_patch_analysis_result_repr_html + ext_point.IQSJupyterTools.analysis_configuration_selector_widget = _get_analysis_configuration_selector_widget + AnalysisApi.register_or_update_model_analysis_configuration = _register_or_update_configuration + _do_monkey_patching() diff --git a/source/incqueryserver-jupyter/iqs_jupyter/config_defaults.py b/source/incqueryserver-jupyter/iqs_jupyter/config_defaults.py index 9c15c3e..9912bfb 100644 --- a/source/incqueryserver-jupyter/iqs_jupyter/config_defaults.py +++ b/source/incqueryserver-jupyter/iqs_jupyter/config_defaults.py @@ -24,6 +24,9 @@ default_use_oidc: bool = False default_use_password: bool = True +default_analysis_config = None +default_model_viewer_address = None + default_IQS_address : str = None default_IQS_username : str = None default_IQS_password : str = None