-
Notifications
You must be signed in to change notification settings - Fork 18
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
Add basic functionality (WIP) #1
Conversation
@yuvipanda basic functionality seems to be done. I haven't added any tests yet. Wanted to wait on your opinion of how it would be best to write those for this extension. IMO, it looks like unit tests might not be too helpful. My initial thoughts were to run selenium tests with travis, and then have it check the correctness of the zipped contents. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty awesome :)
I think the way to test this would be not to use selenium, but:
- Set up a directory layout
- Start notebook process with the extension installed in the background
- Call the eventstream API
- Make sure that the output is what we expect, including finally built zipfile.
This I think will be a lot easier than using selenium!
nbzip/static/tree.js
Outdated
$(".col-sm-4.no-padding.tree-buttons").attr('class', 'col-sm-8 no-padding tree-buttons'); | ||
|
||
$('#notebook_toolbar .pull-right').prepend( | ||
$('<div>').addClass('btn-group').attr('id', 'nbgdrive-link').prepend( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be nbzip-link maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, yeah your right.
nbzip/static/index.js
Outdated
|
||
|
||
var substatus_messages = [ | ||
"Adding Hidden Agendas", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hahaha this is cool :D
@yuvipanda thank you for the great idea of how to test this! I tried implementing this approach in the past few commits. The test first tries to check the json output of the eventstream and then checks to see if it can unzip the zipped file and sees if all the correct files are there with the right contents, and there aren't any extra files. |
Thanks @SaladRaider. The tests look great! I just realized (thanks to a conversation with @mpacer who pointed it out) there's a better way to do this: On clicking the download button, just generate the zip file in memory and stream it out directly! Does not need intermediate UI, and does not need temporary files to be stored on disk (which will create problems when two zip files are attempted to be downloaded simultaneously). https://stackoverflow.com/a/21361057 has some example code on how to do this, and I think that'll let us eliminate the whole eventstream + intermediate UI business. Sorry I didn't think of this earlier, and this might end up undoing some of your work. What do you think of this approach? |
@yuvipanda yeah, that makes a lot of sense. Should we be worried at all about the files in the notebook exceeding the amount of memory allocated to each user pod? If not, I think this approach will def be better. |
@SaladRaider ah good question. I think adapting https://stackoverflow.com/a/19121257 to work with tornado's self.write (rather than stdout.write) should solve that problem, allowing us to stream the data out as we construct the zip file! |
@yuvipanda ok, nbzip should now stream the zip file to the user. The tests are changed as well. I'm also thinking of adding a smaller progress bar to replace the button when you press it to show progress. Or maybe, have a rotating swirly progress icon or something. |
Cool! Does this need the intermetdiate page at all? Can we completely get rid of it? |
@yuvipanda the intermediate page is completely gone. I was talking about the visual progress stuff on the |
Could you explain the use case a bit more… This sounds like it would follow directly from jupyter/notebook#2413 which we are still working on documenting before merging. |
@mpacer jupyterhub/zero-to-jupyterhub-k8s#152 has
more context. That PR in notebook looks fairly unrelated to me.
…On Mon, Oct 16, 2017 at 12:56 PM, M Pacer ***@***.***> wrote:
Could you explain the use case a bit more… This sounds like it would
follow directly from jupyter/notebook#2413
<jupyter/notebook#2413> which we are still
working on documenting before merging.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAB23uxxuzqwMFLPot_InOgOK_XfWZU5ks5ss7TcgaJpZM4Ppv1k>
.
--
Yuvi Panda T
http://yuvi.in/blog
|
this chunk of code might be of particular use. zip_filename = os.path.splitext(name)[0] + '.zip'
handler.set_attachment_header(zip_filename)
handler.set_header('Content-Type', 'application/zip')
# create zip file
buff = io.BytesIO()
with zipfile.ZipFile(buff, mode='w', compression=zipfile.ZIP_STORED) as zipf:
output_filename = os.path.splitext(name)[0] + resources['output_extension']
zipf.writestr(output_filename, cast_bytes(output, 'utf-8'))
for filename, data in output_files.items():
zipf.writestr(filename, data)
# pass zip file back
buff.seek(0)
handler.finish(buff.getvalue()) |
nbzip/handlers.py
Outdated
# We gonna send out event streams! | ||
self.set_header('content-type', 'application/octet-stream') | ||
self.set_header('cache-control', 'no-cache') | ||
self.set_header('content-disposition', 'attachment; filename=\"{}\"'.format(TEMP_ZIP_NAME)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be the name of the directory or somesuch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, that makes sense.
nbzip/handlers.py
Outdated
|
||
self.emit({'output': 'Zipping files:\n', 'phase': 'zipping'}) | ||
|
||
q = Queue() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have no intermediate page, what is this queue being used for? Am a bit confused :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i left it for logging, but I guess we don't really need it. I'll remove it.
nbzip/static/tree.js
Outdated
) { | ||
|
||
// inspired by: https://stackoverflow.com/questions/1106377/detect-when-browser-receives-file-download | ||
currToken = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var, otherwise this becomes a global variable :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, sounds good. thank you for the catch.
return new Date().getTime(); | ||
} | ||
|
||
getCookie = function ( name ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can just use the web.authenticated decorator in the python code, and not implement all this code manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, i'm using the cookie to check if the zipping is done to make the zip button reappear. But, adding the decorator for authentication is a good idea.
In addition, that PR includes code so that the file can be downloaded in the background without needing to use a GET request by using a DataURI attached to a POST request that is downloaded via a simulated click on a hidden anchor triggered once the zipping has been completed. That way you don't need to implement a download progress bar, you can rely on the native browser's download manager. You need to use basic xhr requests because otherwise jquery will corrupt the resulting zip file by trying to interpret its encoding in a weird way. var xsrf_token = utils._get_cookie("_xsrf");
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.responseType = "blob";
xhr.withCredentials = true;
xhr.setRequestHeader("X-XSRFToken", xsrf_token);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var blob = xhr.response;
var content_disposition = xhr.getResponseHeader(
"Content-Disposition"
);
var filename = content_disposition.match(/filename\*=utf-8''(.+)/)[1];
saveData(blob, filename);
}
};
var data = JSON.stringify(that.json_content);
xhr.send(data);
return true; // close the dialog
// return false to keep it open.
};
var saveData = (function() {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
return (
function(data, fileName) {
var blob = new Blob([data], { type: "octet/stream" }),
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
)
})(); |
@yuvipanda I addressed some of the issues you brought up in this last commit, as well as changed the function of the button to zip the directory the user is currently browsing in. |
Starter code.