From a42d19dba52a1271f8bdee1ea764b96cfe94640e Mon Sep 17 00:00:00 2001 From: rarescodemart Date: Fri, 25 Nov 2022 14:32:46 +0200 Subject: [PATCH] WID-100: add functionality to display the description of the component arguments --- .gitignore | 5 +- src/components/CustomNodeModel.ts | 8 ++- src/components/CustomNodeWidget.tsx | 47 +++++++++++++++-- src/components/port/CustomPortLabel.tsx | 14 ++++- src/components/port/CustomPortModel.ts | 11 ++++ src/components/port/WithToggle.tsx | 52 +++++++++++++++++++ src/components/port/types.ts | 13 +++++ src/tray_library/AdvanceComponentLib.tsx | 20 ++++--- style/base.css | 13 +++++ .../kerastransferlearningmodel.json | 16 ++++++ xircuits/handlers/components.py | 15 ++++-- 11 files changed, 195 insertions(+), 19 deletions(-) create mode 100644 src/components/port/WithToggle.tsx create mode 100644 src/components/port/types.ts create mode 100644 xai_components/xai_learning/description/kerastransferlearningmodel.json diff --git a/.gitignore b/.gitignore index 805b0864..19535677 100644 --- a/.gitignore +++ b/.gitignore @@ -136,4 +136,7 @@ labextension/ mlruns/ # Pytorch dataset default dir -data \ No newline at end of file +data + +# pycharm files +/.idea/ \ No newline at end of file diff --git a/src/components/CustomNodeModel.ts b/src/components/CustomNodeModel.ts index 6d9b9281..b4a05ef0 100644 --- a/src/components/CustomNodeModel.ts +++ b/src/components/CustomNodeModel.ts @@ -41,11 +41,13 @@ export class CustomNodeModel extends DefaultNodeModel { this.extras=event.data.extras; } - addOutPortEnhance(label: string, name: string, after: boolean = true, id?: string): CustomPortModel { + addOutPortEnhance(label: string, name: string, after: boolean = true, id?: string, description: string = ""): CustomPortModel { //check if portID is passed, if not SR will generate a new port ID const p = (id) ? new CustomPortModel({in: false, name: name, label: label, id:id}) : new CustomPortModel({in: false, name: name, label: label}); + + p.description = description; if (!after) { this.portsOut.splice(0, 0, p); @@ -54,12 +56,14 @@ export class CustomNodeModel extends DefaultNodeModel { return this.addPort(p); } - addInPortEnhance(label: string, name: string, after: boolean = true, id?: string): CustomPortModel { + addInPortEnhance(label: string, name: string, after: boolean = true, id?: string, description: string = ""): CustomPortModel { //check if portID is passed, if not SR will generate a new port ID const p = (id) ? new CustomPortModel({in: true, name: name, label: label, id:id}) : new CustomPortModel({in: true, name: name, label: label}); + p.description = description; + if (!after) { this.portsOut.splice(0, 0, p); } diff --git a/src/components/CustomNodeWidget.tsx b/src/components/CustomNodeWidget.tsx index 4f34919e..db60b406 100644 --- a/src/components/CustomNodeWidget.tsx +++ b/src/components/CustomNodeWidget.tsx @@ -102,9 +102,7 @@ export interface DefaultNodeProps { */ export class CustomNodeWidget extends React.Component { - generatePort = (port) => { - return ; - }; + element:Object; state = { @@ -127,7 +125,46 @@ export class CustomNodeWidget extends React.Component { original: 'https://picsum.photos/id/1019/1000/600/', thumbnail: 'https://picsum.photos/id/1019/250/150/' }, - ] + ], + showParamDescriptionList: new Array(this.props.node.getInPorts().length + this.props.node.getOutPorts().length).fill(false) + }; + + /** + * creates a particular function for each component so that it can set only it's state + * @param id + */ + setShowParamDescription = (id : number) => { + const _setShowParamDescription = (newShowDescription : boolean) => { + this.setState({ + showParamDescriptionList: this.state.showParamDescriptionList.map((value, index) => ( + id === index ? newShowDescription : false + ) + ) + }) + } + return _setShowParamDescription; + } + + generatePort = (port, index) => { + const argumentDescriptions = this.props.node['extras']['argumentDescriptions']; + + const description = argumentDescriptions && (port.getOptions().label in argumentDescriptions) ? argumentDescriptions[port.getOptions().label] : ""; + + const isOutPort = port.getOptions().name.includes('parameter-out'); + + index = isOutPort ? index + this.props.node.getInPorts().length: index; + + return ( + + ); }; showTooltip() { @@ -295,7 +332,7 @@ export class CustomNodeWidget extends React.Component { /> - + {/*aici se pun intrarile*/} {_.map(this.props.node.getInPorts(), this.generatePort)} {_.map(this.props.node.getOutPorts(), this.generatePort)} diff --git a/src/components/port/CustomPortLabel.tsx b/src/components/port/CustomPortLabel.tsx index 80316413..00e903cf 100644 --- a/src/components/port/CustomPortLabel.tsx +++ b/src/components/port/CustomPortLabel.tsx @@ -2,11 +2,16 @@ import * as React from 'react'; import { DiagramEngine, PortWidget } from '@projectstorm/react-diagrams-core'; import { DefaultNodeModel, DefaultPortModel } from "@projectstorm/react-diagrams"; import styled from '@emotion/styled'; +import WithToggle from "./WithToggle"; + export interface CustomPortLabelProps { port: DefaultPortModel; engine: DiagramEngine; node: DefaultNodeModel; + showDescription: boolean; + setShowDescription: any; + description : string; } namespace S { @@ -122,7 +127,14 @@ export class CustomPortLabel extends React.Component { const label = ( - {this.props.port.getOptions().label} + + {this.props.port.getOptions().label} + ); return ( diff --git a/src/components/port/CustomPortModel.ts b/src/components/port/CustomPortModel.ts index f4113bd4..127f6c1a 100644 --- a/src/components/port/CustomPortModel.ts +++ b/src/components/port/CustomPortModel.ts @@ -8,6 +8,17 @@ import {PortModel} from "@projectstorm/react-diagrams-core"; */ export class CustomPortModel extends DefaultPortModel { + private _description : string; + + get description(): string { + return this._description; + } + + set description(value: string) { + this._description = value; + } + + canLinkToPort(port: PortModel): boolean { if (port instanceof DefaultPortModel) { diff --git a/src/components/port/WithToggle.tsx b/src/components/port/WithToggle.tsx new file mode 100644 index 00000000..db3f0ba8 --- /dev/null +++ b/src/components/port/WithToggle.tsx @@ -0,0 +1,52 @@ +import * as React from "react"; +import Toggle from 'react-toggle' +import {useRef} from "react"; +import {WithToggleProps} from "./types"; +import ToolTip from 'react-portal-tooltip'; + + +export default function WithToggle(props: WithToggleProps){ + const ref = useRef(null); + + return ( +
+ {props.renderToggleBeforeChildren ? + <> + { + props.description && + props.setShowDescription(!props.showDescription)} + /> + } + + {props.children} + + + : + <> + + {props.children} + + { + props.description && + props.setShowDescription(!props.showDescription)} + /> + } + + } + + {props.showDescription && + +
{props.description}
+
+ } +
+ ) +} \ No newline at end of file diff --git a/src/components/port/types.ts b/src/components/port/types.ts new file mode 100644 index 00000000..b4d9199f --- /dev/null +++ b/src/components/port/types.ts @@ -0,0 +1,13 @@ +import {ReactElement} from "react"; + +export type WithToggleProps = { + renderToggleBeforeChildren : boolean; + children: ReactElement[] | ReactElement | string; + showDescription: boolean; + setShowDescription: any; + description: string; +} + +export type WithToggleState = { + showDescription : boolean +} \ No newline at end of file diff --git a/src/tray_library/AdvanceComponentLib.tsx b/src/tray_library/AdvanceComponentLib.tsx index 3b388116..c0c03e37 100644 --- a/src/tray_library/AdvanceComponentLib.tsx +++ b/src/tray_library/AdvanceComponentLib.tsx @@ -31,7 +31,8 @@ export function AdvancedComponentLibrary(props: AdvancedComponentLibraryProps) { extras: { "type": nodeData.type, "path": nodeData.file_path, - "description": nodeData.docstring, + "description": nodeData["json_description"]["description"] || nodeData.docstring, + "argumentDescriptions" : nodeData["json_description"]["arguments"], "lineNo": nodeData.lineno } }); @@ -44,19 +45,24 @@ export function AdvancedComponentLibrary(props: AdvancedComponentLibraryProps) { "str": "string" } - nodeData["variables"].forEach(variable => { - let name = variable["name"]; - let type = type_name_remappings[variable["type"]] || variable["type"]; + const argumentDescriptions = nodeData["json_description"]["arguments"]; + + + nodeData["variables"].forEach((variable, _) => { + const name = variable["name"]; + const type = type_name_remappings[variable["type"]] || variable["type"]; + + const description = argumentDescriptions ? argumentDescriptions[name] || "" : ""; switch (variable["kind"]) { case "InCompArg": - node.addInPortEnhance(`★${name}`, `parameter-${type}-${name}`); + node.addInPortEnhance(`★${name}`, `parameter-${type}-${name}`, true, null, description); break; case "InArg": - node.addInPortEnhance(name, `parameter-${type}-${name}`); + node.addInPortEnhance(name, `parameter-${type}-${name}`, true, null, description); break; case "OutArg": - node.addOutPortEnhance(name, `parameter-out-${type}-${name}`); + node.addOutPortEnhance(name, `parameter-out-${type}-${name}`, true, null, description); break; default: console.warn("Unknown variable kind for variable", variable) diff --git a/style/base.css b/style/base.css index 94b273d4..1e07c30f 100644 --- a/style/base.css +++ b/style/base.css @@ -54,4 +54,17 @@ input[type=number]::-webkit-inner-spin-button { /* Fix arrow position of HTMLSelect tag on run dialog */ .jp-Dialog-content .f1ozlkqi { position: relative; +} + +.alignToggle { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; +} + +.alignToggle span, div { + display: inline-block; + padding: 0; + margin: 0; } \ No newline at end of file diff --git a/xai_components/xai_learning/description/kerastransferlearningmodel.json b/xai_components/xai_learning/description/kerastransferlearningmodel.json new file mode 100644 index 00000000..7b638695 --- /dev/null +++ b/xai_components/xai_learning/description/kerastransferlearningmodel.json @@ -0,0 +1,16 @@ +{ + "description" : "keras_transfer_learning description", + "arguments" : { + "base_model_name" : "base_model_name description", + "include_top" : "include_top description", + "weights" : "weights description", + "input_shape" : "input_shape description", + "freeze_all" : "freeze_all description", + "fine_tune_from" : "fine_tune_from description", + "classes" : "classes description", + "binary" : "binary description", + "classifier_activation" : "classifier_activation description", + "kwargs" : "kwargs description", + "model" : "model description" + } +} \ No newline at end of file diff --git a/xircuits/handlers/components.py b/xircuits/handlers/components.py index 7d910870..a67735e4 100644 --- a/xircuits/handlers/components.py +++ b/xircuits/handlers/components.py @@ -142,7 +142,7 @@ def get(self): "error_msg" : error_msg} self.finish(json.dumps(data)) - + def get_component_directories(self): paths = list(DEFAULT_COMPONENTS_PATHS) paths.append(get_config().get("DEV", "BASE_PATH")) @@ -151,7 +151,7 @@ def get_component_directories(self): def extract_components(self, file_path, base_dir, python_path): with open(file_path) as f: lines = f.readlines() - + parse_tree = ast.parse(file_path.read_text(), file_path) # Look for top level class definitions that are decorated with "@xai_component" is_xai_component = lambda node: isinstance(node, ast.ClassDef) and \ @@ -175,7 +175,7 @@ def extract_component(self, node: ast.ClassDef, file_path, file_lines, python_pa is_arg = lambda n: isinstance(n, ast.AnnAssign) and \ isinstance(n.annotation, ast.Subscript) and \ n.annotation.value.id in ('InArg', 'InCompArg', 'OutArg') - + python_version = platform.python_version_tuple() if int(python_version[1]) == 8: variables = [ @@ -205,6 +205,14 @@ def extract_component(self, node: ast.ClassDef, file_path, file_lines, python_pa } ] + + description = {} + path = os.path.join("xai_components", os.path.dirname(file_path), "description", str(node.name).lower() + ".json") + if os.path.isfile(path): + with open(path) as file: + description = json.load(file) + + output = { "class": name, "package_name": ("xai_components." if python_path is None else "") + file_path.as_posix().replace("/", ".")[:-3], @@ -216,6 +224,7 @@ def extract_component(self, node: ast.ClassDef, file_path, file_lines, python_pa "category": category, "type": "debug", "variables": variables, + "json_description": description, "docstring": docstring, "lineno" : lineno }