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

Upload Multiple Files and Directories #372

Merged
merged 13 commits into from
Oct 2, 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
32 changes: 27 additions & 5 deletions pyflask/apis/neuroconv.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
inspect_nwb_file,
inspect_nwb_folder,
inspect_multiple_filesystem_objects,
upload_to_dandi,
upload_folder_to_dandi,
upload_multiple_filesystem_objects_to_dandi,
)

from errorHandlers import notBadRequestException
Expand Down Expand Up @@ -112,13 +115,11 @@ def post(self):
neuroconv_api.abort(500, str(e))


@neuroconv_api.route("/upload")
@neuroconv_api.route("/upload/project")
class Upload(Resource):
@neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"})
def post(self):
try:
from manageNeuroconv import upload_to_dandi

return upload_to_dandi(**neuroconv_api.payload)

except Exception as e:
Expand All @@ -131,15 +132,36 @@ class Upload(Resource):
@neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"})
def post(self):
try:
from manageNeuroconv import upload_folder_to_dandi

return upload_folder_to_dandi(**neuroconv_api.payload)

except Exception as e:
if notBadRequestException(e):
neuroconv_api.abort(500, str(e))


@neuroconv_api.route("/upload")
class Upload(Resource):
@neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"})
def post(self):
from os.path import isdir

try:
paths = neuroconv_api.payload["filesystem_paths"]

if len(paths) == 1 and isdir(paths[0]):
kwargs = {**neuroconv_api.payload}
del kwargs["filesystem_paths"]
kwargs["nwb_folder_path"] = paths[0]
return upload_folder_to_dandi(**kwargs)

else:
return upload_multiple_filesystem_objects_to_dandi(**neuroconv_api.payload)

except Exception as e:
if notBadRequestException(e):
neuroconv_api.abort(500, str(e))


@neuroconv_api.route("/inspect_file")
class InspectNWBFile(Resource):
@neuroconv_api.doc(responses={200: "Success", 400: "Bad Request", 500: "Internal server error"})
Expand Down
1 change: 1 addition & 0 deletions pyflask/manageNeuroconv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
validate_metadata,
upload_to_dandi,
upload_folder_to_dandi,
upload_multiple_filesystem_objects_to_dandi,
listen_to_neuroconv_events,
generate_dataset,
inspect_nwb_file,
Expand Down
10 changes: 10 additions & 0 deletions pyflask/manageNeuroconv/manage_neuroconv.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,16 @@ def update_conversion_progress(**kwargs):
return dict(file=str(resolved_output_path))


def upload_multiple_filesystem_objects_to_dandi(**kwargs):
tmp_folder_path = aggregate_symlinks_in_new_directory(kwargs["filesystem_paths"], "upload")
innerKwargs = {**kwargs}
del innerKwargs["filesystem_paths"]
innerKwargs["nwb_folder_path"] = tmp_folder_path
result = upload_folder_to_dandi(**innerKwargs)
rmtree(tmp_folder_path)
return result


def upload_folder_to_dandi(
dandiset_id: str,
api_key: str,
Expand Down
11 changes: 7 additions & 4 deletions schemas/json/dandi/standalone.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"properties": {
"nwb_folder_path": {
"type": "string",
"format": "directory"
"filesystem_paths": {
"type": "array",
"items":{
"type": "string",
"format": ["file", "directory"]
}
}
},
"required": ["nwb_folder_path"]
"required": ["filesystem_paths"]
}
4 changes: 4 additions & 0 deletions src/renderer/src/stories/FileSystemSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ const componentCSS = css`
gap: 5px;
}

#button-div > nwb-button {
margin-bottom: 10px;
}

button {
background: WhiteSmoke;
border: 1px solid #c3c3c3;
Expand Down
15 changes: 10 additions & 5 deletions src/renderer/src/stories/pages/uploads/UploadsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { html } from "lit";
import { JSONSchemaForm } from "../../JSONSchemaForm.js";
import { Page } from "../Page.js";
import { onThrow } from "../../../errors";

const folderPathKey = "filesystem_paths";
import dandiUploadSchema from "../../../../../../schemas/json/dandi/upload.json";
import dandiStandaloneSchema from "../../../../../../schemas/json/dandi/standalone.json";
const dandiSchema = merge(dandiStandaloneSchema, merge(dandiUploadSchema, {}), { arrays: true });
Expand All @@ -18,8 +20,10 @@ import { DandiResults } from "../../DandiResults.js";

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

export async function uploadToDandi(info, type = "project" in info ? "" : "folder") {
const staging = isStaging(info.dandiset_id); // Automatically detect staging IDs
export async function uploadToDandi(info, type = "project" in info ? "project" : "") {
const { dandiset_id } = info;

const staging = isStaging(dandiset_id); // Automatically detect staging IDs

const whichAPIKey = staging ? "staging_api_key" : "main_api_key";
const api_key = global.data.DANDI?.api_keys?.[whichAPIKey];
Expand Down Expand Up @@ -54,7 +58,9 @@ export async function uploadToDandi(info, type = "project" in info ? "" : "folde
if (result)
notyf.open({
type: "success",
message: `${info.project ?? info.nwb_folder_path} successfully uploaded to Dandiset ${info.dandiset_id}`,
message: `${
info.project ?? `${info[folderPathKey].length} filesystem entries`
} successfully uploaded to Dandiset ${dandiset_id}`,
});

return result;
Expand Down Expand Up @@ -89,13 +95,12 @@ export class UploadsPage extends Page {
},
});

const folderPathKey = "nwb_folder_path";
// NOTE: API Keys and Dandiset IDs persist across selected project
this.form = new JSONSchemaForm({
results: globalState,
schema: dandiSchema,
sort: ([k1]) => {
if (k1 === "nwb_folder_path") return -1;
if (k1 === folderPathKey) return -1;
},
onUpdate: ([id]) => {
if (id === folderPathKey) {
Expand Down
Loading