diff --git a/.github/workflows/Build-and-deploy-mac.yml b/.github/workflows/Build-and-deploy-mac.yml index 450bd74a7..31b72078b 100644 --- a/.github/workflows/Build-and-deploy-mac.yml +++ b/.github/workflows/Build-and-deploy-mac.yml @@ -1,5 +1,6 @@ name: Mac Release run-name: ${{ github.actor }} is building a MAC release for NWB GUIDE +# NOTE: even though the runner is an arm64 mac, both x64 and arm64 releases will be made on: workflow_dispatch: @@ -22,7 +23,7 @@ jobs: - uses: conda-incubator/setup-miniconda@v2 with: activate-environment: nwb-guide - environment-file: environments/environment-MAC.yml + environment-file: environments/environment-MAC-arm64.yml auto-activate-base: false - uses: actions/setup-node@v3 diff --git a/.github/workflows/pyflask-build-and-dist-tests.yml b/.github/workflows/pyflask-build-and-dist-tests.yml index cee21a443..bbf9e4f93 100644 --- a/.github/workflows/pyflask-build-and-dist-tests.yml +++ b/.github/workflows/pyflask-build-and-dist-tests.yml @@ -32,8 +32,13 @@ jobs: # prefix: /usr/share/miniconda3/envs/nwb-guide - python-version: "3.9" - os: macos-latest - label: environments/environment-Mac.yml + os: macos-latest # Mac arm64 runner + label: environments/environment-MAC-arm64.yml + prefix: /Users/runner/miniconda3/envs/nwb-guide + + - python-version: "3.9" + os: macos-13 # Mac x64 runner + label: environments/environment-MAC.yml prefix: /Users/runner/miniconda3/envs/nwb-guide - python-version: "3.9" @@ -45,6 +50,9 @@ jobs: - uses: actions/checkout@v4 - run: git fetch --prune --unshallow --tags + - name: Printout architecture + run: uname -m + # see https://github.com/conda-incubator/setup-miniconda#caching-environments - name: Setup Mambaforge uses: conda-incubator/setup-miniconda@v3 @@ -81,7 +89,7 @@ jobs: - run: npm ci --verbose # fix for macos build - - if: matrix.os == 'macos-latest' + - if: matrix.os == 'macos-latest' || matrix.os == 'macos-13' run: rm -f /Users/runner/miniconda3/envs/nwb-guide/lib/python3.9/site-packages/sonpy/linux/sonpy.so - name: Build PyFlask distribution diff --git a/.github/workflows/testing-live-services.yml b/.github/workflows/testing-live-services.yml index 1169c03d8..975c7478e 100644 --- a/.github/workflows/testing-live-services.yml +++ b/.github/workflows/testing-live-services.yml @@ -27,8 +27,11 @@ jobs: - os: ubuntu-latest label: environments/environment-Linux.yml - - os: macos-latest - label: environments/environment-Mac.yml + - os: macos-latest # Mac arm64 runner + label: environments/environment-MAC-arm64.yml + + - os: macos-13 # Mac x64 runner + label: environments/environment-MAC.yml - os: windows-latest label: environments/environment-Windows.yml diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index f5b0e6c49..9d22c5c28 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -27,8 +27,11 @@ jobs: - os: ubuntu-latest label: environments/environment-Linux.yml - - os: macos-latest - label: environments/environment-Mac.yml + - os: macos-latest # Mac arm64 runner + label: environments/environment-MAC-arm64.yml + + - os: macos-13 # Mac x64 runner + label: environments/environment-MAC.yml - os: windows-latest label: environments/environment-Windows.yml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 328c18cbe..9273adef7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 24.4.0 + rev: 24.4.2 hooks: - id: black exclude: ^docs/ diff --git a/docs/index.rst b/docs/index.rst index 01f317c28..a4333737f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ -Welcome to the NWB GUIDE documentation! -======================================= +NWB GUIDE documentation +======================= | .. image:: ../src/renderer/assets/img/logo-guide-draft-transparent-tight.png @@ -13,7 +13,7 @@ Welcome to the NWB GUIDE documentation! The **NWB Graphical User Interface for Data Entry (GUIDE)** is an Electron-based application designed to help you navigate the complex process of converting data from common proprietary formats into the :nwb-overview:`Neurodata Without Borders (NWB) <>` standard. -The resulting files are fully compliant with the best practices expected of the :dandi-archive:`Distributed Archives for Neurophysiology Data Integration (DANDI) <>`. +The resulting files are fully compliant with the best practices expected by the :dandi-archive:`Distributed Archives for Neurophysiology Data Integration (DANDI) <>`. .. toctree:: :maxdepth: 2 diff --git a/docs/tutorials/dataset.rst b/docs/tutorials/dataset.rst index 079492639..6b436641a 100644 --- a/docs/tutorials/dataset.rst +++ b/docs/tutorials/dataset.rst @@ -1,5 +1,5 @@ -Dataset Generation -================== +Example Dataset Generation +========================== Our tutorials focus on converting extracellular electrophysiology data in the SpikeGLX and Phy formats. To get you started as quickly as possible, we’ve created a way to generate this Neuropixel-like dataset at the click of a button! diff --git a/src/renderer/src/pages.js b/src/renderer/src/pages.js index 3f15cb90c..8a172bfe2 100644 --- a/src/renderer/src/pages.js +++ b/src/renderer/src/pages.js @@ -144,6 +144,7 @@ const pages = { title: "Conversion Review", label: "Review conversion", section: sections[2], + sync: ["conversion"], }), upload: new GuidedUploadPage({ diff --git a/src/renderer/src/progress/index.js b/src/renderer/src/progress/index.js index 56a180f52..84fc87553 100644 --- a/src/renderer/src/progress/index.js +++ b/src/renderer/src/progress/index.js @@ -182,13 +182,13 @@ export const get = (name) => { ); }; -export function resume(name) { +export async function resume(name) { const global = this ? this.load(name) : get(name); let commandToResume = global["page-before-exit"] || "//details"; updateURLParams({ project: name }); - if (this) this.onTransition(commandToResume); + if (this) await this.onTransition(commandToResume); return commandToResume; } diff --git a/src/renderer/src/stories/Dashboard.js b/src/renderer/src/stories/Dashboard.js index bf7727d8d..e56b50273 100644 --- a/src/renderer/src/stories/Dashboard.js +++ b/src/renderer/src/stories/Dashboard.js @@ -242,7 +242,7 @@ export class Dashboard extends LitElement { this.page.set(toPass, false); - this.page.checkSyncState().then(() => { + this.page.checkSyncState().then(async () => { const projectName = info.globalState?.project?.name; this.subSidebar.header = projectName @@ -264,8 +264,8 @@ export class Dashboard extends LitElement { }); // Skip right over the page if configured as such - if (previous && previous.info.previous === this.page) this.page.onTransition(-1); - else this.page.onTransition(1); + if (previous && previous.info.previous === this.page) await this.page.onTransition(-1); + else await this.page.onTransition(1); } }); } @@ -328,13 +328,13 @@ export class Dashboard extends LitElement { if (typeof transition === "number") { const info = this.page.info; const sign = Math.sign(transition); - if (sign === 1) return this.setAttribute("activePage", info.next.info.id); - else if (sign === -1) return this.setAttribute("activePage", (info.previous ?? info.parent).info.id); // Default to back in time + if (sign === 1) transition = info.next.info.id; + else if (sign === -1) transition = (info.previous ?? info.parent).info.id; // Default to back in time } this.setAttribute("activePage", transition); - return await promise; + return promise; }; this.main.updatePages = () => { diff --git a/src/renderer/src/stories/pages/FormPage.js b/src/renderer/src/stories/pages/FormPage.js index 76b9068e0..8930e8d17 100644 --- a/src/renderer/src/stories/pages/FormPage.js +++ b/src/renderer/src/stories/pages/FormPage.js @@ -63,7 +63,7 @@ export class GuidedFormPage extends Page { onNext: async () => { await this.save(); // Save in case validation fails await this.form.validate(); // Validate the results of the form - this.to(1); + return this.to(1); }, }; diff --git a/src/renderer/src/stories/pages/Page.js b/src/renderer/src/stories/pages/Page.js index 851061e71..7a9bfaff7 100644 --- a/src/renderer/src/stories/pages/Page.js +++ b/src/renderer/src/stories/pages/Page.js @@ -1,7 +1,7 @@ import { LitElement, html } from "lit"; import { runConversion } from "./guided-mode/options/utils.js"; import { get, save } from "../../progress/index.js"; -import { dismissNotification, notify } from "../../dependencies/globals.js"; +import { dismissNotification, isStorybook, notify } from "../../dependencies/globals.js"; import { randomizeElements, mapSessions, merge } from "./utils.js"; import { resolveMetadata } from "./guided-mode/data/utils.js"; @@ -134,9 +134,10 @@ export class Page extends LitElement { // Indicate conversion has run successfully const { desyncedData } = this.info.globalState; + if (!desyncedData) this.info.globalState.desyncedData = {}; + if (desyncedData) { - delete desyncedData[key]; - if (Object.keys(desyncedData).length === 0) delete this.info.globalState.desyncedData; + desyncedData[key] = false; await this.save({}, false); } } @@ -233,18 +234,18 @@ export class Page extends LitElement { checkSyncState = async (info = this.info, sync = info.sync) => { if (!sync) return; + if (isStorybook) return; const { desyncedData } = info.globalState; - if (desyncedData) { - return Promise.all( - sync.map((k) => { - if (desyncedData[k]) { - if (k === "conversion") return this.convert(); - else if (k === "preview") return this.convert({ preview: true }); - } - }) - ); - } + + return Promise.all( + sync.map((k) => { + if (desyncedData?.[k] !== false) { + if (k === "conversion") return this.convert(); + else if (k === "preview") return this.convert({ preview: true }); + } + }) + ); }; updateSections = () => { diff --git a/src/renderer/src/stories/pages/guided-mode/GuidedStart.stories.js b/src/renderer/src/stories/pages/guided-mode/GuidedStart.stories.js deleted file mode 100644 index 1a812dcb2..000000000 --- a/src/renderer/src/stories/pages/guided-mode/GuidedStart.stories.js +++ /dev/null @@ -1,14 +0,0 @@ -import { globalState, PageTemplate } from "./storyStates"; - -export default { - title: "Pages/Guided Mode/Start", - parameters: { - chromatic: { disableSnapshot: false }, - }, -}; - -export const Default = PageTemplate.bind({}); -Default.args = { - activePage: "//start", - globalState, -}; diff --git a/tests/e2e/e2e.test.ts b/tests/e2e/e2e.test.ts index f7b0f2343..44ba41161 100644 --- a/tests/e2e/e2e.test.ts +++ b/tests/e2e/e2e.test.ts @@ -144,17 +144,8 @@ describe('E2E Test', () => { }, { upload_to_dandi: true }) - await toNextPage('structure') // Save data without a popup await to('//conversion') - - // Do not prompt to save - await evaluate(() => { - const dashboard = document.querySelector('nwb-dashboard') - const page = dashboard.page - page.unsavedUpdates = false - }) - - await to('//upload') // NOTE: It would be nice to avoid having to re-run the conversion... + await to('//upload') }) diff --git a/tests/e2e/workflow.ts b/tests/e2e/workflow.ts index fd097e01a..6fadea192 100644 --- a/tests/e2e/workflow.ts +++ b/tests/e2e/workflow.ts @@ -1,4 +1,4 @@ -import { describe, test } from "vitest" +import { describe, expect, test } from "vitest" import { sleep } from '../puppeteer' @@ -429,21 +429,30 @@ export default async function runWorkflow(name, workflow, identifier) { test('Review NWB Inspector output', async () => { - await takeScreenshot(join(identifier, 'inspect-page'), 5000) // Finish file inspection and allow full load of Neurosift page + await takeScreenshot(join(identifier, 'inspect-page'), 5000) // Allow for the completion of file validation await toNextPage('preview') }) test('Review Neurosift visualization', async () => { - await takeScreenshot(join(identifier, 'preview-page'), 1000) // Finish loading Neurosift + await takeScreenshot(join(identifier, 'preview-page'), 1000) // Allow full load of Neurosift page await toNextPage('conversion') }) test('View the conversion results', async () => { + await takeScreenshot(join(identifier, 'conversion-results-page'), 1000) + + const conversionCompleted = await evaluate(() => { + const dashboard = document.querySelector('nwb-dashboard') + const page = dashboard.page + return !!page.info.globalState.conversion + }) - await takeScreenshot(join(identifier, 'conversion-results-page'), 300) if (workflow.upload_to_dandi) await toNextPage('upload') else await toNextPage('') + + expect(conversionCompleted).toBe(true) + })