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

Show Results Page after DANDI Upload from Uploads Page #348

Merged
merged 3 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions src/renderer/assets/css/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
/* src: local('Source Code Pro'), local('SourceCodePro'), url(fonts/SourceCodePro-Regular.ttf) format('truetype'); */
}

/* Notfy */
.notyf__toast {
max-width: clamp(300px, 40vw, 500px) !important;
}

.notyf__message {
word-wrap: break-word;
}

/* Global ---------------------------- */

* {
Expand Down
107 changes: 107 additions & 0 deletions src/renderer/src/stories/DandiResults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { LitElement, css, html } from "lit";

import { get } from "dandi";
import { isStaging } from "./pages/uploads/UploadsPage.js";

export class DandiResults extends LitElement {
static get styles() {
return css`
:host {
display: block;
}
`;
}

constructor(props) {
super();
Object.assign(this, props);
}

async updated() {
const handleId = (str, info) => {
let value = info[str];
if (str === "modified") value = new Date(value).toString();

const el = this.shadowRoot.querySelector(`#${str}`);
el.innerText = value;

if (el.tagName === "A") {
if (str === "doi") value = `http://doi.org/${value}`;
el.href = value;
el.target = "_blank";
}
};

const elIds = ["name", "modified"];

const otherElIds = ["embargo_status"];

const liveId = this.id; // '000552' // From Huszar
const dandiset = await get(liveId, isStaging(this.id) ? "staging" : undefined);

otherElIds.forEach((str) => handleId(str, dandiset));
elIds.forEach((str) => handleId(str, dandiset.draft_version));

const info = await dandiset.getInfo({ version: dandiset.draft_version.version });

const secondElIds = ["description", "url"];
secondElIds.forEach((str) => handleId(str, info));

const publicationEl = this.shadowRoot.querySelector(`#publication`);
publicationEl.innerHTML = "";
const publications = (info.relatedResource ?? []).filter((o) => o.relation === "dcite:IsDescribedBy");

if (publications.length)
publicationEl.append(
...(await Promise.all(
publications.map(async (o) => {
const li = document.createElement("li");
const { message } = await fetch(
`http://api.crossref.org/works${new URL(o.identifier).pathname}`
).then((res) => res.json());
li.innerHTML = `${message.author.map((o) => `${o.family}, ${o.given[0]}.`).join(", ")} (${
message.created["date-parts"][0][0]
}). ${message.title[0]}. <i>${message["container-title"]}</i>, <i>${message.volume}</i>(${
message.issue
}), ${message.page}. doi:${message.DOI}`;
return li;
})
))
);
else publicationEl.innerText = "N/A";
}

render() {
return html`
<div style="text-align: center;">
<div style="display: inline-block; width: 100%; text-align: left;">
<h2 style="margin: 0; margin-bottom: 10px;"><span id="name"></span></h2>
<p><span id="description"></span></p>

<p><b>Identifier:</b> ${this.id}</p>
<p><b>Upload Time:</b> <span id="modified"></span></p>
<p><b>Embargo Status:</b> <span id="embargo_status"></span></p>

<small><b>URL:</b> <a id="url"></a></small><br />

<h3 style="padding: 0;">Related Publications</h3>
<hr />
<ol id="publication"></ol>

${this.files
? html` <h3 style="padding: 0;">Files Uploaded with this Conversion</h3>
<hr />
<ol>
${Object.values(this.files)
.map((v) => Object.values(v))
.flat()
.map((o) => html`<li>${o.file}</li>`)}
</ol>`
: ""}
</div>
</div>
`;
}
}

customElements.get("dandi-results") || customElements.define("dandi-results", DandiResults);
Original file line number Diff line number Diff line change
@@ -1,105 +1,22 @@
import { css, html } from "lit";
import { html } from "lit";
import { Page } from "../../Page.js";

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

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

async updated() {
const handleId = (str, info) => {
let value = info[str];
if (str === "modified") value = new Date(value).toString();

const el = this.query(`#${str}`);
el.innerText = value;

if (el.tagName === "A") {
if (str === "doi") value = `http://doi.org/${value}`;
el.href = value;
el.target = "_blank";
}
};

const { dandiset_id, staging } = this.info.globalState.upload?.info ?? {};

const elIds = ["name", "modified"];

const otherElIds = ["embargo_status"];

const liveId = dandiset_id; // '000552' // From Huszar
const isStaging = staging; //false
const dandiset = await get(liveId, isStaging ? "staging" : undefined);

otherElIds.forEach((str) => handleId(str, dandiset));
elIds.forEach((str) => handleId(str, dandiset.draft_version));

const info = await dandiset.getInfo({ version: dandiset.draft_version.version });

const secondElIds = ["description", "url"];
secondElIds.forEach((str) => handleId(str, info));

const publicationEl = document.getElementById("publication");
publicationEl.innerHTML = "";
const publications = (info.relatedResource ?? []).filter((o) => o.relation === "dcite:IsDescribedBy");

if (publications.length)
publicationEl.append(
...(await Promise.all(
publications.map(async (o) => {
const li = document.createElement("li");
const { message } = await fetch(
`http://api.crossref.org/works${new URL(o.identifier).pathname}`
).then((res) => res.json());
li.innerHTML = `${message.author.map((o) => `${o.family}, ${o.given[0]}.`).join(", ")} (${
message.created["date-parts"][0][0]
}). ${message.title[0]}. <i>${message["container-title"]}</i>, <i>${message.volume}</i>(${
message.issue
}), ${message.page}. doi:${message.DOI}`;
return li;
})
))
);
else publicationEl.innerText = "N/A";
}

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

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

const { dandiset_id } = this.info.globalState.upload?.info ?? {};

return html`
<div style="text-align: center;">
<div style="display: inline-block; width: 100%; text-align: left;">
<h2 style="margin: 0; margin-bottom: 10px;"><span id="name"></span></h2>
<p><span id="description"></span></p>

<p><b>Identifier:</b> ${dandiset_id}</p>
<p><b>Upload Time:</b> <span id="modified"></span></p>
<p><b>Embargo Status:</b> <span id="embargo_status"></span></p>

<small><b>URL:</b> <a id="url"></a></small><br />

<h3 style="padding: 0;">Related Publications</h3>
<hr />
<ol id="publication"></ol>

<h3 style="padding: 0;">Files Uploaded with this Conversion</h3>
<hr />
<ol>
${Object.values(results)
.map((v) => Object.values(v))
.flat()
.map((o) => html`<li>${o.file}</li>`)}
</ol>
</div>
</div>
`;
return DandiResults({ id: dandiset_id, files: conversion });
}
}

Expand Down
34 changes: 27 additions & 7 deletions src/renderer/src/stories/pages/uploads/UploadsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ import { merge } from "../utils.js";
import { run } from "../guided-mode/options/utils.js";
import { notyf } from "../../../dependencies/globals.js";
import Swal from "sweetalert2";
import { Modal } from "../../Modal";
import { DandiResults } from "../../DandiResults.js";

export const isStaging = (id) => parseInt(id) >= 100000;

export async function uploadToDandi(info) {
if (!global.data.DANDI?.api_key) {
const api_key = global.data.DANDI?.api_key;
if (!api_key) {
await Swal.fire({
title: "Your DANDI API key is not configured.",
html: "Edit your settings to include this value.",
Expand All @@ -26,10 +31,15 @@ export async function uploadToDandi(info) {
return this.to("settings");
}

info.staging = parseInt(info.dandiset_id) >= 100000; // Automatically detect staging IDs
info.api_key = global.data.DANDI.api_key;

return await run("upload/folder", info, { title: "Uploading to DANDI" }).catch((e) => {
return await run(
"upload/folder",
{
...info,
staging: isStaging(info.dandiset_id), // Automatically detect staging IDs
api_key,
},
{ title: "Uploading to DANDI" }
).catch((e) => {
this.notify(e.message, "error");
throw e;
});
Expand All @@ -48,15 +58,25 @@ export class UploadsPage extends Page {
label: defaultButtonMessage,
onClick: async () => {
await this.form.validate(); // Will throw an error in the callback
const results = await uploadToDandi.call(this, { ...global.data.uploads });
const results = await uploadToDandi.call(this, { ...globalState });

if (results)
notyf.open({
type: "success",
message: `${global.data.uploads.nwb_folder_path} successfully uploaded to Dandiset ${global.data.uploads.dandiset_id}`,
message: `${globalState.nwb_folder_path} successfully uploaded to Dandiset ${globalState.dandiset_id}`,
});

global.data.uploads = {};
global.save();

const modal = new Modal({ open: true });
modal.header = "DANDI Upload Summary";
const summary = new DandiResults({ id: globalState.dandiset_id });
summary.style.padding = "25px";
modal.append(summary);

document.body.append(modal);

this.requestUpdate();
},
});
Expand Down
Loading