I am trying to make a quart application using multiple websockets at the same time:
<script type="text/javascript">
let socket = new WebSocket('ws://localhost:5000/mensagens/{{ dialog_id }}');
socket.onmessage = function(event) {
var messages_dom = document.getElementsByTagName('ul')[0];
var message_dom = document.createElement('li');
var cotent_dom = document.createTextNode(event.data);
message_dom.appendChild(cotent_dom);
messages_dom.appendChild(message_dom);
};
</script>
<script>
let ws = new WebSocket('ws://localhost:5000/printar/12');
function myFunction() {
var x = document.getElementById("myText").value;
ws.send(x);
document.getElementById("demo").innerHTML = x;
};
</script>
And this is the server side:
#Quart
from quart import Quart, render_template, url_for, websocket, redirect, request
#asyncio
import asyncio
app = Quart(__name__)
...
#websocket da conversa individual
#app.websocket('/mensagens/<dialog_id>')
async def mensagens(dialog_id):
print("123");
try:
output = await ".....function that updates from the SQLite database....";
await websocket.send(f"{output}");
await asyncio.sleep(1);
except Exception as e:
print("-------");
print(e);
#websocket de enviar mensagens
#app.websocket('/printar/<dialog_id>')
async def printar(dialog_id):
print("aqui");
try:
while True:
print(dialog_id);
data = await websocket.receive();
print(data + "\n=====");
except Exception as e:
print(e);
if __name__ == "__main__":
try:
app.run();
except KeyboardInterrupt:
print("=====\nAdeus!\n=====");
except Exception as e:
print(e);
However, for some reason, the second websocket only begin to run after the first websocket receives an update from "mensagens". I really don't understand what and why is happening.
Is there an example of a chat server made with Quart that I could look at?
So... I am idiot. I just found this where explains the user of receive and send indepedently (also, I am not that good with asyncio).
Anyway, here is an example:
<!doctype html>
<html>
<head>
<title>Quart + SQLite</title>
</head>
<body>
<h1>Testes!</h1>
<div class="mensagens">
<ul></ul>
</div>
Mensagem: <input type="text" id="myText" value="Mickey">
<p>Escreva suas mensagens acima!</p>
<button onclick="myFunction()">Enviar</button>
<p id="demo"></p>
<script>
let socket = new WebSocket('ws://localhost:5000/ws/12');
function myFunction() {
var x = document.getElementById("myText").value;
socket.send(x);
document.getElementById("demo").innerHTML = x;
};
socket.onmessage = function(event) {
var messages_dom = document.getElementsByTagName('ul')[0];
var message_dom = document.createElement('li');
var cotent_dom = document.createTextNode(event.data);
message_dom.appendChild(cotent_dom);
messages_dom.appendChild(message_dom);
};
</script>
</body>
</html>
And here is the python side:
#https://pgjones.gitlab.io/quart/how_to_guides/websockets.html
#Quart
from quart import Quart, render_template, url_for, websocket, redirect, request
import asyncio
app = Quart(__name__)
async def sending():
while True:
await websocket.send('f{1}');
asyncio.sleep(1);
async def receiving():
while True:
data = await websocket.receive();
print(data + "\n=====");
#app.websocket('/ws/<num>')
async def ws(num):
print(num);
producer = asyncio.create_task(sending());
consumer = asyncio.create_task(receiving());
await asyncio.gather(producer, consumer);
#app.route('/')
async def main():
return await render_template('teste_html.html');
if __name__ == '__main__':
try:
app.run();
except KeyboardInterrupt:
print("=====\nAdeus!\n=====");
except Exception as e:
print(e);
Related
I want to download a file and at the same time redirect to a new page. The user is shown a page with a link for the download. When they click it, I want to start the download then redirect to another page so they can't keep clicking it. However, I can only seem to get one or the other to work.
from flask import Flask, render_template, redirect, url_for, session, request, flash, send_file
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, BooleanField
from pytube import YouTube
import os
import random, string
import re
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mysecretkey'
session_id = ''.join(random.choices(string.ascii_letters + string.digits, k=6))
class url_form(FlaskForm):
link = StringField('Enter link of YouTube video you would like to convert...')
audio = BooleanField('Audio Only')
submit = SubmitField('Convert')
def download_video(link, just_audio):
yt = YouTube(link)
download_path = 'conversions/'
#global myvar
if just_audio == True:
stream = yt.streams.filter(only_audio=True).first()
tag = 'video'
else:
stream = yt.streams.filter(adaptive=True).first()
tag = 'audio only'
download_video.name = yt.title
download_video.cleanString = re.sub('[^a-zA-Z0-9 \n\.]', '', download_video.name)
download_video.thumbnail = yt.thumbnail_url
download_video.path = stream.download(filename = download_video.cleanString, output_path = download_path)
return
#app.route('/', methods=['GET', 'POST'])
def index():
result = False
form = url_form()
if form.validate_on_submit():
session['link'] = form.link.data
if form.audio.data:
just_audio = True
else:
just_audio = False
session['just_audio'] = just_audio
link = session.get('link')
just_audio = session.get('just_audio')
download_video(link, just_audio)
#download_video(link, just_audio)
#return send_file(download_video.path, attachment_filename=download_video.cleanString + '.mp4', as_attachment=True)
return redirect(url_for('conversion_complete'))
return render_template('index.html', form=form, result=result)
#app.route('/conversion_complete', methods=['GET', 'POST'])
def conversion_complete():
return render_template('conversion_complete.html')
#app.route('/download/', methods=['GET'])
def download():
return send_file(download_video.path, attachment_filename=download_video.cleanString + '.mp4', as_attachment=True)
return render_template('result.html')
<html>
<body>
<h1>Conversion Complete</h1>
<a href='download' target='blank' type='button'><button class='btn btn-default'>Download!</button></a>
</body>
</html>
I am currently using command "python3 -m flask run -h localhost -p 8081" to run a flask server on localhost:8081. The user is able to click buttons to trigger events; additionally, data updates are dynamically pushed to plotly graphs triggered from a javascript interval.
Issue occurs on google-Chrome when both asynchronous AJAX requests are running at the same time-->
There are controls on the page (such as a start button) that handle other actions. When running mozilla firefox, both of these requests will execute with no problems. However, when using google Chrome, the button click request will start to hang after a few seconds, taking longer to reach the "req.done" function until eventually crashing the page. In the code below, I have written some console logs that show in the browser console that the button click event stops replying with "randomstring" from the python route a few seconds after running. This seems to be an issue specific to chrome, as this works consistently on firefox.
Please advise - how could I change this to work across both browsers reliably?
CODE
here are the javascript-jquery AJAX requests:
var numerical_count_rate = document.getElementById("totalCounts");
var numerical_error_rate = document.getElementById("errorRate");
var start_btn = document.getElementById("startButtonClick");
var start_btn_clicked = function (){
console.log("button clicked...")
req = $.ajax({
url : "/_start_button_clicked",
type : "POST",
});
console.log("button got this far...")
req.done(function(data){
console.log("button got even further !!")
var data_test = JSON.parse(data)
var update = data_test.random_string
console.log(update)
});
};
var updateInterval = setInterval(update_values, 1000);
var counts = 0;
console.log("update_interval fired..")
function update_values(){
req = $.ajax({
url : "/_extend_plot",
type : "POST",
});
req.done(function(data){
var updates = JSON.parse(data);
var count_rate_update = {x: [[updates.x_count_rate]],y: [[updates.y_count_rate]]};
var error_rate_update = {x: [[updates.x_error_rate]],y: [[updates.y_error_rate]]};
Plotly.extendTraces('plotly_countrate',count_rate_update, [0], 50);
Plotly.extendTraces('plotly_errorrate',error_rate_update, [0], 50);
numerical_count_rate.innerHTML = "Total Count Rate: " + updates.y_count_rate.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
numerical_error_rate.innerHTML = "Error Rate: " + parseFloat(updates.y_error_rate).toFixed(3).toString() + "%";
});
window.onresize = function() {
Plotly.Plots.resize( 'plotly_countrate' );
Plotly.Plots.resize( 'plotly_errorrate' )
};
counts++;
console.log(counts)
}
Here are snippets from the main thread --> the Flask routes that the ajax requests reference in the main app.py file:
from flask import Flask, render_template, request
import queue
import threading
import plotly
import json
import pandas as pd
import numpy as np
import random
import datetime
app = Flask(__name__)
#app.route("/", methods=['GET'])
def index():
initial_graphs_json = create_plots()
return render_template("index.html", count_rate_plot=initial_graphs_json[0], error_rate_plot=initial_graphs_json[1])
#app.route("/_extend_plot", methods=['GET', 'POST'])
def push_update():
timestamp = get_datetime()
updated_data = queue_q.get()
with queue_q.mutex:
queue_q.queue.clear()
client_post_updates = dict(
x_count_rate=timestamp,
x_error_rate=timestamp,
y_count_rate=updated_data["val0"] + updated_data["val1"],
y_error_rate=updated_data["val2"])
updatesJSON = json.dumps(client_post_updates, cls=plotly.utils.PlotlyJSONEncoder)
return updatesJSON
#app.route("/_start_button_clicked", methods=['GET', 'POST'])
def startstop_clicked():
update_dict = dict(
random_string="randomstring"
)
print("Python click method triggered...")
update = json.dumps(update_dict)
return update
if __name__ == "__main__":
app.run(host="0.0.0.0", port="8081", debug=True)
running- python version 3.7, jquery version 3.5.1
Update: SOLVED
Issue solved by converting functions to asynchronous calls and changing short to long polling. It seems chrome has built in functionality to leave requests in "pending state", and the short polling interval was filling up browser queue with new requests, which caused stalling. Still not completely clear why this does not happen on firefox, but long polling based on a single timer under control of mutex and common memory between threads on the server side is much better overall practice. This way, the frequency of pushed updates is controlled only by data being available, rather then being polled on a timer.
New JS code:
var start_btn_clicked = async function (){
req = $.ajax({
url : "/_start_button_clicked",
type : "POST",
});
req.done(async function(data){
var data_test = JSON.parse(data);
var update = data_test.random_string;
});
};
update_values();
async function update_values(){
req = $.ajax({
url : "/_extend_plot",
type : "POST",
async: true,
cache: false,
timeout: 30000,
success: async function(data){
console.log(req);
var updates = JSON.parse(data);
var count_rate_update = {x: [[updates.x_count_rate]],y: [[updates.y_count_rate]]};
var error_rate_update = {x: [[updates.x_error_rate]],y: [[updates.y_error_rate]]};
Plotly.extendTraces('plotly_countrate',count_rate_update, [0], 50);
Plotly.extendTraces('plotly_errorrate',error_rate_update, [0], 50);
numerical_count_rate.innerHTML = "Total Count Rate: " + updates.y_count_rate.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
numerical_error_rate.innerHTML = "Error Rate: " + parseFloat(updates.y_error_rate).toFixed(3).toString() + "%";
setTimeout(update_values, 1000);
},
});
window.onresize = async function() {
Plotly.Plots.resize( 'plotly_countrate' );
Plotly.Plots.resize( 'plotly_errorrate' );
};
}
I'm trying to implement a django channels chat app. when the submit button of the room view is clicked, the message does not appear in the chat log. which make me think that somethings wrong with chat.onmessage command, it does not seem to fire. can someone help me fix the issue. here is the code for room view:
<!-- chat/templates/room.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br/>
<input id="chat-message-input" type="text" size="100"/><br/>
<button id="chat-message-submit" type="submit" value="Send">Send</button>
</body>z
<script>
var roomName = {{ room_name_json }};
var chatSocket = new WebSocket(
'ws://' + window.location.host +
'/ws/chat/' + roomName + '/');
chatSocket.onmessage = function(e) {
console.log("got to onmessage");
var data = JSON.parse(e.data);
var message = data['message'];
document.getElementById('chat-log').value += (message + '\n');
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.getElementById('chat-message-submit').click();
}
};
document.getElementById('chat-message-submit').onclick = function(e) {
var messageInputDom = document.getElementById('chat-message-input');
var message = messageInputDom.value;
console.log("got message : " + message);
chatSocket.send(JSON.stringify({
'message': message
}));
console.log("This was done?");
messageInputDom.value = '';
};
</script>
</html>
Here is my consumer view :
from channels.generic.websocket import WebsocketConsumer
import json
class ChatConsumer(WebsocketConsumer):
def connect(self):
self.accept()
def disconnect(self, close_code):
pass
def recieve(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
self.send(text_data = json.dumps({
'message' : message
}))
I'm so stupid I literally made a typo, used function name "received" instead of "recieved". Thanks I'll go cry in the corner now.
I am setting up socketio with flask and am having some errors that are preventing it from running.
File "src/gevent/greenlet.py", line 705, in gevent._greenlet.Greenlet.run
File "/Users/hairy/anaconda2/lib/python2.7/site-packages/gevent/baseserver.py", line 26, in _handle_and_close_when_done
return handle(*args_tuple)
File "/Users/hairy/anaconda2/lib/python2.7/site-packages/gevent/server.py", line 193, in wrap_socket_and_handle
ssl_socket.close()
UnboundLocalError: local variable 'ssl_socket' referenced before assignment
2019-07-30T15:07:15Z <Greenlet "Greenlet-0" at 0x102908500: _handle_and_close_when_done(<bound method WSGIServer.wrap_socket_and_handle of, <bound method WSGIServer.do_close of <WSGIServer a, (<socket at 0x1067882d0 fileno=[Errno 9] Bad file )> failed with UnboundLocalError
Here is my flask code:
from flask import Flask, render_template, Markup
from util import getHtml, duplicate, displayArray
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#app.route('/')
def home():
return displayArray()
#socketio.on('connect')
def test_connect():
emit('after connect', {'data':'Lets dance'})
#socketio.on('plz reload', namespace='/test')
def test_message(message):
emit('reload', {'data': message['data']})
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', Debug=True)
Here is my js code:
$(document).ready(function(){
var socket = io.connect('http://localhost:5000');
// var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
// var socket = io.connect('http://localhost:5000');
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'});
});
socket.on('reload', function(msg) {
$('#log').append('<p>Received: ' + msg.data + '</p>');
location.reload();
});
});
The app does not load when I go to localhost:5000
I fixed it by changing
var socket = io.connect('http://localhost:5000');
to
var socket = io.connect();
in the js. I'm not sure why this fixed it, but it did.
Sorry for the noob questions, i'm new on it.
I have a python server, and client and it's working.
There are :
import socket
def Main():
host = "127.0.0.1"
port = 5000
mySocket = socket.socket()
mySocket.bind((host,port))
mySocket.listen(1)
conn, addr = mySocket.accept()
print ("Connection from: " + str(addr))
while True:
data = conn.recv(1024).decode()
if not data:
break
print ("from connected user: " + str(data))
data = str(data).upper()
print ("sending: " + str(data))
conn.send(data.encode())
conn.close()
if __name__ == '__main__':
Main()
and client
import socket
def Main():
host = '127.0.0.1'
port = 5000
mySocket = socket.socket()
mySocket.connect((host,port))
message = input(" -> ")
while message != 'q':
mySocket.send(message.encode())
data = mySocket.recv(1024).decode()
print ('Received from server: ' + data)
message = input(" -> ")
mySocket.close()
if __name__ == '__main__':
Main(
But when i want to make the same client but via JAVASCRIPT, i don's see this messages on server. Could someone please explain why?
There is my html file
<head>
<title>Test</title>
<script src="jquery.js"></script>
<script type="application/javascript">
var ws;
function init() {
var servermsg = document.getElementById("servermsg");
ws = new WebSocket("ws://127.0.0.1:5000/");
ws.onopen = function(){
servermsg.innerHTML = servermsg.innerHTML + "<br>Server connected";
};
ws.onmessage = function(e){
servermsg.innerHTML = servermsg.innerHTML + "<br><< Recieved data: " + e.data;
};
ws.onclose = function(){
servermsg.innerHTML = servermsg.innerHTML + "<br>Server disconnected";
};
}
function postmsg(){
var text = document.getElementById("message").value;
ws.send(text);
servermsg.innerHTML = servermsg.innerHTML + "<br>>> Data sent: " + text;
}
</script>
</head>
<body onload="init();">
<form action="" onSubmit="postmsg();return false;">
<input type="text" name="message" value="" id="message">
<input type="submit" name="submit" value="" id="submit">
</form>
<div id="servermsg"><h1>Message log:</h1></div>
</body>
Thank you everyone for the help