def print_widget_inputs(self, widget_name, tag="release"): """ Prints a list of expected user inputs for a widget. These are printed as the following: variable name - variable type - <extra information> for example: id - string - is a workspace object where the type is one of [KBaseGenomes.Genome, KBaseGenome.GenomeSet] or object_name - string - must be one of ["x", "y", "z"] Parameters ---------- widget_name : string The name of the widget to print the inputs for. tag : string, default="release" The version tag to use when looking up widget information. """ check_tag(tag, raise_exception=True) if widget_name not in self.widget_info[tag]: raise ValueError("Widget %s not found!" % widget_name) params = self.widget_info[tag][widget_name]["params"] print(widget_name) for p in params: if not params[p].get("is_constant", False): p_def = "%s - %s" % (p, params[p]["param_type"]) if "allowed_types" in params[p]: p_def = p_def + " - is a workspace object where the type is one of: %s" % (json.dumps(params[p]["allowed_types"])) if "allowed_values" in params[p]: p_def = p_def + " - must be one of: %s" % (json.dumps(params[p]["allowed_values"])) print(p_def)
def available_apps(self, tag="release"): """ Lists the set of available apps in a pretty HTML way. Usable only in the Jupyter notebook. """ check_tag(tag, raise_exception=True) tmpl=""" <b>Available {{tag}} apps</b><br> <table class="table table-striped table-bordered table-condensed"> <thead> <tr> <th>Id</th> <th>Name</th> <th>Subtitle</th> </tr> </thead> {% for m in apps %} <tr> <td> {{ m.info.id }} </td> <td> {{ m.info.name }} </td> <td> {{ m.info.subtitle }} </td> </tr> {% endfor %} </table> """ return HTML(Template(tmpl).render(tag=tag, apps=sorted(list(self.app_specs[tag].values()), key=lambda m: m['info']['id'])))
def get_widget_constants(self, widget_name, tag="release"): """ Returns a Dict with constants required for each widget. These constants are either part of the widget spec itself, or are provided by the current Narrative environment (e.g. Workspace name, user name). Parameters ---------- widget_name : string The name of the widget to print the constants for. tag : string, default="release" The version tag to use when looking up widget information. """ check_tag(tag, raise_exception=True) if widget_name not in self.widget_info[tag]: raise ValueError("Widget %s not found!" % widget_name) params = self.widget_info[tag][widget_name]["params"] constants = dict() for p in params: if params[p]["is_constant"]: if "param_value" in params[p]: constants[p] = params[p]["param_value"] elif "allowed_values" in params[p] and len(params[p]["allowed_values"]) == 1: constants[p] = params[p]["allowed_values"][0] return constants
def show_output_widget(self, widget_name, params, tag="release", title="", type="method", cell_id=None, check_widget=True, **kwargs): """ Renders a widget using the generic kbaseNarrativeOutputWidget container. Parameters ---------- widget_name : string The name of the widget to print the widgets for. params : dict The dictionary of parameters that gets fed into the widget. tag : string, default="release" The version tag to use when looking up widget information. type : string, default="method" The type of output widget to show (options = method,app,viewer) check_widget: boolean, default=True If True, checks for the presense of the widget_name and get its known constants from the various app specs that invoke it. Raises a ValueError if the widget isn't found. If False, skip that step. **kwargs: These vary, based on the widget. Look up required variable names with WidgetManager.print_widget_inputs() """ input_data = dict() if check_widget: check_tag(tag, raise_exception=True) if widget_name not in self.widget_info[tag]: raise ValueError("Widget %s not found with %s tag!" % (widget_name, tag)) input_data = self.get_widget_constants(widget_name, tag) # Let the kwargs override constants input_data.update(params) input_template = """ element.html("<div id='{{input_id}}' class='kb-vis-area'></div>"); require(['kbaseNarrativeOutputCell'], function(KBaseNarrativeOutputCell) { var w = new KBaseNarrativeOutputCell($('#{{input_id}}'), { "data": {{input_data}}, "type":"{{output_type}}", "widget":"{{widget_name}}", "cellId":"{{cell_id}}", "title":"{{cell_title}}", "time":{{timestamp}} }); }); """ js = Template(input_template).render(input_id=self._cell_id_prefix + str(uuid.uuid4()), output_type=type, widget_name=widget_name, input_data=json.dumps(input_data), cell_title=title, cell_id=cell_id, timestamp=int(round(time.time()*1000))) return Javascript(data=js, lib=None, css=None)
def check_app(self, app_id, tag='release', raise_exception=False): """ Checks if a method (and release tag) is available for running and such. If raise_exception==True, and either the tag or app_id are invalid, a ValueError is raised. If raise_exception==False, and there's something invalid, it just returns False. If everything is hunky-dory, it returns True. """ tag_ok = check_tag(tag, raise_exception=raise_exception) if not tag_ok: return False if app_id not in self.app_specs[tag]: if raise_exception: raise ValueError('Unknown app id "{}" tagged as "{}"'.format(app_id, tag)) return False return True
def test_check_tag_bad_except(self): with self.assertRaises(ValueError): check_tag(self.bad_tag, raise_exception=True)
def test_check_tag_bad(self): self.assertFalse(check_tag(self.bad_tag))
def test_check_tag_good(self): self.assertTrue(check_tag(self.good_tag))
def load_widget_info(self, tag="release", verbose=False): """ Loads widget info and mapping. Eventually will fetch from kbase-ui, a kbase CDN, or the catalog service. For now, it gets known vis widgets from all method specs. This returns the a Dict where all keys are the name of a widget, and all values contain widget information in this structure: { "params": { "param_name": { "is_constant": boolean, "param_type": one of (string|boolean|dropdown), "allowed_values": list of strings (exists when param_type==dropdown), "allowed_types": list of data types (when param_type==string), "param_value": something, mainly when is_constant==True } } """ check_tag(tag, raise_exception=True) methods = self._sm.app_specs[tag].values() all_widgets = dict() # keys = widget names / namespaced require path / etc. # Individual widget values should be: # {params: { # name1: { # is_constant: boolean, # value: (***something*** | None) (something = any structure), # allowed: [ list of allowed values, optional ], # type: (string, int, float, boolean, etc. list? hash?) # allowed_types: [ list of allowed ws types, optional ] # }, # name2: { is_constant, value } # } for method in methods: if 'output' not in method['widgets']: widget_name = self._default_output_widget else: widget_name = method['widgets']['output'] if widget_name == 'null': if verbose: print("Ignoring a widget named 'null' in {} - {}".format(tag, method['info']['id'])) continue out_mapping = method['behavior'].get('kb_service_output_mapping', method['behavior'].get('output_mapping', None)) if out_mapping is not None: params = {} for p in out_mapping: param_name = p['target_property'] allowed_values = set() is_constant = False param_value = None param_type = 'string' allowed_types = set() if 'constant_value' in p: # add this val to a set of constant values for that param in that widget. # if more than one possible, this need to be optional is_constant = True allowed_values.add(p['constant_value']) if 'input_parameter' in p: # this is a user given input. look up what it expects from the # associated parameter of that name in_param = p['input_parameter'] for spec_param in method['parameters']: if spec_param['id'] == in_param: # want its: # field_type = text, float, number, ... in_type = spec_param['field_type'] if in_type == 'text': param_type = 'string' if spec_param.has_key('text_options'): validate_as = spec_param['text_options'].get('validate_as', None) if validate_as == 'int': param_type = 'int' elif validate_as == 'float': param_type = 'float' if spec_param['text_options'].has_key('valid_ws_types'): allowed_types.update(spec_param['text_options']['valid_ws_types']) elif param_type == 'textarea': param_type = 'string' elif param_type == 'checkbox': param_type = 'boolean' elif param_type == 'dropdown': param_type = 'dropdown' allowed_values.update([o['value'] for o in spec_param['dropdown_options']]) if 'narrative_system_variable' in p: # this is something like the ws name or token that needs to get fetched # by the system. Shouldn't be handled by the user. is_constant = True param_value = system_variable(p['narrative_system_variable']) if 'service_method_output_path' in p: param_type = 'from_service_output' param_info = { 'is_constant': is_constant, 'param_type': param_type, } if allowed_values: param_info['allowed_values'] = allowed_values if allowed_types: param_info['allowed_types'] = allowed_types if param_value: param_info['param_value'] = param_value params[param_name] = param_info if widget_name in all_widgets: # if it's already there, just update the allowed_types and allowed_values for some params that have them for p_name in params.keys(): if 'allowed_types' in params[p_name]: if p_name not in all_widgets[widget_name]['params']: all_widgets[widget_name]['params'][p_name] = params[p_name] else: widget_types = all_widgets[widget_name]['params'].get(p_name, {}).get('allowed_types', set()) widget_types.update(params[p_name]['allowed_types']) all_widgets[widget_name]['params'][p_name]['allowed_types'] = widget_types if 'allowed_values' in params[p_name]: if p_name not in all_widgets[widget_name]['params']: all_widgets[widget_name]['params'][p_name] = params[p_name] else: widget_vals = all_widgets[widget_name]['params'].get(p_name, {}).get('allowed_values', set()) widget_vals.update(params[p_name]['allowed_values']) all_widgets[widget_name]['params'][p_name]['allowed_values'] = widget_vals else: all_widgets[widget_name] = { 'params': params } # finally, turn all sets into lists for w in all_widgets: for p in all_widgets[w]["params"]: if "allowed_types" in all_widgets[w]["params"][p]: all_widgets[w]["params"][p]["allowed_types"] = list(all_widgets[w]["params"][p]["allowed_types"]) if "allowed_values" in all_widgets[w]['params'][p]: all_widgets[w]["params"][p]["allowed_values"] = list(all_widgets[w]["params"][p]["allowed_values"]) return all_widgets
def load_widget_info(self, tag="release", verbose=False): """ Loads widget info and mapping. Eventually will fetch from kbase-ui, a kbase CDN, or the catalog service. For now, it gets known vis widgets from all method specs. This returns the a Dict where all keys are the name of a widget, and all values contain widget information in this structure: { "params": { "param_name": { "is_constant": boolean, "param_type": one of (string|boolean|dropdown), "allowed_values": list of strings (exists when param_type==dropdown), "allowed_types": list of data types (when param_type==string), "param_value": something, mainly when is_constant==True } } """ check_tag(tag, raise_exception=True) methods = list(self._sm.app_specs[tag].values()) all_widgets = dict() """ keys = widget names / namespaced require path / etc. Individual widget values should be: {params: { name1: { is_constant: boolean, value: (***something*** | None) (something = any structure), allowed: [ list of allowed values, optional ], type: (string, int, float, boolean, etc. list? hash?) allowed_types: [ list of allowed ws types, optional ] }, name2: { is_constant, value } } """ for method in methods: if "output" not in method["widgets"]: widget_name = self._default_output_widget else: widget_name = method["widgets"]["output"] if widget_name == "null": if verbose: print( f"Ignoring a widget named 'null' in {tag} - {method['info']['id']}" ) continue out_mapping = method["behavior"].get( "kb_service_output_mapping", method["behavior"].get("output_mapping", None), ) if out_mapping is not None: params = {} for p in out_mapping: param_name = p["target_property"] allowed_values = set() is_constant = False param_value = None param_type = "string" allowed_types = set() if "constant_value" in p: # add this val to a set of constant values for that param in that widget. # if more than one possible, this need to be optional is_constant = True allowed_values.add(p["constant_value"]) if "input_parameter" in p: # this is a user given input. look up what it expects from the # associated parameter of that name in_param = p["input_parameter"] for spec_param in method["parameters"]: if spec_param["id"] == in_param: # want its: # field_type = text, float, number, ... in_type = spec_param["field_type"] if in_type == "text": param_type = "string" if "text_options" in spec_param: validate_as = spec_param[ "text_options"].get( "validate_as", None) if validate_as == "int": param_type = "int" elif validate_as == "float": param_type = "float" if ("valid_ws_types" in spec_param["text_options"]): allowed_types.update( spec_param["text_options"] ["valid_ws_types"]) elif param_type == "textarea": param_type = "string" elif param_type == "checkbox": param_type = "boolean" elif param_type == "dropdown": param_type = "dropdown" allowed_values.update([ o["value"] for o in spec_param["dropdown_options"] ]) if "narrative_system_variable" in p: # this is something like the ws name or token that needs to get fetched # by the system. Shouldn't be handled by the user. is_constant = True param_value = system_variable( p["narrative_system_variable"]) if "service_method_output_path" in p: param_type = "from_service_output" param_info = { "is_constant": is_constant, "param_type": param_type, } if allowed_values: param_info["allowed_values"] = allowed_values if allowed_types: param_info["allowed_types"] = allowed_types if param_value: param_info["param_value"] = param_value params[param_name] = param_info if widget_name in all_widgets: # if it's already there, just update the allowed_types and allowed_values # for some params that have them for p_name in params.keys(): if "allowed_types" in params[p_name]: if p_name not in all_widgets[widget_name][ "params"]: all_widgets[widget_name]["params"][ p_name] = params[p_name] else: widget_types = ( all_widgets[widget_name]["params"].get( p_name, {}).get("allowed_types", set())) widget_types.update( params[p_name]["allowed_types"]) all_widgets[widget_name]["params"][p_name][ "allowed_types"] = widget_types if "allowed_values" in params[p_name]: if p_name not in all_widgets[widget_name][ "params"]: all_widgets[widget_name]["params"][ p_name] = params[p_name] else: widget_vals = ( all_widgets[widget_name]["params"].get( p_name, {}).get("allowed_values", set())) widget_vals.update( params[p_name]["allowed_values"]) all_widgets[widget_name]["params"][p_name][ "allowed_values"] = widget_vals else: all_widgets[widget_name] = {"params": params} # finally, turn all sets into lists for w in all_widgets: for p in all_widgets[w]["params"]: if "allowed_types" in all_widgets[w]["params"][p]: all_widgets[w]["params"][p]["allowed_types"] = list( all_widgets[w]["params"][p]["allowed_types"]) if "allowed_values" in all_widgets[w]["params"][p]: all_widgets[w]["params"][p]["allowed_values"] = list( all_widgets[w]["params"][p]["allowed_values"]) return all_widgets