download an excel file from Bokeh server via "button" - python

I have a Bokeh directory format like below.
BTSapp.py is my equivalent of 'main.py'
In data folder, I have 1 input (excel) file and 1 output (excel) file. I wrote a script to transform the data from the input file and write it to the output file. I would like to create a bokeh button, which when the end users click they can download the output file.
Can someone please help me? I found this question also on stackoverflow: send file from server to client on bokeh but I couldn't make it work in my case. I kept getting syntax error for JScode_xhr.
Thank you in advance.

I tried myself and below is the correct code. It will also fix the issue of double alert and of the generation of 2 excel files after the js code is activated.
Note: this is an adjusted version from this post
JScode_fetch = """
var filename = 'my_BSS_result.xlsx';
fetch('/app/static/output.xlsx', {cache: "no-store"}).then(response => response.blob())
.then(blob => {
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, filename);
}
else {
var link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = filename;
link.target = "_blank";
link.style.visibility = 'hidden';
link.dispatchEvent(new MouseEvent('click'))
URL.revokeObjectURL(url);
}
return response.text();
});
"""

Related

how can i show PDF file as Bytes using QWebEngineView in PyQt5? [duplicate]

So, using PyQt5's QWebEngineView and the .setHTML and .setContent methods have a 2 MB size limitation. When googling for solutions around this, I found two methods:
Use SimpleHTTPServer to serve the file. This however gets nuked by a firewall employed in the company.
Use File Urls and point to local files. This however is a rather bad solution, as the HTML contains confidential data and I can't leave it on the harddrive, under any circumstance.
The best solution I currently see is to use file urls, and get rid of the file on program exit/when loadCompleted reports it is done, whichever comes first.
This is however not a great solution and I wanted to ask if there is a solution I'm overlooking that would be better?
Why don't you load/link most of the content through a custom url scheme handler?
webEngineView->page()->profile()->installUrlSchemeHandler("app", new UrlSchemeHandler(e));
class UrlSchemeHandler : public QWebEngineUrlSchemeHandler
{ Q_OBJECT
public:
void requestStarted(QWebEngineUrlRequestJob *request) {
QUrl url = request->requestUrl();
QString filePath = url.path().mid(1);
// get the data for this url
QByteArray data = ..
//
if (!data.isEmpty())
{
QMimeDatabase db;
QString contentType = db.mimeTypeForFileNameAndData(filePath,data).name();
QBuffer *buffer = new QBuffer();
buffer->open(QIODevice::WriteOnly);
buffer->write(data);
buffer->close();
connect(request, SIGNAL(destroyed()), buffer, SLOT(deleteLater()));
request->reply(contentType.toUtf8(), buffer);
} else {
request->fail(QWebEngineUrlRequestJob::UrlNotFound);
}
}
};
you can then load a website by webEngineView->load(new QUrl("app://start.html"));
All relative pathes from inside will also be forwarded to your UrlSchemeHandler..
And rember to add the respective includes
#include <QWebEngineUrlRequestJob>
#include <QWebEngineUrlSchemeHandler>
#include <QBuffer>
One way you can go around this is to use requests and QWebEnginePage's method runJavaScript:
web_engine = QWebEngineView()
web_page = web_engine.page()
web_page.setHtml('')
url = 'https://youtube.com'
page_content = requests.get(url).text
# document.write writes a string of text to a document stream
# https://developer.mozilla.org/en-US/docs/Web/API/Document/write
# And backtick symbol(``) is for multiline strings
web_page.runJavaScript('document.write(`{}`);'.format(page_content))

Live streaming using videojs-record and videojs

I'm trying to make a video chat inside my application, and using videojs-record + videojs to do so. videojs-record to record the webcam (which is already working), and videojs to reproduce the video in the other side. By using the timestamp event, with the timeSlice property, I've managed to split the recorded video by each second.
this.player = videojs('#myVideo', this.options, () => {});
var player = this.player
var that = this
this.player.on('startRecord', () => {
that.segment = 0
});
player.on('timestamp', function() {
if (player.recordedData && player.recordedData.length > 0) {
var binaryData = player.recordedData[player.recordedData.length - 1]
that.$emit('dataVideoEvent', {video:binaryData, segment_index:that.segment})
that.segment += 1
}
});
So I've managed to upload the said segments to Amazon's S3, and have used a Python endpoint to return a HLS file containing the uploaded files:
duration = 1
totalDuration = len(list)
string = ('#EXTM3U\n'
'#EXT-X-PLAYLIST-TYPE:EVENT\n'
'#EXT-X-TARGETDURATION:3600\n'
'#EXT-X-VERSION:3\n'
'#EXT-X-MEDIA-SEQUENCE:0\n')
ended = False
now = datetime.utcnow().replace(tzinfo=pytz.utc)
for index, obj in enumerate(list):
url = {retrieving url based in obj}
string += '#EXTINF:{duration}, no desc\n{url}\n'.format(duration=duration, url=url)
ended = (obj.created_at.replace(tzinfo=pytz.utc) + timedelta(seconds=10)) < now
if ended:
string += '#EXT-X-ENDLIST'
Although, that HLS file isn't working. The videojs player shows the proper full time of the video, but never starts to play it, and logs no errors. If I try to reproduce the video using Bitmovin's player demo, it says SOURCE_MANIFEST_INVALID.
I've also tried to create a XML+DASH file instead of a HLS one, but it didn't work as well. And I've also tried to change the videojs-record videoMimeType property to other values, like video/webm;codecs=vp8,opus or video/mp2t;codecs=vp8,opus, but it also didn't change a thing.
Also, the #action that returns the HLS file has a renderer_classes property using that renderer:
class HLSRenderer(BaseRenderer):
media_type = 'application/x-mpegurl'
format = 'm3u8'
level_sep = '.'
header = None
labels = None
writer_opts = None
def render(self, data, media_type=None, renderer_context={}, writer_opts=None):
return data
And at last, I've configured CORS in that way in S3, in case that's relevant:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Range</AllowedHeader>
</CORSRule>
</CORSConfiguration>
And by the way, the #action is usually blocked so only the expected user can see it, but at this moment I've changed its permission_classes value to AllowAny, so I can test in websites like Bitmovin.
What exactly am I doing wrong?
I gave up and ended up using Jitsi instead of videojs-record. I had to run it with an iframe within Vue, but it succeed where all the rest failed.
I've spent the last 18 days trying to make a live stream by myself, when I could make Jitsi work with my project in almost one single night. I recommend their Docker, it's pretty easy to set up.
https://github.com/jitsi/docker-jitsi-meet

Get the current contents of the entire Jupyter Notebook

I have a Jupyter Notebook running. I want to be able to access the source of the current Jupyter Notebook from within Python. My end goal is to pass it into ast.parse so I can do some analysis on the user's code. Ideally, I'd be able to do something like this:
import ast
ast.parse(get_notebooks_code())
Obviously, if the source code was an IPYNB file, there'd be an intermediary step of extracting the code from the Python cells, but that's a relatively easy problem to solve.
So far, I've found code that will use the list_running_servers function of the IPython object in order to make a request and match up kernel IDs - this gives me the filename of the currently running notebook. This would work, except for the fact that the source code on disk may not match up with what the user has in the browser (until you save a new checkpoint).
I've seen some ideas involving extracting out data using JavaScript, but that requires either a separate cell with magic or calling the display.Javascript function - which fires asynchronously, and therefore doesn't allow me to pass the result to ast.parse.
Anyone have any clever ideas for how to dynamically get the current notebooks source code available as a string in Python for immediate processing? I'm perfectly fine if I need to make this be an extension or even a kernel wrapper, I just need to get the source code somehow.
Well, this isn't exactly what I wanted, but here's my current strategy. I need to run some Python code based on the user's code, but it doesn't actually have to be connected to the user's code directly. So I'm just going to run the following magic afterwards:
%%javascript
// Get source code from cells
var source_code = Jupyter.notebook.get_cells().map(function(cell) {
if (cell.cell_type == "code") {
var source = cell.code_mirror.getValue();
if (!source.startsWith("%%javascript")) {
return source;
}
}
}).join("\n");
// Embed the code as a Python string literal.
source_code = JSON.stringify(source_code);
var instructor_code = "student_code="+source_code;
instructor_code += "\nimport ast\nprint(ast.dump(ast.parse(student_code)))\nprint('Great')"
// Run the Python code along with additional code I wanted.
var kernel = IPython.notebook.kernel;
var t = kernel.execute(instructor_code, { 'iopub' : {'output' : function(x) {
if (x.msg_type == "error") {
console.error(x.content);
element.text(x.content.ename+": "+x.content.evalue+"\n"+x.content.traceback.join("\n"))
} else {
element.html(x.content.text.replace(/\n/g, "<br>"));
console.log(x);
}
}}});
What about combining https://stackoverflow.com/a/44589075/1825043 and https://stackoverflow.com/a/54350786/1825043 ? That gives something like
%%javascript
IPython.notebook.kernel.execute('nb_name = "' + IPython.notebook.notebook_name + '"')
and
import os
from nbformat import read, NO_CONVERT
nb_full_path = os.path.join(os.getcwd(), nb_name)
with open(nb_full_path) as fp:
notebook = read(fp, NO_CONVERT)
cells = notebook['cells']
code_cells = [c for c in cells if c['cell_type'] == 'code']
for no_cell, cell in enumerate(code_cells):
print(f"####### Cell {no_cell} #########")
print(cell['source'])
print("")
I get
####### Cell 0 #########
%%javascript
IPython.notebook.kernel.execute('nb_name = "' + IPython.notebook.notebook_name + '"')
####### Cell 1 #########
import os
from nbformat import read, NO_CONVERT
nb_full_path = os.path.join(os.getcwd(), nb_name)
with open(nb_full_path) as fp:
notebook = read(fp, NO_CONVERT)
cells = notebook['cells']
code_cells = [c for c in cells if c['cell_type'] == 'code']
for no_cell, cell in enumerate(code_cells):
print(f"####### Cell {no_cell} #########")
print(cell['source'])
print("")

Using PyperClip on web app

I am using pyperclip.py to grab a list of E-Mail Addresses in my web app using a form so a user can paste it locally via clipboard. It works perfect locally. However, while running it on a server (Linux 14.04 with Apache2) and accessed from a client system through the browser it doesn't copy. How can I get it to copy to the clipboard of the client's system?
Right now I'm just trying to get it to work and as such I'm only using a single line. I'm using pyperclip 1.5.15 with xclip and Python 3.4. The server is running Linux 14.04 and the client has noticed issues on Windows 8 and Windows 10 using Google Chrome and IE. No other os has currently been tested.
pyperclip.copy("HELLO")
Since I couldn't find many details on this subject I thought I'd answer my question. Unfortunately, it doesn't appear that browsers will support pyperclip so an HTML + Javascript work around is required (meaning on pyperclip). First, add your Django Template var as an HTML attribute from there you can use Javascript to handle the copy functionality. Below is an example of how to do this, sorry in advance because stackoverflow was giving some weird formatting to the example. It also assumes you have a form below with the id of email_list_clipboard. I hope this helps anyone else who may of run into a similar issue!
Example:
<html email-list="{{request.session.email_list}}">
<script>
$(document).ready(function () {
function copyTextToClipboard(text) {
var textArea = document.createElement("textarea");
// Place in top-left corner of screen regardless of scroll position.
textArea.style.position = 'fixed';
textArea.style.top = 0;
textArea.style.left = 0;
textArea.style.width = '2em';
textArea.style.height = '2em';
// We don't need padding, reducing the size if it does flash render.
textArea.style.padding = 0;
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
textArea.style.background = 'transparent';
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Copying text command was ' + msg);
} catch (err) {
console.log('Oops, unable to copy');
}
document.body.removeChild(textArea);
}
// set things up so my function will be called when field_three changes
$('#email_list_clipboard').click(function (click) {
event.preventDefault();
copyTextToClipboard(document.documentElement.getAttribute("email-list"));
});
</script>

Django/Python - check if the user uploads a file - if not, show an image

I have an input tag where the user has to upload a file. Right now if the user doesn't upload anything I do this in the main view:
if len(request.FILES) != 0:
data = request.FILES['some_file']
...do some work...
else:
return render(request, 'App/nofile.html' )
If there is no file I take the user the another page where it says that no file was uploaded and make him/her go back to the main page.
Is there a way that I can check if the user did not uploaded a file and not go to another page just show a PICTURE in the middle of the screen in that main page with the message?
NOT JUST A STRING MESSAGE, a picture with the message.
Maybe javascript is the answer but I know nothing about javascript.
Thanks in advance for any help.
There are two ways:
1) If you do validation on server side just like you do right now. It seems that you do it right, but you should use redirect instead of render if you handle POST data:
if len(request.FILES) != 0:
data = request.FILES['some_file']
...do some work...
else:
return redirect('/nofile/' {'foo': bar})
2) If you want client-side validation. Then you must prevent default submit action by javascript and check if file length equals 0. You can check this question or use this jQuery snippet:
$('form').submit(function(event){
validated = true;
if ($('#some_file_input').get(0).files.length === 0) {
validated = false;
console.log("No files selected.");
// Or some div with image showing
}
if (validated != true) {
event.preventDefault();
}
});

Categories

Resources