From 0641300fb1656d00084e0bd1b943e14b1fe2ea9f Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 3 Oct 2023 12:47:52 +0200 Subject: [PATCH 1/4] All updates from Retreat User Test 1 --- schemas/json/base_metadata_schema.json | 330 +++++++++--------- schemas/json/project/subject.json | 35 -- src/main/main.ts | 36 +- src/renderer/assets/css/global.css | 2 +- src/renderer/src/dependencies/globals.js | 7 +- src/renderer/src/index.ts | 4 +- .../src/stories/FileSystemSelector.js | 26 +- src/renderer/src/stories/Main.js | 2 +- src/renderer/src/stories/Search.js | 29 +- src/renderer/src/stories/Search.stories.js | 1 + src/renderer/src/stories/assets/server.svg | 1 + src/renderer/src/stories/pages/Page.js | 4 +- .../stories/pages/guided-mode/GuidedStart.js | 2 +- .../pages/guided-mode/data/GuidedStructure.js | 1 + .../guided-mode/setup/GuidedNewDatasetInfo.js | 22 +- .../src/stories/pages/preview/PreviewPage.js | 6 +- 16 files changed, 249 insertions(+), 259 deletions(-) delete mode 100644 schemas/json/project/subject.json create mode 100644 src/renderer/src/stories/assets/server.svg diff --git a/schemas/json/base_metadata_schema.json b/schemas/json/base_metadata_schema.json index 60076184c..aef6287c5 100644 --- a/schemas/json/base_metadata_schema.json +++ b/schemas/json/base_metadata_schema.json @@ -1,173 +1,171 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "base_metafile.schema.json", - "title": "Base schema for the metafile", - "description": "Base schema for the metafile", - "version": "0.1.0", - "type": "object", - "required": ["NWBFile"], - "properties": { - "NWBFile": { - "type": "object", - "additionalProperties": false, - "required": ["session_start_time"], - "properties": { - "keywords": { - "description": "Terms to search over", - "type": "array", - "items": { - "title": "keyword", - "type": "string" - } - }, - "experiment_description": { - "type": "string", - "description": "general description of the experiment" - }, - "session_id": { - "type": "string", - "description": "lab-specific ID for the session" - }, - "experimenter": { - "description": "Name of person/people who performed experiment", - "type": "array", - "items": { - "type": "string", - "title": "experimenter" - } - }, - "identifier": { - "type": "string", - "description": "A unique text identifier for the file. If one is not provided it will be randomly assigned" - }, - "institution": { - "type": "string", - "description": "Institution(s) where experiment is performed" - }, - "lab": { - "type": "string", - "description": "Lab where experiment was performed" - }, - "session_description": { - "type": "string", - "description": "A description of the session where this data was generated" - }, - "session_start_time": { - "type": "string", - "format": "date-time", - "description": "The start date and time of the recording session" - }, - "surgery": { - "type": "string", - "description": "Narrative description about surgery/surgeries, including date(s) and who performed surgery." - }, - "pharmacology": { - "type": "string", - "description": "Description of drugs used, including how and when they were administered. Anesthesia(s), painkiller(s), etc., plus dosage, concentration, etc." - }, - "protocol": { - "type": "string", - "description": "Experimental protocol, if applicable. E.g., include IACUC protocol" - }, - "related_publications": { - "type": "array", - "items": { - "title": "related publication", - "type": "string" - } - }, - "slices": { - "type": "string", - "description": "Description of slices, including information about preparation thickness, orientation, temperature and bath solution." - }, - "source_script": { - "type": "string", - "description": "Script file used to create this NWB file." - }, - "source_script_file_name": { - "type": "string", - "description": "Script file used to create this NWB file." - }, - "notes": { - "type": "string", - "description": "Notes about the experiment." - }, - "virus": { - "type": "string", - "description": "Narrative description about surgery/surgeries, including date(s) and who performed surgery." - }, - "data_collection": { - "type": "string", - "description": "Notes about data collection and analysis." - }, - "stimulus_notes": { - "type": "string", - "description": "Notes about data collection and analysis." + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "base_metafile.schema.json", + "title": "Base schema for the metafile", + "description": "Base schema for the metafile", + "version": "0.1.0", + "type": "object", + "required": ["NWBFile"], + "properties": { + "NWBFile": { + "type": "object", + "additionalProperties": false, + "required": ["session_start_time"], + "properties": { + "keywords": { + "description": "Terms to search over", + "type": "array", + "items": { + "title": "keyword", + "type": "string" } - } - }, - "Subject": { - "type": "object", - "required": [ - "subject_id", - "sex", - "species" - ], - "additionalProperties": false, - "properties": { - "age": { - "type": "string", - "description": "The age of the subject. The ISO 8601 Duration format is recommended, e.g., ‘P90D‘ for 90 days old, ‘P2W‘ for 2 weeks old, ‘P18Y‘ for 18 years old.", - "pattern": "^P((\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+S)?))|(\\d+W)?$" - }, - "age__reference": { - "type": "string", - "enum": ["birth", "gestational"], - "description": "Age is with reference to this event. Can be ‘birth’ or ‘gestational’.", - "default": "birth" - }, - "description": { - "type": "string", - "default": "no description", - "description": "Description of subject and where subject came from (e.g., breeder, if animal)." - }, - "genotype": { - "type": "string", - "description": "Genetic strain. If absent, assume Wild Type (WT)" - }, - "sex": { - "type": "string", - "enum": [ - "M", - "F", - "U", - "O" - ], - "default": "U" - }, - "species": { - "type": "string", - "description": "Species of subject. Use latin name.", - "pattern": "^[A-Z][a-z]+ [a-z]+" - }, - "subject_id": { - "type": "string", - "description": "ID of animal/person used/participating in experiment (lab convention)" - }, - "weight": { - "type": "string", - "description": "Weight at time of experiment, at time of surgery and at other important times." - }, - "date_of_birth": { - "type": "string", - "format": "date-time", - "description": "Date of birth of subject. Can be supplied instead of 'age'." - }, - "strain": { - "type": "string", - "description": "The strain of the subject, e.g., ‘C57BL/6J‘" + }, + "experiment_description": { + "type": "string", + "description": "general description of the experiment" + }, + "session_id": { + "type": "string", + "description": "lab-specific ID for the session" + }, + "experimenter": { + "description": "Name of person/people who performed experiment", + "type": "array", + "items": { + "type": "string", + "title": "experimenter" } + }, + "identifier": { + "type": "string", + "description": "A unique text identifier for the file. If one is not provided it will be randomly assigned" + }, + "institution": { + "type": "string", + "description": "Institution(s) where experiment is performed" + }, + "lab": { + "type": "string", + "description": "Lab where experiment was performed" + }, + "session_description": { + "type": "string", + "description": "A description of the session where this data was generated" + }, + "session_start_time": { + "type": "string", + "format": "date-time", + "description": "The start date and time of the recording session" + }, + "surgery": { + "type": "string", + "description": "Narrative description about surgery/surgeries, including date(s) and who performed surgery." + }, + "pharmacology": { + "type": "string", + "description": "Description of drugs used, including how and when they were administered. Anesthesia(s), painkiller(s), etc., plus dosage, concentration, etc." + }, + "protocol": { + "type": "string", + "description": "Experimental protocol, if applicable. E.g., include IACUC protocol" + }, + "related_publications": { + "type": "array", + "items": { + "title": "related publication", + "type": "string" + } + }, + "slices": { + "type": "string", + "description": "Description of slices, including information about preparation thickness, orientation, temperature and bath solution." + }, + "source_script": { + "type": "string", + "description": "Script file used to create this NWB file." + }, + "source_script_file_name": { + "type": "string", + "description": "Script file used to create this NWB file." + }, + "notes": { + "type": "string", + "description": "Notes about the experiment." + }, + "virus": { + "type": "string", + "description": "Narrative description about surgery/surgeries, including date(s) and who performed surgery." + }, + "data_collection": { + "type": "string", + "description": "Notes about data collection and analysis." + }, + "stimulus_notes": { + "type": "string", + "description": "Notes about data collection and analysis." + } + } + }, + "Subject": { + "type": "object", + "required": [ + "subject_id", + "sex", + "species" + ], + "additionalProperties": false, + "properties": { + "age": { + "type": "string", + "description": "The age of the subject. The ISO 8601 Duration format is recommended, e.g., 'P90D' for 90 days old, 'P2W' for 2 weeks old, 'P18Y' for 18 years old.", + "pattern": "^P((\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+S)?))|(\\d+W)?$" + }, + "age__reference": { + "type": "string", + "enum": ["birth", "gestational"], + "description": "Age is with reference to this event. Can be 'birth' or 'gestational'.", + "default": "birth" + }, + "description": { + "type": "string", + "description": "Description of subject and where subject came from (e.g., breeder, if animal)." + }, + "genotype": { + "type": "string", + "description": "Genetic strain. If absent, assume Wild Type (WT)" + }, + "sex": { + "type": "string", + "enum": [ + "M", + "F", + "U", + "O" + ] + }, + "species": { + "type": "string", + "description": "Species of subject. Use latin name.", + "pattern": "^[A-Z][a-z]+ [a-z]+" + }, + "subject_id": { + "type": "string", + "description": "ID of animal/person used/participating in experiment (lab convention)" + }, + "weight": { + "type": "string", + "description": "Weight at time of experiment, at time of surgery and at other important times." + }, + "date_of_birth": { + "type": "string", + "format": "date-time", + "description": "Date of birth of subject. Can be supplied instead of 'age'." + }, + "strain": { + "type": "string", + "description": "The strain of the subject, e.g., 'C57BL/6J'" } } } } +} \ No newline at end of file diff --git a/schemas/json/project/subject.json b/schemas/json/project/subject.json deleted file mode 100644 index a6f07f665..000000000 --- a/schemas/json/project/subject.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "properties": { - "Subject": { - "type": "object", - "properties": { - "species": { - "type": "string", - "description": "Enter a common species for your subjects.", - "placeholder": "Enter species here" - }, - "description": { - "type": "string", - "description": "Enter a common description for your subjects.", - "placeholder": "Enter subject description here" - }, - "genotype": { - "type": "string", - "description": "Enter a common genotype for your subjects.", - "placeholder": "Enter genotype here" - }, - "strain": { - "type": "string", - "description": "Enter a common strain for your subjects.", - "placeholder": "Enter strain here" - }, - "sex": { - "type": "string", - "enum": ["M", "F", "U", "O"], - "description": "Enter a common sex for your subjects.", - "placeholder": "Enter sex here" - } - } - } - } -} diff --git a/src/main/main.ts b/src/main/main.ts index f15f363fb..7feb5b6da 100755 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -7,7 +7,7 @@ import main from '@electron/remote/main'; main.initialize() import path from 'path'; -import { autoUpdater } from 'electron-updater'; +// import { autoUpdater } from 'electron-updater'; import { ipcMain } from 'electron'; import fp from 'find-free-port'; import 'v8-compile-cache' @@ -20,7 +20,7 @@ import './application-menu.js'; import icon from '../renderer/assets/img/logo-guide-draft.png?asset' import splashHTML from './splash-screen.html?asset' -autoUpdater.channel = "latest"; +// autoUpdater.channel = "latest"; /************************************************************* * Python Process @@ -218,11 +218,11 @@ function initialize() { return { action: 'deny' }; }); - globals.mainWindow.webContents.once("dom-ready", () => { - if (updatechecked == false) { - autoUpdater.checkForUpdatesAndNotify(); - } - }); + // globals.mainWindow.webContents.once("dom-ready", () => { + // if (updatechecked == false) { + // autoUpdater.checkForUpdatesAndNotify(); + // } + // }); } function onAppReady () { @@ -304,7 +304,7 @@ function initialize() { globals.mainWindow.show(); createWindow(); - autoUpdater.checkForUpdatesAndNotify(); + // autoUpdater.checkForUpdatesAndNotify(); updatechecked = true; // Clear ready queue @@ -396,8 +396,6 @@ app.on("before-quit", async (ev: Event) => { ev.preventDefault() - console.log('AHH', globals.python.status) - const { response } = await dialog .showMessageBox(BrowserWindow.getFocusedWindow() as BrowserWindow, { type: "question", @@ -434,17 +432,17 @@ ipcMain.on("resize-window", (event, dir) => { globals.mainWindow.setSize(x, y); }); -autoUpdater.on("update-available", () => { - onWindowReady(win => send.call(win, "update_available")); -}); +// autoUpdater.on("update-available", () => { +// onWindowReady(win => send.call(win, "update_available")); +// }); -autoUpdater.on("update-downloaded", () => { - onWindowReady(win => send.call(win, "update_downloaded")); -}); +// autoUpdater.on("update-downloaded", () => { +// onWindowReady(win => send.call(win, "update_downloaded")); +// }); -ipcMain.on("restart_app", async () => { - autoUpdater.quitAndInstall(); -}); +// ipcMain.on("restart_app", async () => { +// autoUpdater.quitAndInstall(); +// }); ipcMain.on("get-port", (event) => { event.returnValue = selectedPort; diff --git a/src/renderer/assets/css/global.css b/src/renderer/assets/css/global.css index 252a2e657..a238aa68f 100755 --- a/src/renderer/assets/css/global.css +++ b/src/renderer/assets/css/global.css @@ -10,7 +10,7 @@ /* Notfy */ .notyf__toast { - max-width: clamp(300px, 40vw, 500px) !important; + max-width: clamp(300px, 40vw, min-content) !important; } .notyf__message { diff --git a/src/renderer/src/dependencies/globals.js b/src/renderer/src/dependencies/globals.js index 76f19dc83..d5cdeaad8 100644 --- a/src/renderer/src/dependencies/globals.js +++ b/src/renderer/src/dependencies/globals.js @@ -22,6 +22,8 @@ export const startLottie = (lottieElement, animationData) => { return thisLottie; }; +const longDuration = 20000 + // ---------- Notification Helper ---------- export const notyf = new Notyf({ position: { x: "right", y: "bottom" }, @@ -82,7 +84,7 @@ export const notyf = new Notyf({ tagName: "i", color: "white", }, - duration: 20000, + duration: longDuration, }, { type: "app_update_warning", @@ -92,6 +94,7 @@ export const notyf = new Notyf({ tagName: "i", color: "white", }, + duration: longDuration }, { type: "error", @@ -101,7 +104,7 @@ export const notyf = new Notyf({ tagName: "i", color: "white", }, - duration: 20000, + duration: longDuration, }, ], }); diff --git a/src/renderer/src/index.ts b/src/renderer/src/index.ts index c13b6f258..7fa37ac61 100644 --- a/src/renderer/src/index.ts +++ b/src/renderer/src/index.ts @@ -15,7 +15,7 @@ import Swal from 'sweetalert2' import { StatusBar } from "./stories/status/StatusBar.js"; import { unsafeSVG } from "lit/directives/unsafe-svg.js"; -import pythonSVG from "./stories/assets/python.svg?raw"; +import serverSVG from "./stories/assets/server.svg?raw"; import webAssetSVG from "./stories/assets/web_asset.svg?raw"; import wifiSVG from "./stories/assets/wifi.svg?raw"; @@ -27,7 +27,7 @@ const statusBar = new StatusBar({ items: [ { label: unsafeSVG(webAssetSVG), value: appVersion ?? 'Web' }, { label: unsafeSVG(wifiSVG) }, - { label: unsafeSVG(pythonSVG) } + { label: unsafeSVG(serverSVG) } ] }) diff --git a/src/renderer/src/stories/FileSystemSelector.js b/src/renderer/src/stories/FileSystemSelector.js index 902c714f8..7da7e91d7 100644 --- a/src/renderer/src/stories/FileSystemSelector.js +++ b/src/renderer/src/stories/FileSystemSelector.js @@ -121,17 +121,10 @@ export class FilesystemSelector extends LitElement { const result = await dialog[this.dialogType](options); this.classList.remove("active"); - if (result.canceled) this.#onCancel(); + if (result.canceled) return []; return result; }; - #onCancel = () => { - this.#onThrow( - `No ${getObjectTypeReferenceString(this.type, this.multiple, { native: true })} selected`, - "The request was cancelled by the user" - ); - }; - #checkType = (value) => { const isLikelyFile = value.split(".").length !== 1; if ((this.type === "directory" && isLikelyFile) || (this.type === "file" && !isLikelyFile)) @@ -139,13 +132,8 @@ export class FilesystemSelector extends LitElement { }; #handleFiles = async (pathOrPaths, type) => { - if (!pathOrPaths) - this.#onThrow( - "No paths detected", - `Unable to parse ${getObjectTypeReferenceString(this.type, false, { native: true })} path${ - this.multiple ? "s" : "" - }` - ); + + const resolvedType = type ?? this.type if (Array.isArray(pathOrPaths)) pathOrPaths.forEach(this.#checkType); else if (!type) this.#checkType(pathOrPaths); @@ -155,7 +143,7 @@ export class FilesystemSelector extends LitElement { if (Array.isArray(resolvedValue) && !this.multiple) { if (resolvedValue.length > 1) this.#onThrow( - `Too many ${this.type === "directory" ? "directories" : "files"} detected`, + `Too many ${resolvedType === "directory" ? "directories" : "files"} detected`, `This selector will only accept one.` ); resolvedValue = resolvedValue[0]; @@ -163,6 +151,8 @@ export class FilesystemSelector extends LitElement { if (this.multiple && !Array.isArray(resolvedValue)) resolvedValue = []; + console.log(resolvedValue) + this.value = resolvedValue; this.onSelect(this.value); const event = new Event("change"); // Create a new change event @@ -178,7 +168,7 @@ export class FilesystemSelector extends LitElement { let handles = await (type === "directory" ? window.showDirectoryPicker() : window.showOpenFilePicker({ multiple: this.multiple }) - ).catch((e) => this.#onCancel()); // Call using the same options + ).catch((e) => []); // Call using the same options const result = Array.isArray(handles) ? handles.map((o) => o.name) : handles.name; this.#handleFiles(result, type); @@ -197,7 +187,7 @@ export class FilesystemSelector extends LitElement { resolved = this.value.map((str) => str.replaceAll("\\", "/")); isUpdated = JSON.stringify(resolved) !== JSON.stringify(this.value); } else { - resolved = this.value.replaceAll("\\", "/"); + resolved = typeof this.value === 'string' ? this.value.replaceAll("\\", "/") : this.value; isUpdated = resolved !== this.value; } diff --git a/src/renderer/src/stories/Main.js b/src/renderer/src/stories/Main.js index 78ce1f2be..cb416ce10 100644 --- a/src/renderer/src/stories/Main.js +++ b/src/renderer/src/stories/Main.js @@ -90,7 +90,7 @@ export class Main extends LitElement { if (!info.next) footer = { next: "Back to Home Screen", - exit: "Exit", + exit: false, onNext: () => this.toRender.page.to("/"), }; // Allow navigating laterally if there is a next page diff --git a/src/renderer/src/stories/Search.js b/src/renderer/src/stories/Search.js index 9796b7195..65b1f3afd 100644 --- a/src/renderer/src/stories/Search.js +++ b/src/renderer/src/stories/Search.js @@ -2,10 +2,11 @@ import { LitElement, html, css } from "lit"; import { styleMap } from "lit/directives/style-map.js"; export class Search extends LitElement { - constructor({ options, showAllWhenEmpty } = {}) { + constructor({ options, showAllWhenEmpty, disabledLabel } = {}) { super(); this.options = options; this.showAllWhenEmpty = showAllWhenEmpty; + this.disabledLabel = disabledLabel } static get styles() { @@ -63,9 +64,23 @@ export class Search extends LitElement { } [disabled] { + display: flex; + justify-content: space-between; + align-items: center; pointer-events: none; opacity: 0.4; } + + [disabled]::after { + content: var(--disabled-label, "Not available"); + text-align: left; + padding-left: 25px; + opacity: 70%; + font-size: 90%; + white-space: nowrap; + min-width: min-content; + + } `; } @@ -103,10 +118,15 @@ export class Search extends LitElement { list = document.createElement("ul"); render() { + // Update list this.list.remove(); this.list = document.createElement("ul"); + if (this.disabledLabel) this.style.setProperty('--disabled-label', `"${this.disabledLabel}"`); + else this.style.removeProperty('--disabled-label') + + const slot = document.createElement("slot"); this.list.appendChild(slot); @@ -131,16 +151,19 @@ export class Search extends LitElement { if (option.disabled) li.setAttribute("disabled", ""); + const container = document.createElement('div') + const label = document.createElement("h4"); label.classList.add("label"); label.innerText = option.label; - li.appendChild(label); + container.appendChild(label); const keywords = document.createElement("small"); keywords.classList.add("keywords"); keywords.innerText = option.keywords.join(", "); - li.appendChild(keywords); + container.appendChild(keywords); + li.append(container) this.list.appendChild(li); return option.disabled; diff --git a/src/renderer/src/stories/Search.stories.js b/src/renderer/src/stories/Search.stories.js index 841511c84..dffa8be67 100644 --- a/src/renderer/src/stories/Search.stories.js +++ b/src/renderer/src/stories/Search.stories.js @@ -10,6 +10,7 @@ const Template = (args) => new Search(args); export const Default = Template.bind({}); Default.args = { + disabledLabel: "Interface not supported", options: [ { label: "SpikeGLXRecording", diff --git a/src/renderer/src/stories/assets/server.svg b/src/renderer/src/stories/assets/server.svg new file mode 100644 index 000000000..50a2510d1 --- /dev/null +++ b/src/renderer/src/stories/assets/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/src/stories/pages/Page.js b/src/renderer/src/stories/pages/Page.js index 2a44e2200..e23e96ba5 100644 --- a/src/renderer/src/stories/pages/Page.js +++ b/src/renderer/src/stories/pages/Page.js @@ -143,10 +143,11 @@ export class Page extends LitElement { popup.hideLoading(); const element = popup.getHtmlContainer(); element.innerText = ""; - + element.style.textAlign = 'left' const progressBar = new ProgressBar(); elements.progress = progressBar; element.append(progressBar); + element.insertAdjacentHTML('beforeend', `Note: This may take a while to complete...`) // } let completed = 0; @@ -193,6 +194,7 @@ export class Page extends LitElement { } popup.close(); + element.style.textAlign = '' // Clear style update return results; } diff --git a/src/renderer/src/stories/pages/guided-mode/GuidedStart.js b/src/renderer/src/stories/pages/guided-mode/GuidedStart.js index 21b7a273b..d3426d1c2 100644 --- a/src/renderer/src/stories/pages/guided-mode/GuidedStart.js +++ b/src/renderer/src/stories/pages/guided-mode/GuidedStart.js @@ -56,7 +56,7 @@ export class GuidedStartPage extends Page {

Guided Mode is divided into four high-level sections. During the first three sections, you will be directed to specify the data formats and files to include in your dataset and - provide information about your dataset. In the final section, NWB GUIDE will automatically + provide information about your conversion pipeline. In the final section, NWB GUIDE will automatically generate a valid NWB file and ask for your review before uploading to DANDI. Note that none of your local data files will ever be modified or moved.

diff --git a/src/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js b/src/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js index 7d6fe9bbe..f1e28e5b0 100644 --- a/src/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js +++ b/src/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js @@ -35,6 +35,7 @@ export class GuidedStructurePage extends Page { search = new Search({ showAllWhenEmpty: true, + disabledLabel: "Not supported" }); list = new List({ diff --git a/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js b/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js index af3d08bc3..94418fafd 100644 --- a/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js +++ b/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js @@ -6,17 +6,24 @@ import { validateOnChange } from "../../../../validation/index.js"; import projectGeneralSchema from "../../../../../../../schemas/json/project/general.json" assert { type: "json" }; import projectGlobalSchema from "../../../../../../../schemas/json/project/globals.json" assert { type: "json" }; -import projectNWBFileSchema from "../../../../../../../schemas/json/project/nwbfile.json" assert { type: "json" }; -import projectSubjectSchema from "../../../../../../../schemas/json/project/subject.json" assert { type: "json" }; import { merge } from "../../utils.js"; +import { schemaToPages } from "../../FormPage.js"; +import { onThrow } from "../../../../errors"; +import baseMetadataSchema from "../../../../../../../schemas/base-metadata.schema"; -const projectMetadataSchema = merge(projectGlobalSchema, projectGeneralSchema); -merge(projectNWBFileSchema, projectMetadataSchema); -merge(projectSubjectSchema, projectMetadataSchema); +const changesAcrossSessions = { + Subject: ["weight", "subject_id", "age", "date_of_birth", "age__reference"], + NWBFile: ["session_id", "session_start_time", "identifier", "data_collection", "notes", "pharmacolocy", "session_description", "slices", "source_script", "source_script_file_name"] +} -import { schemaToPages } from "../../FormPage.js"; +const projectMetadataSchema = merge(projectGlobalSchema, projectGeneralSchema); -import { onThrow } from "../../../../errors"; +Object.entries(baseMetadataSchema.properties).forEach(([globalProp, v]) => { + Object.keys(v.properties).filter(prop => !(changesAcrossSessions[globalProp] ?? []).includes(prop)).forEach(prop => { + const globalNestedProp = projectMetadataSchema.properties[globalProp] ?? (projectMetadataSchema.properties[globalProp] = { properties: {} }) + globalNestedProp.properties[prop] = baseMetadataSchema.properties[globalProp].properties[prop] + }) +}) export class GuidedNewDatasetPage extends Page { constructor(...args) { @@ -93,7 +100,6 @@ export class GuidedNewDatasetPage extends Page { info.title = `${info.label} Global Metadata`; return info; }); - console.log(pages); pages.forEach((page) => { page.header = { diff --git a/src/renderer/src/stories/pages/preview/PreviewPage.js b/src/renderer/src/stories/pages/preview/PreviewPage.js index 13fea6714..7ee86017f 100644 --- a/src/renderer/src/stories/pages/preview/PreviewPage.js +++ b/src/renderer/src/stories/pages/preview/PreviewPage.js @@ -11,8 +11,10 @@ export class PreviewPage extends Page { } updatePath = async (path) => { - const result = await fetch(`${baseUrl}/files/${path}`, { method: "POST" }).then((res) => res.text()); - if (result) this.neurosift.url = result; + if (path) { + const result = await fetch(`${baseUrl}/files/${path}`, { method: "POST" }).then((res) => res.text()); + if (result) this.neurosift.url = result; + } else this.neurosift.url = undefined }; neurosift = new Neurosift(); From 81b6f26685c7755ce4670908c6a65e531f000919 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:50:59 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- schemas/json/base_metadata_schema.json | 2 +- src/renderer/src/dependencies/globals.js | 4 +-- .../src/stories/FileSystemSelector.js | 7 ++--- src/renderer/src/stories/Search.js | 13 ++++----- src/renderer/src/stories/assets/server.svg | 2 +- src/renderer/src/stories/pages/Page.js | 9 ++++-- .../stories/pages/guided-mode/GuidedStart.js | 6 ++-- .../pages/guided-mode/data/GuidedStructure.js | 2 +- .../guided-mode/setup/GuidedNewDatasetInfo.js | 29 ++++++++++++++----- .../src/stories/pages/preview/PreviewPage.js | 2 +- 10 files changed, 45 insertions(+), 31 deletions(-) diff --git a/schemas/json/base_metadata_schema.json b/schemas/json/base_metadata_schema.json index aef6287c5..201620788 100644 --- a/schemas/json/base_metadata_schema.json +++ b/schemas/json/base_metadata_schema.json @@ -168,4 +168,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/renderer/src/dependencies/globals.js b/src/renderer/src/dependencies/globals.js index d5cdeaad8..e391b27b6 100644 --- a/src/renderer/src/dependencies/globals.js +++ b/src/renderer/src/dependencies/globals.js @@ -22,7 +22,7 @@ export const startLottie = (lottieElement, animationData) => { return thisLottie; }; -const longDuration = 20000 +const longDuration = 20000; // ---------- Notification Helper ---------- export const notyf = new Notyf({ @@ -94,7 +94,7 @@ export const notyf = new Notyf({ tagName: "i", color: "white", }, - duration: longDuration + duration: longDuration, }, { type: "error", diff --git a/src/renderer/src/stories/FileSystemSelector.js b/src/renderer/src/stories/FileSystemSelector.js index 7da7e91d7..5bdc10c9d 100644 --- a/src/renderer/src/stories/FileSystemSelector.js +++ b/src/renderer/src/stories/FileSystemSelector.js @@ -132,8 +132,7 @@ export class FilesystemSelector extends LitElement { }; #handleFiles = async (pathOrPaths, type) => { - - const resolvedType = type ?? this.type + const resolvedType = type ?? this.type; if (Array.isArray(pathOrPaths)) pathOrPaths.forEach(this.#checkType); else if (!type) this.#checkType(pathOrPaths); @@ -151,7 +150,7 @@ export class FilesystemSelector extends LitElement { if (this.multiple && !Array.isArray(resolvedValue)) resolvedValue = []; - console.log(resolvedValue) + console.log(resolvedValue); this.value = resolvedValue; this.onSelect(this.value); @@ -187,7 +186,7 @@ export class FilesystemSelector extends LitElement { resolved = this.value.map((str) => str.replaceAll("\\", "/")); isUpdated = JSON.stringify(resolved) !== JSON.stringify(this.value); } else { - resolved = typeof this.value === 'string' ? this.value.replaceAll("\\", "/") : this.value; + resolved = typeof this.value === "string" ? this.value.replaceAll("\\", "/") : this.value; isUpdated = resolved !== this.value; } diff --git a/src/renderer/src/stories/Search.js b/src/renderer/src/stories/Search.js index 65b1f3afd..95109ddab 100644 --- a/src/renderer/src/stories/Search.js +++ b/src/renderer/src/stories/Search.js @@ -6,7 +6,7 @@ export class Search extends LitElement { super(); this.options = options; this.showAllWhenEmpty = showAllWhenEmpty; - this.disabledLabel = disabledLabel + this.disabledLabel = disabledLabel; } static get styles() { @@ -79,7 +79,6 @@ export class Search extends LitElement { font-size: 90%; white-space: nowrap; min-width: min-content; - } `; } @@ -118,14 +117,12 @@ export class Search extends LitElement { list = document.createElement("ul"); render() { - // Update list this.list.remove(); this.list = document.createElement("ul"); - if (this.disabledLabel) this.style.setProperty('--disabled-label', `"${this.disabledLabel}"`); - else this.style.removeProperty('--disabled-label') - + if (this.disabledLabel) this.style.setProperty("--disabled-label", `"${this.disabledLabel}"`); + else this.style.removeProperty("--disabled-label"); const slot = document.createElement("slot"); this.list.appendChild(slot); @@ -151,7 +148,7 @@ export class Search extends LitElement { if (option.disabled) li.setAttribute("disabled", ""); - const container = document.createElement('div') + const container = document.createElement("div"); const label = document.createElement("h4"); label.classList.add("label"); @@ -163,7 +160,7 @@ export class Search extends LitElement { keywords.innerText = option.keywords.join(", "); container.appendChild(keywords); - li.append(container) + li.append(container); this.list.appendChild(li); return option.disabled; diff --git a/src/renderer/src/stories/assets/server.svg b/src/renderer/src/stories/assets/server.svg index 50a2510d1..11dea85b4 100644 --- a/src/renderer/src/stories/assets/server.svg +++ b/src/renderer/src/stories/assets/server.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/renderer/src/stories/pages/Page.js b/src/renderer/src/stories/pages/Page.js index e23e96ba5..d9a016d02 100644 --- a/src/renderer/src/stories/pages/Page.js +++ b/src/renderer/src/stories/pages/Page.js @@ -143,11 +143,14 @@ export class Page extends LitElement { popup.hideLoading(); const element = popup.getHtmlContainer(); element.innerText = ""; - element.style.textAlign = 'left' + element.style.textAlign = "left"; const progressBar = new ProgressBar(); elements.progress = progressBar; element.append(progressBar); - element.insertAdjacentHTML('beforeend', `Note: This may take a while to complete...`) + element.insertAdjacentHTML( + "beforeend", + `Note: This may take a while to complete...` + ); // } let completed = 0; @@ -194,7 +197,7 @@ export class Page extends LitElement { } popup.close(); - element.style.textAlign = '' // Clear style update + element.style.textAlign = ""; // Clear style update return results; } diff --git a/src/renderer/src/stories/pages/guided-mode/GuidedStart.js b/src/renderer/src/stories/pages/guided-mode/GuidedStart.js index d3426d1c2..f596c2dd7 100644 --- a/src/renderer/src/stories/pages/guided-mode/GuidedStart.js +++ b/src/renderer/src/stories/pages/guided-mode/GuidedStart.js @@ -56,9 +56,9 @@ export class GuidedStartPage extends Page {

Guided Mode is divided into four high-level sections. During the first three sections, you will be directed to specify the data formats and files to include in your dataset and - provide information about your conversion pipeline. In the final section, NWB GUIDE will automatically - generate a valid NWB file and ask for your review before uploading to DANDI. Note that none - of your local data files will ever be modified or moved. + provide information about your conversion pipeline. In the final section, NWB GUIDE will + automatically generate a valid NWB file and ask for your review before uploading to DANDI. + Note that none of your local data files will ever be modified or moved.

${new InfoBox({ header: "Learn more about the conversion process", diff --git a/src/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js b/src/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js index f1e28e5b0..648f91402 100644 --- a/src/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js +++ b/src/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js @@ -35,7 +35,7 @@ export class GuidedStructurePage extends Page { search = new Search({ showAllWhenEmpty: true, - disabledLabel: "Not supported" + disabledLabel: "Not supported", }); list = new List({ diff --git a/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js b/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js index 94418fafd..662adc7ff 100644 --- a/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js +++ b/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js @@ -13,17 +13,32 @@ import baseMetadataSchema from "../../../../../../../schemas/base-metadata.schem const changesAcrossSessions = { Subject: ["weight", "subject_id", "age", "date_of_birth", "age__reference"], - NWBFile: ["session_id", "session_start_time", "identifier", "data_collection", "notes", "pharmacolocy", "session_description", "slices", "source_script", "source_script_file_name"] -} + NWBFile: [ + "session_id", + "session_start_time", + "identifier", + "data_collection", + "notes", + "pharmacolocy", + "session_description", + "slices", + "source_script", + "source_script_file_name", + ], +}; const projectMetadataSchema = merge(projectGlobalSchema, projectGeneralSchema); Object.entries(baseMetadataSchema.properties).forEach(([globalProp, v]) => { - Object.keys(v.properties).filter(prop => !(changesAcrossSessions[globalProp] ?? []).includes(prop)).forEach(prop => { - const globalNestedProp = projectMetadataSchema.properties[globalProp] ?? (projectMetadataSchema.properties[globalProp] = { properties: {} }) - globalNestedProp.properties[prop] = baseMetadataSchema.properties[globalProp].properties[prop] - }) -}) + Object.keys(v.properties) + .filter((prop) => !(changesAcrossSessions[globalProp] ?? []).includes(prop)) + .forEach((prop) => { + const globalNestedProp = + projectMetadataSchema.properties[globalProp] ?? + (projectMetadataSchema.properties[globalProp] = { properties: {} }); + globalNestedProp.properties[prop] = baseMetadataSchema.properties[globalProp].properties[prop]; + }); +}); export class GuidedNewDatasetPage extends Page { constructor(...args) { diff --git a/src/renderer/src/stories/pages/preview/PreviewPage.js b/src/renderer/src/stories/pages/preview/PreviewPage.js index 7ee86017f..e1893abe0 100644 --- a/src/renderer/src/stories/pages/preview/PreviewPage.js +++ b/src/renderer/src/stories/pages/preview/PreviewPage.js @@ -14,7 +14,7 @@ export class PreviewPage extends Page { if (path) { const result = await fetch(`${baseUrl}/files/${path}`, { method: "POST" }).then((res) => res.text()); if (result) this.neurosift.url = result; - } else this.neurosift.url = undefined + } else this.neurosift.url = undefined; }; neurosift = new Neurosift(); From b21629a6af48a50540a6caed96120bdffd418f7d Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 3 Oct 2023 13:07:19 +0200 Subject: [PATCH 3/4] Update server.svg --- src/renderer/src/stories/assets/server.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/stories/assets/server.svg b/src/renderer/src/stories/assets/server.svg index 50a2510d1..a3bc2bfbc 100644 --- a/src/renderer/src/stories/assets/server.svg +++ b/src/renderer/src/stories/assets/server.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 79c88b9ca568b289d006cfdda447b93d798bd7cc Mon Sep 17 00:00:00 2001 From: CodyCBakerPhD Date: Tue, 3 Oct 2023 07:22:10 -0400 Subject: [PATCH 4/4] remove SpikeGLXLFP --- pyflask/manageNeuroconv/manage_neuroconv.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pyflask/manageNeuroconv/manage_neuroconv.py b/pyflask/manageNeuroconv/manage_neuroconv.py index af15b1ab0..4ad7ae95c 100644 --- a/pyflask/manageNeuroconv/manage_neuroconv.py +++ b/pyflask/manageNeuroconv/manage_neuroconv.py @@ -132,10 +132,11 @@ def get_all_interface_info() -> dict: """Format an information structure to be used for selecting interfaces based on modality and technique.""" from neuroconv.datainterfaces import interface_list - # Hard coded for now - eventual goal will be to import this from NeuroConv - interfaces_to_load = { - interface.__name__.replace("Interface", ""): interface for interface in interface_list - } # dict(SpikeGLX=SpikeGLXRecordingInterface, Phy=PhySortingInterface) + exclude_interfaces_from_selection = ["SpikeGLXLFP"] # Should have 'interface' stripped from name + + interfaces_to_load = {interface.__name__.replace("Interface", ""): interface for interface in interface_list} + for excluded_interface in exclude_interfaces_from_selection: + interfaces_to_load.pop(excluded_interface) return { interface.__name__: {