Flask-socketio, send message only to one chat - javascript

I am developing flask app with chat feature. When somenone sends message, it saves into db. Now I want to display it on screen with socketio but when someone sends message, it shows in every currently used chat. Does anyone know how to display message only in one correct chat? Every two users have own chat with ID or can create it by sending message.
main.py code:
#app.route('/chat/<int:id>', methods=['GET', 'POST'])
#login_required
def chat(id):
chat = Chats.query.get_or_404(id)
form = MessageForm()
messages = chat.messages
chat_id = chat.id
if current_user.id == chat.first_user or current_user.id == chat.second_user:
if request.method == "POST":
form1 = request.form.get("myMessage")
chat_id = chat.id
author = current_user.id
message = Messages(author=author, chat=chat_id, content=form1)
chat.last_message = datetime.utcnow()
db.session.add(message)
db.session.commit()
return render_template('chat.html', chat=chat, messages=messages, form=form, chat_id = chat_id)
else:
return redirect(url_for('index'))
#socketio.on('message')
def handleMessage(msg):
print('Message: ' + msg)
send(msg, broadcast=True)
chat.html code:
<script type="text/javascript">
$(document).ready(function() {
var socket = io.connect('http://127.0.0.1:5000');
socket.on('connect', function() {
socket.send('User has connected!');
});
socket.on('message', function(msg) {
$("#messages").append('<li>'+msg+'</li>');
console.log('Received message');
});
$('#sendbutton').on('click', function() {
socket.send($('#myMessage').val());
$('#myMessage').val('');
});
});
</script>
<ul id="messages"></ul>
<input type="text" id="myMessage">
<button id="sendbutton">Send</button>

Related

django: issue with javascript redirection after form submission and download

I need to redirect a user after he finishes filling up a form and clicking on download, which generate a pdf template and then downloads it.
I have tried redirecting from the backend side in django as well as in client side with javascript and nothing makes it.
here is what I currently have:
USER INPUT VIEW
def New_Sales(request):
#context = {}
form = modelformset_factory(historical_recent_data, fields=('id','Id', 'Description','Date','Quantity', 'NetAmount', 'customer_name', 'invoice_number'))
if request.method == 'GET':
formset = form(queryset= historical_recent_data.objects.none())
#blank_form = formset.empty_form
elif request.method == 'POST':
formset = form(request.POST)
#invoice_hidden_form = CreateInvoiceForm(request.POST)
#blank_form = formset.empty_form
if formset.is_valid():
#request.session['sale'] = formset.cleaned_data
for check_form in formset:
check_form.save()
quantity = check_form.cleaned_data.get('Quantity')
id = check_form.cleaned_data.get('Id')
update = replenishment.objects.filter(Id = id).update(StockOnHand = F('StockOnHand') - quantity)
update2 = Item2.objects.filter(reference = id).update(stock_reel = F('stock_reel') - quantity)
request.session['sale'] = formset.cleaned_data
#if invoice_hidden_form.is_valid():
#invoice_hidden_form.save()
#print('invoice_hidden_form is saved successfully')
#request.session['invoice'] = invoice_hidden_form.cleaned_data
print(formset.cleaned_data)
return redirect('/invoice/pdf/assembly/')
PDF GENERATION + DOWNLOAD VIEW:
def generate_pdf_assembly(request):
data = request.session['sale']
invoice_number = data[0]['invoice_number']
print(data)
#total_ht = request.session['sale'].get('NetAmount')
rate_list = []
for index in range(len(data)):
rate_list.append(round(data[index]['NetAmount']/data[index]['Quantity'],1))
total_ht = []
for index in range(len(data)):
total_ht.append(data[index]['NetAmount'])
print('total_ht', total_ht)
total_ht = sum(total_ht)
my_company = MyCompany.objects.get(id = 1)
tva = MyCompany.objects.aggregate(Sum('TVA'))['TVA__sum']
tva_value = round(total_ht * tva,1)
total_ttc = total_ht + tva_value
tableau = zip(rate_list, data)
context = {'data' : data,
'my_company' : my_company,
'total_ht':total_ht,
'tva_value':tva_value,
'total_ttc':total_ttc,
'rate_list':rate_list,
'tableau':tableau,
'invoice_number':invoice_number,
}
print("context",context)
pdf = render_to_pdf('pdf/invoice_generator_assembly.html', context)
if pdf:
response = HttpResponse(pdf, content_type='application/pdf')
filename = "Melt_Invoice_{}.pdf".format(data[0]['customer_name'])
content = "inline; filename={}".format(filename)
content = "attachment; filename={}".format(filename)
response['Content-Disposition'] = content
return response
return HttpResponse("Not found")
here is the redirection code looks like in pdf/invoice_generator_assembly.html, I try redirecting as a success when the data has been acquired by AJAX
<script>
$.ajax({
method: "GET",
url: "/new_sale.html",
sucess: function(context){
alert(context);
window.location = '/dash2.html';
},
failure: function(context){
alert('got an error');
}
});
</script>
But here is what gets outputed in Chrome dev tool
the redirection never occurs, send help pleaaase

generate csv in JS and send it using ajax request POST to flask

I am trying to create a CSV file in JS using my code table2csv. Then I want to send it to flask using an ajax request and return it back again to the client.
But as I try to send the file to server it returns the error that ajax can't find my file.
I used console.log to check if my file is created and it is. I am stuck and don't know what to do anymore, since I am pretty new to ajax requests so any help would be great.
This is my JS part and what I am doing currently:
//On Update click renders table to csv, activates the be_filter and reopens it in the filtered_file.html
var isClicked;
jQuery("#update").on('click', function(){
var response = confirm('Are you sure you want to UPDATE rows ?');
if(response == true){
isClicked = $('#my_id').table2csv();
$.ajax({
type:'POST',
url:"{{url_for('update_file')}}",
data: {'data': isClicked},
success: function(result){
console.log(result);
},
error: function(error){
console.log(JSON.stringify(error));
}
});event.preventDefault();
//window.location.href='/update_file';
}else{
return false;
}
});
And the flask call:
#app.route('/update_file', methods=['GET', 'POST'])
#login_required
def update_file():
'''Opens the filtered_file page but with updated file'''
clicked = None
if request.method == 'POST':
clicked = request.form['data']
file_to_filter = pd.read_csv(clicked, sep=';', engine='python', encoding='utf_8_sig')
table1 = update_csv(file_to_filter)
table2 = table1.to_html(classes='my_class" id = "my_id')
return render_template('3_filtered_file.html', data=table2)
EDIT: console.log() for the error message :
POST http://127.0.0.1:5000/update_file 500 (INTERNAL SERVER ERROR)
{"readyState":4,"responseText":"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n <head>\n <title>FileNotFoundError: [Errno 2] No such file or directory: '"Auftragsdatum","OrderNo","ReferenceOrder","Pos","Quantity","ArtNo","ManufactureNo","ProductName","ReferencePosition","NetPerPiece","InvoiceNo","DeliveryNoteNo","SerialNumbers","Manufacturer","CI","Type","Import_ID","State","Supplier","NetPerPieceSale","OU","Modified_Date","Added_by","Modified_by","isSupplier","isManufacturer"\\n"01.04.2019","7027856072","a","100","1","2099882","GS1900-24HP-EU0101F","ZYXEL GS1900-24HP 24P GbE L2 PoE Switch","CLINO","251,09","950347427","6054042579","S182L37002129","ZYXEL","sel","","","716","ALSO","","OU00100","11-11-2019 09:58","admin","","","BPT07939"\\n"01.04.2019","7027856072","bg","200","1","3074862","EAP225 V3","TP-LINK AC1350 WLAN Dual Band Gigabit AP","CLINO","64,56","950347427","6054042579","218B410001725","TP-LINK","sel","","","716","ALSO","","OU00100","11-11-2019 09:58","admin","","","BPT07134"\\n"01.04.2019","7027856072","cd","300","1","7003581","","Mautgebühr","nan","2,09","950347427","6054042579","","","sel","","","716","ALSO","","sel","11-11-2019 ...
EDIT 2 ** this is my code for **table2csv:
(function ($) {
const _trim_text = (text) => {
return text.trim();
};
const _quote_text = (text) => {
return '"' + text + '"';
};
function convert(tb){
let output = "";
let lines = [];
$(tb).find('thead>tr').each(function () {
let line = [];
$(this).find('th:not(th:eq(0))').each(function () {
line.push(_quote_text(_trim_text($(this).text())));
});
lines.push(line.splice(0).toString());
})
$(tb).find('tbody>tr').each(function () {
let line = [];
$(this).find('td').each(function () {
if($(this).find('select').length){
line.push(_quote_text($(this).find('option:selected').val()));
}else if($(this).find('input').length){
line.push(_quote_text($(this).find('input').val()));
}
else
line.push(_quote_text(_trim_text($(this).text())));
});
lines.push(line.splice(0).toString());
})
output = lines.join('\n');
return output;
};
$.fn.table2csv = function () {
let csv = convert(this);
//cases = $('#out').append($("<pre>").text(csv));
return csv;
};
})(jQuery);
It seems you are you some jQuery plugin to convert table data to csv. It doesn't actually create file on you disk. When you are making the ajax POST request to server you are sending the form data. On the server side you have clicked = request.form['data'] here clicked is not the file. But your pandas read_csv expects the url or buffer type. You can get around this issue with StringIO.
#app.route('/update_file', methods=['GET', 'POST'])
#login_required
def update_file():
'''Opens the filtered_file page but with updated file'''
clicked = None
if request.method == 'POST':
clicked = StringIO(request.form['data'])
file_to_filter = pd.read_csv(clicked, sep=';', engine='python', encoding='utf_8_sig')
table1 = update_csv(file_to_filter)
table2 = table1.to_html(classes='my_class" id = "my_id')
return render_template('3_filtered_file.html', data=table2)

Django-channels:chatSocket.onmessage or chatSocket.send does not work

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.

Concise example of how to join and leave rooms using Flask and Socket.io?

I'm trying to use socket.io with a Flask server connecting to JS.. I'm struggling with basically everything, but my first step is to make it so that users can connect to different channels. My broadcast message function is working, but when I click on a different channel, the messages do not get sent to a different channel.. What am I doing wrong?
JS:
document.addEventListener('DOMContentLoaded', ()=>{
// Send user back to login page if they didn't sign in
const username = localStorage.getItem('username');
if (username == null){
window.location = "/";
}
// Switch button active class when clicked
$('.list-group .list-group-item.list-group-item-action').click(function(e) {
$('.list-group .list-group-item.list-group-item-action.active').removeClass('active');
var $this = $(this);
if (!$this.hasClass('active')) {
$this.addClass('active');
}
e.preventDefault();
});
// Connect to socket.io
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect', () => {
// Automatically connect to general channel
socket.emit('join',{"channel": "general", "username":username});
// When a channel is clicked, connect to that channel
document.querySelectorAll('.list-group-item').forEach(function(channel){
channel.onclick = () =>{
socket.emit('join',{"channel":channel.innerHTML, "username":username});
return false;
}
});
// When a message is sent, call 'send message' function from server
document.querySelector('#send-message').onsubmit = () => {
const message = document.querySelector('#m').value
socket.emit('send message', {'message': message});
// Clear message form
document.querySelector('#m').value = "";
return false;
};
});
// Callback from server for sending messages
socket.on('broadcast message', data =>{
console.log(data);
// Append message to list of messages
const li = document.createElement('li');
li.innerHTML = `${data.message}`;
document.querySelector('#messages').append(li);
});
});
Python Flask:
import os
from flask import Flask, render_template, url_for
from flask_socketio import SocketIO, emit, join_room, leave_room
from collections import defaultdict
app = Flask(__name__)
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")
socketio = SocketIO(app)
messages = defaultdict(list)
channels = ["Programming"]
#app.route("/")
def index():
return render_template("login.html")
#app.route("/chatroom/")
def chatroom():
return render_template("chatroom.html", channels=channels, messages=messages)
#socketio.on("send message")
def message(data):
print(data)
emit("broadcast message", {"message": message}, broadcast=True)
#socketio.on('join')
def on_join(data):
username = data['username']
channel = data['channel']
join_room(channel)
#send(username + ' has entered the room.', channel=channel)
if __name__ == '__main__':
socketio.run(app)
Think of a room as an array of users that stays on the server. When you send your message in "send message", you set broadcast=True, so it sends it as a global message to all users, as long as they are connected. If you only want to send to users in specific rooms, you will need to specify which room you want to send the message to from the client, each time you send a message, like this:
// client.js
socket.emit('join', { 'channel': channel, ... });
socket.emit('send message', {'message': message, 'channel': channel});
// server.py
#socketio.on("send message")
def message(data):
room = data['channel']
emit('broadcast message', data['message'], room=room)
I believe the first answer is correct. Just as a note, avoid using 'innerhtml' where possible especially in this case. By setting the innerhtml, anything a user writes in a message will be treated as html at the other end. This includes script tags which means someone could remotely run javascript on someone else's machine by sending a malicious message.
I would suggest using innerText or textContent. This will treat the message as plain text not html. They are slightly different so it may be worth looking into which one you need.
I would have done this as a comment but my rep isn't high enough.
tl:dr Use textContent or innerText instead of innerhtml.

Integrating Flask-Python with HTML and JavaScript

In my code , I want the Javascript defined in index.html page to run when a particular condition in app.py is true, else the script should not run. How can I achieve this?
app = Flask(__name__)
#app.route('/',methods=["GET","POST"])
def index():
#print "came here"
if request.method == 'POST':
search_token = request.args.get("validationtoken")
print "search", search_token
if search_token != None:
# text = search_token
resp = Response(search_token, status=200, mimetype='plain/text')
print "resp",resp
return resp
else:
print " Notification received "
##### HERE I NEED TO CALL THE Java SCRIPT DEFINED IN HTML PAGE TO EXECUTE
elif request.method=="GET":
code=request.args.get('code')
state=request.args.get('state')
----
pls help..
The python script should go like this
app = Flask(__name__)
#app.route('/',methods=["GET","POST"])
def index():
script = False
if request.method == 'POST':
search_token = request.args.get("validationtoken")
print "search", search_token
if search_token != None:
# text = search_token
resp = Response(search_token, status=200, mimetype='plain/text')
print "resp",resp
return resp
else:
print " Notification
script = True
render_template('index.html', Script=script)
elif request.method=="GET":
code=request.args.get('code')
state=request.args.get('state')
----
And the index.html should be something like this
{% if Script %}
<script type="text/javascript">
"Your code lies here"
</script>
You can pass the variable in your python script (Your app) to the index.html template using this way as Flask uses Jinja templates.

Categories

Resources