Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip dandi upload #704

Merged
merged 8 commits into from
Mar 27, 2024
6 changes: 6 additions & 0 deletions src/renderer/assets/css/guided.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
margin-top: 3px;
padding-left: 5px;
}

.guided--nav-bar-section-page.skipped {
border-left: 3px solid black !important;
}

.guided--nav-bar-section-page.completed {
border-left: 3px solid green;
}
Expand All @@ -60,6 +65,7 @@
pointer-events: none;
opacity: 0.5;
}

.guided--nav-bar-section-page.active {
border-left: 3px solid rgb(0, 133, 210);
background-color: var(--color-transparent-soda-green);
Expand Down
17 changes: 12 additions & 5 deletions src/renderer/src/pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { SettingsPage } from "./stories/pages/settings/SettingsPage";
import { InspectPage } from "./stories/pages/inspect/InspectPage";
import { PreviewPage } from "./stories/pages/preview/PreviewPage";
import { GuidedPreform } from "./stories/pages/guided-mode/setup/Preform";
import { GuidedDandiResultsPage } from "./stories/pages/guided-mode/results/GuidedDandiResults";

let dashboard = document.querySelector("nwb-dashboard");
if (!dashboard) dashboard = new Dashboard();
Expand Down Expand Up @@ -132,28 +133,34 @@ const pages = {

inspect: new GuidedInspectorPage({
title: "Inspector Report",
label: "Inspect files",
label: "Validate metadata",
section: sections[2],
sync: ["preview"],
}),

preview: new GuidedStubPreviewPage({
title: "Conversion Preview",
label: "Preview files",
label: "Preview NWB files",
section: sections[2],
sync: ["preview"],
}),

conversion: new GuidedResultsPage({
title: "Conversion Review",
label: "Review conversion",
section: sections[2],
}),

upload: new GuidedUploadPage({
title: "DANDI Upload Options",
label: "Upload to DANDI",
section: sections[3],
sync: ["conversion"],
}),

review: new GuidedResultsPage({
title: "Conversion Review",
label: "View conversion report",
review: new GuidedDandiResultsPage({
title: "Upload Review",
label: "Review published data",
section: sections[3],
}),
},
Expand Down
12 changes: 2 additions & 10 deletions src/renderer/src/stories/Dashboard.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { LitElement, html } from "lit";
import useGlobalStyles from "./utils/useGlobalStyles.js";

import { Main } from "./Main.js";
import { Main, checkIfPageIsSkipped } from "./Main.js";
import { Sidebar } from "./sidebar.js";
import { NavigationSidebar } from "./NavigationSidebar.js";

Expand Down Expand Up @@ -280,15 +280,7 @@ export class Dashboard extends LitElement {
pageState.active = false;

// Check if page is skipped based on workflow state (if applicable)
if (page.workflow) {
const workflow = page.workflow;
const workflowValues = globalState.project?.workflow ?? {};
const skipped = Object.entries(workflow).some(([key, state]) => {
if (!workflowValues[key]) return state.skip;
});

pageState.skipped = skipped;
}
pageState.skipped = checkIfPageIsSkipped(page, globalState.project?.workflow);

if (page.info.pages) this.#getSections(page.info.pages, globalState); // Show all states

Expand Down
22 changes: 8 additions & 14 deletions src/renderer/src/stories/JSONSchemaForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -508,27 +508,21 @@ export class JSONSchemaForm extends LitElement {
const resolvedSchema = e.schema; // Get offending schema

// ------------ Exclude Certain Errors ------------
// Allow for constructing types from object types
if (
e.message.includes("is not of a type(s)") &&
"properties" in resolvedSchema &&
resolvedSchema.type === "string"
)
return;
// Allow referring to floats as null (i.e. JSON NaN representation)
if (e.message === "is not of a type(s) number") {
if (resolvedValue === "NaN") return;
else if (resolvedValue === null) return;
else if (isRow) e.message = `${e.message}. ${templateNaNMessage}`;

if ("properties" in resolvedSchema && resolvedSchema.type === "string") return; // Allow for constructing types from object types
}

// Ignore required errors if value is empty
if (e.name === "required" && this.validateEmptyValues === null && !(e.property in e.instance)) return;

// Non-Strict Rule
if (resolvedSchema.strict === false && e.message.includes("is not one of enum values")) return;

// Allow referring to floats as null (i.e. JSON NaN representation)
if (e.message === "is not of a type(s) number") {
if (resolvedValue === "NaN") return;
else if (resolvedValue === null) {
} else if (isRow) e.message = `${e.message}. ${templateNaNMessage}`;
}

const prevHeader = name ? header(name) : "Row";

return {
Expand Down
34 changes: 32 additions & 2 deletions src/renderer/src/stories/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@ import useGlobalStyles from "./utils/useGlobalStyles.js";
import { GuidedFooter } from "./pages/guided-mode/GuidedFooter";
import { GuidedCapsules } from "./pages/guided-mode/GuidedCapsules.js";
import { GuidedHeader } from "./pages/guided-mode/GuidedHeader.js";

import { unsafeHTML } from "lit/directives/unsafe-html.js";

export const checkIfPageIsSkipped = (page, workflowValues) => {
if (page.workflow) {
const workflow = page.workflow;
const skipped = Object.entries(workflow).some(([key, state]) => {
const value = workflowValues[key];
if (state.condition) return state.condition(value) ? state.skip : false;
if (!value) return state.skip;
});

return skipped;
}

return false;
};

const componentCSS = `
:host {
display: grid;
Expand Down Expand Up @@ -97,6 +111,20 @@ export class Main extends LitElement {
section.scrollTop = 0;
}

#hasAvailableNextPages = (page) => {
const allNext = [];
let currentPage = page;
const workflowValues = page.info.globalState?.project?.workflow ?? {};
while (currentPage.info.next) {
const nextPage = currentPage.info.next;
const skipped = checkIfPageIsSkipped(nextPage, workflowValues);
if (!skipped) allNext.push(nextPage);
currentPage = nextPage;
}

return allNext.length > 0;
};

render() {
let { page = "", sections = {} } = this.toRender ?? {};

Expand All @@ -113,8 +141,10 @@ export class Main extends LitElement {
if (info.parent) {
if (!("footer" in page)) footer = true; // Allow navigating laterally if there is a next page

const hasAvailableNextPages = this.#hasAvailableNextPages(page);

// Go to home screen if there is no next page
if (!info.next) {
if (!info.next || !hasAvailableNextPages) {
footer = Object.assign(
{
exit: false,
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/stories/NavigationSidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export class NavigationSidebar extends LitElement {
class="
guided--nav-bar-section-page
hidden
${state.skipped ? " skipped" : ""}
${state.visited && !state.skipped ? " completed" : " not-completed"}
${state.active ? "active" : ""}"f
"
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/stories/pages/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class Page extends LitElement {
delete this.info.globalState.results[subject][session];
}

mapSessions = (callback, data = this.info.globalState) => mapSessions(callback, data);
mapSessions = (callback, data = this.info.globalState.results) => mapSessions(callback, data);

async convert({ preview } = {}) {
const key = preview ? "preview" : "conversion";
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/stories/pages/globals.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const sections = ["Project Structure", "Data Entry", "Conversion Preview", "Upload & Review"];
export const sections = ["Project Structure", "Data Entry", "File Conversion", "Dataset Publication"];
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ export class GuidedMetadataPage extends ManagedPage {

this.localState = { results: structuredClone(this.info.globalState.results ?? {}) };

this.forms = this.mapSessions(this.createForm, this.localState);
this.forms = this.mapSessions(this.createForm, this.localState.results);

let instances = {};
this.forms.forEach(({ subject, session, form }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ export class GuidedSourceDataPage extends ManagedPage {
render() {
this.localState = { results: structuredClone(this.info.globalState.results ?? {}) };

this.forms = this.mapSessions(this.createForm, this.localState);
this.forms = this.mapSessions(this.createForm, this.localState.results);

let instances = {};
this.forms.forEach(({ subject, session, form }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ export class GuidedUploadPage extends Page {
],
};

workflow = {
upload_to_dandi: {
condition: (v) => v === false,
skip: true,
},
};

globalModal = null;
#saveNotification;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { html } from "lit";
import { Page } from "../../Page.js";

import { DandiResults } from "../../../DandiResults";

export class GuidedDandiResultsPage extends Page {
constructor(...args) {
super(...args);
}

footer = {};

workflow = {
upload_to_dandi: {
condition: (v) => v === false,
skip: true,
},
};

updated() {
this.save(); // Save the current state
}

render() {
const { conversion } = this.info.globalState;

if (!conversion)
return html`<div style="text-align: center;"><p>Your conversion failed. Please try again.</p></div>`;

const { info = {}, results } = this.info.globalState.upload ?? {};
const { dandiset } = info;

return html`<div style="padding: 10px 20px;">
${new DandiResults({
id: dandiset,
files: {
subject: results.map((file) => {
return { file };
}),
},
})}
</div>`;
}
}

customElements.get("nwbguide-guided-dandi-results-page") ||
customElements.define("nwbguide-guided-dandi-results-page", GuidedDandiResultsPage);
28 changes: 13 additions & 15 deletions src/renderer/src/stories/pages/guided-mode/results/GuidedResults.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { html } from "lit";
import { Page } from "../../Page.js";

import { DandiResults } from "../../../DandiResults.js";

export class GuidedResultsPage extends Page {
constructor(...args) {
super(...args);
Expand All @@ -20,19 +18,19 @@ export class GuidedResultsPage extends Page {
if (!conversion)
return html`<div style="text-align: center;"><p>Your conversion failed. Please try again.</p></div>`;

const { info = {}, results } = this.info.globalState.upload ?? {};
const { dandiset } = info;

return html`<div style="padding: 10px 20px;">
${new DandiResults({
id: dandiset,
files: {
subject: results.map((file) => {
return { file };
}),
},
})}
</div>`;
return html`
<p>Your data was successfully converted to NWB!</p>
${Object.entries(conversion)
.map(([subject, sessions]) => {
return html` <h3 style="margin: 0; padding: 0;">sub-${subject}</h3>
<ol style="margin: 10px 0px; padding-top: 0;">
${Object.entries(sessions).map(([session, info]) => {
return html`<li><b>ses-${session}</b> — ${info.file}</li>`;
})}
</ol>`;
})
.flat()}
`;
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/renderer/src/stories/pages/guided-mode/setup/Preform.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ const questions = {
},
default: false,
},
upload_to_dandi: {
type: "boolean",
title: "Would you like to upload your data to DANDI?",
default: true,
},
};

// -------------------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/src/stories/pages/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ export function merge(toMerge = {}, target = {}, mergeOptions = {}) {
return target;
}

export function mapSessions(callback = (value) => value, globalState) {
return Object.entries(globalState.results)
export function mapSessions(callback = (value) => value, toIterate = {}) {
return Object.entries(toIterate)
.map(([subject, sessions]) => {
return Object.entries(sessions).map(([session, info], i) => callback({ subject, session, info }, i));
})
Expand Down
2 changes: 1 addition & 1 deletion tests/metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('metadata is specified correctly', () => {
// Allow mouse (full list populated from server)
baseMetadataSchema.properties.Subject.properties.species.enum = ['Mus musculus']

const result = mapSessions(info => createResults(info, globalState), globalState)
const result = mapSessions(info => createResults(info, globalState), globalState.results)
const res = validator.validate(result[0], baseMetadataSchema) // Check first session with JSON Schema
expect(res.errors).toEqual([])
})
Expand Down
Loading