diff --git a/pyflask/manageNeuroconv/manage_neuroconv.py b/pyflask/manageNeuroconv/manage_neuroconv.py index 27727640b..890ddefa4 100644 --- a/pyflask/manageNeuroconv/manage_neuroconv.py +++ b/pyflask/manageNeuroconv/manage_neuroconv.py @@ -246,6 +246,7 @@ def is_supported_recording_interface(recording_interface, metadata): Alpha build release should therefore always return False for this. """ + return ( recording_interface and recording_interface.get_electrode_table_json @@ -266,40 +267,39 @@ def get_metadata_schema(source_data: Dict[str, dict], interfaces: dict) -> Dict[ schema = converter.get_metadata_schema() metadata = converter.get_metadata() - # recording_interface = get_first_recording_interface(converter) + recording_interface = get_first_recording_interface(converter) - # if is_supported_recording_interface(recording_interface, metadata): - # metadata["Ecephys"]["Electrodes"] = recording_interface.get_electrode_table_json() + if is_supported_recording_interface(recording_interface, metadata): + metadata["Ecephys"]["Electrodes"] = recording_interface.get_electrode_table_json() - # # Get Electrode metadata - # ecephys_properties = schema["properties"]["Ecephys"]["properties"] - # original_electrodes_schema = ecephys_properties["Electrodes"] + # Get Electrode metadata + ecephys_properties = schema["properties"]["Ecephys"]["properties"] + original_electrodes_schema = ecephys_properties["Electrodes"] - # new_electrodes_properties = { - # properties["name"]: {key: value for key, value in properties.items() if key != "name"} - # for properties in original_electrodes_schema["default"] - # } + new_electrodes_properties = { + properties["name"]: {key: value for key, value in properties.items() if key != "name"} + for properties in original_electrodes_schema["default"] + } - # ecephys_properties["Electrodes"] = { - # "type": "array", - # "minItems": 0, - # "items": { - # "type": "object", - # "properties": new_electrodes_properties, - # "additionalProperties": True, # Allow for new columns - # }, - # } + ecephys_properties["Electrodes"] = { + "type": "array", + "minItems": 0, + "items": { + "type": "object", + "properties": new_electrodes_properties, + "additionalProperties": True, # Allow for new columns + }, + } - # metadata["Ecephys"]["ElectrodeColumns"] = original_electrodes_schema["default"] - # defs = ecephys_properties["definitions"] + metadata["Ecephys"]["ElectrodeColumns"] = original_electrodes_schema["default"] + defs = ecephys_properties["definitions"] - # ecephys_properties["ElectrodeColumns"] = {"type": "array", "items": defs["Electrodes"]} - # ecephys_properties["ElectrodeColumns"]["items"]["required"] = list(defs["Electrodes"]["properties"].keys()) - # del defs["Electrodes"] + ecephys_properties["ElectrodeColumns"] = {"type": "array", "items": defs["Electrodes"]} + ecephys_properties["ElectrodeColumns"]["items"]["required"] = list(defs["Electrodes"]["properties"].keys()) + del defs["Electrodes"] - # # Delete Ecephys metadata if ElectrodeTable helper function is not available - # else: - if "Ecephys" in schema["properties"]: + # Delete Ecephys metadata if ElectrodeTable helper function is not available + elif "Ecephys" in schema["properties"]: schema["properties"].pop("Ecephys", dict()) return json.loads(json.dumps(replace_nan_with_none(dict(results=metadata, schema=schema)), cls=NWBMetaDataEncoder)) @@ -451,29 +451,30 @@ def update_conversion_progress(**kwargs): else None ) - # Update the first recording interface with Ecephys table data - # This will be refactored after the ndx-probe-interface integration - # recording_interface = get_first_recording_interface(converter) - if "Ecephys" not in info["metadata"]: info["metadata"].update(Ecephys=dict()) - resolved_metadata = replace_none_with_nan( - info["metadata"], resolve_references(converter.get_metadata_schema()) - ) # Ensure Ophys NaN values are resolved + # Ensure Ophys NaN values are resolved + resolved_metadata = replace_none_with_nan(info["metadata"], resolve_references(converter.get_metadata_schema())) - # if is_supported_recording_interface(recording_interface, info["metadata"]): - # electrode_column_results = ecephys_metadata["ElectrodeColumns"] - # electrode_results = ecephys_metadata["Electrodes"] + # Update the first recording interface with Ecephys table data + # NOTE: This will be refactored after the ndx-probe-interface integration + recording_interface = get_first_recording_interface(converter) + + ecephys_metadata = resolved_metadata["Ecephys"] - # recording_interface.update_electrode_table( - # electrode_table_json=electrode_results, electrode_column_info=electrode_column_results - # ) + if is_supported_recording_interface(recording_interface, info["metadata"]): + electrode_column_results = ecephys_metadata["ElectrodeColumns"] + electrode_results = ecephys_metadata["Electrodes"] + + recording_interface.update_electrode_table( + electrode_table_json=electrode_results, electrode_column_info=electrode_column_results + ) - # # Update with the latest metadata for the electrodes - # ecephys_metadata["Electrodes"] = electrode_column_results + # Update with the latest metadata for the electrodes + ecephys_metadata["Electrodes"] = electrode_column_results - # ecephys_metadata.pop("ElectrodeColumns", dict()) + ecephys_metadata.pop("ElectrodeColumns", dict()) # Actually run the conversion converter.run_conversion( diff --git a/schemas/base-metadata.schema.ts b/schemas/base-metadata.schema.ts index 7fea3bd08..a82a1adba 100644 --- a/schemas/base-metadata.schema.ts +++ b/schemas/base-metadata.schema.ts @@ -4,6 +4,11 @@ import { header, replaceRefsWithValue } from '../src/renderer/src/stories/forms/ import baseMetadataSchema from './json/base_metadata_schema.json' assert { type: "json" } +const propsToInclude = { + ecephys: ["Device", "ElectrodeGroup", "Electrodes", "ElectrodeColumns", "definitions"] +} + + function getSpeciesNameComponents(arr: any[]) { const split = arr[arr.length - 1].split(' - ') return { @@ -41,8 +46,6 @@ export const preprocessMetadataSchema = (schema: any = baseMetadataSchema, globa copy.additionalProperties = false - - copy.required = Object.keys(copy.properties) // Require all properties at the top level copy.order = [ "NWBFile", "Subject" ] @@ -89,9 +92,18 @@ export const preprocessMetadataSchema = (schema: any = baseMetadataSchema, globa // Override description of keywords nwbProps.keywords.description = 'Terms to describe your dataset (e.g. Neural circuits, V1, etc.)' // Add description to keywords - + const ecephys = copy.properties.Ecephys const ophys = copy.properties.Ophys + if (ecephys) { + // Only include a select group of Ecephys metadata here + const ecephysProps = ecephys.properties; + Object.keys(ecephysProps).forEach((k) => (!propsToInclude.ecephys.includes(k) ? delete ecephysProps[k] : "")); + + // Change rendering order for electrode table columns + ecephysProps["Electrodes"].items.order = ["channel_name", "group_name", "shank_electrode_number"]; + } + if (ophys) { ophys.required = Object.keys(ophys.properties) diff --git a/src/renderer/src/stories/BasicTable.js b/src/renderer/src/stories/BasicTable.js index c56e8329e..801f75d90 100644 --- a/src/renderer/src/stories/BasicTable.js +++ b/src/renderer/src/stories/BasicTable.js @@ -234,7 +234,7 @@ export class BasicTable extends LitElement { if (type.includes("int") || type.includes("float")) type = "number"; if (type.startsWith("bool")) type = "boolean"; if (type.startsWith("str")) type = "string"; - } + } else type = "string"; // Default to string type // Check if required if (!value && "required" in this.#itemSchema.required.includes(col)) @@ -367,6 +367,7 @@ export class BasicTable extends LitElement { this.#updateRendered(); const entries = this.#itemProps; + for (let key in this.ignore) delete entries[key]; for (let key in this.ignore["*"] ?? {}) delete entries[key]; diff --git a/src/renderer/src/stories/JSONSchemaForm.js b/src/renderer/src/stories/JSONSchemaForm.js index c7e4d865f..77239650a 100644 --- a/src/renderer/src/stories/JSONSchemaForm.js +++ b/src/renderer/src/stories/JSONSchemaForm.js @@ -1275,7 +1275,6 @@ export class JSONSchemaForm extends LitElement { results, additionalPropPattern ); - return [...rendered, additionalElement]; } 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 dbec3445f..be24f28a1 100644 --- a/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js +++ b/src/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js @@ -236,6 +236,8 @@ export class GuidedMetadataPage extends ManagedPage { // JSON Schema Exceptions if (error.message.includes('does not conform to the "date-time" format.')) return false; if (error.message.includes('not allowed to have the additional property "Ecephys".')) return false; // NOTE: Remove after including Ecephys metadata + if (error.message.includes('not allowed to have the additional property "ElectricalSeriesAP')) + return false; // NOTE: Remove after including Icephys metadata }, groups: [ diff --git a/src/renderer/src/stories/pages/guided-mode/data/GuidedSourceData.js b/src/renderer/src/stories/pages/guided-mode/data/GuidedSourceData.js index fee0343cf..56c389e33 100644 --- a/src/renderer/src/stories/pages/guided-mode/data/GuidedSourceData.js +++ b/src/renderer/src/stories/pages/guided-mode/data/GuidedSourceData.js @@ -124,15 +124,9 @@ export class GuidedSourceDataPage extends ManagedPage { const { results: metadata, schema } = result; - // Always delete Ecephys if absent ( NOTE: temporarily manually removing from schema on backend...) - const alwaysDelete = ["Ecephys"]; - alwaysDelete.forEach((k) => { - if (!metadata[k]) delete info.metadata[k]; // Delete directly on metadata - }); - - for (let key in info.metadata) { - if (!alwaysDelete.includes(key) && !(key in schema.properties)) metadata[key] = undefined; - } + // for (let key in info.metadata) { + // if (!alwaysDelete.includes(key) && !(key in schema.properties)) metadata[key] = undefined; + // } // Merge metadata results with the generated info merge(metadata, info.metadata); diff --git a/src/renderer/src/stories/pages/guided-mode/storyStates.ts b/src/renderer/src/stories/pages/guided-mode/storyStates.ts index 211ee2c5c..76ba4afbd 100644 --- a/src/renderer/src/stories/pages/guided-mode/storyStates.ts +++ b/src/renderer/src/stories/pages/guided-mode/storyStates.ts @@ -1,5 +1,5 @@ import nwbBaseSchema from "../../../../../../schemas/base-metadata.schema"; -// import exephysExampleSchema from "../../../../../../schemas/json/ecephys_metadata_schema_example.json"; +import exephysExampleSchema from "../../../../../../schemas/json/ecephys_metadata_schema_example.json"; import { dashboard } from "../../../pages.js"; import { activateServer } from "../../../server/globals"; @@ -12,7 +12,7 @@ export const PageTemplate = (args = {}) => { }; -// nwbBaseSchema.properties.Ecephys = exephysExampleSchema; +nwbBaseSchema.properties.Ecephys = exephysExampleSchema; export const globalState = { project: { diff --git a/src/renderer/src/stories/table/Cell.ts b/src/renderer/src/stories/table/Cell.ts index 86fe647a1..491deb29a 100644 --- a/src/renderer/src/stories/table/Cell.ts +++ b/src/renderer/src/stories/table/Cell.ts @@ -110,7 +110,7 @@ export class TableCell extends LitElement { } set value(value) { - if (!value) value = [] + // if (!value) value = [] if (this.input) this.input.set(renderValue(value, this.schema)) // Allow null to be set directly this.#value = this.input diff --git a/src/renderer/src/stories/table/cells/base.ts b/src/renderer/src/stories/table/cells/base.ts index 1dca267cb..57f244d26 100644 --- a/src/renderer/src/stories/table/cells/base.ts +++ b/src/renderer/src/stories/table/cells/base.ts @@ -235,14 +235,12 @@ export class TableCellBase extends LitElement { const editor = this.#editor = this.#render('editor') const renderer = this.#renderer = this.#render('renderer') - this.addEventListener('blur', (ev) => { ev.stopPropagation() this.toggle(false) }) - if (!editor || !renderer || renderer === editor) return editor || renderer return html`
${renderer}
${editor}
` diff --git a/src/renderer/src/stories/table/convert.ts b/src/renderer/src/stories/table/convert.ts index cef7a43a4..b046b8410 100644 --- a/src/renderer/src/stories/table/convert.ts +++ b/src/renderer/src/stories/table/convert.ts @@ -1,5 +1,4 @@ export const renderValue = (value: any, schema: any) => { - if (schema.type === 'number' || schema.type === 'integer') { if (value === null) return '' } diff --git a/src/renderer/src/validation/index.js b/src/renderer/src/validation/index.js index 524795f83..075599877 100644 --- a/src/renderer/src/validation/index.js +++ b/src/renderer/src/validation/index.js @@ -13,7 +13,8 @@ export async function validateOnChange(name, parent, path, value) { let functions = []; const fullPath = [...path, name]; - const toIterate = fullPath; //fullPathNoRows // fullPath + + const toIterate = fullPath; const copy = { ...parent }; // Validate on a copy of the parent if (arguments.length > 3) copy[name] = value; // Update value on copy @@ -37,6 +38,7 @@ export async function validateOnChange(name, parent, path, value) { // Otherwise set the last wildcard else { lastWildcard = typeof acc["*"] === "string" ? acc["*"].replace(`{*}`, `${name}`) : acc["*"]; + overridden = false; // Re-enable if a new one is specified below } } else if (lastWildcard && typeof lastWildcard === "object") {