I wrote the following Python code in the app_consumer.py
and tasks_consumer.py
files to stream data to the consumer.html
Jinja template.
app_consumer.py
from flask import render_template, Response, request
from flask_socketio import join_room
from init_consumer import app, socketio
import tasks_consumer
import uuid
def render_template_stream(template_name, **context):
app.update_template_context(context)
t = app.jinja_env.get_template(template_name)
rv = t.stream(context)
rv.enable_buffering(5)
return rv
@app.before_request
def initialize_params():
if not hasattr(app.config,'uid'):
sid = str(uuid.uuid4())
app.config['uid'] = sid
print("initialize_params - Session ID stored =", sid)
@app.route("/", methods=['GET'])
def index():
return render_template('consumer.html', stockInfo = {})
@app.route('/consumetasks', methods=['GET','POST'])
def getStockStatus():
if request.method == 'POST':
print("Retrieving stock status")
return Response(render_template_stream('consumer.html', stockInfo = tasks_consumer.sendStockStatus()))
elif request.method == 'GET':
return '''
<!doctype html>
<html>
<head>
<title>Stock Sheet</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body class="container">
<h1>Stock Sheet</h1>
<div>
<button id="consumeTasks">Check stock status</button>
</div>
</body>
</html>
'''
# Run using port 5001
if __name__ == "__main__":
socketio.run(app,host='localhost', port=5001,debug=True)
tasks_consumer.py
import csv
from flask import request, stream_with_context
from init_consumer import app, socketio
import json
# Receive the webhook requests and emit a SocketIO event back to the client
def send_message(data):
status_code = 0
if request.method == 'POST':
roomid = app.config['uid']
msg = json.dumps(data)
event = "Send_stock_status"
socketio.emit(event, msg, namespace = '/collectHooks', room = roomid)
status_code = 200
else:
status_code = 405 # Method not allowed
return status_code
# Retrieve the stock status of the products sent through the webhook requests and return them back to the client.
@app.route('/consumetasks', methods=['POST'])
def sendStockStatus():
stockList = [] # List of products in stock
with open("NZ_NVJ_Apparel_SKUs_sheet.csv", newline='') as csvFile:
stockReader = csv.reader(csvFile, delimiter=',', quotechar='"')
for row in stockReader:
stockList.append(row[0])
stockSheet = {} # Dictionary of products sent in the request and their stock status
def generateStockStatus():
request_data = request.get_json()
if request_data:
if 'SKU' in request_data:
stockRequest = request_data['SKU'] # List of products sent in the request
for stock in stockRequest:
if stock in stockList:
stockStatus = "In Stock"
stockSheet.update({str(stock):stockStatus})
send_message(stockSheet)
yield stock, stockStatus
else:
stockStatus = "Out of Stock"
stockSheet.update({str(stock):stockStatus})
send_message(stockSheet)
yield stock, stockStatus
return stream_with_context(generateStockStatus())
When I ran the app_consumer.py
file, I got the following output:
127.0.0.1 - - [02/Jul/2023 00:28:02] "GET / HTTP/1.1" 200 -
initialize_params - Session ID stored = 69b0e5e8-d5ea-4279-88d1-9653007662d5
emitting event "Send_stock_status" to 69b0e5e8-d5ea-4279-88d1-9653007662d5 [/collectHooks]
127.0.0.1 - - [02/Jul/2023 00:28:05] "POST /consumetasks HTTP/1.1" 200 -
followed by the error:
Error on request:
Traceback (most recent call last):
File "C:\Users\yuanl\AppData\Local\Programs\Python\Python311\Lib\site-packages\werkzeug\serving.py", line 364, in run_wsgi
execute(self.server.app)
File "C:\Users\yuanl\AppData\Local\Programs\Python\Python311\Lib\site-packages\werkzeug\serving.py", line 328, in execute
write(data)
File "C:\Users\yuanl\AppData\Local\Programs\Python\Python311\Lib\site-packages\werkzeug\serving.py", line 296, in write
assert isinstance(data, bytes), "applications must write bytes"
AssertionError: applications must write bytes
Note that both the stock
and stockStatus
variables are of type string
. A sample stockSheet
looks like this:
{'PFMTSHIRT_CN_WM_CLS_L_OLIVEDRAB': 'Out of Stock'}
I initially thought the error was generated by the yield
statements in the generateStockStatus()
function in tasks_consumer.py
. Therefore I tried to change the yield statements to yield str(stock), str(stockStatus)
, yield stock.encode('utf-8'), stockStatus.encode('utf-8')
and yield bytes(stock, 'utf-8'), bytes(stockStatus, 'utf-8')
, however the error persisted.
I then thought the error was generated by the return
statement after the elif request.method == 'GET':
statement in the app_consumer.py
file, therefore I added the .encode()
method on the string returned, however I got a new error:
TypeError: Object of type bytes is not JSON serializable
.
Could anyone point me in the right direction in regards to fixing the error?
You should yield
only one value at a time. Such as this;
yield stock
yield stockStatus
For more information, you can check this answer.