From 40708000387c684c54582d5d725fd1e3b7ce406b Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 22 Aug 2023 17:54:40 -0700 Subject: [PATCH 01/43] Request the appropriate details from Python and create new custom elements --- pyflask/manageNeuroconv/manage_neuroconv.py | 18 ++- src/renderer/src/index.ts | 4 +- src/renderer/src/stories/JSONSchemaForm.js | 7 +- src/renderer/src/stories/List.stories.js | 4 +- src/renderer/src/stories/List.ts | 151 +++++++++++------ .../src/stories/inspector/InspectorList.js | 122 ++++++++++++++ .../inspector/InspectorList.stories.js | 13 ++ src/renderer/src/stories/inspector/test.json | 152 ++++++++++++++++++ .../guided-mode/options/GuidedStubPreview.js | 20 ++- 9 files changed, 429 insertions(+), 62 deletions(-) create mode 100644 src/renderer/src/stories/inspector/InspectorList.js create mode 100644 src/renderer/src/stories/inspector/InspectorList.stories.js create mode 100644 src/renderer/src/stories/inspector/test.json diff --git a/pyflask/manageNeuroconv/manage_neuroconv.py b/pyflask/manageNeuroconv/manage_neuroconv.py index fbcfd62d8..20fe167b9 100644 --- a/pyflask/manageNeuroconv/manage_neuroconv.py +++ b/pyflask/manageNeuroconv/manage_neuroconv.py @@ -237,6 +237,11 @@ def validate_metadata(metadata: dict, check_function_name: str) -> dict: def convert_to_nwb(info: dict) -> str: """Function used to convert the source data to NWB format using the specified metadata.""" + from pynwb import NWBHDF5IO + from nwbinspector import inspect_nwbfile_object + from nwbinspector.nwbinspector import InspectorOutputJSONEncoder + + nwbfile_path = Path(info["nwbfile_path"]) custom_output_directory = info.get("output_folder") project_name = info.get("project_name") @@ -327,7 +332,18 @@ def update_conversion_progress(**kwargs): if not default_output_directory.exists(): os.symlink(resolved_output_directory, default_output_directory) - return str(resolved_output_path) + + io = NWBHDF5IO(resolved_output_path, mode="r") + file = io.read() + html = file._repr_html_() + report = json.loads(json.dumps(list(inspect_nwbfile_object(file)), cls=InspectorOutputJSONEncoder)) + io.close() + + return dict( + file = str(resolved_output_path), + report = report, + html = html + ) def upload_to_dandi( diff --git a/src/renderer/src/index.ts b/src/renderer/src/index.ts index f00643058..dca74bf7b 100644 --- a/src/renderer/src/index.ts +++ b/src/renderer/src/index.ts @@ -50,7 +50,7 @@ async function isOffline() { await Swal.fire({ title: "No Internet Connection", icon: "warning", - text: "It appears that your computer is not connected to the internet. You may continue, but you will not be able to use features of NWB GUIDE related to uploading data to DANDI.", + text: "It appears that your computer is not connected to the internet. You may continue, but you will not be able to use certain features (e.g. uploading data to DANDI, viewing data on Neurosift, etc.).", heightAuto: false, backdrop: "rgba(0,0,0, 0.4)", confirmButtonText: "I understand", @@ -87,7 +87,7 @@ async function checkInternetConnection() { window.addEventListener('online', isOnline); window.addEventListener('offline', isOffline); - let hasInternet = navigator.onLine + const hasInternet = navigator.onLine if (hasInternet) isOnline() else await isOffline() diff --git a/src/renderer/src/stories/JSONSchemaForm.js b/src/renderer/src/stories/JSONSchemaForm.js index d4b68320c..d532bb8f5 100644 --- a/src/renderer/src/stories/JSONSchemaForm.js +++ b/src/renderer/src/stories/JSONSchemaForm.js @@ -8,6 +8,7 @@ import { merge } from "./pages/utils"; import { resolveProperties } from "./pages/guided-mode/data/utils"; import { JSONSchemaInput } from "./JSONSchemaInput"; +import { InspectorListItem } from "./inspector/InspectorList"; const componentCSS = ` @@ -277,9 +278,9 @@ export class JSONSchemaForm extends LitElement { #addMessage = (name, message, type) => { if (Array.isArray(name)) name = name.join("-"); // Convert array to string const container = this.shadowRoot.querySelector(`#${name} .${type}`); - const p = document.createElement("p"); - p.innerText = message; - container.appendChild(p); + const item = new InspectorListItem({ message }) + container.appendChild(item); + }; #clearMessages = (fullPath, type) => { diff --git a/src/renderer/src/stories/List.stories.js b/src/renderer/src/stories/List.stories.js index 36fc9152b..e1d721a07 100644 --- a/src/renderer/src/stories/List.stories.js +++ b/src/renderer/src/stories/List.stories.js @@ -6,9 +6,11 @@ export default { const Template = (args) => new List(args); +const generateString = () => Math.floor(Math.random() * Date.now()).toString(36) + export const Default = Template.bind({}); Default.args = { - items: [{ value: "test" }], + items: [{ value: "test" }, { value: (Array.from({length: 1000}).map(generateString).join(''))}], }; export const WithKeys = Template.bind({}); diff --git a/src/renderer/src/stories/List.ts b/src/renderer/src/stories/List.ts index e2f72d87d..508eb6066 100644 --- a/src/renderer/src/stories/List.ts +++ b/src/renderer/src/stories/List.ts @@ -1,17 +1,20 @@ import { LitElement, html, css } from 'lit'; import { Button } from './Button' -import { empty } from 'handsontable/helpers/dom'; +import { styleMap } from "lit/directives/style-map.js"; type ListItemType = { key: string, - label: string, + content: string, value: any, } export interface ListProps { onChange?: () => void; items?: ListItemType[] - emptyMessage?: string + emptyMessage?: string, + editable?: boolean, + unordered?: boolean, + listStyles?: any } export class List extends LitElement { @@ -19,16 +22,15 @@ export class List extends LitElement { static get styles() { return css` + :host { + overflow: auto; + } + #empty { padding: 20px 10px; color: gray; } - li > div { - display: flex; - align-items: center; - } - li { padding-bottom: 10px; } @@ -37,19 +39,35 @@ export class List extends LitElement { padding-bottom: 0px; } + li > div { + display: flex; + align-items: center; + } + li > div > div { white-space: nowrap; overflow: hidden; text-overflow: ellipsis } - :host([keys]) ol { - list-style-type:none; + :host([unordered]) ol { + list-style-type: none; + display: flex; + flex-wrap: wrap; + margin: 0; + padding: 0; + } + + :host([unordered]) ol > li { + width: 100%; } - :host([keys]) ol > li > div { + :host([unordered]) ol > li { justify-content: center; + display: flex; + align-items: center; } + `; } @@ -57,8 +75,18 @@ export class List extends LitElement { return { items: { type: Array, - reflect: true - } + // reflect: true // NOTE: Cannot reflect items since they could include an element + }, + + editable: { + type: Boolean, + reflect: true + }, + + unordered: { + type: Boolean, + // reflect: true + } }; } @@ -73,12 +101,22 @@ export class List extends LitElement { declare items: ListItemType[] declare emptyMessage: string + + declare editable: boolean + declare unordered: boolean + + declare listStyles: any + constructor(props: ListProps = {}) { super(); this.items = props.items ?? [] this.emptyMessage = props.emptyMessage ?? '' + this.editable = props.editable ?? true + this.unordered = props.unordered ?? false + this.listStyles = props.listStyles ?? {} + if (props.onChange) this.onChange = props.onChange } @@ -93,14 +131,14 @@ export class List extends LitElement { } #renderListItem = (item: ListItemType, i: number) => { - const { key, value, label = value } = item; + const { key, value, content = value } = item; const li = document.createElement("li"); - const div = document.createElement('div') - li.append(div) - - const innerDiv = document.createElement('div') - div.append(innerDiv) + const outerDiv = document.createElement('div') + const div = document.createElement('div') + li.append(outerDiv) + outerDiv.append(div) + const keyEl = document.createElement("span"); let resolvedKey = key; @@ -108,7 +146,7 @@ export class List extends LitElement { if (key) { - this.setAttribute('keys', '') + this.setAttribute('unordered', '') // Ensure no duplicate keys let i = 0; @@ -120,51 +158,58 @@ export class List extends LitElement { keyEl.innerText = resolvedKey; keyEl.contentEditable = true; keyEl.style.cursor = "text"; - innerDiv.appendChild(keyEl); const sepEl = document.createElement("span"); sepEl.innerHTML = " - "; - innerDiv.appendChild(sepEl); + div.append(keyEl, sepEl); this.object[resolvedKey] = value; } else this.object[i] = value; - const valueEl = document.createElement("span"); - valueEl.innerText = label; - innerDiv.appendChild(valueEl); + if (typeof content === 'string') { + const valueEl = document.createElement("span"); + valueEl.innerText = content; + div.appendChild(valueEl); + } + else li.append(content) + + - const button = new Button({ - label: "Delete", - size: "small", - }); + if (div.innerText) li.title = div.innerText - button.style.marginLeft = "1rem"; - div.appendChild(button); + if (this.editable) { + const button = new Button({ + label: "Delete", + size: "small", + }); - // Stop enter key from creating new line - keyEl.addEventListener("keydown", function (e) { - if (e.keyCode === 13) { - keyEl.blur(); - return false; - } - }); + button.style.marginLeft = "1rem"; - const deleteListItem = () => this.delete(i); + outerDiv.appendChild(button); - keyEl.addEventListener("blur", () => { - const newKey = keyEl.innerText; - if (newKey === "") keyEl.innerText = resolvedKey; // Reset to original value - else { - delete this.object[resolvedKey]; - resolvedKey = newKey; - this.object[resolvedKey] = value; - } - }); + // Stop enter key from creating new line + keyEl.addEventListener("keydown", function (e) { + if (e.keyCode === 13) { + keyEl.blur(); + return false; + } + }); - button.onClick = deleteListItem; + const deleteListItem = () => this.delete(i); - innerDiv.title = innerDiv.innerText + keyEl.addEventListener("blur", () => { + const newKey = keyEl.innerText; + if (newKey === "") keyEl.innerText = resolvedKey; // Reset to original value + else { + delete this.object[resolvedKey]; + resolvedKey = newKey; + this.object[resolvedKey] = value; + } + }); + + button.onClick = deleteListItem; + } return li }; @@ -180,13 +225,15 @@ export class List extends LitElement { render() { - this.removeAttribute('keys') + this.removeAttribute('unordered') + if (this.unordered) this.setAttribute('unordered', '') + this.object = {} const { items, emptyMessage} = this return items.length || !emptyMessage ? html` -
    +
      ${items.map(this.#renderListItem)}
    ` : html`
    ${emptyMessage}
    ` } diff --git a/src/renderer/src/stories/inspector/InspectorList.js b/src/renderer/src/stories/inspector/InspectorList.js new file mode 100644 index 000000000..34c9f5f52 --- /dev/null +++ b/src/renderer/src/stories/inspector/InspectorList.js @@ -0,0 +1,122 @@ + +import { LitElement, css, html } from "lit"; +import { List } from "../List"; + +const sortList = (items) => { + return items.sort((a, b) => { + const aCritical = a.importance === "CRITICAL" + const bCritical = b.importance === "CRITICAL" + if (aCritical && bCritical) return 0 + else if (aCritical) return -1 + else return 1 + }) + .sort((a, b) => { + const aLow = a.severity == "LOW" + const bLow = b.severity === "LOW" + if (aLow && bLow) return 0 + else if (aLow) return 1 + else return -1 + }) +} + +export class InspectorList extends List { + constructor({ items, listStyles }) { + + super({ + editable: false, + unordered: true, + items: sortList(items).map(o => { + const item = new InspectorListItem(o) + item.style.flexGrow = '1' + return { content: item } + }), + listStyles + }) + } +} + + +customElements.get("inspector-list") || + customElements.define("inspector-list", InspectorList); + + + + +export class InspectorListItem extends LitElement { + + static get styles() { + return css` + + + :host { + display: block; + background: gainsboro; + border: 1px solid gray; + border-radius: 10px; + padding: 5px 10px; + overflow: hidden; + text-wrap: wrap; + } + + #message { + display: block; + font-size: 14px; + font-weight: bold; + } + + :host > * { + margin: 0px; + } + + :host([type=error]) { + color: #9d0b0b; + padding: 25px; + background: #f8d7da; + border: 1px solid #f5c2c7; + border-radius: 4px; + margin: 0 0 1em; + } + + :host([type=warning]) { + color: #856404; + padding: 25px; + background: #fff3cd; + border: 1px solid #ffeeba; + border-radius: 4px; + margin: 0 0 1em; + } + ` + } + constructor(props) { + super() + Object.assign(this, props) + } + + static get properties() { + return { + type: { + type: String, + reflect: true + } + } + } + + render() { + + this.type = this.importance === "CRITICAL" ? "error" : "warning" + + this.setAttribute('title', this.message) + + const hasObjectType = 'object_type' in this + const hasMetadata = hasObjectType && 'object_name' in this + + return html` + ${hasMetadata ? html`${this.message}` : html`

    ${this.message}

    `} + ${hasMetadata ? html`${this.object_name}${hasObjectType ? ` (${this.object_type})` : ''} ` : ''} + ` + } +} + +customElements.get("inspector-list-item") || + customElements.define("inspector-list-item", InspectorListItem); + diff --git a/src/renderer/src/stories/inspector/InspectorList.stories.js b/src/renderer/src/stories/inspector/InspectorList.stories.js new file mode 100644 index 000000000..7815098f5 --- /dev/null +++ b/src/renderer/src/stories/inspector/InspectorList.stories.js @@ -0,0 +1,13 @@ +import { InspectorList } from "./InspectorList"; +import testInspectorList from './test.json' + +export default { + title: "Components/Inspector List", +}; + +const Template = (args) => new InspectorList(args); + +export const Default = Template.bind({}); +Default.args = { + items: testInspectorList +}; \ No newline at end of file diff --git a/src/renderer/src/stories/inspector/test.json b/src/renderer/src/stories/inspector/test.json new file mode 100644 index 000000000..7b5c3341a --- /dev/null +++ b/src/renderer/src/stories/inspector/test.json @@ -0,0 +1,152 @@ +[ + { + "message": "Description ('No description.') is a placeholder.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "VectorData", + "object_name": "KSLabel_repeat", + "location": null, + "file_path": null + }, + { + "message": "Description ('No description.') is a placeholder.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "VectorData", + "object_name": "Amplitude", + "location": null, + "file_path": null + }, + { + "message": "Description ('No description.') is a placeholder.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "VectorData", + "object_name": "original_cluster_id", + "location": null, + "file_path": null + }, + { + "message": "Description ('No description.') is a placeholder.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "VectorData", + "object_name": "ContamPct", + "location": null, + "file_path": null + }, + { + "message": "Description ('No description.') is a placeholder.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "VectorData", + "object_name": "KSLabel", + "location": null, + "file_path": null + }, + { + "message": "Description is missing.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "Subject", + "object_name": "subject", + "location": "/general/subject", + "file_path": null + }, + { + "message": "Description ('no description') is a placeholder.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "VectorData", + "object_name": "offset_to_uV", + "location": null, + "file_path": null + }, + { + "message": "Description ('no description') is a placeholder.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "VectorData", + "object_name": "inter_sample_shift", + "location": null, + "file_path": null + }, + { + "message": "Description ('no description') is a placeholder.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "VectorData", + "object_name": "gain_to_uV", + "location": null, + "file_path": null + }, + { + "message": "Description ('no description') is a placeholder.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_description", + "object_type": "VectorData", + "object_name": "channel_name", + "location": null, + "file_path": null + }, + { + "message": "Experimenter is missing.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_experimenter_exists", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": null + }, + { + "message": "Experiment description is missing.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_experiment_description", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": null + }, + { + "message": "Metadata /general/institution is missing.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_institution", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": null + }, + { + "message": "Metadata /general/keywords is missing.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_keywords", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": null + }, + { + "message": "Data may be in the wrong orientation. Time should be in the first dimension, and is usually the longest dimension. Here, another dimension is longer.", + "importance": "CRITICAL", + "severity": "LOW", + "check_function_name": "check_data_orientation", + "object_type": "ElectricalSeries", + "object_name": "ElectricalSeriesAP", + "location": "/acquisition/ElectricalSeriesAP", + "file_path": null + } +] \ No newline at end of file diff --git a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js index 8dbb94e88..9279694a6 100644 --- a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js +++ b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js @@ -2,23 +2,27 @@ import { html } from "lit"; import { Page } from "../../Page.js"; import { unsafeSVG } from "lit/directives/unsafe-svg.js"; +import { unsafeHTML } from "lit/directives/unsafe-html.js"; import folderOpenSVG from "../../../assets/folder_open.svg?raw"; import { electron } from "../../../../electron/index.js"; import { Neurosift, getURLFromFilePath } from "../../../Neurosift.js"; +import { InspectorList } from "../../../inspector/InspectorList.js"; const { shell } = electron; export class GuidedStubPreviewPage extends Page { constructor(...args) { super(...args); + window.addEventListener('online', () => this.requestUpdate()); + window.addEventListener('offline', () => this.requestUpdate()); } header = { - subtitle: () => this.info.globalState.preview, + subtitle: () => this.info.globalState.preview.file, controls: () => html` (shell ? shell.showItemInFolder(this.info.globalState.preview) : "")} + @click=${() => (shell ? shell.showItemInFolder(this.info.globalState.preview.file) : "")} >${unsafeSVG(folderOpenSVG)}`, }; @@ -40,7 +44,17 @@ export class GuidedStubPreviewPage extends Page { const { project, preview } = this.info.globalState; return preview - ? new Neurosift({ url: getURLFromFilePath(preview, project.name) }) + ? html`
    +
    ${(navigator.onLine ? new Neurosift({ url: getURLFromFilePath(preview.file, project.name) }) : html`
    ${unsafeHTML(preview.html)}
    `)}
    +
    +

    Inspector Report

    + ${(() => { + const list = new InspectorList({ items: preview.report, listStyles: { maxWidth: '350px' }}) + list.style.padding = '10px' + return list + })()} +
    +
    ` : html`

    Your conversion preview failed. Please try again.

    `; } } From 479699ad1ed9952695c70cfe9153af7ae113d421 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:58:07 +0000 Subject: [PATCH 02/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pyflask/manageNeuroconv/manage_neuroconv.py | 8 +- src/renderer/src/stories/JSONSchemaForm.js | 3 +- src/renderer/src/stories/List.stories.js | 4 +- src/renderer/src/stories/List.ts | 8 +- .../src/stories/inspector/InspectorList.js | 86 ++++++++----------- .../inspector/InspectorList.stories.js | 6 +- src/renderer/src/stories/inspector/test.json | 2 +- .../guided-mode/options/GuidedStubPreview.js | 28 +++--- 8 files changed, 66 insertions(+), 79 deletions(-) diff --git a/pyflask/manageNeuroconv/manage_neuroconv.py b/pyflask/manageNeuroconv/manage_neuroconv.py index 20fe167b9..ab3a9c703 100644 --- a/pyflask/manageNeuroconv/manage_neuroconv.py +++ b/pyflask/manageNeuroconv/manage_neuroconv.py @@ -241,7 +241,6 @@ def convert_to_nwb(info: dict) -> str: from nwbinspector import inspect_nwbfile_object from nwbinspector.nwbinspector import InspectorOutputJSONEncoder - nwbfile_path = Path(info["nwbfile_path"]) custom_output_directory = info.get("output_folder") project_name = info.get("project_name") @@ -332,18 +331,13 @@ def update_conversion_progress(**kwargs): if not default_output_directory.exists(): os.symlink(resolved_output_directory, default_output_directory) - io = NWBHDF5IO(resolved_output_path, mode="r") file = io.read() html = file._repr_html_() report = json.loads(json.dumps(list(inspect_nwbfile_object(file)), cls=InspectorOutputJSONEncoder)) io.close() - return dict( - file = str(resolved_output_path), - report = report, - html = html - ) + return dict(file=str(resolved_output_path), report=report, html=html) def upload_to_dandi( diff --git a/src/renderer/src/stories/JSONSchemaForm.js b/src/renderer/src/stories/JSONSchemaForm.js index d532bb8f5..4f6fe6dde 100644 --- a/src/renderer/src/stories/JSONSchemaForm.js +++ b/src/renderer/src/stories/JSONSchemaForm.js @@ -278,9 +278,8 @@ export class JSONSchemaForm extends LitElement { #addMessage = (name, message, type) => { if (Array.isArray(name)) name = name.join("-"); // Convert array to string const container = this.shadowRoot.querySelector(`#${name} .${type}`); - const item = new InspectorListItem({ message }) + const item = new InspectorListItem({ message }); container.appendChild(item); - }; #clearMessages = (fullPath, type) => { diff --git a/src/renderer/src/stories/List.stories.js b/src/renderer/src/stories/List.stories.js index e1d721a07..718492ef9 100644 --- a/src/renderer/src/stories/List.stories.js +++ b/src/renderer/src/stories/List.stories.js @@ -6,11 +6,11 @@ export default { const Template = (args) => new List(args); -const generateString = () => Math.floor(Math.random() * Date.now()).toString(36) +const generateString = () => Math.floor(Math.random() * Date.now()).toString(36); export const Default = Template.bind({}); Default.args = { - items: [{ value: "test" }, { value: (Array.from({length: 1000}).map(generateString).join(''))}], + items: [{ value: "test" }, { value: Array.from({ length: 1000 }).map(generateString).join("") }], }; export const WithKeys = Template.bind({}); diff --git a/src/renderer/src/stories/List.ts b/src/renderer/src/stories/List.ts index 508eb6066..16c731052 100644 --- a/src/renderer/src/stories/List.ts +++ b/src/renderer/src/stories/List.ts @@ -57,7 +57,7 @@ export class List extends LitElement { margin: 0; padding: 0; } - + :host([unordered]) ol > li { width: 100%; } @@ -101,7 +101,7 @@ export class List extends LitElement { declare items: ListItemType[] declare emptyMessage: string - + declare editable: boolean declare unordered: boolean @@ -138,7 +138,7 @@ export class List extends LitElement { const div = document.createElement('div') li.append(outerDiv) outerDiv.append(div) - + const keyEl = document.createElement("span"); let resolvedKey = key; @@ -172,7 +172,7 @@ export class List extends LitElement { div.appendChild(valueEl); } else li.append(content) - + if (div.innerText) li.title = div.innerText diff --git a/src/renderer/src/stories/inspector/InspectorList.js b/src/renderer/src/stories/inspector/InspectorList.js index 34c9f5f52..06d4629a9 100644 --- a/src/renderer/src/stories/inspector/InspectorList.js +++ b/src/renderer/src/stories/inspector/InspectorList.js @@ -1,53 +1,44 @@ - import { LitElement, css, html } from "lit"; import { List } from "../List"; const sortList = (items) => { - return items.sort((a, b) => { - const aCritical = a.importance === "CRITICAL" - const bCritical = b.importance === "CRITICAL" - if (aCritical && bCritical) return 0 - else if (aCritical) return -1 - else return 1 - }) + return items .sort((a, b) => { - const aLow = a.severity == "LOW" - const bLow = b.severity === "LOW" - if (aLow && bLow) return 0 - else if (aLow) return 1 - else return -1 + const aCritical = a.importance === "CRITICAL"; + const bCritical = b.importance === "CRITICAL"; + if (aCritical && bCritical) return 0; + else if (aCritical) return -1; + else return 1; }) -} + .sort((a, b) => { + const aLow = a.severity == "LOW"; + const bLow = b.severity === "LOW"; + if (aLow && bLow) return 0; + else if (aLow) return 1; + else return -1; + }); +}; export class InspectorList extends List { constructor({ items, listStyles }) { - super({ editable: false, unordered: true, - items: sortList(items).map(o => { - const item = new InspectorListItem(o) - item.style.flexGrow = '1' - return { content: item } + items: sortList(items).map((o) => { + const item = new InspectorListItem(o); + item.style.flexGrow = "1"; + return { content: item }; }), - listStyles - }) + listStyles, + }); } } - -customElements.get("inspector-list") || - customElements.define("inspector-list", InspectorList); - - - +customElements.get("inspector-list") || customElements.define("inspector-list", InspectorList); export class InspectorListItem extends LitElement { - static get styles() { return css` - - :host { display: block; background: gainsboro; @@ -68,7 +59,7 @@ export class InspectorListItem extends LitElement { margin: 0px; } - :host([type=error]) { + :host([type="error"]) { color: #9d0b0b; padding: 25px; background: #f8d7da; @@ -77,7 +68,7 @@ export class InspectorListItem extends LitElement { margin: 0 0 1em; } - :host([type=warning]) { + :host([type="warning"]) { color: #856404; padding: 25px; background: #fff3cd; @@ -85,38 +76,37 @@ export class InspectorListItem extends LitElement { border-radius: 4px; margin: 0 0 1em; } - ` + `; } constructor(props) { - super() - Object.assign(this, props) + super(); + Object.assign(this, props); } static get properties() { return { type: { type: String, - reflect: true - } - } + reflect: true, + }, + }; } render() { + this.type = this.importance === "CRITICAL" ? "error" : "warning"; - this.type = this.importance === "CRITICAL" ? "error" : "warning" - - this.setAttribute('title', this.message) + this.setAttribute("title", this.message); - const hasObjectType = 'object_type' in this - const hasMetadata = hasObjectType && 'object_name' in this + const hasObjectType = "object_type" in this; + const hasMetadata = hasObjectType && "object_name" in this; return html` ${hasMetadata ? html`${this.message}` : html`

    ${this.message}

    `} - ${hasMetadata ? html`${this.object_name}${hasObjectType ? ` (${this.object_type})` : ''} ` : ''} - ` + ${hasMetadata + ? html`${this.object_name}${hasObjectType ? ` (${this.object_type})` : ""} ` + : ""} + `; } } -customElements.get("inspector-list-item") || - customElements.define("inspector-list-item", InspectorListItem); - +customElements.get("inspector-list-item") || customElements.define("inspector-list-item", InspectorListItem); diff --git a/src/renderer/src/stories/inspector/InspectorList.stories.js b/src/renderer/src/stories/inspector/InspectorList.stories.js index 7815098f5..bcb331913 100644 --- a/src/renderer/src/stories/inspector/InspectorList.stories.js +++ b/src/renderer/src/stories/inspector/InspectorList.stories.js @@ -1,5 +1,5 @@ import { InspectorList } from "./InspectorList"; -import testInspectorList from './test.json' +import testInspectorList from "./test.json"; export default { title: "Components/Inspector List", @@ -9,5 +9,5 @@ const Template = (args) => new InspectorList(args); export const Default = Template.bind({}); Default.args = { - items: testInspectorList -}; \ No newline at end of file + items: testInspectorList, +}; diff --git a/src/renderer/src/stories/inspector/test.json b/src/renderer/src/stories/inspector/test.json index 7b5c3341a..2aee6f704 100644 --- a/src/renderer/src/stories/inspector/test.json +++ b/src/renderer/src/stories/inspector/test.json @@ -149,4 +149,4 @@ "location": "/acquisition/ElectricalSeriesAP", "file_path": null } -] \ No newline at end of file +] diff --git a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js index 9279694a6..198fa6e63 100644 --- a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js +++ b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js @@ -13,8 +13,8 @@ const { shell } = electron; export class GuidedStubPreviewPage extends Page { constructor(...args) { super(...args); - window.addEventListener('online', () => this.requestUpdate()); - window.addEventListener('offline', () => this.requestUpdate()); + window.addEventListener("online", () => this.requestUpdate()); + window.addEventListener("offline", () => this.requestUpdate()); } header = { @@ -45,16 +45,20 @@ export class GuidedStubPreviewPage extends Page { return preview ? html`
    -
    ${(navigator.onLine ? new Neurosift({ url: getURLFromFilePath(preview.file, project.name) }) : html`
    ${unsafeHTML(preview.html)}
    `)}
    -
    -

    Inspector Report

    - ${(() => { - const list = new InspectorList({ items: preview.report, listStyles: { maxWidth: '350px' }}) - list.style.padding = '10px' - return list - })()} -
    -
    ` +
    + ${navigator.onLine + ? new Neurosift({ url: getURLFromFilePath(preview.file, project.name) }) + : html`
    ${unsafeHTML(preview.html)}
    `} +
    +
    +

    Inspector Report

    + ${(() => { + const list = new InspectorList({ items: preview.report, listStyles: { maxWidth: "350px" } }); + list.style.padding = "10px"; + return list; + })()} +
    + ` : html`

    Your conversion preview failed. Please try again.

    `; } } From 1f1da67e9b0836035682b93a7663b6d7f138e5f5 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 10:14:30 -0700 Subject: [PATCH 03/43] Create a file preview component and asynchronously resolve additional features (e.g. inspector report, html, etc.) --- pyflask/apis/neuroconv.py | 38 ++++++++++++++ pyflask/manageNeuroconv/__init__.py | 3 ++ pyflask/manageNeuroconv/manage_neuroconv.py | 23 ++++++--- src/renderer/src/stories/JSONSchemaForm.js | 2 +- .../pages/guided-mode/data/GuidedMetadata.js | 16 +++--- .../options/GuidedConversionOptions.js | 4 +- .../guided-mode/options/GuidedStubPreview.js | 28 +++-------- .../src/stories/preview/NWBFilePreview.js | 50 +++++++++++++++++++ .../src/stories/{ => preview}/Neurosift.js | 2 +- .../{ => preview}/inspector/InspectorList.js | 2 +- .../inspector/InspectorList.stories.js | 0 .../stories/{ => preview}/inspector/test.json | 0 12 files changed, 126 insertions(+), 42 deletions(-) create mode 100644 src/renderer/src/stories/preview/NWBFilePreview.js rename src/renderer/src/stories/{ => preview}/Neurosift.js (94%) rename src/renderer/src/stories/{ => preview}/inspector/InspectorList.js (98%) rename src/renderer/src/stories/{ => preview}/inspector/InspectorList.stories.js (100%) rename src/renderer/src/stories/{ => preview}/inspector/test.json (100%) diff --git a/pyflask/apis/neuroconv.py b/pyflask/apis/neuroconv.py index 77661b8ff..91d5f84d9 100644 --- a/pyflask/apis/neuroconv.py +++ b/pyflask/apis/neuroconv.py @@ -121,6 +121,44 @@ def post(self): if notBadRequestException(e): neuroconv_api.abort(500, str(e)) +@neuroconv_api.route('/inspect_nwbfile') +class InspectNWBFile(Resource): + @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) + def post(self): + try: + from manageNeuroconv import inspect_nwbfile + return inspect_nwbfile(**neuroconv_api.payload) + + except Exception as e: + if notBadRequestException(e): + neuroconv_api.abort(500, str(e)) + +@neuroconv_api.route('/inspect') +class InspectNWBFiles(Resource): + @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) + def post(self): + try: + from manageNeuroconv import inspect + return inspect(**neuroconv_api.payload) + + except Exception as e: + if notBadRequestException(e): + neuroconv_api.abort(500, str(e)) + + +@neuroconv_api.route('/html') +class NWBToHTML(Resource): + @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) + def post(self): + try: + from manageNeuroconv import nwb_to_html + return nwb_to_html(**neuroconv_api.payload) + + except Exception as e: + if notBadRequestException(e): + neuroconv_api.abort(500, str(e)) + + @neuroconv_api.route("/generate_dataset") class GenerateDataset(Resource): diff --git a/pyflask/manageNeuroconv/__init__.py b/pyflask/manageNeuroconv/__init__.py index e13648dab..234fe2ef2 100644 --- a/pyflask/manageNeuroconv/__init__.py +++ b/pyflask/manageNeuroconv/__init__.py @@ -8,6 +8,9 @@ upload_to_dandi, listen_to_neuroconv_events, generate_dataset, + inspect, + inspect_nwbfile, + nwb_to_html ) diff --git a/pyflask/manageNeuroconv/manage_neuroconv.py b/pyflask/manageNeuroconv/manage_neuroconv.py index ab3a9c703..28378357d 100644 --- a/pyflask/manageNeuroconv/manage_neuroconv.py +++ b/pyflask/manageNeuroconv/manage_neuroconv.py @@ -237,10 +237,6 @@ def validate_metadata(metadata: dict, check_function_name: str) -> dict: def convert_to_nwb(info: dict) -> str: """Function used to convert the source data to NWB format using the specified metadata.""" - from pynwb import NWBHDF5IO - from nwbinspector import inspect_nwbfile_object - from nwbinspector.nwbinspector import InspectorOutputJSONEncoder - nwbfile_path = Path(info["nwbfile_path"]) custom_output_directory = info.get("output_folder") project_name = info.get("project_name") @@ -331,13 +327,26 @@ def update_conversion_progress(**kwargs): if not default_output_directory.exists(): os.symlink(resolved_output_directory, default_output_directory) - io = NWBHDF5IO(resolved_output_path, mode="r") + + return dict(file=str(resolved_output_path)) + +def nwb_to_html(nwbfile_path: str): + from pynwb import NWBHDF5IO + io = NWBHDF5IO(nwbfile_path, mode="r") file = io.read() html = file._repr_html_() - report = json.loads(json.dumps(list(inspect_nwbfile_object(file)), cls=InspectorOutputJSONEncoder)) io.close() + return html + +def inspect_nwbfile(**kwargs): + from nwbinspector import inspect_nwbfile + from nwbinspector.nwbinspector import InspectorOutputJSONEncoder + return json.loads(json.dumps(list(inspect_nwbfile(**kwargs)), cls=InspectorOutputJSONEncoder)) - return dict(file=str(resolved_output_path), report=report, html=html) +def inspect(**kwargs): + from nwbinspector import inspect_all + from nwbinspector.nwbinspector import InspectorOutputJSONEncoder + return json.loads(json.dumps(list(inspect_all(**kwargs)), cls=InspectorOutputJSONEncoder)) def upload_to_dandi( diff --git a/src/renderer/src/stories/JSONSchemaForm.js b/src/renderer/src/stories/JSONSchemaForm.js index 3bd2d85f5..3b9c8633a 100644 --- a/src/renderer/src/stories/JSONSchemaForm.js +++ b/src/renderer/src/stories/JSONSchemaForm.js @@ -8,7 +8,7 @@ import { merge } from "./pages/utils"; import { resolveProperties } from "./pages/guided-mode/data/utils"; import { JSONSchemaInput } from "./JSONSchemaInput"; -import { InspectorListItem } from "./inspector/InspectorList"; +import { InspectorListItem } from "./preview/inspector/InspectorList"; const componentCSS = ` diff --git a/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js b/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js index 26da77c09..f23a0d00a 100644 --- a/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js +++ b/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js @@ -10,7 +10,7 @@ import Swal from "sweetalert2"; import { SimpleTable } from "../../../SimpleTable.js"; import { onThrow } from "../../../../errors"; import { merge } from "../../utils.js"; -import { Neurosift, getURLFromFilePath } from "../../../Neurosift.js"; +import { NWBFilePreview } from "../../../preview/NWBFilePreview.js"; const getInfoFromId = (key) => { let [subject, session] = key.split("/"); @@ -38,13 +38,11 @@ export class GuidedMetadataPage extends ManagedPage { for (let { form } of this.forms) await form.validate(); // Will throw an error in the callback // Preview a single random conversion - delete this.info.globalState.preview; // Clear the preview results - const [result] = await this.runConversions({ stub_test: true }, 1, { - title: "Testing conversion on a random session", - }); + delete this.info.globalState.stubs; // Clear the preview results + const stubs = await this.runConversions({ stub_test: true }, 1, { title: "Testing conversion on a random session" }); // Save the preview results - this.info.globalState.preview = result; + this.info.globalState.stubs = stubs; this.unsavedUpdates = true; @@ -185,7 +183,7 @@ export class GuidedMetadataPage extends ManagedPage { onClick: async (key, el) => { const { subject, session } = getInfoFromId(key); - const [{ file, html }] = await this.runConversions( + const [ file ] = await this.runConversions( { stub_test: true }, [ { @@ -208,7 +206,9 @@ export class GuidedMetadataPage extends ManagedPage { height: "100%", }); - modal.append(new Neurosift({ url: getURLFromFilePath(file, project.name) })); + const { project } = this.info.globalState + + modal.append(new NWBFilePreview({ project: project.name, files: [file] })); document.body.append(modal); }, }, diff --git a/src/renderer/src/stories/pages/guided-mode/options/GuidedConversionOptions.js b/src/renderer/src/stories/pages/guided-mode/options/GuidedConversionOptions.js index cdab8f1b0..a3f60670e 100644 --- a/src/renderer/src/stories/pages/guided-mode/options/GuidedConversionOptions.js +++ b/src/renderer/src/stories/pages/guided-mode/options/GuidedConversionOptions.js @@ -15,11 +15,11 @@ export class GuidedConversionOptionsPage extends Page { await this.form.validate(); // Will throw an error in the callback // Preview a random conversion - delete this.info.globalState.preview; // Clear the preview results + delete this.info.globalState.stubs; // Clear the preview results const results = await this.runConversions({ stub_test: true }, 1, { title: "Testing conversion on a random session", }); - this.info.globalState.preview = results[0]; // Save the preview results + this.info.globalState.stubs = results; // Save the preview results this.to(1); }, diff --git a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js index 198fa6e63..357306a46 100644 --- a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js +++ b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js @@ -2,12 +2,10 @@ import { html } from "lit"; import { Page } from "../../Page.js"; import { unsafeSVG } from "lit/directives/unsafe-svg.js"; -import { unsafeHTML } from "lit/directives/unsafe-html.js"; import folderOpenSVG from "../../../assets/folder_open.svg?raw"; import { electron } from "../../../../electron/index.js"; -import { Neurosift, getURLFromFilePath } from "../../../Neurosift.js"; -import { InspectorList } from "../../../inspector/InspectorList.js"; +import { NWBFilePreview } from "../../../preview/NWBFilePreview.js"; const { shell } = electron; export class GuidedStubPreviewPage extends Page { @@ -18,11 +16,11 @@ export class GuidedStubPreviewPage extends Page { } header = { - subtitle: () => this.info.globalState.preview.file, + subtitle: () => this.info.globalState.stubs?.[0]?.file, controls: () => html` (shell ? shell.showItemInFolder(this.info.globalState.preview.file) : "")} + @click=${() => (shell ? shell.showItemInFolder(this.info.globalState.stubs[0].file) : "")} >${unsafeSVG(folderOpenSVG)}`, }; @@ -41,24 +39,10 @@ export class GuidedStubPreviewPage extends Page { }; render() { - const { project, preview } = this.info.globalState; + const { project, stubs } = this.info.globalState; - return preview - ? html`
    -
    - ${navigator.onLine - ? new Neurosift({ url: getURLFromFilePath(preview.file, project.name) }) - : html`
    ${unsafeHTML(preview.html)}
    `} -
    -
    -

    Inspector Report

    - ${(() => { - const list = new InspectorList({ items: preview.report, listStyles: { maxWidth: "350px" } }); - list.style.padding = "10px"; - return list; - })()} -
    -
    ` + return stubs + ? new NWBFilePreview({ project: project.name, files: stubs }) : html`

    Your conversion preview failed. Please try again.

    `; } } diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js new file mode 100644 index 000000000..cb9866e24 --- /dev/null +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -0,0 +1,50 @@ +import { LitElement, css, html } from "lit"; +import { InspectorList } from "./inspector/InspectorList"; +import { Neurosift, getURLFromFilePath } from "./Neurosift"; +import { unsafeHTML } from "lit/directives/unsafe-html.js"; +import { run } from "../pages/guided-mode/options/utils"; +import { until } from 'lit/directives/until.js'; + +export class NWBFilePreview extends LitElement { + static get styles() { + return css` + iframe { + width: 100%; + height: 100%; + border: 0; + } + `; + } + + constructor({ project, files = [] }) { + super(); + this.project = project; + this.files = files + } + + render() { + + const filepath = this.files[0].file + console.log(filepath, this.files[0]) + + return html`
    +
    + ${navigator.onLine + ? new Neurosift({ url: getURLFromFilePath(filepath, this.project) }) + : html`
    ${ + until((async () => unsafeHTML(await run('html', { nwbfile_path: filepath })))(), 'Loading HTML representation...') + }
    `} +
    +
    +

    Inspector Report

    + ${until((async () => { + const list = new InspectorList({ items: await run('inspect_nwbfile', { nwbfile_path: filepath, ignore: ["check_description"] }), listStyles: { maxWidth: "350px" } }); + list.style.padding = "10px"; + return list; + })(), html`Loading inspector report...`)} +
    +
    ` + } +} + +customElements.get("nwb-file-preview") || customElements.define("nwb-file-preview", NWBFilePreview); diff --git a/src/renderer/src/stories/Neurosift.js b/src/renderer/src/stories/preview/Neurosift.js similarity index 94% rename from src/renderer/src/stories/Neurosift.js rename to src/renderer/src/stories/preview/Neurosift.js index 7fb3e9029..b960b842a 100644 --- a/src/renderer/src/stories/Neurosift.js +++ b/src/renderer/src/stories/preview/Neurosift.js @@ -1,5 +1,5 @@ import { LitElement, css, html } from "lit"; -import { baseUrl } from "../globals"; +import { baseUrl } from "../../globals"; export function getURLFromFilePath(file, projectName) { const regexp = new RegExp(`.+(${projectName}.+)`); diff --git a/src/renderer/src/stories/inspector/InspectorList.js b/src/renderer/src/stories/preview/inspector/InspectorList.js similarity index 98% rename from src/renderer/src/stories/inspector/InspectorList.js rename to src/renderer/src/stories/preview/inspector/InspectorList.js index 06d4629a9..803a2af33 100644 --- a/src/renderer/src/stories/inspector/InspectorList.js +++ b/src/renderer/src/stories/preview/inspector/InspectorList.js @@ -1,5 +1,5 @@ import { LitElement, css, html } from "lit"; -import { List } from "../List"; +import { List } from "../../List"; const sortList = (items) => { return items diff --git a/src/renderer/src/stories/inspector/InspectorList.stories.js b/src/renderer/src/stories/preview/inspector/InspectorList.stories.js similarity index 100% rename from src/renderer/src/stories/inspector/InspectorList.stories.js rename to src/renderer/src/stories/preview/inspector/InspectorList.stories.js diff --git a/src/renderer/src/stories/inspector/test.json b/src/renderer/src/stories/preview/inspector/test.json similarity index 100% rename from src/renderer/src/stories/inspector/test.json rename to src/renderer/src/stories/preview/inspector/test.json From 17e64b89783aa121351f509fea062d47a6c46ddf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 17:14:54 +0000 Subject: [PATCH 04/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pyflask/apis/neuroconv.py | 12 ++++-- pyflask/manageNeuroconv/__init__.py | 2 +- pyflask/manageNeuroconv/manage_neuroconv.py | 7 +++- .../pages/guided-mode/data/GuidedMetadata.js | 10 +++-- .../src/stories/preview/NWBFilePreview.js | 39 ++++++++++++------- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/pyflask/apis/neuroconv.py b/pyflask/apis/neuroconv.py index 91d5f84d9..2405a8311 100644 --- a/pyflask/apis/neuroconv.py +++ b/pyflask/apis/neuroconv.py @@ -121,24 +121,28 @@ def post(self): if notBadRequestException(e): neuroconv_api.abort(500, str(e)) -@neuroconv_api.route('/inspect_nwbfile') + +@neuroconv_api.route("/inspect_nwbfile") class InspectNWBFile(Resource): @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) def post(self): try: from manageNeuroconv import inspect_nwbfile + return inspect_nwbfile(**neuroconv_api.payload) except Exception as e: if notBadRequestException(e): neuroconv_api.abort(500, str(e)) -@neuroconv_api.route('/inspect') + +@neuroconv_api.route("/inspect") class InspectNWBFiles(Resource): @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) def post(self): try: from manageNeuroconv import inspect + return inspect(**neuroconv_api.payload) except Exception as e: @@ -146,12 +150,13 @@ def post(self): neuroconv_api.abort(500, str(e)) -@neuroconv_api.route('/html') +@neuroconv_api.route("/html") class NWBToHTML(Resource): @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) def post(self): try: from manageNeuroconv import nwb_to_html + return nwb_to_html(**neuroconv_api.payload) except Exception as e: @@ -159,7 +164,6 @@ def post(self): neuroconv_api.abort(500, str(e)) - @neuroconv_api.route("/generate_dataset") class GenerateDataset(Resource): @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) diff --git a/pyflask/manageNeuroconv/__init__.py b/pyflask/manageNeuroconv/__init__.py index 234fe2ef2..dd4fd2327 100644 --- a/pyflask/manageNeuroconv/__init__.py +++ b/pyflask/manageNeuroconv/__init__.py @@ -10,7 +10,7 @@ generate_dataset, inspect, inspect_nwbfile, - nwb_to_html + nwb_to_html, ) diff --git a/pyflask/manageNeuroconv/manage_neuroconv.py b/pyflask/manageNeuroconv/manage_neuroconv.py index 28378357d..fe482b402 100644 --- a/pyflask/manageNeuroconv/manage_neuroconv.py +++ b/pyflask/manageNeuroconv/manage_neuroconv.py @@ -327,25 +327,30 @@ def update_conversion_progress(**kwargs): if not default_output_directory.exists(): os.symlink(resolved_output_directory, default_output_directory) - return dict(file=str(resolved_output_path)) + def nwb_to_html(nwbfile_path: str): from pynwb import NWBHDF5IO + io = NWBHDF5IO(nwbfile_path, mode="r") file = io.read() html = file._repr_html_() io.close() return html + def inspect_nwbfile(**kwargs): from nwbinspector import inspect_nwbfile from nwbinspector.nwbinspector import InspectorOutputJSONEncoder + return json.loads(json.dumps(list(inspect_nwbfile(**kwargs)), cls=InspectorOutputJSONEncoder)) + def inspect(**kwargs): from nwbinspector import inspect_all from nwbinspector.nwbinspector import InspectorOutputJSONEncoder + return json.loads(json.dumps(list(inspect_all(**kwargs)), cls=InspectorOutputJSONEncoder)) diff --git a/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js b/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js index f23a0d00a..eb95cc97a 100644 --- a/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js +++ b/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js @@ -39,7 +39,9 @@ export class GuidedMetadataPage extends ManagedPage { // Preview a single random conversion delete this.info.globalState.stubs; // Clear the preview results - const stubs = await this.runConversions({ stub_test: true }, 1, { title: "Testing conversion on a random session" }); + const stubs = await this.runConversions({ stub_test: true }, 1, { + title: "Testing conversion on a random session", + }); // Save the preview results this.info.globalState.stubs = stubs; @@ -183,7 +185,7 @@ export class GuidedMetadataPage extends ManagedPage { onClick: async (key, el) => { const { subject, session } = getInfoFromId(key); - const [ file ] = await this.runConversions( + const [file] = await this.runConversions( { stub_test: true }, [ { @@ -206,9 +208,9 @@ export class GuidedMetadataPage extends ManagedPage { height: "100%", }); - const { project } = this.info.globalState + const { project } = this.info.globalState; - modal.append(new NWBFilePreview({ project: project.name, files: [file] })); + modal.append(new NWBFilePreview({ project: project.name, files: [file] })); document.body.append(modal); }, }, diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index cb9866e24..6ada29312 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -3,7 +3,7 @@ import { InspectorList } from "./inspector/InspectorList"; import { Neurosift, getURLFromFilePath } from "./Neurosift"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { run } from "../pages/guided-mode/options/utils"; -import { until } from 'lit/directives/until.js'; +import { until } from "lit/directives/until.js"; export class NWBFilePreview extends LitElement { static get styles() { @@ -19,31 +19,42 @@ export class NWBFilePreview extends LitElement { constructor({ project, files = [] }) { super(); this.project = project; - this.files = files + this.files = files; } render() { - - const filepath = this.files[0].file - console.log(filepath, this.files[0]) + const filepath = this.files[0].file; + console.log(filepath, this.files[0]); return html`
    ${navigator.onLine ? new Neurosift({ url: getURLFromFilePath(filepath, this.project) }) - : html`
    ${ - until((async () => unsafeHTML(await run('html', { nwbfile_path: filepath })))(), 'Loading HTML representation...') - }
    `} + : html`
    + ${until( + (async () => unsafeHTML(await run("html", { nwbfile_path: filepath })))(), + "Loading HTML representation..." + )} +
    `}

    Inspector Report

    - ${until((async () => { - const list = new InspectorList({ items: await run('inspect_nwbfile', { nwbfile_path: filepath, ignore: ["check_description"] }), listStyles: { maxWidth: "350px" } }); - list.style.padding = "10px"; - return list; - })(), html`Loading inspector report...`)} + ${until( + (async () => { + const list = new InspectorList({ + items: await run("inspect_nwbfile", { + nwbfile_path: filepath, + ignore: ["check_description"], + }), + listStyles: { maxWidth: "350px" }, + }); + list.style.padding = "10px"; + return list; + })(), + html`Loading inspector report...` + )}
    -
    ` + `; } } From 9c7dd03afee505361c93ed698565881451a0f55b Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 10:23:49 -0700 Subject: [PATCH 05/43] Fix JSON Schema errors --- src/renderer/src/stories/JSONSchemaForm.js | 30 ++----------------- .../preview/inspector/InspectorList.js | 5 +++- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/src/renderer/src/stories/JSONSchemaForm.js b/src/renderer/src/stories/JSONSchemaForm.js index 3b9c8633a..abb1918d1 100644 --- a/src/renderer/src/stories/JSONSchemaForm.js +++ b/src/renderer/src/stories/JSONSchemaForm.js @@ -30,30 +30,6 @@ const componentCSS = ` background: rgb(255, 229, 228) !important; } - .errors { - color: #9d0b0b; - } - - .errors > * { - padding: 25px; - background: #f8d7da; - border: 1px solid #f5c2c7; - border-radius: 4px; - margin: 0 0 1em; - } - - .warnings { - color: #856404; - } - - .warnings > * { - padding: 25px; - background: #fff3cd; - border: 1px solid #ffeeba; - border-radius: 4px; - margin: 0 0 1em; - } - .guided--form-label { display: block; width: 100%; @@ -278,7 +254,7 @@ export class JSONSchemaForm extends LitElement { #addMessage = (name, message, type) => { if (Array.isArray(name)) name = name.join("-"); // Convert array to string const container = this.shadowRoot.querySelector(`#${name} .${type}`); - const item = new InspectorListItem({ message }); + const item = new InspectorListItem(message); container.appendChild(item); }; @@ -625,7 +601,7 @@ export class JSONSchemaForm extends LitElement { this.checkStatus(); // Show aggregated errors and warnings (if any) - warnings.forEach((info) => this.#addMessage(fullPath, info.message, "warnings")); + warnings.forEach((info) => this.#addMessage(fullPath, info, "warnings")); const isFunction = typeof valid === "function"; @@ -658,7 +634,7 @@ export class JSONSchemaForm extends LitElement { [...path, name] ); - errors.forEach((info) => this.#addMessage(fullPath, info.message, "errors")); + errors.forEach((info) => this.#addMessage(fullPath, info, "errors")); // element.title = errors.map((info) => info.message).join("\n"); // Set all errors to show on hover return false; diff --git a/src/renderer/src/stories/preview/inspector/InspectorList.js b/src/renderer/src/stories/preview/inspector/InspectorList.js index 803a2af33..f1df9c181 100644 --- a/src/renderer/src/stories/preview/inspector/InspectorList.js +++ b/src/renderer/src/stories/preview/inspector/InspectorList.js @@ -78,8 +78,11 @@ export class InspectorListItem extends LitElement { } `; } + + constructor(props) { super(); + this.ORIGINAL_TYPE = props.type Object.assign(this, props); } @@ -93,7 +96,7 @@ export class InspectorListItem extends LitElement { } render() { - this.type = this.importance === "CRITICAL" ? "error" : "warning"; + this.type = this.ORIGINAL_TYPE ?? (this.importance === "CRITICAL" ? "error" : "warning"); this.setAttribute("title", this.message); From d2ebbdfc47d9859c549b84fb74aff065d2e6ff2e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 17:25:11 +0000 Subject: [PATCH 06/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/stories/preview/inspector/InspectorList.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/renderer/src/stories/preview/inspector/InspectorList.js b/src/renderer/src/stories/preview/inspector/InspectorList.js index f1df9c181..8897c6ea3 100644 --- a/src/renderer/src/stories/preview/inspector/InspectorList.js +++ b/src/renderer/src/stories/preview/inspector/InspectorList.js @@ -79,10 +79,9 @@ export class InspectorListItem extends LitElement { `; } - constructor(props) { super(); - this.ORIGINAL_TYPE = props.type + this.ORIGINAL_TYPE = props.type; Object.assign(this, props); } From e6f29b0edc89854b56ae7d686793442baec3756e Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 14:57:05 -0700 Subject: [PATCH 07/43] Proper merge --- .../src/stories/preview/NWBFilePreview.js | 136 ++++++++++++++---- .../preview/inspector/InspectorList.js | 5 + 2 files changed, 110 insertions(+), 31 deletions(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 6ada29312..bbf1c143f 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -4,6 +4,49 @@ import { Neurosift, getURLFromFilePath } from "./Neurosift"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { run } from "../pages/guided-mode/options/utils"; import { until } from "lit/directives/until.js"; +import { InstanceManager } from "../InstanceManager"; + +function sharedPath(array){ + const mapped = array.map(str => str.split('/')) + let shared = mapped.shift() + mapped.forEach((arr, i) => { + for (let j in arr) { + if (arr[j] !== shared[j]) { + shared = shared.slice(0, j) + break; + } + } + }) + + return shared.join('/') +} + +class NWBPreviewInstance extends LitElement { + constructor({ file }, project){ + super() + this.file = file + this.project = project + + window.addEventListener("online", () => this.requestUpdate()); + window.addEventListener("offline", () => this.requestUpdate()); + } + + render(){ + const isOnline = navigator.onLine + + return isOnline + ? new Neurosift({ url: getURLFromFilePath(this.file, this.project) }) + : until( + (async () => { + const htmlRep = await run("html", { nwbfile_path: this.file}) + return unsafeHTML(htmlRep) + })(), + html`Loading HTML representation...` + ) + } +} + +customElements.get("nwb-preview-instance") || customElements.define("nwb-preview-instance", NWBPreviewInstance); export class NWBFilePreview extends LitElement { static get styles() { @@ -16,45 +59,76 @@ export class NWBFilePreview extends LitElement { `; } - constructor({ project, files = [] }) { + constructor({ files = {}, project}) { super(); this.project = project; this.files = files; } + createInstance = ({ subject, session, info }) => { + return { + subject, + session, + display: new NWBPreviewInstance(info, this.project) + } + }; + + render() { - const filepath = this.files[0].file; - console.log(filepath, this.files[0]); + + const fileArr = Object.entries(this.files).map(([subject, v]) => Object.entries(v).map(([session, info]) => { return { subject, session, info } })).flat() + + const onlyFirstFile = fileArr.length <= 1 return html`
    -
    - ${navigator.onLine - ? new Neurosift({ url: getURLFromFilePath(filepath, this.project) }) - : html`
    - ${until( - (async () => unsafeHTML(await run("html", { nwbfile_path: filepath })))(), - "Loading HTML representation..." - )} -
    `} -
    -
    -

    Inspector Report

    - ${until( - (async () => { - const list = new InspectorList({ - items: await run("inspect_nwbfile", { - nwbfile_path: filepath, - ignore: ["check_description"], - }), - listStyles: { maxWidth: "350px" }, - }); - list.style.padding = "10px"; - return list; - })(), - html`Loading inspector report...` - )} -
    -
    `; +
    + ${(() => { + if (onlyFirstFile) return new NWBPreviewInstance(fileArr[0].info, this.project) + else { + const _instances = fileArr.map(this.createInstance); + + const instances = _instances.reduce((acc, { subject, session, display }) => { + if (!acc[`sub-${subject}`]) acc[`sub-${subject}`] = {}; + acc[`sub-${subject}`][`ses-${session}`] = display; + return acc; + }, {}); + + return new InstanceManager({ + header: "Sessions", + instanceType: "Session", + instances, + }) + } + + })()} +
    +
    +

    Inspector Report

    + ${until( + (async () => { + + const opts = { ignore: ["check_description"] } + + const items = onlyFirstFile ? await run("inspect_nwbfile", { nwbfile_path: fileArr[0].info.file, ...opts }) // Inspect the first file + : await (async () => { + const path = sharedPath(fileArr.map(o => o.info.file)) + return (await run("inspect", { path, ...opts })).map(o => { + o.file_path = o.file_path.replace(`${path}/`, '') + return o + }) + })() + + const list = new InspectorList({ + items: items, + listStyles: { maxWidth: "350px" }, + }); + list.style.padding = "10px"; + return list; + })(), + html`Loading inspector report...` + )} +
    + `; } } diff --git a/src/renderer/src/stories/preview/inspector/InspectorList.js b/src/renderer/src/stories/preview/inspector/InspectorList.js index 8897c6ea3..7915b048b 100644 --- a/src/renderer/src/stories/preview/inspector/InspectorList.js +++ b/src/renderer/src/stories/preview/inspector/InspectorList.js @@ -55,6 +55,10 @@ export class InspectorListItem extends LitElement { font-weight: bold; } + #filepath { + font-size: 10px; + } + :host > * { margin: 0px; } @@ -103,6 +107,7 @@ export class InspectorListItem extends LitElement { const hasMetadata = hasObjectType && "object_name" in this; return html` + ${this.file_path ? html`${this.file_path}` : ''} ${hasMetadata ? html`${this.message}` : html`

    ${this.message}

    `} ${hasMetadata ? html`${this.object_name}${hasObjectType ? ` (${this.object_type})` : ""} ` From a7c6882100500b3ea5e4a68c6e44122c652ec93f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 21:57:05 +0000 Subject: [PATCH 08/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../stories/pages/guided-mode/options/GuidedStubPreview.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js index fad65bc2e..53a7c56c5 100644 --- a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js +++ b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js @@ -40,9 +40,7 @@ export class GuidedStubPreviewPage extends Page { const { stubs, project } = this.info.globalState; return stubs - ? - new NWBFilePreview({ project: project.name, files: stubs }) - + ? new NWBFilePreview({ project: project.name, files: stubs }) : html`

    Your conversion preview failed. Please try again.

    `; } } From 6e6d68086cfdcf2b490825d39148862ad45245e8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 21:57:32 +0000 Subject: [PATCH 09/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../src/stories/preview/NWBFilePreview.js | 125 +++++++++--------- .../preview/inspector/InspectorList.js | 2 +- 2 files changed, 65 insertions(+), 62 deletions(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index bbf1c143f..d3eb469f4 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -6,43 +6,43 @@ import { run } from "../pages/guided-mode/options/utils"; import { until } from "lit/directives/until.js"; import { InstanceManager } from "../InstanceManager"; -function sharedPath(array){ - const mapped = array.map(str => str.split('/')) - let shared = mapped.shift() +function sharedPath(array) { + const mapped = array.map((str) => str.split("/")); + let shared = mapped.shift(); mapped.forEach((arr, i) => { for (let j in arr) { if (arr[j] !== shared[j]) { - shared = shared.slice(0, j) + shared = shared.slice(0, j); break; } } - }) + }); - return shared.join('/') + return shared.join("/"); } class NWBPreviewInstance extends LitElement { - constructor({ file }, project){ - super() - this.file = file - this.project = project + constructor({ file }, project) { + super(); + this.file = file; + this.project = project; window.addEventListener("online", () => this.requestUpdate()); - window.addEventListener("offline", () => this.requestUpdate()); + window.addEventListener("offline", () => this.requestUpdate()); } - - render(){ - const isOnline = navigator.onLine + + render() { + const isOnline = navigator.onLine; return isOnline - ? new Neurosift({ url: getURLFromFilePath(this.file, this.project) }) - : until( - (async () => { - const htmlRep = await run("html", { nwbfile_path: this.file}) - return unsafeHTML(htmlRep) - })(), - html`Loading HTML representation...` - ) + ? new Neurosift({ url: getURLFromFilePath(this.file, this.project) }) + : until( + (async () => { + const htmlRep = await run("html", { nwbfile_path: this.file }); + return unsafeHTML(htmlRep); + })(), + html`Loading HTML representation...` + ); } } @@ -59,7 +59,7 @@ export class NWBFilePreview extends LitElement { `; } - constructor({ files = {}, project}) { + constructor({ files = {}, project }) { super(); this.project = project; this.files = files; @@ -69,21 +69,25 @@ export class NWBFilePreview extends LitElement { return { subject, session, - display: new NWBPreviewInstance(info, this.project) - } + display: new NWBPreviewInstance(info, this.project), + }; }; - render() { + const fileArr = Object.entries(this.files) + .map(([subject, v]) => + Object.entries(v).map(([session, info]) => { + return { subject, session, info }; + }) + ) + .flat(); - const fileArr = Object.entries(this.files).map(([subject, v]) => Object.entries(v).map(([session, info]) => { return { subject, session, info } })).flat() - - const onlyFirstFile = fileArr.length <= 1 + const onlyFirstFile = fileArr.length <= 1; return html`
    -
    +
    ${(() => { - if (onlyFirstFile) return new NWBPreviewInstance(fileArr[0].info, this.project) + if (onlyFirstFile) return new NWBPreviewInstance(fileArr[0].info, this.project); else { const _instances = fileArr.map(this.createInstance); @@ -97,38 +101,37 @@ export class NWBFilePreview extends LitElement { header: "Sessions", instanceType: "Session", instances, - }) + }); } - })()} -
    -
    -

    Inspector Report

    - ${until( - (async () => { - - const opts = { ignore: ["check_description"] } - - const items = onlyFirstFile ? await run("inspect_nwbfile", { nwbfile_path: fileArr[0].info.file, ...opts }) // Inspect the first file - : await (async () => { - const path = sharedPath(fileArr.map(o => o.info.file)) - return (await run("inspect", { path, ...opts })).map(o => { - o.file_path = o.file_path.replace(`${path}/`, '') - return o - }) - })() - - const list = new InspectorList({ - items: items, - listStyles: { maxWidth: "350px" }, - }); - list.style.padding = "10px"; - return list; - })(), - html`Loading inspector report...` - )} -
    -
    `; +
    +
    +

    Inspector Report

    + ${until( + (async () => { + const opts = { ignore: ["check_description"] }; + + const items = onlyFirstFile + ? await run("inspect_nwbfile", { nwbfile_path: fileArr[0].info.file, ...opts }) // Inspect the first file + : await (async () => { + const path = sharedPath(fileArr.map((o) => o.info.file)); + return (await run("inspect", { path, ...opts })).map((o) => { + o.file_path = o.file_path.replace(`${path}/`, ""); + return o; + }); + })(); + + const list = new InspectorList({ + items: items, + listStyles: { maxWidth: "350px" }, + }); + list.style.padding = "10px"; + return list; + })(), + html`Loading inspector report...` + )} +
    + `; } } diff --git a/src/renderer/src/stories/preview/inspector/InspectorList.js b/src/renderer/src/stories/preview/inspector/InspectorList.js index 7915b048b..5475d3a5e 100644 --- a/src/renderer/src/stories/preview/inspector/InspectorList.js +++ b/src/renderer/src/stories/preview/inspector/InspectorList.js @@ -107,7 +107,7 @@ export class InspectorListItem extends LitElement { const hasMetadata = hasObjectType && "object_name" in this; return html` - ${this.file_path ? html`${this.file_path}` : ''} + ${this.file_path ? html`${this.file_path}` : ""} ${hasMetadata ? html`${this.message}` : html`

    ${this.message}

    `} ${hasMetadata ? html`${this.object_name}${hasObjectType ? ` (${this.object_type})` : ""} ` From 566e7e01a58aecef8b28e428cd25ad2565598958 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 14:59:06 -0700 Subject: [PATCH 10/43] Fix results page --- .../src/stories/pages/guided-mode/results/GuidedResults.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/stories/pages/guided-mode/results/GuidedResults.js b/src/renderer/src/stories/pages/guided-mode/results/GuidedResults.js index f0a97aed5..b8aba9782 100644 --- a/src/renderer/src/stories/pages/guided-mode/results/GuidedResults.js +++ b/src/renderer/src/stories/pages/guided-mode/results/GuidedResults.js @@ -93,7 +93,7 @@ export class GuidedResultsPage extends Page {
      ${Object.values(results) - .flat((v) => Object.values(v)) + .map((v) => Object.values(v)).flat() .map((o) => html`
    1. ${o.file}
    2. `)}
    From 385ac1074c424412191b6030fa6f58bcbdda1ffc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 21:59:42 +0000 Subject: [PATCH 11/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../src/stories/pages/guided-mode/results/GuidedResults.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/stories/pages/guided-mode/results/GuidedResults.js b/src/renderer/src/stories/pages/guided-mode/results/GuidedResults.js index b8aba9782..fc3d64df2 100644 --- a/src/renderer/src/stories/pages/guided-mode/results/GuidedResults.js +++ b/src/renderer/src/stories/pages/guided-mode/results/GuidedResults.js @@ -93,7 +93,8 @@ export class GuidedResultsPage extends Page {
      ${Object.values(results) - .map((v) => Object.values(v)).flat() + .map((v) => Object.values(v)) + .flat() .map((o) => html`
    1. ${o.file}
    2. `)}
    From ab5c97cfe02b6c751deefbb020b95cd1f045f1cd Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 15:09:09 -0700 Subject: [PATCH 12/43] Update popup title --- src/renderer/src/stories/preview/NWBFilePreview.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index d3eb469f4..7ac883522 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -101,6 +101,10 @@ export class NWBFilePreview extends LitElement { header: "Sessions", instanceType: "Session", instances, + // renderInstance: (_, value) => { + // console.log('RENDERING', value) + // return value.content ?? value; + // } }); } })()} @@ -111,11 +115,13 @@ export class NWBFilePreview extends LitElement { (async () => { const opts = { ignore: ["check_description"] }; + const title = 'Inspecting your file' + const items = onlyFirstFile - ? await run("inspect_nwbfile", { nwbfile_path: fileArr[0].info.file, ...opts }) // Inspect the first file + ? await run("inspect_nwbfile", { nwbfile_path: fileArr[0].info.file, ...opts }, { title }) // Inspect the first file : await (async () => { const path = sharedPath(fileArr.map((o) => o.info.file)); - return (await run("inspect", { path, ...opts })).map((o) => { + return (await run("inspect", { path, ...opts }, { title: title + 's' })).map((o) => { o.file_path = o.file_path.replace(`${path}/`, ""); return o; }); From 1592ca865e7034912649cd6ad556fdeac3d9e107 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 22:09:57 +0000 Subject: [PATCH 13/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/stories/preview/NWBFilePreview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 7ac883522..bb17bd289 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -115,13 +115,13 @@ export class NWBFilePreview extends LitElement { (async () => { const opts = { ignore: ["check_description"] }; - const title = 'Inspecting your file' + const title = "Inspecting your file"; const items = onlyFirstFile ? await run("inspect_nwbfile", { nwbfile_path: fileArr[0].info.file, ...opts }, { title }) // Inspect the first file : await (async () => { const path = sharedPath(fileArr.map((o) => o.info.file)); - return (await run("inspect", { path, ...opts }, { title: title + 's' })).map((o) => { + return (await run("inspect", { path, ...opts }, { title: title + "s" })).map((o) => { o.file_path = o.file_path.replace(`${path}/`, ""); return o; }); From 6814cadbbb522e58ef150083ba8d3ed7341fba25 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 22:24:53 +0000 Subject: [PATCH 14/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/progress.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/renderer/src/progress.js b/src/renderer/src/progress.js index b3ce59c96..724483a6b 100644 --- a/src/renderer/src/progress.js +++ b/src/renderer/src/progress.js @@ -57,8 +57,8 @@ export const getCurrentProjectName = () => { function updateURLParams(paramsToUpdate) { const params = new URLSearchParams(location.search); for (let key in paramsToUpdate) { - const value = paramsToUpdate[key] - if (value == undefined) params.remove(key) + const value = paramsToUpdate[key]; + if (value == undefined) params.remove(key); else params.set(key, value); } @@ -76,11 +76,11 @@ export const updateAppProgress = ( const transitionOffPipeline = pageId && pageId.split("/")[0] !== "conversion"; if (transitionOffPipeline) { - updateURLParams({ project: undefined }) + updateURLParams({ project: undefined }); return; // Only save last page if within the conversion workflow } - if (projectName) updateURLParams({ project: projectName }) + if (projectName) updateURLParams({ project: projectName }); // Is a project name if (dataOrProjectName === projectName) updateFile(dataOrProjectName, (data) => (data["page-before-exit"] = pageId)); @@ -165,7 +165,7 @@ export function resume(name) { const global = this ? this.load(name) : get(name); const commandToResume = global["page-before-exit"] || "conversion/start"; - updateURLParams({ project: name }) + updateURLParams({ project: name }); if (this) this.onTransition(commandToResume); From f4991506c842ff6babafbf10ea03c3d8db22ec8a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 22:25:58 +0000 Subject: [PATCH 15/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/progress.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/progress.js b/src/renderer/src/progress.js index 2a3320c6f..329b379ed 100644 --- a/src/renderer/src/progress.js +++ b/src/renderer/src/progress.js @@ -64,7 +64,7 @@ function updateURLParams(paramsToUpdate) { // Update browser history state const value = `${location.pathname}?${params}`; - Object.assign(history.state, paramsToUpdate) + Object.assign(history.state, paramsToUpdate); window.history.pushState(history.state, null, value); } From 55641ec9026025abea8b49e7300e389b8bd6fdf2 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 15:42:17 -0700 Subject: [PATCH 16/43] Create instance renders dynamically --- src/renderer/src/stories/InstanceManager.js | 24 +++++++++++++++---- .../src/stories/preview/NWBFilePreview.js | 11 +++------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/renderer/src/stories/InstanceManager.js b/src/renderer/src/stories/InstanceManager.js index 328882416..7f1fe6a7d 100644 --- a/src/renderer/src/stories/InstanceManager.js +++ b/src/renderer/src/stories/InstanceManager.js @@ -132,13 +132,20 @@ export class InstanceManager extends LitElement { this.instances = props.instances ?? {}; this.header = props.header; this.instanceType = props.instanceType ?? "Instance"; - if (props.renderInstance) this.renderInstance = props.renderInstance; if (props.onAdded) this.onAdded = props.onAdded; if (props.onRemoved) this.onRemoved = props.onRemoved; this.controls = props.controls ?? []; } - renderInstance = (_, value) => value.content ?? value; + #dynamicInstances = {} + + getInstanceContent = ({ id, metadata }) => { + const content = metadata.content ?? metadata; + if (typeof content === 'function') { + this.#dynamicInstances[id] = content + return '' + } else return content + } updateState = (id, state) => { const item = this.#items.find((i) => i.id === id); @@ -211,6 +218,8 @@ export class InstanceManager extends LitElement { el.removeAttribute("selected"); this.shadowRoot.querySelector(`div[data-instance="${instance}"]`).setAttribute("hidden", ""); }); + + this.#onSelected() }; #isCategory(value) { @@ -258,6 +267,11 @@ export class InstanceManager extends LitElement { #onSelected = () => { const selected = this.shadowRoot.querySelector("#selectedName"); selected.innerText = this.#selected; + + const dynamic = this.#dynamicInstances[this.#selected] + if (typeof dynamic === 'function') { + this.shadowRoot.querySelector(`div[data-instance="${this.#selected}"]`).append(this.#dynamicInstances[this.#selected] = dynamic()) + } }; #render(toRender = this.instances, path = []) { @@ -293,7 +307,7 @@ export class InstanceManager extends LitElement { const list = html` ${Object.entries(instances).map(([key, info], i) => { - if (info instanceof HTMLElement) info = { content: info }; + if (info instanceof HTMLElement || typeof info === 'function') info = { content: info }; const listItemInfo = { id: key, label: key.split("/").pop(), @@ -399,7 +413,7 @@ export class InstanceManager extends LitElement {
    - ${this.#selected} +
    ${this.controls.map((o) => { return html` { return html`
    - ${this.renderInstance(item.id, item.metadata)} + ${this.getInstanceContent(item)}
    `; })} diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index bb17bd289..33e57ed8e 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -69,7 +69,7 @@ export class NWBFilePreview extends LitElement { return { subject, session, - display: new NWBPreviewInstance(info, this.project), + display: () => new NWBPreviewInstance(info, this.project), }; }; @@ -98,13 +98,8 @@ export class NWBFilePreview extends LitElement { }, {}); return new InstanceManager({ - header: "Sessions", - instanceType: "Session", - instances, - // renderInstance: (_, value) => { - // console.log('RENDERING', value) - // return value.content ?? value; - // } + header: "Stub Files", + instances }); } })()} From bbb1cebc790d3fe2a6afa2c01cecaa015e6b3c6c Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 16:07:21 -0700 Subject: [PATCH 17/43] Show loader for iframe --- src/renderer/src/stories/preview/Neurosift.js | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/stories/preview/Neurosift.js b/src/renderer/src/stories/preview/Neurosift.js index faa7716cf..83eca75df 100644 --- a/src/renderer/src/stories/preview/Neurosift.js +++ b/src/renderer/src/stories/preview/Neurosift.js @@ -1,6 +1,8 @@ import { LitElement, css, html } from "lit"; import { baseUrl } from "../../globals"; +import { Loader } from '../Loader' + export function getURLFromFilePath(file, projectName) { const regexp = new RegExp(`.+(${projectName}.+)`); return `${baseUrl}/stubs/${file.match(regexp)[1]}`; @@ -9,9 +11,36 @@ export function getURLFromFilePath(file, projectName) { export class Neurosift extends LitElement { static get styles() { return css` - iframe { + + :host { width: 100%; height: 100%; + display: grid; + grid-template-rows: 100%; + grid-template-columns: 100%; + --loader-color: hsl(200, 80%, 50%); + } + + :host > * { + width: 100%; + height: 100%; + } + + div { + display: flex; + align-items: center; + justify-content: center; + } + + span { + font-size: 14px; + } + + small { + padding-left: 10px; + } + + iframe { border: 0; } `; @@ -22,10 +51,26 @@ export class Neurosift extends LitElement { this.url = url; } + render() { - return html``; } } From 14684e3cf38cb59b47305d227c5931d7f62b17cb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 23:07:59 +0000 Subject: [PATCH 18/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/stories/InstanceManager.js | 24 ++++++----- .../src/stories/preview/NWBFilePreview.js | 2 +- src/renderer/src/stories/preview/Neurosift.js | 40 +++++++++---------- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/renderer/src/stories/InstanceManager.js b/src/renderer/src/stories/InstanceManager.js index 7f1fe6a7d..639766b94 100644 --- a/src/renderer/src/stories/InstanceManager.js +++ b/src/renderer/src/stories/InstanceManager.js @@ -137,15 +137,15 @@ export class InstanceManager extends LitElement { this.controls = props.controls ?? []; } - #dynamicInstances = {} + #dynamicInstances = {}; getInstanceContent = ({ id, metadata }) => { const content = metadata.content ?? metadata; - if (typeof content === 'function') { - this.#dynamicInstances[id] = content - return '' - } else return content - } + if (typeof content === "function") { + this.#dynamicInstances[id] = content; + return ""; + } else return content; + }; updateState = (id, state) => { const item = this.#items.find((i) => i.id === id); @@ -219,7 +219,7 @@ export class InstanceManager extends LitElement { this.shadowRoot.querySelector(`div[data-instance="${instance}"]`).setAttribute("hidden", ""); }); - this.#onSelected() + this.#onSelected(); }; #isCategory(value) { @@ -268,9 +268,11 @@ export class InstanceManager extends LitElement { const selected = this.shadowRoot.querySelector("#selectedName"); selected.innerText = this.#selected; - const dynamic = this.#dynamicInstances[this.#selected] - if (typeof dynamic === 'function') { - this.shadowRoot.querySelector(`div[data-instance="${this.#selected}"]`).append(this.#dynamicInstances[this.#selected] = dynamic()) + const dynamic = this.#dynamicInstances[this.#selected]; + if (typeof dynamic === "function") { + this.shadowRoot + .querySelector(`div[data-instance="${this.#selected}"]`) + .append((this.#dynamicInstances[this.#selected] = dynamic())); } }; @@ -307,7 +309,7 @@ export class InstanceManager extends LitElement { const list = html` ${Object.entries(instances).map(([key, info], i) => { - if (info instanceof HTMLElement || typeof info === 'function') info = { content: info }; + if (info instanceof HTMLElement || typeof info === "function") info = { content: info }; const listItemInfo = { id: key, label: key.split("/").pop(), diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 33e57ed8e..bc8e631be 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -99,7 +99,7 @@ export class NWBFilePreview extends LitElement { return new InstanceManager({ header: "Stub Files", - instances + instances, }); } })()} diff --git a/src/renderer/src/stories/preview/Neurosift.js b/src/renderer/src/stories/preview/Neurosift.js index 83eca75df..098c2da9c 100644 --- a/src/renderer/src/stories/preview/Neurosift.js +++ b/src/renderer/src/stories/preview/Neurosift.js @@ -1,7 +1,7 @@ import { LitElement, css, html } from "lit"; import { baseUrl } from "../../globals"; -import { Loader } from '../Loader' +import { Loader } from "../Loader"; export function getURLFromFilePath(file, projectName) { const regexp = new RegExp(`.+(${projectName}.+)`); @@ -11,7 +11,6 @@ export function getURLFromFilePath(file, projectName) { export class Neurosift extends LitElement { static get styles() { return css` - :host { width: 100%; height: 100%; @@ -28,7 +27,7 @@ export class Neurosift extends LitElement { div { display: flex; - align-items: center; + align-items: center; justify-content: center; } @@ -51,27 +50,24 @@ export class Neurosift extends LitElement { this.url = url; } - render() { - - return html` -
    -
    - ${new Loader()} - Loading Neurosift view... + return html`
    +
    + ${new Loader()} + Loading Neurosift view... +
    -
    - `; + `; } } From d765b85ad1c27a006dd96f291ebd8746d258f540 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 16:10:59 -0700 Subject: [PATCH 19/43] Improved loader --- src/renderer/src/stories/Loader.ts | 25 +++++++++++++++++-- src/renderer/src/stories/preview/Neurosift.js | 5 +--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/stories/Loader.ts b/src/renderer/src/stories/Loader.ts index 9e0b93062..bd1ca84cf 100644 --- a/src/renderer/src/stories/Loader.ts +++ b/src/renderer/src/stories/Loader.ts @@ -10,6 +10,19 @@ export class Loader extends LitElement { :host { display: block; } + + span { + font-size: 90%; + padding-left: 10px; + } + + :host > div { + display: flex; + align-items: center; + justif-content: center; + } + + .lds-default { display: inline-block; position: relative; @@ -96,12 +109,20 @@ export class Loader extends LitElement { ` } - constructor(){ + declare message: string + + constructor(props: any){ super() + Object.assign(this, props) } render() { - return html`
    ` + return html` +
    +
    + ${this.message ? html`${this.message}` : ''} +
    + ` } } diff --git a/src/renderer/src/stories/preview/Neurosift.js b/src/renderer/src/stories/preview/Neurosift.js index 83eca75df..7daf674b8 100644 --- a/src/renderer/src/stories/preview/Neurosift.js +++ b/src/renderer/src/stories/preview/Neurosift.js @@ -56,10 +56,7 @@ export class Neurosift extends LitElement { return html`
    -
    - ${new Loader()} - Loading Neurosift view... -
    + ${new Loader({ message: 'Loading Neurosift view...' })}
    `; + return html`
    ${new Loader({ message: "Loading Neurosift view..." })}
    + `; } } From 1eb61bc72725b8c1510909234d45cb41066c3f50 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 16:22:29 -0700 Subject: [PATCH 21/43] Avoid loading popup for html request --- src/renderer/src/stories/pages/guided-mode/options/utils.js | 2 +- src/renderer/src/stories/preview/NWBFilePreview.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/stories/pages/guided-mode/options/utils.js b/src/renderer/src/stories/pages/guided-mode/options/utils.js index deda08db6..6d1d0020e 100644 --- a/src/renderer/src/stories/pages/guided-mode/options/utils.js +++ b/src/renderer/src/stories/pages/guided-mode/options/utils.js @@ -21,7 +21,7 @@ export const openProgressSwal = (options) => { }; export const run = async (url, payload, options = {}) => { - const needsSwal = !options.swal; + const needsSwal = !options.swal && options.swal !== false; if (needsSwal) openProgressSwal(options).then((swal) => (options.onOpen ? options.onOpen(swal) : undefined)); const results = await fetch(`${baseUrl}/neuroconv/${url}`, { diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index bc8e631be..6d046b7f1 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -38,7 +38,7 @@ class NWBPreviewInstance extends LitElement { ? new Neurosift({ url: getURLFromFilePath(this.file, this.project) }) : until( (async () => { - const htmlRep = await run("html", { nwbfile_path: this.file }); + const htmlRep = await run("html", { nwbfile_path: this.file }, { swal: false }); return unsafeHTML(htmlRep); })(), html`Loading HTML representation...` From 0344525bd38fc6dd4eaeeea2c7e0943a7b614c76 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 16:23:43 -0700 Subject: [PATCH 22/43] Update index.ts --- src/renderer/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/index.ts b/src/renderer/src/index.ts index dca74bf7b..efbc4b093 100644 --- a/src/renderer/src/index.ts +++ b/src/renderer/src/index.ts @@ -50,7 +50,7 @@ async function isOffline() { await Swal.fire({ title: "No Internet Connection", icon: "warning", - text: "It appears that your computer is not connected to the internet. You may continue, but you will not be able to use certain features (e.g. uploading data to DANDI, viewing data on Neurosift, etc.).", + text: "You may continue, but certain features (e.g. uploading data to DANDI, viewing data on Neurosift, etc.) will be unavailable.", heightAuto: false, backdrop: "rgba(0,0,0, 0.4)", confirmButtonText: "I understand", From 68ac48d23974476b9fe8ccc4369b9d43e5ba1a00 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 23 Aug 2023 16:35:23 -0700 Subject: [PATCH 23/43] Fix list re-rendering --- src/renderer/src/stories/List.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/stories/List.ts b/src/renderer/src/stories/List.ts index 2d8b39156..1b9bb2210 100644 --- a/src/renderer/src/stories/List.ts +++ b/src/renderer/src/stories/List.ts @@ -102,7 +102,16 @@ export class List extends LitElement { return this.items.map(o => o.value) } - declare items: ListItemType[] + #items: ListItemType[] = [] + set items(value) { + const oldVal = this.#items + this.#items = value + this.onChange() + this.requestUpdate('items', oldVal) + } + + get items() { return this.#items } + declare emptyMessage: string @@ -125,11 +134,6 @@ export class List extends LitElement { } - attributeChangedCallback(name: string, _old: string | null, value: string | null): void { - super.attributeChangedCallback(name, _old, value) - if (name === 'items') this.onChange() - } - add = (item: ListItemType) => { this.items = [...this.items, item] } From e3f1dc3fe7fea7a78ca1d16c69d2be6d8b13a3d4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 23:36:06 +0000 Subject: [PATCH 24/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/stories/List.ts | 2 +- src/renderer/src/stories/preview/NWBFilePreview.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/stories/List.ts b/src/renderer/src/stories/List.ts index 1b9bb2210..69c57b1b8 100644 --- a/src/renderer/src/stories/List.ts +++ b/src/renderer/src/stories/List.ts @@ -109,7 +109,7 @@ export class List extends LitElement { this.onChange() this.requestUpdate('items', oldVal) } - + get items() { return this.#items } diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 6d046b7f1..cf2746c7a 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -38,7 +38,7 @@ class NWBPreviewInstance extends LitElement { ? new Neurosift({ url: getURLFromFilePath(this.file, this.project) }) : until( (async () => { - const htmlRep = await run("html", { nwbfile_path: this.file }, { swal: false }); + const htmlRep = await run("html", { nwbfile_path: this.file }, { swal: false }); return unsafeHTML(htmlRep); })(), html`Loading HTML representation...` From 6f12ef847cd0da1ce1183b599ebedfd3c3cfcc70 Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Thu, 24 Aug 2023 09:21:14 -0700 Subject: [PATCH 25/43] Update pyflask/apis/neuroconv.py Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> --- pyflask/apis/neuroconv.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/pyflask/apis/neuroconv.py b/pyflask/apis/neuroconv.py index 2405a8311..8c19ab45d 100644 --- a/pyflask/apis/neuroconv.py +++ b/pyflask/apis/neuroconv.py @@ -136,14 +136,31 @@ def post(self): neuroconv_api.abort(500, str(e)) -@neuroconv_api.route("/inspect") -class InspectNWBFiles(Resource): +@neuroconv_api.route("/inspect_folder") +class InspectNWBFolder(Resource): @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) def post(self): try: - from manageNeuroconv import inspect - - return inspect(**neuroconv_api.payload) + import json + from nwbinspector import inspect_all + from nwbinspector.nwbinspector import InspectorOutputJSONEncoder + from nwbinspector.inspector_tools import get_report_header, format_messages + + messages = list( + inspect_all( + n_jobs=-2, # uses number of CPU - 1 + **neuroconv_api.payload, + ) + ) + json_report = dict(header=get_report_header(), messages=messages) + # If you want to get the full JSON listing of all messages to render/organize yourself + # json.dumps(obj=json_report, cls=InspectorOutputJSONEncoder) + formatted_messages = format_messages(messages=messages) + + return formatted_messages + except Exception as e: + if notBadRequestException(e): + neuroconv_api.abort(500, str(e)) except Exception as e: if notBadRequestException(e): From b88c928c058ddfa23c1a3273e4bf2106667faa7b Mon Sep 17 00:00:00 2001 From: Garrett Date: Thu, 24 Aug 2023 09:40:06 -0700 Subject: [PATCH 26/43] Update endpoints --- pyflask/apis/neuroconv.py | 34 +++++++++---------- pyflask/manageNeuroconv/__init__.py | 5 +-- pyflask/manageNeuroconv/manage_neuroconv.py | 24 ------------- .../src/stories/preview/NWBFilePreview.js | 7 ++-- 4 files changed, 23 insertions(+), 47 deletions(-) diff --git a/pyflask/apis/neuroconv.py b/pyflask/apis/neuroconv.py index 8c19ab45d..421a9641e 100644 --- a/pyflask/apis/neuroconv.py +++ b/pyflask/apis/neuroconv.py @@ -122,14 +122,15 @@ def post(self): neuroconv_api.abort(500, str(e)) -@neuroconv_api.route("/inspect_nwbfile") +@neuroconv_api.route("/inspect_file") class InspectNWBFile(Resource): @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) def post(self): try: - from manageNeuroconv import inspect_nwbfile - - return inspect_nwbfile(**neuroconv_api.payload) + import json + from nwbinspector import inspect_nwbfile + from nwbinspector.nwbinspector import InspectorOutputJSONEncoder + return json.loads(json.dumps(list(inspect_nwbfile(**neuroconv_api.payload)), cls=InspectorOutputJSONEncoder)) except Exception as e: if notBadRequestException(e): @@ -144,7 +145,7 @@ def post(self): import json from nwbinspector import inspect_all from nwbinspector.nwbinspector import InspectorOutputJSONEncoder - from nwbinspector.inspector_tools import get_report_header, format_messages + # from nwbinspector.inspector_tools import get_report_header, format_messages messages = list( inspect_all( @@ -152,29 +153,28 @@ def post(self): **neuroconv_api.payload, ) ) - json_report = dict(header=get_report_header(), messages=messages) - # If you want to get the full JSON listing of all messages to render/organize yourself - # json.dumps(obj=json_report, cls=InspectorOutputJSONEncoder) - formatted_messages = format_messages(messages=messages) - return formatted_messages - except Exception as e: - if notBadRequestException(e): - neuroconv_api.abort(500, str(e)) + # json_report = dict(header=get_report_header(), messages=messages) + + # formatted_messages = format_messages(messages=messages) + # return json.loads(json.dumps(obj=json_report, cls=InspectorOutputJSONEncoder)) # Object of type Version is not JSON serializable + return json.loads(json.dumps(messages, cls=InspectorOutputJSONEncoder)) except Exception as e: if notBadRequestException(e): neuroconv_api.abort(500, str(e)) - @neuroconv_api.route("/html") class NWBToHTML(Resource): @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) def post(self): try: - from manageNeuroconv import nwb_to_html - - return nwb_to_html(**neuroconv_api.payload) + from pynwb import NWBHDF5IO + io = NWBHDF5IO(neuroconv_api.payload.nwbfile_path, mode="r") + file = io.read() + html = file._repr_html_() + io.close() + return html except Exception as e: if notBadRequestException(e): diff --git a/pyflask/manageNeuroconv/__init__.py b/pyflask/manageNeuroconv/__init__.py index dd4fd2327..7383954eb 100644 --- a/pyflask/manageNeuroconv/__init__.py +++ b/pyflask/manageNeuroconv/__init__.py @@ -7,10 +7,7 @@ validate_metadata, upload_to_dandi, listen_to_neuroconv_events, - generate_dataset, - inspect, - inspect_nwbfile, - nwb_to_html, + generate_dataset ) diff --git a/pyflask/manageNeuroconv/manage_neuroconv.py b/pyflask/manageNeuroconv/manage_neuroconv.py index fe482b402..29949b0c6 100644 --- a/pyflask/manageNeuroconv/manage_neuroconv.py +++ b/pyflask/manageNeuroconv/manage_neuroconv.py @@ -330,30 +330,6 @@ def update_conversion_progress(**kwargs): return dict(file=str(resolved_output_path)) -def nwb_to_html(nwbfile_path: str): - from pynwb import NWBHDF5IO - - io = NWBHDF5IO(nwbfile_path, mode="r") - file = io.read() - html = file._repr_html_() - io.close() - return html - - -def inspect_nwbfile(**kwargs): - from nwbinspector import inspect_nwbfile - from nwbinspector.nwbinspector import InspectorOutputJSONEncoder - - return json.loads(json.dumps(list(inspect_nwbfile(**kwargs)), cls=InspectorOutputJSONEncoder)) - - -def inspect(**kwargs): - from nwbinspector import inspect_all - from nwbinspector.nwbinspector import InspectorOutputJSONEncoder - - return json.loads(json.dumps(list(inspect_all(**kwargs)), cls=InspectorOutputJSONEncoder)) - - def upload_to_dandi( dandiset_id: str, project: str, diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index cf2746c7a..6edbbbd5c 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -113,10 +113,13 @@ export class NWBFilePreview extends LitElement { const title = "Inspecting your file"; const items = onlyFirstFile - ? await run("inspect_nwbfile", { nwbfile_path: fileArr[0].info.file, ...opts }, { title }) // Inspect the first file + ? (await run("inspect_file", { nwbfile_path: fileArr[0].info.file, ...opts }, { title })).map(o => { + delete o.file_path + return o + }) // Inspect the first file : await (async () => { const path = sharedPath(fileArr.map((o) => o.info.file)); - return (await run("inspect", { path, ...opts }, { title: title + "s" })).map((o) => { + return (await run("inspect_folder", { path, ...opts }, { title: title + "s" })).map((o) => { o.file_path = o.file_path.replace(`${path}/`, ""); return o; }); From 72f339d315e6335999380c994dfbe85e339cd977 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 16:40:25 +0000 Subject: [PATCH 27/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pyflask/apis/neuroconv.py | 8 +++++++- pyflask/manageNeuroconv/__init__.py | 2 +- .../src/stories/preview/NWBFilePreview.js | 20 +++++++++++-------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/pyflask/apis/neuroconv.py b/pyflask/apis/neuroconv.py index 421a9641e..f07ee373f 100644 --- a/pyflask/apis/neuroconv.py +++ b/pyflask/apis/neuroconv.py @@ -130,7 +130,10 @@ def post(self): import json from nwbinspector import inspect_nwbfile from nwbinspector.nwbinspector import InspectorOutputJSONEncoder - return json.loads(json.dumps(list(inspect_nwbfile(**neuroconv_api.payload)), cls=InspectorOutputJSONEncoder)) + + return json.loads( + json.dumps(list(inspect_nwbfile(**neuroconv_api.payload)), cls=InspectorOutputJSONEncoder) + ) except Exception as e: if notBadRequestException(e): @@ -145,6 +148,7 @@ def post(self): import json from nwbinspector import inspect_all from nwbinspector.nwbinspector import InspectorOutputJSONEncoder + # from nwbinspector.inspector_tools import get_report_header, format_messages messages = list( @@ -164,12 +168,14 @@ def post(self): if notBadRequestException(e): neuroconv_api.abort(500, str(e)) + @neuroconv_api.route("/html") class NWBToHTML(Resource): @neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"}) def post(self): try: from pynwb import NWBHDF5IO + io = NWBHDF5IO(neuroconv_api.payload.nwbfile_path, mode="r") file = io.read() html = file._repr_html_() diff --git a/pyflask/manageNeuroconv/__init__.py b/pyflask/manageNeuroconv/__init__.py index 7383954eb..e13648dab 100644 --- a/pyflask/manageNeuroconv/__init__.py +++ b/pyflask/manageNeuroconv/__init__.py @@ -7,7 +7,7 @@ validate_metadata, upload_to_dandi, listen_to_neuroconv_events, - generate_dataset + generate_dataset, ) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 6edbbbd5c..f572df7e3 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -113,16 +113,20 @@ export class NWBFilePreview extends LitElement { const title = "Inspecting your file"; const items = onlyFirstFile - ? (await run("inspect_file", { nwbfile_path: fileArr[0].info.file, ...opts }, { title })).map(o => { - delete o.file_path - return o - }) // Inspect the first file + ? ( + await run("inspect_file", { nwbfile_path: fileArr[0].info.file, ...opts }, { title }) + ).map((o) => { + delete o.file_path; + return o; + }) // Inspect the first file : await (async () => { const path = sharedPath(fileArr.map((o) => o.info.file)); - return (await run("inspect_folder", { path, ...opts }, { title: title + "s" })).map((o) => { - o.file_path = o.file_path.replace(`${path}/`, ""); - return o; - }); + return (await run("inspect_folder", { path, ...opts }, { title: title + "s" })).map( + (o) => { + o.file_path = o.file_path.replace(`${path}/`, ""); + return o; + } + ); })(); const list = new InspectorList({ From c805d034c6b4cd6190d459a944ff2e94f0739ef7 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 25 Aug 2023 08:43:55 -0700 Subject: [PATCH 28/43] Align conversion message with main --- .../src/stories/pages/guided-mode/data/GuidedMetadata.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js b/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js index 558322aac..fc65953bd 100644 --- a/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js +++ b/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js @@ -40,7 +40,7 @@ export class GuidedMetadataPage extends ManagedPage { // Preview a single random conversion delete this.info.globalState.stubs; // Clear the preview results const stubs = await this.runConversions({ stub_test: true }, undefined, { - title: "Testing conversion on a random session", + title: "Running stub conversion on all sessions...", }); // Save the preview results From 6ce616a17f5f4ef11a2406f7329a7406247e93d3 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 25 Aug 2023 08:51:46 -0700 Subject: [PATCH 29/43] Update header for stub preview --- .../guided-mode/options/GuidedStubPreview.js | 30 ++++++++++--------- .../src/stories/preview/NWBFilePreview.js | 4 +-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js index 53a7c56c5..f3898d920 100644 --- a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js +++ b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js @@ -1,27 +1,29 @@ import { html } from "lit"; import { Page } from "../../Page.js"; -// import { unsafeSVG } from "lit/directives/unsafe-svg.js"; -// import folderOpenSVG from "../../../assets/folder_open.svg?raw"; +import { unsafeSVG } from "lit/directives/unsafe-svg.js"; +import folderOpenSVG from "../../../assets/folder_open.svg?raw"; -// import { electron } from "../../../../electron/index.js"; -import { NWBFilePreview } from "../../../preview/NWBFilePreview.js"; -// const { shell } = electron; +import { electron } from "../../../../electron/index.js"; +import { NWBFilePreview, getSharedPath } from "../../../preview/NWBFilePreview.js"; +const { shell } = electron; + +const getStubArray = (stubs) => Object.values(stubs).map(o => Object.values(o)).flat() export class GuidedStubPreviewPage extends Page { constructor(...args) { super(...args); } - // header = { - // subtitle: () => this.info.globalState.stubs?.[0]?.file, - // controls: () => - // html` (shell ? shell.showItemInFolder(this.info.globalState.stubs[0].file) : "")} - // >${unsafeSVG(folderOpenSVG)}`, - // }; + header = { + subtitle: () => `${getStubArray(this.info.globalState.stubs).length} Files`, + controls: () => + html` (shell ? shell.showItemInFolder(getSharedPath(getStubArray(this.info.globalState.stubs).map(o => o.file))) : "")} + >${unsafeSVG(folderOpenSVG)}`, + }; // NOTE: We may want to trigger this whenever (1) this page is visited AND (2) data has been changed. footer = { diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index f572df7e3..8fa0f3d43 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -6,7 +6,7 @@ import { run } from "../pages/guided-mode/options/utils"; import { until } from "lit/directives/until.js"; import { InstanceManager } from "../InstanceManager"; -function sharedPath(array) { +export function getSharedPath(array) { const mapped = array.map((str) => str.split("/")); let shared = mapped.shift(); mapped.forEach((arr, i) => { @@ -120,7 +120,7 @@ export class NWBFilePreview extends LitElement { return o; }) // Inspect the first file : await (async () => { - const path = sharedPath(fileArr.map((o) => o.info.file)); + const path = getSharedPath(fileArr.map((o) => o.info.file)); return (await run("inspect_folder", { path, ...opts }, { title: title + "s" })).map( (o) => { o.file_path = o.file_path.replace(`${path}/`, ""); From 4ceb940b7621f80a3116165c80780f813df275d0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:52:01 +0000 Subject: [PATCH 30/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../pages/guided-mode/options/GuidedStubPreview.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js index f3898d920..b39a1e91b 100644 --- a/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js +++ b/src/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js @@ -8,7 +8,10 @@ import { electron } from "../../../../electron/index.js"; import { NWBFilePreview, getSharedPath } from "../../../preview/NWBFilePreview.js"; const { shell } = electron; -const getStubArray = (stubs) => Object.values(stubs).map(o => Object.values(o)).flat() +const getStubArray = (stubs) => + Object.values(stubs) + .map((o) => Object.values(o)) + .flat(); export class GuidedStubPreviewPage extends Page { constructor(...args) { @@ -20,7 +23,12 @@ export class GuidedStubPreviewPage extends Page { controls: () => html` (shell ? shell.showItemInFolder(getSharedPath(getStubArray(this.info.globalState.stubs).map(o => o.file))) : "")} + @click=${() => + shell + ? shell.showItemInFolder( + getSharedPath(getStubArray(this.info.globalState.stubs).map((o) => o.file)) + ) + : ""} >${unsafeSVG(folderOpenSVG)}`, }; From 697ab70c4f64e6f57056c15277e8dbccf2ac0493 Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Fri, 25 Aug 2023 08:54:04 -0700 Subject: [PATCH 31/43] Update pyflask/apis/neuroconv.py Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> --- pyflask/apis/neuroconv.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pyflask/apis/neuroconv.py b/pyflask/apis/neuroconv.py index f07ee373f..22257b587 100644 --- a/pyflask/apis/neuroconv.py +++ b/pyflask/apis/neuroconv.py @@ -176,10 +176,8 @@ def post(self): try: from pynwb import NWBHDF5IO - io = NWBHDF5IO(neuroconv_api.payload.nwbfile_path, mode="r") - file = io.read() - html = file._repr_html_() - io.close() + with NWBHDF5IO(neuroconv_api.payload.nwbfile_path, mode="r") as io: + html = io.read()._repr_html_() return html except Exception as e: From d245ae9aeb0d9282126843669dd7eeb04bb94021 Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Fri, 25 Aug 2023 08:55:56 -0700 Subject: [PATCH 32/43] Apply suggestions from code review Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> --- pyflask/apis/neuroconv.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pyflask/apis/neuroconv.py b/pyflask/apis/neuroconv.py index 22257b587..9986bd1b5 100644 --- a/pyflask/apis/neuroconv.py +++ b/pyflask/apis/neuroconv.py @@ -132,7 +132,15 @@ def post(self): from nwbinspector.nwbinspector import InspectorOutputJSONEncoder return json.loads( - json.dumps(list(inspect_nwbfile(**neuroconv_api.payload)), cls=InspectorOutputJSONEncoder) + json.dumps( + list( + inspect_nwbfile( + ignore=["check_description", "check_data_orientation"], # TODO: remove when metadata control is exposed + **neuroconv_api.payload, + ) + ), + cls=InspectorOutputJSONEncoder + ) ) except Exception as e: @@ -149,19 +157,15 @@ def post(self): from nwbinspector import inspect_all from nwbinspector.nwbinspector import InspectorOutputJSONEncoder - # from nwbinspector.inspector_tools import get_report_header, format_messages messages = list( inspect_all( n_jobs=-2, # uses number of CPU - 1 + ignore=["check_description", "check_data_orientation"], # TODO: remove when metadata control is exposed **neuroconv_api.payload, ) ) - # json_report = dict(header=get_report_header(), messages=messages) - - # formatted_messages = format_messages(messages=messages) - # return json.loads(json.dumps(obj=json_report, cls=InspectorOutputJSONEncoder)) # Object of type Version is not JSON serializable return json.loads(json.dumps(messages, cls=InspectorOutputJSONEncoder)) except Exception as e: From 3e08b03118c380ae36f82868413bbd388e1590b8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:56:08 +0000 Subject: [PATCH 33/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pyflask/apis/neuroconv.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pyflask/apis/neuroconv.py b/pyflask/apis/neuroconv.py index 9986bd1b5..8b0dbd375 100644 --- a/pyflask/apis/neuroconv.py +++ b/pyflask/apis/neuroconv.py @@ -135,11 +135,14 @@ def post(self): json.dumps( list( inspect_nwbfile( - ignore=["check_description", "check_data_orientation"], # TODO: remove when metadata control is exposed + ignore=[ + "check_description", + "check_data_orientation", + ], # TODO: remove when metadata control is exposed **neuroconv_api.payload, ) ), - cls=InspectorOutputJSONEncoder + cls=InspectorOutputJSONEncoder, ) ) @@ -157,11 +160,13 @@ def post(self): from nwbinspector import inspect_all from nwbinspector.nwbinspector import InspectorOutputJSONEncoder - messages = list( inspect_all( n_jobs=-2, # uses number of CPU - 1 - ignore=["check_description", "check_data_orientation"], # TODO: remove when metadata control is exposed + ignore=[ + "check_description", + "check_data_orientation", + ], # TODO: remove when metadata control is exposed **neuroconv_api.payload, ) ) From b60df94197130cfafabc88f9ccdc7265e2bfd618 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 25 Aug 2023 08:57:47 -0700 Subject: [PATCH 34/43] Remove inspector kwargs overrides from the frontend --- src/renderer/src/stories/preview/NWBFilePreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 8fa0f3d43..7e2e6165d 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -108,7 +108,7 @@ export class NWBFilePreview extends LitElement {

    Inspector Report

    ${until( (async () => { - const opts = { ignore: ["check_description"] }; + const opts = { }; // NOTE: Currently options are handled on the Python end until exposed to the user const title = "Inspecting your file"; From e2dca4e9c120971bc1a389bb8f7fd54abef07b25 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:58:31 +0000 Subject: [PATCH 35/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/stories/preview/NWBFilePreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 7e2e6165d..df40177f8 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -108,7 +108,7 @@ export class NWBFilePreview extends LitElement {

    Inspector Report

    ${until( (async () => { - const opts = { }; // NOTE: Currently options are handled on the Python end until exposed to the user + const opts = {}; // NOTE: Currently options are handled on the Python end until exposed to the user const title = "Inspecting your file"; From 006c7490351fed3ce721ddde16956e38deccc7fa Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 25 Aug 2023 09:00:53 -0700 Subject: [PATCH 36/43] Update inspector test JSON --- .../src/stories/preview/NWBFilePreview.js | 3 +- .../src/stories/preview/inspector/test.json | 160 ++++++++++-------- 2 files changed, 87 insertions(+), 76 deletions(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 7e2e6165d..86838aecd 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -121,7 +121,8 @@ export class NWBFilePreview extends LitElement { }) // Inspect the first file : await (async () => { const path = getSharedPath(fileArr.map((o) => o.info.file)); - return (await run("inspect_folder", { path, ...opts }, { title: title + "s" })).map( + const report = await run("inspect_folder", { path, ...opts }, { title: title + "s" }) + return report.map( (o) => { o.file_path = o.file_path.replace(`${path}/`, ""); return o; diff --git a/src/renderer/src/stories/preview/inspector/test.json b/src/renderer/src/stories/preview/inspector/test.json index 2aee6f704..409c975cf 100644 --- a/src/renderer/src/stories/preview/inspector/test.json +++ b/src/renderer/src/stories/preview/inspector/test.json @@ -1,103 +1,123 @@ [ { - "message": "Description ('No description.') is a placeholder.", + "message": "Experimenter is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "VectorData", - "object_name": "KSLabel_repeat", - "location": null, - "file_path": null + "check_function_name": "check_experimenter_exists", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse1/sub-mouse1_ses-070623.nwb" }, { - "message": "Description ('No description.') is a placeholder.", + "message": "Experiment description is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "VectorData", - "object_name": "Amplitude", - "location": null, - "file_path": null + "check_function_name": "check_experiment_description", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse1/sub-mouse1_ses-070623.nwb" }, { - "message": "Description ('No description.') is a placeholder.", + "message": "Metadata /general/institution is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "VectorData", - "object_name": "original_cluster_id", - "location": null, - "file_path": null + "check_function_name": "check_institution", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse1/sub-mouse1_ses-070623.nwb" }, { - "message": "Description ('No description.') is a placeholder.", + "message": "Metadata /general/keywords is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "VectorData", - "object_name": "ContamPct", - "location": null, - "file_path": null + "check_function_name": "check_keywords", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse1/sub-mouse1_ses-070623.nwb" }, { - "message": "Description ('No description.') is a placeholder.", + "message": "Experimenter is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "VectorData", - "object_name": "KSLabel", - "location": null, - "file_path": null + "check_function_name": "check_experimenter_exists", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse2/sub-mouse2_ses-070623.nwb" }, { - "message": "Description is missing.", + "message": "Experiment description is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "Subject", - "object_name": "subject", - "location": "/general/subject", - "file_path": null + "check_function_name": "check_experiment_description", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse2/sub-mouse2_ses-070623.nwb" }, { - "message": "Description ('no description') is a placeholder.", + "message": "Metadata /general/institution is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "VectorData", - "object_name": "offset_to_uV", - "location": null, - "file_path": null + "check_function_name": "check_institution", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse2/sub-mouse2_ses-070623.nwb" }, { - "message": "Description ('no description') is a placeholder.", + "message": "Metadata /general/keywords is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "VectorData", - "object_name": "inter_sample_shift", - "location": null, - "file_path": null + "check_function_name": "check_keywords", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse2/sub-mouse2_ses-070623.nwb" }, { - "message": "Description ('no description') is a placeholder.", + "message": "Experimenter is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "VectorData", - "object_name": "gain_to_uV", - "location": null, - "file_path": null + "check_function_name": "check_experimenter_exists", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse2/sub-mouse2_ses-060623.nwb" }, { - "message": "Description ('no description') is a placeholder.", + "message": "Experiment description is missing.", "importance": "BEST_PRACTICE_SUGGESTION", "severity": "LOW", - "check_function_name": "check_description", - "object_type": "VectorData", - "object_name": "channel_name", - "location": null, - "file_path": null + "check_function_name": "check_experiment_description", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse2/sub-mouse2_ses-060623.nwb" + }, + { + "message": "Metadata /general/institution is missing.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_institution", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse2/sub-mouse2_ses-060623.nwb" + }, + { + "message": "Metadata /general/keywords is missing.", + "importance": "BEST_PRACTICE_SUGGESTION", + "severity": "LOW", + "check_function_name": "check_keywords", + "object_type": "NWBFile", + "object_name": "root", + "location": "/", + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse2/sub-mouse2_ses-060623.nwb" }, { "message": "Experimenter is missing.", @@ -107,7 +127,7 @@ "object_type": "NWBFile", "object_name": "root", "location": "/", - "file_path": null + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse1/sub-mouse1_ses-060623.nwb" }, { "message": "Experiment description is missing.", @@ -117,7 +137,7 @@ "object_type": "NWBFile", "object_name": "root", "location": "/", - "file_path": null + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse1/sub-mouse1_ses-060623.nwb" }, { "message": "Metadata /general/institution is missing.", @@ -127,7 +147,7 @@ "object_type": "NWBFile", "object_name": "root", "location": "/", - "file_path": null + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse1/sub-mouse1_ses-060623.nwb" }, { "message": "Metadata /general/keywords is missing.", @@ -137,16 +157,6 @@ "object_type": "NWBFile", "object_name": "root", "location": "/", - "file_path": null - }, - { - "message": "Data may be in the wrong orientation. Time should be in the first dimension, and is usually the longest dimension. Here, another dimension is longer.", - "importance": "CRITICAL", - "severity": "LOW", - "check_function_name": "check_data_orientation", - "object_type": "ElectricalSeries", - "object_name": "ElectricalSeriesAP", - "location": "/acquisition/ElectricalSeriesAP", - "file_path": null + "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse1/sub-mouse1_ses-060623.nwb" } -] +] \ No newline at end of file From 053d69d498faa3ce750e191f06ab0712d841b217 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 16:02:07 +0000 Subject: [PATCH 37/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/stories/preview/NWBFilePreview.js | 12 +++++------- src/renderer/src/stories/preview/inspector/test.json | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 4f1e9913b..0151743cf 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -121,13 +121,11 @@ export class NWBFilePreview extends LitElement { }) // Inspect the first file : await (async () => { const path = getSharedPath(fileArr.map((o) => o.info.file)); - const report = await run("inspect_folder", { path, ...opts }, { title: title + "s" }) - return report.map( - (o) => { - o.file_path = o.file_path.replace(`${path}/`, ""); - return o; - } - ); + const report = await run("inspect_folder", { path, ...opts }, { title: title + "s" }); + return report.map((o) => { + o.file_path = o.file_path.replace(`${path}/`, ""); + return o; + }); })(); const list = new InspectorList({ diff --git a/src/renderer/src/stories/preview/inspector/test.json b/src/renderer/src/stories/preview/inspector/test.json index 409c975cf..aa7f9d4dc 100644 --- a/src/renderer/src/stories/preview/inspector/test.json +++ b/src/renderer/src/stories/preview/inspector/test.json @@ -159,4 +159,4 @@ "location": "/", "file_path": "/Users/garrettflynn/NWB_GUIDE/stubs/NWB GUIDE Tutorial Data/sub-mouse1/sub-mouse1_ses-060623.nwb" } -] \ No newline at end of file +] From 8a228ae383dbfe3a1b67a2255872bdc71c09e478 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 25 Aug 2023 10:33:28 -0700 Subject: [PATCH 38/43] Fix directory derivation on Windows --- src/renderer/src/stories/preview/NWBFilePreview.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 0151743cf..db6a6dd65 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -5,8 +5,11 @@ import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { run } from "../pages/guided-mode/options/utils"; import { until } from "lit/directives/until.js"; import { InstanceManager } from "../InstanceManager"; +import { path } from "../../electron"; export function getSharedPath(array) { + array = array.map(str => path.normalize(str)) + console.log(array) const mapped = array.map((str) => str.split("/")); let shared = mapped.shift(); mapped.forEach((arr, i) => { From ea2fb84e5cb0ccb4b280b7aa99ada38375716169 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 17:33:45 +0000 Subject: [PATCH 39/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/stories/preview/NWBFilePreview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index db6a6dd65..be271c2f9 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -8,8 +8,8 @@ import { InstanceManager } from "../InstanceManager"; import { path } from "../../electron"; export function getSharedPath(array) { - array = array.map(str => path.normalize(str)) - console.log(array) + array = array.map((str) => path.normalize(str)); + console.log(array); const mapped = array.map((str) => str.split("/")); let shared = mapped.shift(); mapped.forEach((arr, i) => { From 348bb7c0016210fe8b09bbf8df3d97a72a244665 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 25 Aug 2023 11:03:17 -0700 Subject: [PATCH 40/43] Update NWBFilePreview.js --- src/renderer/src/stories/preview/NWBFilePreview.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index be271c2f9..2546924ee 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -8,8 +8,7 @@ import { InstanceManager } from "../InstanceManager"; import { path } from "../../electron"; export function getSharedPath(array) { - array = array.map((str) => path.normalize(str)); - console.log(array); + array = array.map((str) => str.replace(/\\/g, '/')); // Convert to Mac-style path const mapped = array.map((str) => str.split("/")); let shared = mapped.shift(); mapped.forEach((arr, i) => { @@ -21,7 +20,7 @@ export function getSharedPath(array) { } }); - return shared.join("/"); + return path.normalize(shared.join("/")); // Convert back to OS-specific path } class NWBPreviewInstance extends LitElement { From f9937b93fa0c8ca2afb3937ec41398399143efad Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 18:03:59 +0000 Subject: [PATCH 41/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/stories/preview/NWBFilePreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 2546924ee..00c8ce06b 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -8,7 +8,7 @@ import { InstanceManager } from "../InstanceManager"; import { path } from "../../electron"; export function getSharedPath(array) { - array = array.map((str) => str.replace(/\\/g, '/')); // Convert to Mac-style path + array = array.map((str) => str.replace(/\\/g, "/")); // Convert to Mac-style path const mapped = array.map((str) => str.split("/")); let shared = mapped.shift(); mapped.forEach((arr, i) => { From 90986930d0e580853d7177cd5a6f1b21de23f8aa Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 25 Aug 2023 11:08:50 -0700 Subject: [PATCH 42/43] Update NWBFilePreview.js --- src/renderer/src/stories/preview/NWBFilePreview.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 00c8ce06b..0a1a17fd4 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -125,7 +125,9 @@ export class NWBFilePreview extends LitElement { const path = getSharedPath(fileArr.map((o) => o.info.file)); const report = await run("inspect_folder", { path, ...opts }, { title: title + "s" }); return report.map((o) => { - o.file_path = o.file_path.replace(`${path}/`, ""); + o.file_path = o.file_path + .replace(`${path}/`, "") // Mac + .replace(`${path}\\`, ""); // Windows return o; }); })(); From 096327d102a4d26e4963f523a0dcffe64673a886 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 18:09:06 +0000 Subject: [PATCH 43/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/renderer/src/stories/preview/NWBFilePreview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 0a1a17fd4..b752f9917 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -126,8 +126,8 @@ export class NWBFilePreview extends LitElement { const report = await run("inspect_folder", { path, ...opts }, { title: title + "s" }); return report.map((o) => { o.file_path = o.file_path - .replace(`${path}/`, "") // Mac - .replace(`${path}\\`, ""); // Windows + .replace(`${path}/`, "") // Mac + .replace(`${path}\\`, ""); // Windows return o; }); })();