Wednesday 1 January 2020

django mpld3 live stock price plot triggered by html post request

4 clients live plot 

#views

from django.shortcuts import render
import matplotlib.pyplot as plt
from mpld3 import plugins, fig_to_html, save_html, fig_to_dict
import json
import numpy as np
from datetime import datetime
from yahoo_fin import stock_info as si
import pickle
import codecs

class NumpyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.ndarray):
            return obj.tolist()
        return json.JSONEncoder.default(self, obj)

def index(request):
    return render(request, 'mpld/index.html')

#receive data from client -> unpack data -> add new data -> pack data -> send data to client
#server support multiple clients simultaneously; however as clients' data grows with time, server runs out memory quickly
def live_price(request):
    fig1, ax1 = plt.subplots()

    #load data from client
    x, y = [], []
    try:
        client_data_base64 = request.POST['client_data']
        print('client data base64: ', client_data_base64, '\n')
        client_data_binary = codecs.decode(client_data_base64.encode(), 'base64')
        print('client data binary: ', client_data_binary, '\n')
        client_data = pickle.loads(client_data_binary)
        print('client data: ', client_data, '\n')
        x, y = client_data['x'], client_data['y']
    except Exception as e:
        print(e)
        pass

    #append new data
    x.append(datetime.now())
    price = si.get_live_price('aapl')
    y.append(price)
    print('xarray, yarray: ', x, y, '\n')

    #format data to base64 string 
    server_data_binary = pickle.dumps({'x': x, 'y': y})
    print('server data binary: ', server_data_binary, '\n')
    server_data_base64 = codecs.encode(server_data_binary, 'base64').decode()
    print('server data base64: ', server_data_base64, '\n')

    if len(x) < 2:
        return render(request, 'mpld/live_price.html',
                      {'serverData': server_data_base64
                      })

    ax1.plot(x, y)
    ax1.grid(color='lightgray', alpha=0.7)
    ax1.set_xlabel('time', fontsize=15)
    ax1.set_ylabel('price', fontsize=15)
    ax1.set_title('Live Apple stock Price', fontsize=20)

    #save_html(fig, 'fig.html')
    g1 = json.dumps(fig_to_dict(fig1), cls=NumpyEncoder)

    fig1.clf()

    #send data to client
    return render(request, 'mpld/live_price.html',
                  {'graph1': g1,
                   'serverData': server_data_base64,
                   })

-------------------------------
#live_price.html


{% extends 'mpld/base.html' %}
{% block head %}
    <!--refresh page every second
    <meta http-equiv="refresh" content="1" />-->
{% endblock %}
{% block body %}
    <div id="fig1">graph1</div>

    <form action="{% url 'mpld:live_price' %}" method="post">
        {% csrf_token %}

        <textarea name="client_data" style="display:none;">{{serverData | safe}}</textarea>

        <input type="submit" id="submitButton" value="Submit">
    </form>

    <script type="text/javascript">
        try {
            mpld3.draw_figure("fig1", {{graph1 | safe}});
         }
         catch(error){}

        window.onload = function(){
            setTimeout(function(){
                document.getElementById('submitButton').click();
            }, 1000);
        };
    </script>
{% endblock %}

---------------------------
#base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    {% block head %}
    {% endblock %}
    <title>MPLD3</title>
    <!--bootstrap-->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
</head>
<body>
    <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
    <script type="text/javascript" src="http://mpld3.github.io/js/mpld3.v0.2.js"></script>

     {% block body %}
    {% endblock %}
</body>
</html>

------------------------------
#logs

[02/Jan/2020 16:22:23] "POST /mpld/live_price/ HTTP/1.1" 200 4713
client data base64:  gAN9cQAoWAEAAAB4cQFdcQIoY2RhdGV0aW1lCmRhdGV0aW1lCnEDQwoH5AECEBYRATc9cQSFcQVS
cQZoA0MKB+QBAhAWEwjfgnEHhXEIUnEJaANDCgfkAQIQFhUHD5VxCoVxC1JxDGgDQwoH5AECEBYX
BP3vcQ2FcQ5ScQ9lWAEAAAB5cRBdcREoY251bXB5LmNvcmUubXVsdGlhcnJheQpzY2FsYXIKcRJj
bnVtcHkKZHR5cGUKcRNYAgAAAGY4cRRLAEsBh3EVUnEWKEsDWAEAAAA8cRdOTk5K/////0r/////
SwB0cRhiQwgAAACgmcVyQHEZhnEaUnEbaBJoFkMIAAAAoJnFckBxHIZxHVJxHmgSaBZDCAAAAKCZ
xXJAcR+GcSBScSFoEmgWQwgAAACgmcVyQHEihnEjUnEkZXUu


client data binary:  b'\x80\x03}q\x00(X\x01\x00\x00\x00xq\x01]q\x02(cdatetime\ndatetime\nq\x03C\n\x07\xe4\x01\x02\x10\x16\x11\x017=q\x04\x85q\x05Rq\x06h\x03C\n\x07\xe4\x01\x02\x10\x16\x13\x08\xdf\x82q\x07\x85q\x08Rq\th\x03C\n\x07\xe4\x01\x02\x10\x16\x15\x07\x0f\x95q\n\x85q\x0bRq\x0ch\x03C\n\x07\xe4\x01\x02\x10\x16\x17\x04\xfd\xefq\r\x85q\x0eRq\x0feX\x01\x00\x00\x00yq\x10]q\x11(cnumpy.core.multiarray\nscalar\nq\x12cnumpy\ndtype\nq\x13X\x02\x00\x00\x00f8q\x14K\x00K\x01\x87q\x15Rq\x16(K\x03X\x01\x00\x00\x00<q\x17NNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00tq\x18bC\x08\x00\x00\x00\xa0\x99\xc5r@q\x19\x86q\x1aRq\x1bh\x12h\x16C\x08\x00\x00\x00\xa0\x99\xc5r@q\x1c\x86q\x1dRq\x1eh\x12h\x16C\x08\x00\x00\x00\xa0\x99\xc5r@q\x1f\x86q Rq!h\x12h\x16C\x08\x00\x00\x00\xa0\x99\xc5r@q"\x86q#Rq$eu.'

client data:  {'x': [datetime.datetime(2020, 1, 2, 16, 22, 17, 79677), datetime.datetime(2020, 1, 2, 16, 22, 19, 581506), datetime.datetime(2020, 1, 2, 16, 22, 21, 462741), datetime.datetime(2020, 1, 2, 16, 22, 23, 327151)], 'y': [300.3500061035156, 300.3500061035156, 300.3500061035156, 300.3500061035156]}

xarray, yarray:  [datetime.datetime(2020, 1, 2, 16, 22, 17, 79677), datetime.datetime(2020, 1, 2, 16, 22, 19, 581506), datetime.datetime(2020, 1, 2, 16, 22, 21, 462741), datetime.datetime(2020, 1, 2, 16, 22, 23, 327151), datetime.datetime(2020, 1, 2, 16, 22, 25, 157144)] [300.3500061035156, 300.3500061035156, 300.3500061035156, 300.3500061035156, 300.3500061035156]

server data binary:  b'\x80\x03}q\x00(X\x01\x00\x00\x00xq\x01]q\x02(cdatetime\ndatetime\nq\x03C\n\x07\xe4\x01\x02\x10\x16\x11\x017=q\x04\x85q\x05Rq\x06h\x03C\n\x07\xe4\x01\x02\x10\x16\x13\x08\xdf\x82q\x07\x85q\x08Rq\th\x03C\n\x07\xe4\x01\x02\x10\x16\x15\x07\x0f\x95q\n\x85q\x0bRq\x0ch\x03C\n\x07\xe4\x01\x02\x10\x16\x17\x04\xfd\xefq\r\x85q\x0eRq\x0fh\x03C\n\x07\xe4\x01\x02\x10\x16\x19\x02e\xd8q\x10\x85q\x11Rq\x12eX\x01\x00\x00\x00yq\x13]q\x14(cnumpy.core.multiarray\nscalar\nq\x15cnumpy\ndtype\nq\x16X\x02\x00\x00\x00f8q\x17K\x00K\x01\x87q\x18Rq\x19(K\x03X\x01\x00\x00\x00<q\x1aNNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00tq\x1bbC\x08\x00\x00\x00\xa0\x99\xc5r@q\x1c\x86q\x1dRq\x1eh\x15h\x19C\x08\x00\x00\x00\xa0\x99\xc5r@q\x1f\x86q Rq!h\x15h\x19C\x08\x00\x00\x00\xa0\x99\xc5r@q"\x86q#Rq$h\x15h\x19C\x08\x00\x00\x00\xa0\x99\xc5r@q%\x86q&Rq\'h\x15h\x19C\x08\x00\x00\x00\xa0\x99\xc5r@q(\x86q)Rq*eu.'

server data base64:  gAN9cQAoWAEAAAB4cQFdcQIoY2RhdGV0aW1lCmRhdGV0aW1lCnEDQwoH5AECEBYRATc9cQSFcQVS
cQZoA0MKB+QBAhAWEwjfgnEHhXEIUnEJaANDCgfkAQIQFhUHD5VxCoVxC1JxDGgDQwoH5AECEBYX
BP3vcQ2FcQ5ScQ9oA0MKB+QBAhAWGQJl2HEQhXERUnESZVgBAAAAeXETXXEUKGNudW1weS5jb3Jl
Lm11bHRpYXJyYXkKc2NhbGFyCnEVY251bXB5CmR0eXBlCnEWWAIAAABmOHEXSwBLAYdxGFJxGShL
A1gBAAAAPHEaTk5OSv////9K/////0sAdHEbYkMIAAAAoJnFckBxHIZxHVJxHmgVaBlDCAAAAKCZ
xXJAcR+GcSBScSFoFWgZQwgAAACgmcVyQHEihnEjUnEkaBVoGUMIAAAAoJnFckBxJYZxJlJxJ2gV
aBlDCAAAAKCZxXJAcSiGcSlScSpldS4=


[02/Jan/2020 16:22:25] "POST /mpld/live_price/ HTTP/1.1" 200 4816
client data base64:  gAN9cQAoWAEAAAB4cQFdcQIoY2RhdGV0aW1lCmRhdGV0aW1lCnEDQwoH5AECEBYRATc9cQSFcQVS
cQZoA0MKB+QBAhAWEwjfgnEHhXEIUnEJaANDCgfkAQIQFhUHD5VxCoVxC1JxDGgDQwoH5AECEBYX
BP3vcQ2FcQ5ScQ9oA0MKB+QBAhAWGQJl2HEQhXERUnESZVgBAAAAeXETXXEUKGNudW1weS5jb3Jl
Lm11bHRpYXJyYXkKc2NhbGFyCnEVY251bXB5CmR0eXBlCnEWWAIAAABmOHEXSwBLAYdxGFJxGShL
A1gBAAAAPHEaTk5OSv////9K/////0sAdHEbYkMIAAAAoJnFckBxHIZxHVJxHmgVaBlDCAAAAKCZ
xXJAcR+GcSBScSFoFWgZQwgAAACgmcVyQHEihnEjUnEkaBVoGUMIAAAAoJnFckBxJYZxJlJxJ2gV
aBlDCAAAAKCZxXJAcSiGcSlScSpldS4=


client data binary:  b'\x80\x03}q\x00(X\x01\x00\x00\x00xq\x01]q\x02(cdatetime\ndatetime\nq\x03C\n\x07\xe4\x01\x02\x10\x16\x11\x017=q\x04\x85q\x05Rq\x06h\x03C\n\x07\xe4\x01\x02\x10\x16\x13\x08\xdf\x82q\x07\x85q\x08Rq\th\x03C\n\x07\xe4\x01\x02\x10\x16\x15\x07\x0f\x95q\n\x85q\x0bRq\x0ch\x03C\n\x07\xe4\x01\x02\x10\x16\x17\x04\xfd\xefq\r\x85q\x0eRq\x0fh\x03C\n\x07\xe4\x01\x02\x10\x16\x19\x02e\xd8q\x10\x85q\x11Rq\x12eX\x01\x00\x00\x00yq\x13]q\x14(cnumpy.core.multiarray\nscalar\nq\x15cnumpy\ndtype\nq\x16X\x02\x00\x00\x00f8q\x17K\x00K\x01\x87q\x18Rq\x19(K\x03X\x01\x00\x00\x00<q\x1aNNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00tq\x1bbC\x08\x00\x00\x00\xa0\x99\xc5r@q\x1c\x86q\x1dRq\x1eh\x15h\x19C\x08\x00\x00\x00\xa0\x99\xc5r@q\x1f\x86q Rq!h\x15h\x19C\x08\x00\x00\x00\xa0\x99\xc5r@q"\x86q#Rq$h\x15h\x19C\x08\x00\x00\x00\xa0\x99\xc5r@q%\x86q&Rq\'h\x15h\x19C\x08\x00\x00\x00\xa0\x99\xc5r@q(\x86q)Rq*eu.'

client data:  {'x': [datetime.datetime(2020, 1, 2, 16, 22, 17, 79677), datetime.datetime(2020, 1, 2, 16, 22, 19, 581506), datetime.datetime(2020, 1, 2, 16, 22, 21, 462741), datetime.datetime(2020, 1, 2, 16, 22, 23, 327151), datetime.datetime(2020, 1, 2, 16, 22, 25, 157144)], 'y': [300.3500061035156, 300.3500061035156, 300.3500061035156, 300.3500061035156, 300.3500061035156]}

xarray, yarray:  [datetime.datetime(2020, 1, 2, 16, 22, 17, 79677), datetime.datetime(2020, 1, 2, 16, 22, 19, 581506), datetime.datetime(2020, 1, 2, 16, 22, 21, 462741), datetime.datetime(2020, 1, 2, 16, 22, 23, 327151), datetime.datetime(2020, 1, 2, 16, 22, 25, 157144), datetime.datetime(2020, 1, 2, 16, 22, 27, 170437)] [300.3500061035156, 300.3500061035156, 300.3500061035156, 300.3500061035156, 300.3500061035156, 300.3500061035156]

server data binary:  b'\x80\x03}q\x00(X\x01\x00\x00\x00xq\x01]q\x02(cdatetime\ndatetime\nq\x03C\n\x07\xe4\x01\x02\x10\x16\x11\x017=q\x04\x85q\x05Rq\x06h\x03C\n\x07\xe4\x01\x02\x10\x16\x13\x08\xdf\x82q\x07\x85q\x08Rq\th\x03C\n\x07\xe4\x01\x02\x10\x16\x15\x07\x0f\x95q\n\x85q\x0bRq\x0ch\x03C\n\x07\xe4\x01\x02\x10\x16\x17\x04\xfd\xefq\r\x85q\x0eRq\x0fh\x03C\n\x07\xe4\x01\x02\x10\x16\x19\x02e\xd8q\x10\x85q\x11Rq\x12h\x03C\n\x07\xe4\x01\x02\x10\x16\x1b\x02\x99\xc5q\x13\x85q\x14Rq\x15eX\x01\x00\x00\x00yq\x16]q\x17(cnumpy.core.multiarray\nscalar\nq\x18cnumpy\ndtype\nq\x19X\x02\x00\x00\x00f8q\x1aK\x00K\x01\x87q\x1bRq\x1c(K\x03X\x01\x00\x00\x00<q\x1dNNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00tq\x1ebC\x08\x00\x00\x00\xa0\x99\xc5r@q\x1f\x86q Rq!h\x18h\x1cC\x08\x00\x00\x00\xa0\x99\xc5r@q"\x86q#Rq$h\x18h\x1cC\x08\x00\x00\x00\xa0\x99\xc5r@q%\x86q&Rq\'h\x18h\x1cC\x08\x00\x00\x00\xa0\x99\xc5r@q(\x86q)Rq*h\x18h\x1cC\x08\x00\x00\x00\xa0\x99\xc5r@q+\x86q,Rq-h\x18h\x1cC\x08\x00\x00\x00\xa0\x99\xc5r@q.\x86q/Rq0eu.'

server data base64:  gAN9cQAoWAEAAAB4cQFdcQIoY2RhdGV0aW1lCmRhdGV0aW1lCnEDQwoH5AECEBYRATc9cQSFcQVS
cQZoA0MKB+QBAhAWEwjfgnEHhXEIUnEJaANDCgfkAQIQFhUHD5VxCoVxC1JxDGgDQwoH5AECEBYX
BP3vcQ2FcQ5ScQ9oA0MKB+QBAhAWGQJl2HEQhXERUnESaANDCgfkAQIQFhsCmcVxE4VxFFJxFWVY
AQAAAHlxFl1xFyhjbnVtcHkuY29yZS5tdWx0aWFycmF5CnNjYWxhcgpxGGNudW1weQpkdHlwZQpx
GVgCAAAAZjhxGksASwGHcRtScRwoSwNYAQAAADxxHU5OTkr/////Sv////9LAHRxHmJDCAAAAKCZ
xXJAcR+GcSBScSFoGGgcQwgAAACgmcVyQHEihnEjUnEkaBhoHEMIAAAAoJnFckBxJYZxJlJxJ2gY
aBxDCAAAAKCZxXJAcSiGcSlScSpoGGgcQwgAAACgmcVyQHErhnEsUnEtaBhoHEMIAAAAoJnFckBx
LoZxL1JxMGV1Lg==

reference:
https://vsupalov.com/avoid-csrf-errors-axios-django/
https://chuanshuoge2.blogspot.com/2019/03/django-13-form.html
https://github.com/axios/axios

pickle codecs
https://stackoverflow.com/questions/30469575/how-to-pickle-and-unpickle-to-portable-string-in-python-3


No comments:

Post a Comment