Skip to content

Commit

Permalink
Merge pull request #337 from NeurodataWithoutBorders/standalone-preview
Browse files Browse the repository at this point in the history
Standalone Preview (responsive to file association)
  • Loading branch information
CodyCBakerPhD authored Sep 13, 2023
2 parents f7ce052 + 400ced5 commit 769c3d2
Show file tree
Hide file tree
Showing 20 changed files with 264 additions and 91 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
{
"ext": "nwb",
"name": "NWB File",
"role": "Editor"
"role": "Viewer"
}
],
"files": [
Expand Down
28 changes: 27 additions & 1 deletion pyflask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
import sys
import json
import multiprocessing
from os.path import sep
from logging import Formatter, DEBUG
from logging.handlers import RotatingFileHandler
from pathlib import Path
from urllib.parse import unquote


# https://stackoverflow.com/questions/32672596/pyinstaller-loads-script-multiple-times#comment103216434_32677108
multiprocessing.freeze_support()


from flask import Flask, request, send_from_directory
from flask import Flask, request, send_from_directory, send_file
from flask_cors import CORS
from flask_restx import Api, Resource

Expand Down Expand Up @@ -52,6 +55,29 @@
api.add_namespace(neuroconv_api)
api.init_app(app)

registered = {}


@app.route("/files")
def get_all_files():
return list(registered.keys())


@app.route("/files/<path:path>", methods=["GET", "POST"])
def handle_file_request(path):
if request.method == "GET":
if registered[path]:
return send_file(unquote(path))
else:
app.abort(404, "Resource is not accessible.")

else:
if ".nwb" in path:
registered[path] = True
return request.base_url
else:
app.abort(400, str("Path does not point to an NWB file."))


@app.route("/conversions/<path:path>")
def send_conversions(path):
Expand Down
10 changes: 7 additions & 3 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,14 +355,18 @@ function initialize() {
else app.on("ready", onAppReady)
}

function onFileOpened(_, path: string) {
function isValidFile(filepath: string) {
return !fs.existsSync(filepath) && path.extname(filepath) === '.nwb'
}

function onFileOpened(_, filepath: string) {
restoreWindow() || initialize(); // Ensure the application is properly visible
onWindowReady((win) => win.webContents.send('fileOpened', path))
onWindowReady((win) => win.webContents.send('fileOpened', filepath))
}

if (isWindows && process.argv.length >= 2) {
const openFilePath = process.argv[1];
if (openFilePath !== "") onFileOpened(null, openFilePath)
if (isValidFile(openFilePath)) onFileOpened(null, openFilePath)
}

// Make this app a single instance app.
Expand Down
9 changes: 7 additions & 2 deletions src/renderer/src/electron/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { updateURLParams } from "../../utils/url.js";
import isElectron from "./check.js";

export { isElectron };
Expand All @@ -21,8 +22,12 @@ if (isElectron) {
remote = require("@electron/remote");
app = remote.app;

electron.ipcRenderer.on("fileOpened", (info, ...args) => {
console.log("File opened!", ...args);
electron.ipcRenderer.on("fileOpened", (info, filepath) => {
updateURLParams({ file: filepath });
const dashboard = document.querySelector("nwb-dashboard");
const activePage = dashboard.getAttribute("activePage");
if (activePage === "preview") dashboard.requestUpdate();
else dashboard.setAttribute("activePage", "preview");
});

["log", "warn", "error"].forEach((method) =>
Expand Down
15 changes: 11 additions & 4 deletions src/renderer/src/pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ import { TutorialPage } from "./stories/pages/tutorial/Tutorial";
import tutorialIcon from "./stories/assets/exploration.svg?raw";
import uploadIcon from "./stories/assets/dandi.svg?raw";
import inspectIcon from "./stories/assets/inspect.svg?raw";
import neurosiftIcon from "./stories/assets/neurosift-logo.svg?raw";

import settingsIcon from "./stories/assets/settings.svg?raw";

import { UploadsPage } from "./stories/pages/uploads/UploadsPage";
import { SettingsPage } from "./stories/pages/settings/SettingsPage";
import { InspectPage } from "./stories/pages/inspect/InspectPage";
import { PreviewPage } from "./stories/pages/preview/PreviewPage";

let dashboard = document.querySelector("nwb-dashboard");
if (!dashboard) dashboard = new Dashboard();
Expand Down Expand Up @@ -165,14 +168,18 @@ const pages = {
}),
},
}),
uploads: new UploadsPage({
label: "Uploads",
icon: uploadIcon,
}),
inspect: new InspectPage({
label: "Inspect",
icon: inspectIcon,
}),
preview: new PreviewPage({
label: "Neurosift",
icon: neurosiftIcon,
}),
uploads: new UploadsPage({
label: "Uploads",
icon: uploadIcon,
}),
tutorial: new TutorialPage({
label: "Tutorial",
icon: tutorialIcon,
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/src/progress/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
import { fs } from "../electron/index.js";
import { joinPath, runOnLoad } from "../globals.js";
import { merge } from "../stories/pages/utils.js";
import { updateAppProgress, updateFile, updateURLParams } from "./update.js";
import { updateAppProgress, updateFile } from "./update.js";
import { updateURLParams } from "../../utils/url.js";

export * from "./update";

Expand Down
16 changes: 1 addition & 15 deletions src/renderer/src/progress/update.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { updateURLParams } from "../../utils/url.js";
import { guidedProgressFilePath } from "../dependencies/simple.js";
import { fs } from "../electron/index.js";
import { joinPath } from "../globals.js";
Expand All @@ -23,21 +24,6 @@ export const update = (newDatasetName, previousDatasetName) => {
return "Dataset name updated";
} else throw new Error("No previous dataset name provided");
};

export function updateURLParams(paramsToUpdate) {
const params = new URLSearchParams(location.search);
for (let key in paramsToUpdate) {
const value = paramsToUpdate[key];
if (value == undefined) params.delete(key);
else params.set(key, value);
}

// Update browser history state
const value = `${location.pathname}?${params}`;
if (history.state) Object.assign(history.state, paramsToUpdate);
window.history.pushState(history.state, null, value);
}

export const updateAppProgress = (
pageId,
dataOrProjectName = {},
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/src/stories/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ export class Dashboard extends LitElement {
this.#updated();
}

requestPageUpdate() {
if (this.#active) this.#active.requestUpdate();
}

createRenderRoot() {
return this;
}
Expand Down
15 changes: 1 addition & 14 deletions src/renderer/src/stories/JSONSchemaForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { LitElement, css, html } from "lit";
import { Accordion } from "./Accordion";

import { checkStatus } from "../validation";
import { capitalize, header } from "./forms/utils";
import { header } from "./forms/utils";
import { resolve } from "../promises";
import { merge } from "./pages/utils";
import { resolveProperties } from "./pages/guided-mode/data/utils";
Expand Down Expand Up @@ -112,14 +112,6 @@ pre {
.required.conditional label:after {
color: transparent;
}
.guided--text-input-instructions {
font-size: 13px;
width: 100%;
padding-top: 4px;
color: dimgray !important;
}
`;

document.addEventListener("dragover", (e) => {
Expand Down Expand Up @@ -422,11 +414,6 @@ export class JSONSchemaForm extends LitElement {
>
<label class="guided--form-label">${header(name)}</label>
${interactiveInput}
${info.description
? html`<p class="guided--text-input-instructions">
${capitalize(info.description)}${info.description.slice(-1)[0] === "." ? "" : "."}
</p>`
: ""}
<div class="errors"></div>
<div class="warnings"></div>
</div>
Expand Down
23 changes: 23 additions & 0 deletions src/renderer/src/stories/JSONSchemaInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { Button } from "./Button";
import { List } from "./List";
import { Modal } from "./Modal";

import { capitalize } from "./forms/utils";

const filesystemQueries = ["file", "directory"];

export class JSONSchemaInput extends LitElement {
Expand Down Expand Up @@ -69,6 +71,13 @@ export class JSONSchemaInput extends LitElement {
input[type="number"].hideStep {
-moz-appearance: textfield;
}
.guided--text-input-instructions {
font-size: 13px;
width: 100%;
padding-top: 4px;
color: dimgray !important;
}
`;
}

Expand Down Expand Up @@ -118,6 +127,20 @@ export class JSONSchemaInput extends LitElement {
}

render() {
const { info } = this;

const input = this.#render();
return html`
${input}
${info.description
? html`<p class="guided--text-input-instructions">
${capitalize(info.description)}${info.description.slice(-1)[0] === "." ? "" : "."}
</p>`
: ""}
`;
}

#render() {
const { validateOnChange, info, path: fullPath } = this;

const path = typeof fullPath === "string" ? fullPath.split("-") : [...fullPath];
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/src/stories/Loader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LitElement, css, html } from "lit";
import { unsafeHTML } from "lit/directives/unsafe-html.js";

// From https://loading.io/css/

Expand Down Expand Up @@ -120,7 +121,7 @@ export class Loader extends LitElement {
return html`
<div>
<div class="lds-default"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>
${this.message ? html`<span>${this.message}</span>` : ''}
${this.message ? html`<span>${unsafeHTML(this.message)}</span>` : ''}
</div>
`
}
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/stories/assets/inspect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions src/renderer/src/stories/assets/neurosift-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/renderer/src/stories/assets/preview.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Search } from "../../../Search.js";
import { Modal } from "../../../Modal";
import { List } from "../../../List";

const defaultEmptyMessage = "No interfaces selected"
const defaultEmptyMessage = "No interfaces selected";

export class GuidedStructurePage extends Page {
constructor(...args) {
Expand All @@ -35,7 +35,7 @@ export class GuidedStructurePage extends Page {

list = new List({
emptyMessage: defaultEmptyMessage,
onChange: () => this.unsavedUpdates = true
onChange: () => (this.unsavedUpdates = true),
});

addButton = new Button();
Expand Down Expand Up @@ -96,7 +96,7 @@ export class GuidedStructurePage extends Page {
)
.catch((e) => console.error(e));

this.list.emptyMessage = defaultEmptyMessage
this.list.emptyMessage = defaultEmptyMessage;

for (const [key, name] of Object.entries(interfaces)) {
let found = this.search.options?.find((o) => o.value === name);
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/stories/pages/guided-mode/options/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export const run = async (url, payload, options = {}) => {
const needsSwal = !options.swal && options.swal !== false;
if (needsSwal) openProgressSwal(options).then((swal) => (options.onOpen ? options.onOpen(swal) : undefined));

const results = await fetch(`${baseUrl}/neuroconv/${url}`, {
if (!("base" in options)) options.base = "/neuroconv";

const results = await fetch(`${baseUrl}${options.base || ""}/${url}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
Expand Down
Loading

0 comments on commit 769c3d2

Please sign in to comment.