Add initial server implementation
This commit is contained in:
commit
e0136a3517
13
fgs/__init__.py
Normal file
13
fgs/__init__.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///./data.sqlite'
|
||||
app.secret_key = os.environ.get('FGS_SECRET_KEY') or 'default secret key'
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
from .views import *
|
||||
from .model import *
|
||||
from .jwt import *
|
32
fgs/jwt.py
Normal file
32
fgs/jwt.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
import jwt
|
||||
from flask import request, g, abort
|
||||
from . import db, app
|
||||
from .model import User
|
||||
from functools import wraps
|
||||
|
||||
def get_jwt(self):
|
||||
return jwt.encode({'id': self.id}, app.secret_key, algorithm='HS256')
|
||||
|
||||
User.get_jwt = get_jwt
|
||||
|
||||
def jwt_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if 'Authorization' not in request.headers:
|
||||
abort(400)
|
||||
authorization = request.headers['Authorization'].split(' ')
|
||||
if len(authorization) < 2:
|
||||
abort(400)
|
||||
|
||||
try:
|
||||
decoded = jwt.decode(
|
||||
authorization[1].encode(),
|
||||
app.secret_key,
|
||||
algorithms = ['HS256'])
|
||||
g.user = User.query.filter_by(id=decoded['id']).first()
|
||||
if g.user is None: abort(400)
|
||||
return f(*args, **kwargs)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
abort(500)
|
||||
return decorated_function
|
48
fgs/model.py
Normal file
48
fgs/model.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
from . import db
|
||||
import bcrypt
|
||||
|
||||
class Collar(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String)
|
||||
active = db.Column(db.Boolean)
|
||||
data_points = db.relationship('DataPoint')
|
||||
|
||||
class DataPoint(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
collar_id = db.Column(db.Integer, db.ForeignKey('collar.id'))
|
||||
longitude = db.Column(db.Float(precision=10))
|
||||
latitude = db.Column(db.Float(precision=10))
|
||||
battery_level = db.Column(db.Integer)
|
||||
datetime = db.Column(db.DateTime)
|
||||
outside = db.Column(db.Boolean)
|
||||
|
||||
def to_dict(self):
|
||||
return {'longitude': self.longitude,
|
||||
'latitude': self.latitude,
|
||||
'battery_level': self.battery_level,
|
||||
'datetime': self.datetime,
|
||||
'outside': self.outside
|
||||
}
|
||||
|
||||
class StimulusActivation(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
longitude = db.Column(db.Float(precision=10))
|
||||
latitude = db.Column(db.Float(precision=10))
|
||||
volume_level = db.Column(db.Integer)
|
||||
voltage_level = db.Column(db.Integer)
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String)
|
||||
password_hash = db.Column(db.String)
|
||||
|
||||
def get_password(self):
|
||||
return self.password_hash
|
||||
|
||||
def set_password(self, password):
|
||||
self.password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
|
||||
|
||||
def check_password(self, password):
|
||||
return bcrypt.checkpw(password.encode(), self.password_hash)
|
||||
|
||||
password = property(get_password, set_password)
|
52
fgs/views.py
Normal file
52
fgs/views.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
from . import app
|
||||
from .jwt import jwt_required
|
||||
from .model import *
|
||||
from flask import g, jsonify, request, abort
|
||||
from sqlalchemy import func
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return 'Hello, world!'
|
||||
|
||||
@app.route('/login', methods=['POST'])
|
||||
def login():
|
||||
username = request.form.get('username')
|
||||
password = request.form.get('password')
|
||||
if username is None or password is None: abort(400)
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if user is None or not user.check_password(password): abort(400)
|
||||
return jsonify({ 'token': user.get_jwt().decode() })
|
||||
|
||||
@app.route('/me')
|
||||
@jwt_required
|
||||
def me():
|
||||
return jsonify({ 'username' : g.user.username })
|
||||
|
||||
@app.route('/collars')
|
||||
@jwt_required
|
||||
def collars():
|
||||
active_collars = Collar.query.\
|
||||
filter_by(active=True).\
|
||||
join(DataPoint, isouter=True).\
|
||||
order_by(DataPoint.datetime.desc()).\
|
||||
group_by(Collar.id).\
|
||||
with_entities(
|
||||
Collar.id, Collar.name,
|
||||
DataPoint.longitude, DataPoint.latitude).\
|
||||
all()
|
||||
active_collars = [
|
||||
{'id': id, 'name': name, 'pos': {'longitude': lo, 'latitude': la}}
|
||||
for (id, name, lo, la) in active_collars]
|
||||
return jsonify(active_collars)
|
||||
|
||||
@app.route('/collars/<int:id>/history')
|
||||
@jwt_required
|
||||
def collar_history(id):
|
||||
collar = Collar.query.filter_by(id=id).first()
|
||||
if collar is None: abort(404)
|
||||
|
||||
data_points = DataPoint.query.\
|
||||
filter_by(collar_id=id).\
|
||||
order_by(DataPoint.datetime.desc()).\
|
||||
all()
|
||||
return jsonify([point.to_dict() for point in data_points])
|
Loading…
Reference in New Issue
Block a user