django-channels webSocketBridge read data sent by consumers - django

I am having a simple setup with testing channels 2.0.
I have a routed consumer with three methods:
consumers.py
from channels.generic.websocket import JsonWebsocketConsumer
from datetime import datetime
#
class Feedback_001_Consumer(JsonWebsocketConsumer):
def connect(self,text_data=None, bytes_data=None):
self.accept()
self.send_json("Connection for Feedback 1 ready.")
#
def receive(self,text_data=None):
#self.accept()
self.send_json("{}".format(datetime.now()))
print(datetime.now())
#
def disconnect(self,close_code):
pass
and my js looks like this:
const webSocketBridge = new channels.WebSocketBridge();
#...
webSocketBridge.connect('my_route/etc...');
#...
console.log(webSocketBridge.send(''));
While the datetime is printing in the console, I cannot get the one sent by self.send_json in the receive method of the consumer. What would be the proper way to do this?

consumer.py
from channels.generic.websocket import JsonWebsocketConsumer
from datetime import datetime
class Feedback_001_Consumer(JsonWebsocketConsumer):
def connect(self):
self.accept()
def receive_json(self, content, **kwargs):
if content['command'] == 'time':
time = datetime.now()
self.send_json({'time': time})
print(time)
def disconnect(self,close_code):
pass
your.js
webSocketBridge.socket.onopen = function(){
console.log('connected to server');
// do something else. eg
webSocketBridge.send({'command': 'time'})
};
webSocketBridge.listen(function(message, stream){
var data = message.time
console.log(data)
});

Related

Send value from __init__.py to consumer.py websocket Django channels

I want to send data from the firebase stream(pyrebase) via django channels websocket. Web scoket is working fine, But I can't imagine how I can use consumer.py file send message function in init_.py.
Here my django channel web socket Consumer.py file.
import json
from random import randint
from asyncio import sleep
from channels.generic.websocket import AsyncWebsocketConsumer
from django.conf import settings
class WSConsumer(AsyncWebsocketConsumer):
group_name = settings.STREAM_SOCKET_GROUP_NAME
async def connect(self):
# Joining group
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave group
await self.channel_layer.group_discard(
self.group_name,
self.channel_name
)
async def receive(self, text_data):
print(text_data)
# Send data to group
await self.channel_layer.group_send(
self.group_name,
{
'type': 'system_load',
'data': text_data
}
)
async def system_load(self, event):
# Receive data from group
print('sending message to client')
await self.send(text_data=json.dumps(event['data']))
This file works fine.
This is my inti.py file. In this file, I want to send data to the WebSocket in this stream_handler function.
import pyrebase
from configFiles.config import config
firebase = pyrebase.initialize_app(config)
authe = firebase.auth()
database = firebase.database()
safe_temperature = 20
safe_humidity = 40
def stream_handler(message):
if "Humidity" in message["path"]:
if message["data"] > safe_humidity:
database.child("Controller").child("Light").set(1)
else:
database.child("Controller").child("Light").set(0)
if "Temperature" in message["path"]:
if message["data"] > safe_humidity:
database.child("Controller").child("Fan").set(1)
else:
database.child("Controller").child("Fan").set(0)
# Here place I want to send data which is message["data"] to the WebSocket
mystream = database.child("Sensor").stream(stream_handler)
Thank you!

how to get data from database using django channels?

I'm trying to practice WebSocket implementation using django channels by querying the database and printing the data but I am unsuccessful.
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from .models import Chart
from chart.api.serializers import ChartSerializer
class ChartConsumer(AsyncWebsocketConsumer):
def get_chart(self):
return database_sync_to_async(Chart.objects.all)()
async def connect(self):
data = self.get_chart()
print(data) # <-------- I want to get this data
# for i in data:
# chart_data = ChartSerializer(i).data
# await self.send(json.dumps({'number': chart_data.number}))
# print(chart_data)
await self.accept()
async def disconnect(self, code):
pass
Output
You need the database_sync_to_async to be called in the connect method. Like so:
class ChartConsumer(AsyncWebsocketConsumer):
def get_chart(self):
return Chart.objects.all()
async def connect(self):
data = await database_sync_to_async(self.get_chart)()
print(data)
# for i in data:
# chart_data = ChartSerializer(i).data
# await self.send(json.dumps({'number': chart_data.number}))
# print(chart_data)
await self.accept()

Async function in django that returns a json response

I want to create an async function in django to return a json response but every time a call the function from ajax call i get 500 error cooroutine has no attribute get
from asgiref.sync import sync_to_async
from django.core.serializers import serialize
def _get_data(request):
context = {}
poi_ids = request.GET.getlist('poi_ids[]')
if len(poi_ids)> 0:
poi_list = serialize('geojson', Poi.objects.filter(id__in=poi_ids).distinct('name'),
geometry_field='geom',
fields=('name',))
context['poi_list'] = poi_list
return JsonResponse(context)
get_poi = sync_to_async(_get_data, thread_sensitive=True)
To undestand how async views work in django i created this simple example
in views.py i created the following code
import asyncio
from django.http import JsonResponse
from django.core import serializers
from asgiref.sync import sync_to_async
from django.shortcuts import render
from .models import Todo
def _get_todo_data(request):
context = {}
data = Todo.objects.all()
context['data'] = serializers.serialize("json", data)
return JsonResponse(context)
get_todo_data = sync_to_async(_get_todo_data, thread_sensitive=True)
def todo_list_view(request):
context = {}
template = 'todo/todo_list.html'
return render(request,template,context)
so in frontend to get all todos as ajax promise i have this code
$(document).ready(function(){
$.when($.ajax({
url: '/todo/get/data/',
method: 'GET',
datatype: 'json'
})).then(function( response, textStatus, jqXHR ) {
console.info(response)
var data = JSON.parse(response.data);
console.info(data);
});
})

Connection error while connection has already been established

I use the Telethon == 1.4.3 in my code:
import telepot
import threading
from telethon import TelegramClient
from flask import Flask, request
from telepot.loop import OrderedWebhook
from telepot.delegate import (
per_chat_id, create_open, pave_event_space, include_callback_query_chat_id)
class Main_Class(telepot.helper.ChatHandler):
def __init__(self, *args, **kwargs):
super(Main_Class, self).__init__(*args, **kwargs)
def on_chat_message(self, msg):
content_type, chat_type, chat_id = telepot.glance(msg)
if content_type == 'text':
client = TelegramClient('session_name', api_id, api_hash)
client.connect()
client.send_message('me', 'Hello World from Telethon!')
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def pass_update():
webhook.feed(request.data)
return 'OK'
TOKEN = my_token
bot = telepot.DelegatorBot(TOKEN, [
include_callback_query_chat_id(
pave_event_space())(
per_chat_id(types=['private']), create_open, Main_Class, timeout=100000),
])
webhook = OrderedWebhook(bot)
webhook.run_as_thread()
Since I also use the Flask, these two interfere with each other and I got the following error:
RuntimeError: There is no current event loop in thread 'Thread-1'
I imported asyncio and added the following lines to the code and the problem was solved
class Main_Class(telepot.helper.ChatHandler):
......
loop = asyncio.new_event_loop()
client = TelegramClient('session_name', api_id, api_hash,loop=loop)
loop.run_until_complete(goo(loop,client))
loop.close()
.....
async def goo(loop,client):
client.connect()
await client.send_message('me', 'Hello World from Telethon!')
The following error occurs despite the fact that I have already established a connection:
ConnectionError: Cannot send requests while disconnected
You should also wait for the connection to finish. Because it is done asynchronously.
async def goo(loop,client):
await client.connect()
await client.send_message('me', 'Hello World from Telethon!')
Read more, here.

Using django signals in channels consumer classes

I am trying to develop an auction type system, where a customer makes an order, and then different stores can offer a price for that order.
An interesting part of this system is that when the order is initially created, the available stores will have 60 seconds to make their respective offer. When a first store makes their offer, the "auction" will now only have the next 20 seconds for other stores to make their own offer. If they do make another offer, in this smaller allocated time, then this 20 second is refreshed. Offers can keep on being received as long as there is enough time, which cannot surpass the initial 60 seconds given.
class Order(models.Model):
customer = models.ForeignKey(Customer)
create_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now_add=True)
total = models.FloatField(default=0)
status = models.IntegerField(default=0)
delivery_address = models.ForeignKey(DeliveryAddress)
store = models.ForeignKey(Store, null=True, blank=True, related_name='orders', on_delete=models.CASCADE)
credit_card = models.ForeignKey(CreditCard, null=True, blank=True, related_name='orders')
class OrderOffer(models.Model):
store = models.ForeignKey(Store, related_name="offers", on_delete=models.CASCADE)
order = models.ForeignKey(Order, related_name="offers", on_delete=models.CASCADE)
create_time = models.DateTimeField(auto_now_add=True)
Besides these requirements, I also want to update the client when new offers arrive in real-time. For this, I'm using django-channels implementation of WebSockets.
I have the following consumers.pyfile:
from channels.generic.websockets import WebsocketConsumer
from threading import Timer
from api.models import Order, OrderOffer
from django.db.models.signals import post_save
from django.dispatch import receiver
class OrderConsumer(WebsocketConsumer):
def connect(self, message, **kwargs):
"""
Initialize objects here.
"""
order_id = int(kwargs['order_id'])
self.order = Order.objects.get(id=order_id)
self.timer = Timer(60, self.sendDone)
self.timer.start()
self.message.reply_channel.send({"accept": True})
def sendDone(self):
self.send(text="Done")
# How do I bind self to onOffer?
#receiver(post_save, sender=OrderOffer)
def onOffer(self, sender, **kwargs):
self.send(text="Offer received!")
if (len(self.offers) == 0):
self.offerTimer = Timer(20, self.sendDone)
self.offers = [kwargs['instance'],]
else:
self.offerTimer = Timer(20, self.sendDone)
self.offers.append(kwargs['instance'])
def receive(self, text=None, bytes=None, **kwargs):
# Echo
self.send(text=text, bytes=bytes)
def disconnect(self, message, **kwargs):
"""
Perform necessary disconnect operations.
"""
pass
I have successfully been able to establish a WebSocket communication channel between my client and the server. I've tested sending messages, and everything seems ok. Now I want to detect the creation of new OrderOffer's, and send a notification to the client. For this, I need access to the self variable, to use self.send, which is impossible, as the signals decorator does not send this parameter. I've tried forcing it by declaring onOffer with self, but I get the following error:
TypeError: onOffer() missing 1 required positional argument: 'self'
If I could somehow access the keyword arguments, that signals sets, I could maybe do something like:
context = self.
I would appreciate any help, or even alternative solutions to my original problem.
If someone stumbles upon on that, this is the way I solved it in the signals.py. I have a Job and need to send its status to the client every time it changes. This is my signals.py:
import channels.layers
from asgiref.sync import async_to_sync
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Job
def send_message(event):
'''
Call back function to send message to the browser
'''
message = event['text']
channel_layer = channels.layers.get_channel_layer()
# Send message to WebSocket
async_to_sync(channel_layer.send)(text_data=json.dumps(
message
))
#receiver(post_save, sender=Job, dispatch_uid='update_job_status_listeners')
def update_job_status_listeners(sender, instance, **kwargs):
'''
Sends job status to the browser when a Job is modified
'''
user = instance.owner
group_name = 'job-user-{}'.format(user.username)
message = {
'job_id': instance.id,
'title': instance.title,
'status': instance.status,
'modified': instance.modified.isoformat(),
}
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'send_message',
'text': message
}
)
By the way, I have a Consumer class JobUserConsumer(AsyncWebsocketConsumer) where I define the groups:
async def connect(self):
user = self.scope["user"]
self.group_name = 'job-user-{}'.format(user.username)
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
The project I used this is here: https://github.com/ornl-ndav/django-remote-submission/tree/master/django_remote_submission
For those who still have problems with web sockets, this could be helpful:
from api.models import Order, OrderOffer
from asgiref.sync import async_to_sync
import channels.layers
from channels.generic.websocket import JsonWebsocketConsumer
from django.db.models import signals
from django.dispatch import receiver
class OrderOfferConsumer(JsonWebsocketConsumer):
def connect(self):
async_to_sync(self.channel_layer.group_add)(
'order_offer_group',
self.channel_name
)
self.accept()
def disconnect(self, close_code):
async_to_sync(self.channel_layer.group_discard)(
'order_offer_group',
self.channel_name
)
self.close()
def receive_json(self, content, **kwargs):
print(f"Received event: {content}")
def events_alarm(self, event):
self.send_json(event['data'])
#staticmethod
#receiver(signals.post_save, sender=OrderOffer)
def order_offer_observer(sender, instance, **kwargs):
layer = channels.layers.get_channel_layer()
async_to_sync(layer.group_send)('order_offer_group', {
'type': 'events.alarm',
'data': {
'text': 'Offer received',
'id': instance.pk
}
})
In urls.py you need to register a new webscoket route:
websocket_urlpatterns = [url(r'^order_offer$', OrderOfferConsumer)]
If you want to talk to the consumer from "outside" - in this case, from a model save method - you'll need to use a Channel Layer to talk to it: http://channels.readthedocs.io/en/latest/topics/channel_layers.html
Essentially, you'll need to:
Add the consumer to a Group on startup (probably based on its order ID)
Send a message to the Group whenever there's a new OrderOffer with a custom type - e.g. {"type": "order.new_offer", "order_offer_id": 45}
Define a handler on the Consumer that handles this - it matches the type name, so in this case it would be def order_new_offer(self, event):
In that handler you can then use self.send to talk down the socket (and query the database if you need extra info to send to the client you didn't put into the event message).
You can see a variant of this in the MultiChat example project: https://github.com/andrewgodwin/channels-examples/tree/master/multichat