dandi / dandisets-healthstatus

Healthchecks of dandisets and support libraries (pynwb and matnwb)
0 stars 1 forks source link

Add rudimentary webnwb testing #42

Open yarikoptic opened 1 year ago

yarikoptic commented 1 year ago

https://github.com/brainsatplay/webnwb#loading-a-local-file gives an example

const file = io.load(filename)

Since I know no JS, @garrettmflynn, would you be so kind to guide me and @jwodder on

garrettmflynn commented 1 year ago

Just out of curiosity, are y'all intending to use webnwb in Node.js in the long run? Generally, I've created the library for the browser—though it should be possible to fully support node with a bit of finessing.

1. Execute WebNWB

Generally, if you'd like to use webnwb using Node.js, you'd install the library using Node Package Manager (NPM), import that in a .js file (e.g. a file called main.js with const nwb = require('webnwb') or import nwb from 'webnwb') and insert the code you'd like to execute, then run node main.js in the console.

Internally in the webnwb and hdf5-io repos, I'm using Jest for testing. For this, I'm writing a test file and running npm run test, which executes the Jest CLI based on the script property of the package.json file.

To help you both get started, I created a simple test repo using the browser and Node.js. It seems like there are some leftover bugs for streaming NWB files in Node.js and loading local files. The installation and execution steps are contained in that repo's README.md file. Generally, just run npm i then npm start for Node.js, and use Visual Studio Code's Live Server extension for the HTML file.

Check WebNWB Will Work

You should be able to check that required properties such as identifier or nwb_version are present.

Are you expecting to load NWB files that aren't able to be read by webnwb?

yarikoptic commented 1 year ago

Just out of curiosity, are y'all intending to use webnwb in Node.js in the long run?

ATM I have no immediate "use" cases for webnwb so the answer would be No I guess ;) in the effort of this particular (dandisets-healthstatus) effort we are trying to make sure that all .nwb files uploaded to the archive could be at least opened with most common libraries for .nwb (pynwb, matnwb, and now webnwb ;) ). Depending on how people would like to use - in browsers pure JS or via node, we might want to adjust/extend our tests battery.

To help you both get started, I created a simple test repo using the browser and Node.js. It seems like there are some leftover bugs for streaming NWB files in Node.js and loading local files.

THANK YOU!

Indeed the script errors out for me ```shell ❯ git clone https://github.com/garrettmflynn/webnwb-test Cloning into 'webnwb-test'... remote: Enumerating objects: 14, done. remote: Counting objects: 100% (14/14), done. remote: Compressing objects: 100% (11/11), done. remote: Total 14 (delta 0), reused 14 (delta 0), pack-reused 0 Receiving objects: 100% (14/14), 1.51 MiB | 1.38 MiB/s, done. ❯ cd webnwb-test README.md dir/ index.html index.js localFerguson.nwb package-lock.json package.json polyfill.js ❯ npm install added 10 packages, and audited 11 packages in 28s 3 packages are looking for funding run `npm fund` for details found 0 vulnerabilities npm install 8.35s user 1.71s system 35% cpu 28.376 total changes on filesystem: package-lock.json | 3 +++ ❯ node index.js [webnwb]: Generated core in 4.661597013473511 ms [webnwb]: Loading hdmf-common extension. [webnwb]: Generated hdmf-common in 0.628790020942688 ms [webnwb]: Loading hdmf-experimental extension. [webnwb]: Generated hdmf-experimental in 0.2535330057144165 ms file [String: '2.4.0'] [String: 'WebNWB_Test_a3x1l'] [String: 'Just a test.'] [webnwb]: Generated core in 8.464712023735046 ms [webnwb]: Loading hdmf-common extension. [webnwb]: Generated hdmf-common in 0.5233869552612305 ms remote [String: '2.2.5'] [String: 'b1c5c436-b615-459b-bf54-0ef91d908139'] [String: 'Ferguson et al. PYR1.abf [Pyramidal cell 1, strongly adapting]'] HDF5-DIAG: Error detected in HDF5 (1.12.1) thread 0: #000: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5F.c line 620 in H5Fopen(): unable to open file major: File accessibility minor: Unable to open file #001: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLcallback.c line 3502 in H5VL_file_open(): failed to iterate over available VOL connector plugins major: Virtual Object Layer minor: Iteration failed #002: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5PLpath.c line 579 in H5PL__path_table_iterate(): can't iterate over plugins in plugin path '(null)' major: Plugin for dynamically loaded library minor: Iteration failed #003: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5PLpath.c line 620 in H5PL__path_table_iterate_process_path(): can't open directory: /usr/local/hdf5/lib/plugin major: Plugin for dynamically loaded library minor: Can't open directory or file #004: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLcallback.c line 3351 in H5VL__file_open(): open failed major: Virtual Object Layer minor: Can't open object #005: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLnative_file.c line 97 in H5VL__native_file_open(): unable to open file major: File accessibility minor: Unable to open file #006: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5Fint.c line 1835 in H5F_open(): unable to open file: name = 'localFerguson.nwb', tent_flags = 0 major: File accessibility minor: Unable to open file #007: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5FD.c line 723 in H5FD_open(): open failed major: Virtual File Layer minor: Unable to initialize object #008: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5FDsec2.c line 355 in H5FD__sec2_open(): unable to open file: name = 'localFerguson.nwb', errno = 44, error message = 'No such file or directory', flags = 0, o_flags = 0 major: File accessibility minor: Unable to open file [hdf5-io]: Could not open file localFerguson.nwb [webnwb]: Generated core in 7.830779016017914 ms [webnwb]: Loading hdmf-common extension. [webnwb]: Generated hdmf-common in 0.44422799348831177 ms [webnwb]: Loading hdmf-experimental extension. [webnwb]: Generated hdmf-experimental in 0.2418140172958374 ms WEBNWB ERROR: Failed to load local file properly... undefined ```

as I care only about opening/loading ATM, I took only opening part and with help of chatgpt I added loading filename from cmdline

to get this script ```javascript import './polyfill.js' // This is required since the bundle assumes there is a global Blob object import nwb from 'webnwb' import process from 'process' const args = process.argv.slice(2) const filename = args[0] const io = new nwb.NWBHDF5IO() const file = await io.load(filename) console.log('file', file.nwb_version, file.identifier,file.session_description, '\n\n') ```
which failed similarly on a sample .nwb file (fuse mounted from s3 url locally) ```shell ❯ node opennwb.js ../../dandisets-fused/000003/sub-YutaMouse20/sub-YutaMouse20_ses-YutaMouse20-140321_behavior+ecephys.nwb HDF5-DIAG: Error detected in HDF5 (1.12.1) thread 0: #000: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5F.c line 620 in H5Fopen(): unable to open file major: File accessibility minor: Unable to open file #001: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLcallback.c line 3502 in H5VL_file_open(): failed to iterate over available VOL connector plugins major: Virtual Object Layer minor: Iteration failed #002: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5PLpath.c line 579 in H5PL__path_table_iterate(): can't iterate over plugins in plugin path '(null)' major: Plugin for dynamically loaded library minor: Iteration failed #003: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5PLpath.c line 620 in H5PL__path_table_iterate_process_path(): can't open directory: /usr/local/hdf5/lib/plugin major: Plugin for dynamically loaded library minor: Can't open directory or file #004: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLcallback.c line 3351 in H5VL__file_open(): open failed major: Virtual Object Layer minor: Can't open object #005: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLnative_file.c line 97 in H5VL__native_file_open(): unable to open file major: File accessibility minor: Unable to open file #006: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5Fint.c line 1835 in H5F_open(): unable to open file: name = '../../dandisets-fused/000003/sub-YutaMouse20/sub-YutaMouse20_ses-YutaMouse20-140321_behavior+ecephys.nwb', tent_flags = 0 major: File accessibility minor: Unable to open file #007: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5FD.c line 723 in H5FD_open(): open failed major: Virtual File Layer minor: Unable to initialize object #008: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5FDsec2.c line 355 in H5FD__sec2_open(): unable to open file: name = '../../dandisets-fused/000003/sub-YutaMouse20/sub-YutaMouse20_ses-YutaMouse20-140321_behavior+ecephys.nwb', errno = 44, error message = 'No such file or directory', flags = 0, o_flags = 0 major: File accessibility minor: Unable to open file [hdf5-io]: Could not open file ../../dandisets-fused/000003/sub-YutaMouse20/sub-YutaMouse20_ses-YutaMouse20-140321_behavior+ecephys.nwb [webnwb]: Generated core in 12.945061028003693 ms [webnwb]: Loading hdmf-common extension. [webnwb]: Generated hdmf-common in 0.8175250291824341 ms [webnwb]: Loading hdmf-experimental extension. [webnwb]: Generated hdmf-experimental in 0.37734299898147583 ms file [String: '2.4.0'] undefined undefined ```

Are you expecting to load NWB files that aren't able to be read by webnwb?

sorry , I am not following... FWIW: I am expecting webnwb to be able to load and read any legit .nwb file. I expect that only legit .nwb files would be uploaded to dandiarchive.

garrettmflynn commented 1 year ago

Thank you for explaining the goals of the repo. Now your questions make much more sense.

Can you explain to me how MatNWB and PyNWB are currently being checked? That way, I can propose an analogous solution to what you already have for those.

There will be very few inconsistencies between the browser and Node—aside from the latter being more difficult to set up read/stream and write operations, while also being better to access local files without user input. As such, I'd expect we could get by with finalized tests for Node.

yarikoptic commented 1 year ago

Can you explain to me how MatNWB and PyNWB are currently being checked? That way, I can propose an analogous solution to what you already have for those.

ATM we are trying to stay as basic (as minimal/simplest invocation) as possible to test the most basic functionality - being able to open an .nwb file without blowing up, but even that still shows to be challenging ;-)

with pynwb.NWBHDF5IO(sys.argv[1], "r", load_namespaces=True) as reader:
    nwbfile = reader.read()
assert repr(nwbfile)
assert str(nwbfile)
generateCore()
nwb = nwbRead(f, 'savedir', '../out')

but we might need to change, following the discussion in https://github.com/NeurodataWithoutBorders/matnwb/issues/493 and https://github.com/NeurodataWithoutBorders/matnwb/issues/491 .

Unfortunately our full sweep process is still too slow due to need to be at large serial so we can't quickly change, but soon will do after current sweep through all dandisets is done and we get our https://github.com/dandi/dandisets-healthstatus#readme updated.

garrettmflynn commented 1 year ago

Got it. The conversion of the PyNWB test for WebNWB would look like the following:

import './polyfill.js' // This is required since the bundle assumes there is a global Blob object
import nwb from 'webnwb'
import process from 'process'

const args = process.argv.slice(2)
const filename = args[0]

const io = new nwb.NWBHDF5IO()
const file = await io.load(filename)

if (!file) throw new Error(`File ${filename} could not be loaded`)

// // Works in current version (see note in text below)
// const identifier = async file.identifier
// if (!identifier) throw new Error(`File ${filename} could not be loaded`)

However, I've noticed that this currently would not be thrown for files that currently error, as they'll return an empty NWBFile object (which passes this check), which is why I suggested checking properties like identifier that are required but not set by the API if missing.

I will fix this in the next release of WebNWB so that files that cannot be loaded / streamed return an undefined value and, therefore, trigger the above check.