Обновление диаграммы и базы данных в Flask / Chart js - PullRequest
0 голосов
/ 24 февраля 2020

Я делаю веб-приложение в python, которое читает температуру и влажность. Необходимо каждую минуту считывать датчик и сохранять значения в базе данных в sqlite. На веб-странице я представляю данные в виде графика, используя javascript (диаграмма. js). Цель, которой я не достиг, состоит в том, чтобы график обновлялся по минутам. Таким образом, каждое измерение обновляет график, который я вижу на веб-странице.

Как есть, это работает, но я думаю, что я неправильно использую соединения с базой данных, поскольку у меня было много ошибок параллелизма с базой данных, но я все еще не не знаю, как правильно реализовать это. Я знаю, что не могу использовать базу данных из разных потоков, но единственное обнаруженное мною исправление - запуск сеанса каждый раз, когда я что-то ищу.

Итак, мои вопросы: есть ли хороший способ использовать База данных действовать аналогично тому, что я имею? И самое главное, как мне отправлять данные на javascript каждый раз, когда я делаю измерение? Я нашел веб-сокеты, опросы различных типов и Quart в качестве альтернативы - flask, а также socketIO и . Насколько я понял из многих вопросов здесь, Long Polling устарел (но ответ на многие вопросы), и веб-розетки - это путь к go. Должен ли я использовать socketIO или Quart или просто постоянный javascript опрос для этого простого приложения?

Это то, что я использую в настоящее время:

#!/usr/bin/python3

# Web Server Gateway WSGI
from flask import Flask, render_template, make_response, request #, redirect, url_for

from databasis import Databasis

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

from flask import Flask, render_template, request, redirect, url_for
from datetime import datetime, timedelta, timezone

# _________
# SETUP:

# Flask webserver app
app = Flask(__name__, static_url_path='/static')

# Scheduler Setup
jobStores = { 'default': SQLAlchemyJobStore(url='sqlite:////home/thermostat/alarms.db'), }
scheduler = BackgroundScheduler(jobstores=jobStores)
scheduler.start()

# measurement database setup
databasis = Databasis()



# _________
# server routes

@app.route("/")
def index():
    meas = databasis.getLastNmeasures(10)
    temps = [pt.temperature for pt in meas]
    hums = [pt.humidity for pt in meas]
    datetimes = [pt.timestamp for pt in meas]
    templateData = {
        'time' : meas[0].timestamp,
        'temps' : temps,
        'humidities' : hums,
        'datetimes' : datetimes,
    }
    return render_template('index.html', **templateData)


def measureTH():
    global databasis
    temp = 25 # Here goes a sensor
    hum =  55
    databasis.addTHMeasurement(temp,hum)
    meas = databasis.getLastNmeasures(5)

    print("temperature {:.3f} deg C".format(meas[0].temperature))
    print("humidity {:.3f} %".format(meas[0].humidity))
    return 'hello world'


scheduler.add_job(measureTH, 'cron',\
        day_of_week='*',hour='*',minute='*',\
        id=str(1), replace_existing=True,\
        args=[]) 

# _________
# main function
if __name__ == "__main__":
    app.debug = True
    app.use_debugger = True
    app.use_reloader = False
    app.run(host='0.0.0.0', port=80, threaded=False)

# _________
# on exit

и класс данных:

#!/usr/bin/python3

from sqlalchemy import Column, Integer, Float, DateTime, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from datetime import datetime, timedelta


Base = declarative_base()

class Databasis():
    def __init__(self):
        # Create an engine that stores data in the .db file.
        engine = create_engine('sqlite:////home/Documents/projects/measurements.db')
        Base.metadata.bind = engine
        Base.metadata.create_all(engine)
        DBSession = sessionmaker(bind=engine)
        Session = scoped_session(DBSession)
        self.session = Session

    def addTHMeasurement(self,temp,hum):
        entry = TempHumMeasurement(\
            timestamp=datetime.now(),\
            temperature=temp,\
            humidity=hum,\
            location='room1')
        session = self.session()
        session.add(entry)
        session.commit()
        self.session.remove()
        return

    def getLastNDays(self,n):
        starttime = datetime.now() - timedelta(days=n)
        return self.session.query(TempHumMeasurement).\
            filter(TempHumMeasurement.timestamp>starttime).all()

    def getLastNmeasures(self,n):
        return self.session.query(TempHumMeasurement).\
            order_by(TempHumMeasurement.timestamp.desc()).\
            limit(n).all()


# Define a Table in the database
class TempHumMeasurement(Base):
    __tablename__  = 'temphummeasurement'
    id = Column(Integer, primary_key = True)
    timestamp = Column(DateTime(timezone=True))
    temperature = Column('temperature', Float)
    humidity = Column('humidity', Float)
    location = Column('location', String(32))

и, наконец, индекс. html

<!DOCTYPE html>
<head>
  <title>Temperature & Humidity History</title>
  <link rel="stylesheet" type="text/css" href='../static/style.css'/>
  <script src="static/Chart.min.js"></script>
</head>
<body>
  <div class="content">
    <h1>Temperature</h1>
    <h3> Last measurement {{ temps[0] }} deg at {{ time.strftime("%d %m %Y, %H:%M") }}  
      <a href="/" class="button">REFRESH</a></h3>
  </div>
  <div class="content">
    <canvas id="temperature" width="600" height="400"></canvas>
  </div>
    <script>
      var chartData = {
        labels : [
          {% for item in datetimes %}
            "{{ item }}",
          {% endfor %}
        ],

        datasets : [{
          fillColor: "rgba(151,187,205,0.2)",
          strokeColor: "rgba(151,187,205,1)",
          pointColor: "rgba(151,187,205,1)",
          data : [
            {% for item in temps %}
              "{{ item }}",
            {% endfor %}
          ]
        }
        ]
      }

      var ctx = document.getElementById("temperature").getContext("2d");
      steps = 4
      max = 3

      //draw chart
      var temperature = new  Chart(ctx, {
        type: 'line',
        data: chartData,
      });

      function addData(chart, label, data) {
        chart.data.labels.push(label);
        chart.data.datasets.forEach((dataset) => {
          dataset.data.push(data);
        });
        chart.update();
      }
    </script>
</body>
</html>
...