From 8231426229ac34d661913dd4b5f6cae4e46c1e00 Mon Sep 17 00:00:00 2001 From: Spurs20 <22457841+Spurs20@users.noreply.github.com> Date: Wed, 21 Aug 2024 10:28:05 -0600 Subject: [PATCH] Hdf5 browser (#1175) * Removing log statement * Creating prototype hdf5 browser for parameter space wizard * Update HDF5 browser to query user-entered path * Formatting user-entered path for web service call, and changing mime-type based on HDF5 group/dataset * Fixing double clicking and path appending bug in HDF5 browser * Formatting fix * Fixing bug to browse up a directory on button click * Removing unnecessary log * Removing "save project data" checkbox from HDF5 browser tab * Removing blank space at bottom of HDF5 browser tab * Adding visible 'HDF5 Browser' tab identifier when HDF5 filetype is selected * Removing unnecessary filetype selector from HDF5 browser * Creating new html template for hdf5 browser and removing unnecessary hostname labels * Adding HDF5 browser for input/output, and storing table paths in model Adding one HDF5 browser for input table, one HDF5 browser for output table, and storing the paths to those two tables in the couch model doc. These paths will then be used to retrieve those input/output tables, and combine them to make the model. * Fixing HDF5 pathing in react HDF5 browser * Creating web service that combines input and output tables for model creation * Enabling continue button coloring in HDF5 table selection * Adding model naming tab for hdf5 model creation * Fixing highlighting in hdf5 table selection and indexing error in input/output column header * Adding separate tab titles for input/output selection * Addressing PR comments --- packages/slycat/web/server/engine.py | 3 + packages/slycat/web/server/handlers.py | 137 +++++- packages/slycat/web/server/upload.py | 4 + web-server/components/HDF5Browser.tsx | 399 ++++++++++++++++++ web-server/js/slycat-web-client.js | 46 +- web-server/plugins/slycat-hdf5-parser.py | 16 +- .../slycat-parameter-image/js/wizard-ui.js | 196 +++++++-- .../slycat-parameter-image/wizard-ui.html | 26 +- web-server/templates/slycat-hdf5-browser.html | 64 +++ 9 files changed, 839 insertions(+), 52 deletions(-) create mode 100644 web-server/components/HDF5Browser.tsx create mode 100644 web-server/templates/slycat-hdf5-browser.html diff --git a/packages/slycat/web/server/engine.py b/packages/slycat/web/server/engine.py index 7fd48157a..3c6e9c096 100644 --- a/packages/slycat/web/server/engine.py +++ b/packages/slycat/web/server/engine.py @@ -158,6 +158,9 @@ def abspath(path): dispatcher.connect("post-remotes", "/remotes", slycat.web.server.handlers.post_remotes, conditions={"method" : ["POST"]}) dispatcher.connect("post-remotes-smb", "/remotes/smb", slycat.web.server.handlers.post_remotes_smb, conditions={"method" : ["POST"]}) dispatcher.connect("post-smb-browse", "/smb/remotes/:hostname/browse{path:.*}", slycat.web.server.handlers.post_smb_browse, conditions={"method" : ["POST"]}) + dispatcher.connect("post-hdf5-browse", "/hdf5/browse/:path/:pid/:mid", slycat.web.server.handlers.post_browse_hdf5, conditions={"method" : ["POST"]}) + dispatcher.connect("post-hdf5-table", "/hdf5/table/:path/:pid/:mid", slycat.web.server.handlers.post_hdf5_table, conditions={"method" : ["POST"]}) + dispatcher.connect("post-combine-hdf5-tables", "/hdf5/combine/:mid", slycat.web.server.handlers.post_combine_hdf5_tables, conditions={"method": ["POST"]}) dispatcher.connect("put-model-arrayset-array", "/models/:mid/arraysets/:aid/arrays/:array", slycat.web.server.handlers.put_model_arrayset_array, conditions={"method" : ["PUT"]}) dispatcher.connect("put-model-arrayset-data", "/models/:mid/arraysets/:aid/data", slycat.web.server.handlers.put_model_arrayset_data, conditions={"method" : ["PUT"]}) dispatcher.connect("put-model-arrayset", "/models/:mid/arraysets/:aid", slycat.web.server.handlers.put_model_arrayset, conditions={"method" : ["PUT"]}) diff --git a/packages/slycat/web/server/handlers.py b/packages/slycat/web/server/handlers.py index 71022e37a..f44e8e7f4 100644 --- a/packages/slycat/web/server/handlers.py +++ b/packages/slycat/web/server/handlers.py @@ -2702,7 +2702,6 @@ def post_remotes(): msg = "login could not save session for remote host" return {"sid": sid, "status": True, "msg": msg} - @cherrypy.tools.json_in(on=True) @cherrypy.tools.json_out(on=True) def post_remotes_smb(): @@ -2759,6 +2758,142 @@ def post_smb_browse(hostname, path): with slycat.web.server.smb.get_session(sid) as session: return session.browse(path=path) +def post_hdf5_table(path, pid, mid): + """ + Takes a user selected path inside an HDF5 file, and stores the + table at that path as either inputs or outputs for the model. + + path {string} -- path to table inside of HDF5 file + """ + # Need to find the HDF5 stored on Slycat server, so we can query it for the path. + path = path.replace('-', '/') + database = slycat.web.server.database.couchdb.connect() + model = database.get("model", mid) + project = database.get("project", pid) + did = model['project_data'][0] + project_data = database.get("project_data", did) + file_name = project_data['hdf5_name'] + hdf5_path = cherrypy.request.app.config["slycat-web-server"]["data-store"] + "/" + file_name + h5 = h5py.File(hdf5_path, 'r') + table = list(h5[path]) + column_headers = list(h5[path].dims[1][0]) + headers = [] + attributes = [] + dimensions = [{"name": "row", "type": "int64", "begin": 0, "end": len(table[0])}] + + if 'hdf5-inputs' not in model: + model['hdf5-inputs'] = path + else: + model['hdf5-outputs'] = path + slycat.web.server.authentication.require_project_writer(project) + database.save(model) + + cherrypy.response.status = "200 Project updated." + +def post_combine_hdf5_tables(mid): + """ + Combines user selected input/output tables inside of an HDF5 file into one table. + Uses the hdf5-input and hdf5-output paths in the model to find the tables in the HDF5 file. + """ + + database = slycat.web.server.database.couchdb.connect() + model = database.get("model", mid) + project = model['project'] + slycat.web.server.authentication.require_project_writer(project) + input_path = '/' + model['hdf5-inputs'] + output_path = '/' + model['hdf5-outputs'] + did = model['project_data'][0] + project_data = database.get("project_data", did) + file_name = project_data['hdf5_name'] + hdf5_path = cherrypy.request.app.config["slycat-web-server"]["data-store"] + "/" + file_name + h5 = h5py.File(hdf5_path, 'r') + + # Getting indices for input/output columns + unformatted_input = list(h5[input_path]) + input_column_headers_indices = [i for i in range(0, len(unformatted_input[0]))] + unformatted_output = list(h5[output_path]) + output_column_headers_indices = [i for i in range(len(unformatted_input[0]), (len(unformatted_input[0]) + len(unformatted_output[0])))] + + slycat.web.server.put_model_parameter(database, model, 'input-columns', input_column_headers_indices, True) + slycat.web.server.put_model_parameter(database, model, 'output-columns', output_column_headers_indices, True) + + combined_dataset = [] + output_headers = [] + input_headers = [] + attributes = [] + + column_headers_input = list(h5[input_path].dims[1][0]) + column_headers_output = list(h5[output_path].dims[1][0]) + + # Once we have column headers, this is how we can get/store them. + for i, column in enumerate(column_headers_input): + input_headers.append(str(column.decode('utf-8'))) + attributes.append({"name": str(column.decode('utf-8')), "type": str(type(unformatted_input[0][i])).split('numpy.')[1].split("'>")[0]}) + for j, column in enumerate(column_headers_output): + output_headers.append(str(column.decode('utf-8'))) + attributes.append({"name": str(column.decode('utf-8')), "type": str(type(unformatted_output[0][j])).split('numpy.')[1].split("'>")[0]}) + + combined_headers = numpy.concatenate((input_headers, output_headers), axis=0) + combined_headers = combined_headers.tolist() + combined_data = numpy.concatenate((unformatted_input, unformatted_output), axis=1) + combined_data = numpy.transpose(combined_data) + + combined_data = combined_data.tolist() + + for row in combined_data: + combined_dataset.append(numpy.asarray(row)) + + dimensions = [{"name": "row", "type": "int64", "begin": 0, "end": len(combined_dataset[0])}] + + array_index = 0 + slycat.web.server.put_model_arrayset(database, model, 'data-table', input) + slycat.web.server.put_model_array(database, model, 'data-table', 0, attributes, dimensions) + slycat.web.server.put_model_arrayset_data(database, model, 'data-table', "%s/.../..." % array_index, combined_data) + + cherrypy.response.status = "200 Project updated." + +def post_browse_hdf5(path, pid, mid): + """ + Given a path inside of an HDF5 file, builds out the current tree at that path. + Formats the tree structure to conform with remote/hdf5 browser requirements. + """ + def allkeys_single_level(obj, tree_structure): + path = obj.name # This is current top level path + # Need to include all these fields because we are repurposing the remote file browser, which expects all these + tree_structure['path'] = path + tree_structure['name'] = [] + tree_structure['sizes'] = [] + tree_structure['types'] = [] + tree_structure['mtimes'] = [] + tree_structure['mime-types'] = [] + all_items = obj.items() + for key, value in all_items: + # key will be all the sub groups and datasets in the current path + tree_structure['name'].append(key) + tree_structure['sizes'].append(0) + tree_structure['mtimes'].append('2024') + if isinstance(value, h5py.Group): + tree_structure['mime-types'].append('application/x-directory') + tree_structure['types'].append('d') + else: + tree_structure['mime-types'].append('file') + tree_structure['types'].append('f') + return tree_structure + # Need to find the HDF5 stored on Slycat server, so we can query it for the path. + path = path.replace('-', '/') + database = slycat.web.server.database.couchdb.connect() + model = database.get("model", mid) + did = model['project_data'][0] + project_data = database.get("project_data", did) + file_name = project_data['hdf5_name'] + hdf5_path = cherrypy.request.app.config["slycat-web-server"]["data-store"] + "/" + file_name + h5 = h5py.File(hdf5_path, 'r') + tree_structure = {} + current_level = allkeys_single_level(h5[path], tree_structure) + json_payload = json.dumps(current_level) + + return json_payload + @cherrypy.tools.json_out(on=True) def get_remotes(hostname): """ diff --git a/packages/slycat/web/server/upload.py b/packages/slycat/web/server/upload.py index a6e1feb9e..02345b3e2 100644 --- a/packages/slycat/web/server/upload.py +++ b/packages/slycat/web/server/upload.py @@ -245,6 +245,10 @@ def numeric_order(x): # adding this check for backwards compatibility # new way self._aids[0] is the file name being added to the model and hdf5 # self._aids[1] is the name of the file being pushed to the project_data data object + if len(self._aids) > 1: + if '.h5' in self._aids[1] or '.hdf5' in self._aids[1]: + slycat.web.server.plugin.manager.parsers[self._parser]["parse"](database, model, self._input, + files, self._aids, **self._kwargs) if isinstance(self._aids[0], list): slycat.web.server.plugin.manager.parsers[self._parser]["parse"](database, model, self._input, files, self._aids[0], **self._kwargs) diff --git a/web-server/components/HDF5Browser.tsx b/web-server/components/HDF5Browser.tsx new file mode 100644 index 000000000..3970c94f0 --- /dev/null +++ b/web-server/components/HDF5Browser.tsx @@ -0,0 +1,399 @@ +'use strict'; +import * as React from 'react'; +import client from 'js/slycat-web-client'; +import SlycatSelector, {Option} from 'components/SlycatSelector.tsx'; + +/** + * @member hostname name of the host we are connecting + * (assumes we have a connection already to the host) + * @member persistenceId uuid for local storage + * @member onSelectFileCallBack called every time a file is selected + * returns the files info (path, file.type, file:FileMetaData) + * @member onSelectParserCallBack called every time a parser is selected + * returns the parser type (dakota or csv) + * @member onReauthCallBack called every time we lose connection to the host + * @export + * @interface HDF5BrowserProps + */ +export interface HDF5BrowserProps { + hostname: string + persistenceId?: string + onSelectFileCallBack: Function + onSelectParserCallBack: Function + onReauthCallBack: Function + pid: string + mid: string +} + +/** + * @member path path shown in box + * @member pathInput path to current file when selected + * @member persistenceId uuid add to get local storage say if there were + * two of these classes being used + * @member rawFiles list of the current files meta data we are looking at + * @member pathError do we have a path error + * @member browseError do we have a browsing error + * @member browserUpdating are we in the middle of getting data + * @member selected id of selected file + * @export + * @interface HDF5BrowserState + */ +export interface HDF5BrowserState { + path:string + pathInput:string + persistenceId:string + rawFiles: FileMetaData[] + pathError: boolean + browseError: boolean + browserUpdating: boolean + selected:number, + pid:string, + mid:string +} + +/** + * @member type file type + * @member name filename + * @member size size of file + * @member mtime last accessed time + * @member mimeType type of file + * @interface FileMetaData + */ +interface FileMetaData { + type: string + name: string + size: string + mtime: string + mimeType: string +} + +/** + * used to create a file browsing window like using 'ls' and 'cd' in a linux terminal + * + * @export + * @class RemoteFileBrowser + * @extends {React.Component} + */ +export default class HDF5Browser extends React.Component { + public constructor(props:HDF5BrowserProps) { + super(props) + this.state = { + path:"//", + pathInput: "//", + rawFiles: [], + pathError: false, + browseError: false, + persistenceId: props.persistenceId === undefined ? '' : props.persistenceId, + browserUpdating: false, + selected:-1, + pid: props.pid, + mid: props.mid + } + } + + /** + * given a path return all the items in said path (like ls) + * + * @param pathInput path to return all ls properties from + * @private + * @memberof HDF5Browser + */ + private browse = (pathInput:string) => { + let first_char = Array.from(pathInput)[0]; + if(first_char != '/') { + pathInput = '/' + pathInput; + } + this.setState({pathInput:pathInput}); + pathInput = pathInput.replace(/(?!^)\//g, "-"); + client.post_browse_hdf5( + { + hostname : this.props.hostname, + path : pathInput, + pid: this.props.pid, + mid: this.props.mid, + success : (results:any) => + { + localStorage.setItem("slycat-remote-browser-path-" + this.state.persistenceId + this.props.hostname, pathInput); + this.setState({ + browseError:false, + pathError:false, + }); + let json_results = JSON.parse(results) + let files: FileMetaData[] = [] + if(pathInput != "/") + files.push({type: "", name: "..", size: "", mtime: "", mimeType:"application/x-directory"}); + for(let i = 0; i != json_results['name'].length; ++i) + files.push({name:json_results['name'][i], size:json_results['sizes'][i], type:json_results['types'][i], mtime:json_results['mtimes'][i], mimeType:json_results["mime-types"][i]}); + this.setState({ + rawFiles:files, + browserUpdating:false + }); + }, + error : (results:any) => + { + if(this.state.path != this.state.pathInput) + { + this.setState({pathError:true, browserUpdating:false}); + } + if(results.status == 400){ + alert("bad file path") + } + this.setState({browseError:true, browserUpdating:false}); + } + }); + } + + /** + * takes a path and returns the directory above it + * + * @param path string path + * @private + * @returns new string path one level up + * @memberof RemoteFileBrowser + */ + private pathDirname = (path:string):string => + { + var new_path = path.replace(/\/\.?(\w|\-|\.)*\/?$/, ""); + if(new_path == "") + new_path = "/"; + return new_path; + } + + /** + * takes left path and right path and joins them + * @param right string path + * @param left string path + * @private + * @requires joined paths + * @memberof RemoteFileBrowser + */ + private pathJoin = (left:string, right:string):string => + { + var new_path = left; + if(new_path.slice(-1) != "/") + new_path += "/"; + new_path += right; + return new_path; + } + + private browseUpDirectory = (currentPath:string) => { + let split_path = currentPath.split('/'); + let parent_directory = ''; + for (let i = 1; i < split_path.length - 1; i++) { + parent_directory = parent_directory + '/' + split_path[i]; + } + if (parent_directory == '/' || parent_directory == '') { + parent_directory = '//'; + } + this.browse(parent_directory); + } + + /** + * given a file(which includes its full path), browse to the path above it + * + * @param file meta data for the file selected to browse up + * one level from said path + * @private + * @memberof RemoteFileBrowser + */ + private browseUpByFile = (file:FileMetaData) => { + this.setState({selected:-1}); + // If it's a table, need to parse + if(file.type === "f") { + // callback + } + + // If the file is our parent directory, move up the hierarchy. + if(file.name === "..") + { + this.browse(this.pathDirname(this.state.path)); + } + // If the file is a directory, move down the hierarchy. + else if(file.type === "d") + { + let current_path = this.state.pathInput; + let new_path = ''; + if(current_path == '//') { + current_path = current_path.substring(1); + new_path = current_path + file.name; + } + else { + new_path = current_path + '/' + file.name; + } + let substr = new_path.substring(0, 2); + if(substr == '//') { + new_path = new_path.substring(1); + } + this.browse(new_path); + } + } + + keyPress = (event:any, pathInput:string) => { + if (event.key == 'Enter'){ + this.browse(pathInput); + } + } + + /** + * Given a row id and file info set the selected file and + * callBack to tell caller Path, file.type, file:FileMetaData + * + * @param file an object of FileMetaData + * @param i index of selected row in the table + * @private + * @memberof RemoteFileBrowser + */ + private selectRow = (file:FileMetaData, i:number) => { + let newPath:string = this.state.path; + const path_split:string[] = this.state.path.split("/"); + + /** + * If the user types out the full path, including file name, + * we don't want to join the file name with the path + * (resulting in duplicate file names). + */ + + if(path_split[path_split.length - 1] !== file.name) { + newPath = this.pathJoin(this.state.path, file.name); + } + + this.setState({selected:i},() => { + // tell our create what we selected + this.props.onSelectFileCallBack((this.state.pathInput + '/' + file.name), file.type, file); + }) + } + + /** + * takes a list of file info from the state and converts it + * to an html + * + * @private + * @memberof RemoteFileBrowser + * @returns JSX.Element[] with the styled file list + */ + private getFilesAsJsx = ():JSX.Element[] => { + const rawFilesJSX = this.state.rawFiles.map((rawFile, i) => { + return ( + this.selectRow(rawFile,i)} + onDoubleClick={()=> this.browseUpByFile(rawFile)}> + + {rawFile.mimeType === "application/x-directory"? + : + } + + {rawFile.name} + {rawFile.size} + {rawFile.mtime} + + ) + }) + return rawFilesJSX; + // if(file.mime_type() in component.icon_map) + // { + // icon = component.icon_map[file.mime_type()]; + // } + // else if(_.startsWith(file.mime_type(), "text/")) + // { + // icon = ""; + // } + // else if(_.startsWith(file.mime_type(), "image/")) + // { + // icon = ""; + // } + // else if(_.startsWith(file.mime_type(), "video/")) + // { + // icon = ""; + // } + } + + public async componentDidMount() { + // const path = localStorage.getItem("slycat-remote-browser-path-" + // + this.state.persistenceId + // + this.props.hostname); + if(this.state.pathInput != null){ + // this.setState({path,pathInput:path}); + this.browse(this.state.pathInput); + } + } + + public render() { + const options: Option[] = [{ + text:'Comma separated values (CSV)', + value:'slycat-csv-parser' + }, + { + text:'Dakota tabular', + value:'slycat-dakota-parser' + }]; + const pathStyle:any = { + width: 'calc(100% - 44px)', + float: 'left', + marginRight: '5px' + } + const styleTable:any = { + position: "relative", + height: (window.innerHeight*0.4)+"px", + overflow: "auto", + display: "block", + border: "1px solid rgb(222, 226, 230)", + } + return ( +
+
+
+
+ this.keyPress(event, this.state.pathInput)} + onChange={(e:React.ChangeEvent) => { + this.setState({pathInput:e.target.value}) + } + } + /> +
+ +
+
+
+ +
+
+ {/*
+ Oops, that path is not accessible. Please try again. +
*/} +
+ + {!this.state.browserUpdating? +
+ + + + + + + + + + + {this.getFilesAsJsx()} + +
NameSizeDate Modified
+
: + } +
+ ); + } +} \ No newline at end of file diff --git a/web-server/js/slycat-web-client.js b/web-server/js/slycat-web-client.js index c3bf99c4b..ba1d27c98 100644 --- a/web-server/js/slycat-web-client.js +++ b/web-server/js/slycat-web-client.js @@ -1391,8 +1391,52 @@ module.post_remote_command = function (params) { }); }; +module.post_combine_hdf5_tables = function (params) { + $.ajax({ + contentType: 'application/json', + data: JSON.stringify({}), + type: 'POST', + url: `${api_root}hdf5/combine/${params.mid}`, + success(result) { + if (params.success) params.success(result); + }, + error(request, status, reason_phrase) { + if (params.error) params.error(request, status, reason_phrase); + } + }); +} + +module.post_hdf5_table = function (params) { + $.ajax({ + contentType: 'application/json', + data: JSON.stringify({}), + type: 'POST', + url: `${api_root}hdf5/table${params.path}/${params.pid}/${params.mid}`, + success(result) { + if (params.success) params.success(result); + }, + error(request, status, reason_phrase) { + if (params.error) params.error(request, status, reason_phrase); + } + }); +} + +module.post_browse_hdf5 = function (params) { + $.ajax({ + contentType: 'application/json', + data: JSON.stringify({}), + type: 'POST', + url: `${api_root}hdf5/browse${params.path}/${params.pid}/${params.mid}`, + success(result) { + if (params.success) params.success(result); + }, + error(request, status, reason_phrase) { + if (params.error) params.error(request, status, reason_phrase); + } + }); +}; + module.post_remote_browse_smb = function (params) { - console.log('In browse smb'); $.ajax({ contentType: 'application/json', data: JSON.stringify({}), diff --git a/web-server/plugins/slycat-hdf5-parser.py b/web-server/plugins/slycat-hdf5-parser.py index 09413a126..2c850e602 100644 --- a/web-server/plugins/slycat-hdf5-parser.py +++ b/web-server/plugins/slycat-hdf5-parser.py @@ -78,15 +78,19 @@ def parse_file(file, model, database): def parse(database, model, input, files, aids, **kwargs): # Read HDF5 file + slycat.web.server.handlers.create_project_data(model['_id'], aids, files) start = time.time() - parsed = [parse_file(file, model, database) for file in files] - array_index = int(kwargs.get("array", "0")) - for (attributes, dimensions, combined_data, separate_data), aid in zip(parsed, aids): - slycat.web.server.put_model_arrayset(database, model, aid, input) - slycat.web.server.put_model_array(database, model, aid, 0, attributes, dimensions) - slycat.web.server.put_model_arrayset_data(database, model, aid, "%s/.../..." % array_index, combined_data) + + # parsed = [parse_file(file, model, database) for file in files] + # array_index = int(kwargs.get("array", "0")) + # for (attributes, dimensions, combined_data, separate_data), aid in zip(parsed, aids): + # slycat.web.server.put_model_arrayset(database, model, aid, input) + # slycat.web.server.put_model_array(database, model, aid, 0, attributes, dimensions) + # slycat.web.server.put_model_arrayset_data(database, model, aid, "%s/.../..." % array_index, combined_data) + end = time.time() model = database.get("model", model['_id']) + slycat.web.server.put_model_parameter(database, model, "error-messages", "") model["db_creation_time"] = (end - start) database.save(model) diff --git a/web-server/plugins/slycat-parameter-image/js/wizard-ui.js b/web-server/plugins/slycat-parameter-image/js/wizard-ui.js index 039c88b52..b48955e0c 100644 --- a/web-server/plugins/slycat-parameter-image/js/wizard-ui.js +++ b/web-server/plugins/slycat-parameter-image/js/wizard-ui.js @@ -20,6 +20,7 @@ import ReactDOM from "react-dom"; import { createRoot } from "react-dom/client"; import SmbRemoteFileBrowser from "components/SmbRemoteFileBrowser.tsx"; import SmbAuthentication from "components/SmbAuthentication.tsx"; +import HDF5Browser from "components/HDF5Browser.tsx"; function constructor(params) { var component = {}; @@ -205,8 +206,14 @@ function constructor(params) { component.error_messages(error_messages); } if (component.error_messages().length == 0) { - component.tab(4); - $(".browser-continue").toggleClass("disabled", false); + if (component.parser() == 'slycat-hdf5-parser') { + component.tab(6); + this.hdf5_input_browse(); + } + else{ + component.tab(4); + $(".browser-continue").toggleClass("disabled", false); + } } }); }; @@ -241,7 +248,6 @@ function constructor(params) { }; const onSelectTableFile = function (path, fileType, file) { - console.log(`newPath:: ${path}, fileType:: ${fileType}, file:: ${file}`); if (fileType === "f") { component.browser.path(path); } @@ -298,42 +304,51 @@ function constructor(params) { var upload_success = function (uploader) { uploader.progress(95); uploader.progress_status("Finishing..."); - client.get_model_command({ - mid: component.model._id(), - type: "parameter-image", - command: "media-columns", - success: function (media_columns) { - client.get_model_table_metadata({ - mid: component.model._id(), - aid: "data-table", - success: function (metadata) { - uploader.progress(100); - uploader.progress_status("Finished"); - var attributes = []; - for (var i = 0; i != metadata["column-names"].length; ++i) - attributes.push({ - name: metadata["column-names"][i], - type: metadata["column-types"][i], - input: false, - output: false, - category: false, - rating: false, - image: media_columns.indexOf(i) !== -1, - Classification: "Neither", - Categorical: false, - Editable: false, - hidden: media_columns.indexOf(i) !== -1, - selected: false, - lastSelected: false, - disabled: false, - tooltip: "", - }); - mapping.fromJS(attributes, component.attributes); - component.get_error_messages(); - }, - }); - }, - }); + // Don't need to get column headers if HDF5 file + if(component.parser() == 'slycat-hdf5-parser') { + $(".local-browser-continue").toggleClass("disabled", false); + uploader.progress(100); + uploader.progress_status("Finished"); + component.get_error_messages(); + } + else { + client.get_model_command({ + mid: component.model._id(), + type: "parameter-image", + command: "media-columns", + success: function (media_columns) { + client.get_model_table_metadata({ + mid: component.model._id(), + aid: "data-table", + success: function (metadata) { + uploader.progress(100); + uploader.progress_status("Finished"); + var attributes = []; + for (var i = 0; i != metadata["column-names"].length; ++i) + attributes.push({ + name: metadata["column-names"][i], + type: metadata["column-types"][i], + input: false, + output: false, + category: false, + rating: false, + image: media_columns.indexOf(i) !== -1, + Classification: "Neither", + Categorical: false, + Editable: false, + hidden: media_columns.indexOf(i) !== -1, + selected: false, + lastSelected: false, + disabled: false, + tooltip: "", + }); + mapping.fromJS(attributes, component.attributes); + component.get_error_messages(); + }, + }); + }, + }); + } }; component.existing_table = function () { @@ -357,7 +372,6 @@ function constructor(params) { // get file data $(".local-browser-continue").toggleClass("disabled", true); var file = component.browser.selection()[0]; - var fileObject = { pid: component.project._id(), mid: component.model._id(), @@ -382,6 +396,35 @@ function constructor(params) { fileUploader.uploadFile(fileObject, component.useProjectData()); }; + + component.hdf5_input_browse = function () { + const hdf5_wizard_browse_root = createRoot(document.querySelector(".hdf5-wizard-input-browse")); + hdf5_wizard_browse_root.render( +
+ +
) + } + + component.hdf5_output_browse = function () { + const hdf5_wizard_browse_root = createRoot(document.querySelector(".hdf5-wizard-output-browse")); + hdf5_wizard_browse_root.render( +
+ +
) + } + component.connectSMB = function () { component.remote.enable(false); component.remote.status_type("info"); @@ -476,8 +519,62 @@ function constructor(params) { } }; + component.load_hdf5_input = function () { + const file_name = component.browser.path().split("/")[ + component.browser.path().split("/").length - 1 + ]; + + let pathInput = component.browser.path(); + pathInput = pathInput.replace(/(?!^)\//g, "-"); + client.post_hdf5_table({ + path : pathInput, + pid: component.project._id(), + mid: component.model._id(), + aids: [["data-table"], file_name], + success : (results) => + { + console.log('Success!'); + component.tab(7); + component.hdf5_output_browse(); + }, + error : (results) => + { + console.log('Failure...'); + } + }); + } + + component.load_hdf5_output = function () { + const file_name = component.browser.path().split("/")[ + component.browser.path().split("/").length - 1 + ]; + + let pathInput = component.browser.path(); + pathInput = pathInput.replace(/(?!^)\//g, "-"); + client.post_hdf5_table({ + path : pathInput, + pid: component.project._id(), + mid: component.model._id(), + aids: [["data-table"], file_name], + success : (results) => + { + component.finish(); + client.post_combine_hdf5_tables({ + mid: component.model._id(), + success : (results) => + { + component.tab(5); + } + }) + }, + error : (results) => + { + console.log('Failure...'); + } + }); + } + component.load_table_smb = function () { - console.log("component.browser.path()", component.browser.path()); $(".remote-browser-continue").toggleClass("disabled", true); const file_name = component.browser.path().split("/")[ component.browser.path().split("/").length - 1 @@ -643,7 +740,6 @@ function constructor(params) { component.name_model = function (formElement) { // Validating formElement.classList.add("was-validated"); - // If valid... if (formElement.checkValidity() === true) { // Clearing form validation @@ -722,6 +818,22 @@ function constructor(params) { target--; target--; } + + if (component.tab() == 6) { + target--; + target--; + target--; + target--; + component.browser.progress(null); + component.browser.progress_status(""); + } + + if (component.parser() == 'slycat-hdf5-parser' && component.tab() == 5) { + target++; + target++; + target++; + } + target--; component.tab(target); }; diff --git a/web-server/plugins/slycat-parameter-image/wizard-ui.html b/web-server/plugins/slycat-parameter-image/wizard-ui.html index 47b42e2cc..fd9ec1283 100644 --- a/web-server/plugins/slycat-parameter-image/wizard-ui.html +++ b/web-server/plugins/slycat-parameter-image/wizard-ui.html @@ -9,10 +9,12 @@ +
+
HDF5 Inputs
+
+ +
+
+ +
+
HDF5 Outputs
+
+ +
+
+