Push live updates to client through Django Channels & Websockets - javascript

I'm trying to make a page which shows live-updating data to the client. The rest of the site is built with Django, so I'm trying to use Channels for this.
The data I am displaying is saved in both a JSON file and a MySQL database for further calculations in other parts of the site. Ideally, I would like to display the latest data received (that is, when the file updates) to the client as it is received.
And even though as I understand Channels are built exactly for this purpose, I am having trouble doing it.
I have tried sending multiple requests from the client-side with delay and loops in the consumer, but it either (ironically) only updates on refresh or updates instantly. However, neither of these approaches are triggered by a change in the file or database.
This is the code that "works", but doesn't really do what's required. (also, admittedly, there's basically nothing there...)
# consumers.py
def ws_connect(message):
message.reply_channel.send({"accept": True})
def ws_receive(message):
with open("data.json") as jsonfile:
jsondata = json.load(jsonfile)
res = json.dumps(jsondata)
message.reply_channel.send({ "text": res, })
#routing.py
from channels.routing import route
from .consumers import ws_receive, ws_connect
channel_routing = [
route("websocket.receive", ws_receive, path=r"^/websockets/$"),
route("websocket.connect", ws_connect, path=r"^/websockets/$"),
]
JS used:
<script>
var wsurl = "ws://" + "mywebsite.com" + "/websockets/";
socket = new WebSocket(wsurl);
socket.onopen = function() {
socket.send("this is a request");
console.log('sent');
}
socket.onmessage = function(message) {
console.log(message.data);
document.getElementById("livedata").innerHTML = message.data;
}
</script>
I'd be perfectly happy with a link to the docs that would help me achieve something like this, as I've managed to not find the solution for a whole week.

Add user to django channels Group on ws_connect
from channels.auth import channel_session_user_from_http
from channels import Group
#channel_session_user_from_http
def ws_connect(message, **kwargs):
http_user = message.user
if not http_user.is_anonymous:
message.reply_channel.send({'accept': True})
Group('user-'+str(http_user.id)).add(message.reply_channel)`
send any new updates data to the user by
Group('user-1').send({'text': 'hello user 1'})
Group('user-2').send({'text': 'hello user 2'})

Related

I want to show live data in my node.js app WITHOUT a loop. How do I use mongoDB like Firebase? Is sockets suitable?

A new message gets inserted into the mongoDB from another application that I don't have access to the code. I do have the database credentials though. I want to make that message show on my application at the moment it gets inserted into mongoDB. I don't want to loop. I want to show the new data the moment it gets Inserted or Updated.
A living example: Auction websites generally update the newest bids really quickly, a few seconds before the auction deadline ends. I am wondering how that works without loops. Like a chat app, but waiting for new data in the database instead of another user typing a message. But in my situation, the data gets Inserted or Updated by another application.
Some additional information: I am also using routers / express and hbs html template. I am happy enough if I get atleast a general idea of how to do this, without working with routers / express yet. But if you guys can come up with a combined solution that would be perfect!
I have something like this so far but it's still a bit confusing. You can pretty much ignore my code since it's not working completely. But maybe it helps a bit.
(some more code above ...)
const WebSocketServer = new WebSocket.Server({port: 8082})
WebSocketServer.on('connection', ws => {
console.log('New client connected!')
ws.on('message', async (data) => {
console.log(`Client has sent us: ${data}`)
const connMtlinki = require('../src/db/mtlinki')
const connection = connMtlinki.connect()
const database = connMtlinki.db('MTLINKi')
const MacroVariableHistory = database.collection('MacroVariableHistory')
const machines = await MacroVariableHistory.findOne({ L0Name: data })
console.log(machines)
ws.send(machines)
})
ws.on('close', () => {
console.log('Client has disconnected!')
})

How to get data from back end side, to use it in the browser side?

I am new to programming, and I heard that some guys on this website are quite angry, but please don't be. I am creating one web app, that has a web page and also makes som ecalculations and works with database (NeDB). I have an index.js
const selects = document.getElementsByClassName("sel");
const arr = ["Yura", "Nairi", "Mher", "Hayko"];
for (let el in selects) {
for (let key in arr) {
selects[el].innerHTML += `<option>${arr[key]}</option>`;
}
}
I have a function which fills the select elements with data from an array.
In other file named: getData.js:
var Datastore = require("nedb");
var users = new Datastore({ filename: "players" });
users.loadDatabase();
const names = [];
users.find({}, function (err, doc) {
for (let key in doc) {
names.push(doc[key].name);
}
});
I have some code that gets data from db and puts it in array. And I need that data to use in the index.js mentioned above, but the problem is that I don't know how to tranfer the data from getData.js to index.js. I have tried module.exports but it is not working, the browser console says that it can't recognize require keyword, I also can't get data directly in index.js because the browse can't recognize the code related to database.
You need to provide a server, which is connected to the Database.
Browser -> Server -> DB
Browser -> Server: Server provides endpoints where the Browser(Client) can fetch data from. https://expressjs.com/en/starter/hello-world.html
Server -> DB: gets the Data out of the Database and can do whatever it want with it. In your case the Data should get provided to the Client.
TODOs
Step 1: set up a server. For example with express.js (google it)
Step 2: learn how to fetch Data from the Browser(Client) AJAX GET are the keywords to google.
Step 3: setup a Database connection from you Server and get your data
Step 4: Do whatever you want with your data.
At first I thought it is a simple method, but them I researched a little bit and realized that I didn't have enough information about how it really works. Now I solved the problem, using promises and templete engine ejs. Thank you all for your time. I appreciate your help)

How to prevent user from accessing JSON data from query parameters?

I am developing a really simple webapp that searches for a company's stocks.
Here is the JS code (uses AJAX to fetch the company's stock from the server):
document.getElementById("requestQuoteBtn").addEventListener("click", function createQuoteRequest(){
var quoteSymbol = document.getElementById("requestedSymbol").value;
var quoteRequest = createAJAX();
quoteRequest.open('GET', '/quote?sym='+quoteSymbol);
quoteRequest.send();
quoteRequest.onload = function getQuoteRequest(){
if(quoteRequest.status == 200){ // SUCCESSFUL
displayQuoteData(false, JSON.parse(quoteRequest.response)); // basically shows a hidden div with the data
}
else // NO COMPANY W/ THIS SYMBOL FOUND
displayQuoteData(true, null);
};
});
Here is the Flask code:
#app.route("/quote", methods=["GET"])
#login_required
def quote():
requestedSymbol = request.args.get("sym")
if not requestedSymbol:
return "no symbol"
quoteData = lookup(requestedSymbol) # USES AN API TO FETCH COMPANY'S STOCK
if quoteData is None:
return "NONE", 404
else:
return quoteData
The issue is that if the user accesses, for example, this URL:
www.mywebsite.com/quote?sym=AAPL
It will literally show a raw HTML with JSON's data, instead of my website with the data:
{"name":"Apple, Inc.","price":"$245.18","symbol":"AAPL"}
How can I prevent this?
If you simply want to make sure that users do not accidentally access your api endpoint when trying to access your website (aka this is about user experience and your not concerned with adding auth to your API endpoint)
The easiest way is to create separate routes for your api and your client routing
Update:
#app.route("/api/quote", methods=["GET"])
Likewise update:
quoteRequest.open('GET', '/api/quote?sym='+quoteSymbol);
Your client routing will still be:
#app.route("/quote", methods=["GET"])
If you want to make sure that nobody can access your api endpoint then you need to add some sort of authorization to your endpoint.
If you do not secure your API with some authorization then anyone can access the data you return from your server API simply by visiting the route.
Either way setting up separate routes for your API endpoints and client side routes should solve the problem of showing API data instead of your client template when visiting:
mywebsite.com/quote?sym=AAPL

OpenTok Session Connection Issue (Javascript/Rails Application)

This is a basic video chat I wanted to create with 2 users connected.
After having done a bit research on opentok Tutorials, I was able to create the 2 way chat without Server SDK.
But When I created the rails server side app, I was unable to get the 2 way chat, rather the link was only creating single session and was not accepting any other users.
I have included the gem opentok in Gemfile.
Problem:
Only Single Video Div appearing, even after a existing screen present in another tab. i.e., I open the link, I get a video screen, When I open the same link in another tab, I get a single video screen instead of two. (Some issue with session connections).
Code from Controller:
class AuthController < ApplicationController
require "opentok"
def index
config_opentok
session = #opentok.create_session
#id = session.session_id
#token = session.generate_token
end
private
def config_opentok
if #opentok.nil?
#opentok = OpenTok::OpenTok.new API_KEY, 'SECRET'
end
end
end
I have entered correct Api_key and Secret above as given from Opentok dashboard.
Code from View:
<div id='myPublisherDiv'></div>
<div id='subscribersDiv'></div>
<script src='//static.opentok.com/v2/js/opentok.min.js'></script>
<script>
var apiKey = API_KEY; // used correct Api_key in APP
var sessionId = '<%= #id %>';
var token = '<%= #token %>';
var session = OT.initSession(apiKey, sessionId);
session.on({
streamCreated: function(event) {
session.subscribe(event.stream, 'subscribersDiv', {insertMode: 'append'});
}
});
session.connect(token, function(error) {
if (error) {
console.log(error.message);
} else {
session.publish('myPublisherDiv', {width: 320, height: 240});
}
});
</script>
I have tried some other variants, adding event handlers etc, but nothing worked for me. Any Help appreciated. Provide sample codes if possible.
The problem is you are creating a new session for each page load. That means each visitor is getting a different session ID so the two will never be able to subscribe to each other.
Instead, you need a way to store a generated session ID (such as a database). Then you think about how you want to place visitors in the same session, such as using the URL for unique "rooms". When you have a page load that should serve a session ID that was previously generated, read the value from the database.

How to store Flash-based games data into server

I have this website, I'll be happy if I could store the game progress of the user, and then retrieve it to their respective users when they login. Is there any way to do this? Maybe storing the data to a DB. I was reading on Internet and I found something about Shared Objects. I don't know if it is useful, I hope it will.. Thanks in advance and sorry for my English.
EDIT: I publish external games, I didn't develop them.
You can use a SharedObject to store data locally, on the client side:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/SharedObject.html
This is very easy to implement:
var user_data_so:SharedObject = SharedObject.getLocal("myUserData");
user_data_so.data.name = "User Name";
user_data_so.data.score = user_score;
SharedObjects also work with a NetConnection (see getRemote), although I haven't used this method. Other than that, a server-side script in any language you're comfortable with can accept a POST (or GET) from the client:
var req:URLRequest = new URLRequest(user_data_url);
req.data = user_data_xml;
req.contentType = "text/xml";
req.method = URLRequestMethod.POST;
// either this (will ignore server response):
sendToURL(req);
// or (handles status, error, completion):
var url_loader = new URLLoader();
url_loader.addEventListener("complete",completion_handler);
url_loader.addEventListener("ioError",error_handler);
url_loader.addEventListener("securityError",error_handler);
url_loader.addEventListener("httpResponseStatus",status_handler);
url_loader.addEventListener("httpStatus",status_handler);
url_loader.load(req);

Categories