ttn-tracker/flask_app/app.py
2021-12-04 10:35:09 -05:00

209 lines
6.6 KiB
Python

import datetime
import json
import logging
import os
import requests
from apscheduler.schedulers.background import BackgroundScheduler
from dateutil.parser import parser
from flask import Flask
from flask import jsonify
from flask import render_template
from flask_sqlalchemy import SQLAlchemy
from math import atan2
from math import cos
from math import radians
from math import sin
from math import sqrt
from config import app_key
from config import application
from config import bing_api_key
from config import cluster
from config import config_app
from config import devices
from config import gateway_locations
from config import path_db
from config import refresh_period_seconds
from config import start_lat
from config import start_lon
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
logger = logging.getLogger(__name__)
app = config_app(Flask(__name__, template_folder="./templates"))
db = SQLAlchemy(app)
def schedule_get_new_data():
get_new_data()
scheduler = BackgroundScheduler()
job = scheduler.add_job(schedule_get_new_data, 'interval', days=1)
scheduler.start()
class Location(db.Model):
__tablename__ = "location"
id = db.Column(db.Integer, primary_key=True)
added_at = db.Column(db.DateTime, default=datetime.datetime.now)
device_id = db.Column(db.String(250))
raw = db.Column(db.String(250))
datetime = db.Column(db.String(250))
datetime_obj = db.Column(db.DateTime)
latitude = db.Column(db.String(250))
longitude = db.Column(db.String(250))
altitude = db.Column(db.Integer)
hdop = db.Column(db.Float)
def __repr__(self):
return '<ID %r>' % self.id
@property
def serialize(self):
"""Return object data in easily serializeable format"""
return {
'device_id': self.device_id,
'datetime': self.datetime,
'latitude': self.latitude,
'longitude': self.longitude,
'altitude': self.altitude,
'hdop': self.hdop
}
class LastAcquisition(db.Model):
__tablename__ = "last_data"
id = db.Column(db.Integer, primary_key=True)
last_datetime = db.Column(db.DateTime)
def __repr__(self):
return '<ID %r>' % self.id
if not os.path.exists(path_db):
db.create_all()
@app.route('/map')
def main_page():
get_new_data()
return render_template('map.html',
bing_api_key=bing_api_key,
devices=devices,
gateway_locations=gateway_locations,
location_data=Location.query.all(),
refresh_period_seconds=refresh_period_seconds,
start_lat=start_lat,
start_lon=start_lon)
@app.route('/past/<seconds>')
def get_past_data(seconds):
if seconds_from_last() > 10:
get_new_data()
if seconds == '0':
markers = Location.query.all()
else:
past_dt_object = datetime.datetime.now() - datetime.timedelta(seconds=int(seconds))
markers = Location.query.filter(Location.added_at > past_dt_object).all()
return jsonify([i.serialize for i in markers])
def get_new_data():
last_seconds = seconds_from_last()
if last_seconds:
past_seconds = int(last_seconds) + 1
else:
past_seconds = 604800 # 7 days, max The Things Network storage allows
for each_device in devices:
endpoint = "https://{cluster_loc}.cloud.thethings.network/api/v3/as/applications/{app}/devices/{dev}/packages/storage/uplink_message?order=-received_at&type=uplink_message?last={time}".format(
cluster_loc=cluster, app=application, dev=each_device, time="{}s".format(past_seconds))
logger.info(endpoint)
key = 'Bearer {}'.format(app_key)
headers = {'Accept': 'text/event-stream', 'Authorization': key}
response = requests.get(endpoint, headers=headers)
if response.status_code != 200:
logger.info(response.reason)
try:
response_format = "{\"data\": [" + response.text.replace("\n\n", ",")[:-1] + "]}"
response_data = json.loads(response_format)
uplink_msg = response_data["data"]
for each_resp in uplink_msg:
response_data = each_resp["result"]
uplink_message = response_data["uplink_message"]
received = response_data["received_at"]
lat = uplink_message["decoded_payload"].get("latitude", "")
lon = uplink_message["decoded_payload"].get("longitude", "")
alt = uplink_message["decoded_payload"].get("altitude", "")
qos = uplink_message["decoded_payload"].get("hdop", "")
end_device_ids = response_data["end_device_ids"]
device = end_device_ids["device_id"]
rawpay = uplink_message["frm_payload"]
if (not Location.query.filter(Location.datetime == received).first() and
-90 < float(lat) <= 90 and -120 <= float(lon) <= 80):
logger.info("{}, {}".format(lat, lon))
new_location = Location(
device_id=device,
raw=rawpay,
datetime_obj=parser().parse(received),
datetime=received,
latitude=lat,
longitude=lon,
altitude=alt,
hdop=qos)
db.session.add(new_location)
db.session.commit()
logger.info(new_location)
except:
pass
set_date_now()
def set_date_now():
date_last = LastAcquisition.query.first()
if not date_last:
new_last = LastAcquisition(last_datetime=datetime.datetime.now())
db.session.add(new_last)
db.session.commit()
else:
date_last.last_datetime = datetime.datetime.now()
db.session.commit()
def seconds_from_last():
date_last = LastAcquisition.query.first()
if date_last:
return (datetime.datetime.now() - date_last.last_datetime).total_seconds()
def distance_coordinates(lat1, lon1, lat2, lon2):
# approximate radius of earth in km
R = 6373.0
lat1 = radians(lat1)
lon1 = radians(lon1)
lat2 = radians(lat2)
lon2 = radians(lon2)
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
distance = R * c
return distance # km
if __name__ == '__main__':
db.create_all()
app.run(debug=True, host='0.0.0.0', port=8000)