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__: { diff --git a/schemas/json/base_metadata_schema.json b/schemas/json/base_metadata_schema.json index 60076184c..201620788 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'" } } } } +} 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..e391b27b6 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..5bdc10c9d 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,7 @@ 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 +142,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 +150,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 +167,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 +186,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..95109ddab 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,22 @@ 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; + } `; } @@ -107,6 +121,9 @@ export class Search extends LitElement { 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 +148,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..a07d9cfbe --- /dev/null +++ b/src/renderer/src/stories/assets/server.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/src/stories/pages/Page.js b/src/renderer/src/stories/pages/Page.js index 2a44e2200..d9a016d02 100644 --- a/src/renderer/src/stories/pages/Page.js +++ b/src/renderer/src/stories/pages/Page.js @@ -143,10 +143,14 @@ 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 +197,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..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 dataset. 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 7d6fe9bbe..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,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..662adc7ff 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,39 @@ 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 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", + ], +}; const projectMetadataSchema = merge(projectGlobalSchema, projectGeneralSchema); -merge(projectNWBFileSchema, projectMetadataSchema); -merge(projectSubjectSchema, projectMetadataSchema); - -import { schemaToPages } from "../../FormPage.js"; -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 +115,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..e1893abe0 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();