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

Standalone Preview (responsive to file association) #337

Merged
merged 19 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
867279b
Create draft of standalone preview page
garrettmflynn Sep 5, 2023
b016322
Support previewing NWB files when selected in native file explorer
garrettmflynn Sep 6, 2023
b9129fa
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 6, 2023
3723610
Merge branch 'main' into standalone-preview
garrettmflynn Sep 6, 2023
1f08530
Merge branch 'main' into standalone-preview
CodyCBakerPhD Sep 6, 2023
5293fdd
Merge branch 'main' into standalone-preview
CodyCBakerPhD Sep 11, 2023
ab452f0
Merge branch 'main' into standalone-preview
CodyCBakerPhD Sep 13, 2023
ee41639
Update logo, name, and backend path usage for Windows
garrettmflynn Sep 13, 2023
eb795ac
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 13, 2023
efd6657
Catch windows file-open bug
garrettmflynn Sep 13, 2023
5368f44
Merge branch 'standalone-preview' of https://github.com/NeurodataWith…
garrettmflynn Sep 13, 2023
33b5cd6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 13, 2023
ad0554b
Fix backend cause of windows issue
garrettmflynn Sep 13, 2023
6db44de
Merge branch 'standalone-preview' of https://github.com/NeurodataWith…
garrettmflynn Sep 13, 2023
b699332
Add description to neurosift loader
garrettmflynn Sep 13, 2023
b79980a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 13, 2023
a7e3e08
Fix reference before declaration
garrettmflynn Sep 13, 2023
5c28ef1
Merge branch 'main' into standalone-preview
CodyCBakerPhD Sep 13, 2023
400ced5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 "../globals.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
14 changes: 14 additions & 0 deletions src/renderer/src/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,17 @@ export let runOnLoad = (fn) => {
export const baseUrl = `http://127.0.0.1:${port}`;

export const supportedInterfaces = guideGlobalMetadata.supported_interfaces;

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);
}
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
4 changes: 2 additions & 2 deletions src/renderer/src/progress/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
conversionSaveFolderPath,
} from "../dependencies/simple.js";
import { fs } from "../electron/index.js";
import { joinPath, runOnLoad } from "../globals.js";
import { joinPath, runOnLoad, updateURLParams } from "../globals.js";
import { merge } from "../stories/pages/utils.js";
import { updateAppProgress, updateFile, updateURLParams } from "./update.js";
import { updateAppProgress, updateFile } from "./update.js";

export * from "./update";

Expand Down
17 changes: 1 addition & 16 deletions src/renderer/src/progress/update.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { guidedProgressFilePath } from "../dependencies/simple.js";
import { fs } from "../electron/index.js";
import { joinPath } from "../globals.js";
import { joinPath, updateURLParams } from "../globals.js";
import { get } from "./index.js";

export const update = (newDatasetName, previousDatasetName) => {
Expand All @@ -23,21 +23,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.
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