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

Update to File widget for uploading multiple files #387

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions frontend/package.json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can't import package from local paths, because it will not work for other folks

Copy link
Author

@rmkolany rmkolany Oct 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to repackage the repo from local to see if my attempt at changes to the react code would work. On my deployed version hosted on a VM I was able to edit one frontend file to allow multiple files to upload in addition to the changes made to file.py. I edited this file: .\mercury\frontend-dist\static\js\2.206848a5.chunk to change allowMultuple:[!1,Ue.BOOLEAN] to allowMultuple:[!0,Ue.BOOLEAN]. Changing this and clearing the browser cache allowed the widget to accept multiple files at once either via drag & drop or selecting multiple via the file browser.

image

Copy link
Author

@rmkolany rmkolany Oct 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried a few different methods including running pip install for my branch and just cloning the repo and running setup.py to build the package. The latter method seems to have worked better but when I go to run mercury I get the 'TemplateDoesNotExist' error in the browser due to the templates and frontend-dist folders not being created. My attempt at changing the React code is within commit 7d78406 by adding the multiple parameter to the file widget. I have never worked in React and will likely need help to get this to work for others.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry! I should explain it earlier ... You need to build the frontend and copy all html, js, css files into Django directory, here are steps:

# run from main directory

# 1. remove previous frontend static files
rm -rf mercury/frontend-dist
# 2. go to fronted directory
cd frontend
# 3. install required packages
yarn install
# 4. build fronted files and copy them (copy is done in the local-build command)
yarn local-build

After above steps you should see changes in frontend when running mercury locally with command (mercury run).

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"font-awesome": "^4.7.0",
"history": "^5.1.0",
"js-file-download": "^0.4.12",
"mercury": "rmkolany/mercury#multiple-files-widget"
"popper": "^1.0.1",
"react": "^17.0.2",
"react-block-ui": "^1.3.5",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/widgets/File.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type FileProps = {
maxFileSize: string | null;
disabled: boolean;
hidden: boolean;
multiple: boolean;
value: string[];
runNb: () => void;
};
Expand All @@ -41,6 +42,7 @@ export default function FileWidget({
maxFileSize,
disabled,
hidden,
multiple,
value,
runNb,
}: FileProps) {
Expand Down
38 changes: 24 additions & 14 deletions mercury/widgets/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,32 @@

class File:
def __init__(
self, label="File upload", max_file_size="100MB", disabled=False, hidden=False
self, label="File upload", max_file_size="100MB", disabled=False, hidden=False, multiple=False
):
self.max_file_size = max_file_size
self.code_uid = WidgetsManager.get_code_uid("File")
self.temp_dir = None
atexit.register(self.cleanup)
self.hidden = hidden
self.multiple = multiple

if WidgetsManager.widget_exists(self.code_uid):
self.file = WidgetsManager.get_widget(self.code_uid)
self.file.description = label
self.file.disabled = disabled
else:
self.file = ipywidgets.FileUpload(description=label, disabled=disabled)
self.file = ipywidgets.FileUpload(description=label, disabled=disabled, multiple=multiple)
self.file.filepath = None
self.file.filename = None
WidgetsManager.add_widget(self.file.model_id, self.code_uid, self.file)
display(self)

@property
def value(self):
if len(self.file.value):
if self.multiple:
return [file.content for file in self.file.value]
elif len(self.file.value):
return self.file.value[0].content

if self.file.filepath is not None:
# read that file
with open(self.file.filepath, "rb") as fin:
Expand All @@ -45,7 +47,9 @@ def value(self):

@property
def filename(self):
if len(self.file.value):
if self.multiple:
return [file.name for file in self.file.value]
elif len(self.file.value):
return self.file.value[0].name
if self.file.filename is not None:
return self.file.filename
Expand All @@ -55,21 +59,27 @@ def filename(self):
def filepath(self):
if self.file.filepath is not None:
return self.file.filepath

if (
len(self.file.value)
and self.filename is not None
and self.value is not None
):
# store file in temp dir
# and return the path
self.temp_dir = tempfile.mkdtemp()
self.file.filepath = os.path.join(self.temp_dir, self.filename)

with open(self.file.filepath, "wb") as fout:
fout.write(self.value)

return self.file.filepath
if self.multiple:
# create filepath list, write all files, return path
self.file.filepath = []
for fn,val in zip(self.filename,self.value):
path = os.path.join(self.temp_dir,fn)
self.file.filepath.append(path)
with open(path, "wb") as fout:
fout.write(val)
return self.file.filepath
else:
# create filepath, write file, return path
self.file.filepath = os.path.join(self.temp_dir, self.filename)
with open(self.file.filepath, "wb") as fout:
fout.write(self.value)
return self.file.filepath

return None

Expand Down