137 lines
4.3 KiB
Python
137 lines
4.3 KiB
Python
import os
|
|||
|
|
|
||
|
|
from flask import Flask, jsonify, render_template, request
|
||
|
|
from sqlalchemy import func
|
||
|
|
|
||
|
|
from models import Sensor, SensorReadout, db
|
||
|
|
|
||
|
|
|
||
|
|
def create_app():
|
||
|
|
app = Flask(__name__)
|
||
|
|
|
||
|
|
db_path = os.path.join(os.path.dirname(__file__), "data.db")
|
||
|
|
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}"
|
||
|
|
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
||
|
|
|
||
|
|
db.init_app(app)
|
||
|
|
|
||
|
|
@app.route("/sensors")
|
||
|
|
def sensors_page():
|
||
|
|
return render_template("sensors.html")
|
||
|
|
|
||
|
|
@app.route("/api/docs")
|
||
|
|
def api_docs():
|
||
|
|
return render_template("docs.html")
|
||
|
|
|
||
|
|
@app.route("/api/sensors")
|
||
|
|
def api_sensors():
|
||
|
|
sensors = Sensor.query.order_by(Sensor.name).all()
|
||
|
|
return jsonify([{"id": s.id, "name": s.name} for s in sensors])
|
||
|
|
|
||
|
|
@app.route("/api/sensors/<int:sensor_id>/types")
|
||
|
|
def api_sensor_types(sensor_id):
|
||
|
|
types = (
|
||
|
|
db.session.query(SensorReadout.type)
|
||
|
|
.filter_by(sensor_id=sensor_id)
|
||
|
|
.distinct()
|
||
|
|
.order_by(SensorReadout.type)
|
||
|
|
.all()
|
||
|
|
)
|
||
|
|
return jsonify([t[0] for t in types])
|
||
|
|
|
||
|
|
@app.route("/api/sensors/<int:sensor_id>/readouts")
|
||
|
|
def api_sensor_readouts(sensor_id):
|
||
|
|
type_ = request.args.get("type")
|
||
|
|
query = SensorReadout.query.filter_by(sensor_id=sensor_id)
|
||
|
|
if type_:
|
||
|
|
query = query.filter_by(type=type_)
|
||
|
|
readouts = query.order_by(SensorReadout.timestamp.desc()).limit(300).all()
|
||
|
|
return jsonify(
|
||
|
|
[
|
||
|
|
{"value": r.value, "timestamp": r.timestamp.isoformat()}
|
||
|
|
for r in readouts
|
||
|
|
]
|
||
|
|
)
|
||
|
|
|
||
|
|
@app.route("/api/sensors/<int:sensor_id>/readouts/hourly")
|
||
|
|
def api_sensor_readouts_hourly(sensor_id):
|
||
|
|
type_ = request.args.get("type")
|
||
|
|
query = (
|
||
|
|
db.session.query(
|
||
|
|
func.strftime("%Y-%m-%dT%H:00:00", SensorReadout.timestamp).label("hour"),
|
||
|
|
func.avg(SensorReadout.value).label("avg_value"),
|
||
|
|
)
|
||
|
|
.filter(SensorReadout.sensor_id == sensor_id)
|
||
|
|
)
|
||
|
|
if type_:
|
||
|
|
query = query.filter(SensorReadout.type == type_)
|
||
|
|
rows = (
|
||
|
|
query.group_by("hour")
|
||
|
|
.order_by("hour")
|
||
|
|
.all()
|
||
|
|
)
|
||
|
|
return jsonify(
|
||
|
|
[{"timestamp": r.hour, "value": round(r.avg_value, 2)} for r in rows]
|
||
|
|
)
|
||
|
|
|
||
|
|
@app.route("/api/sensors", methods=["POST"])
|
||
|
|
def api_create_sensor():
|
||
|
|
data = request.get_json(silent=True)
|
||
|
|
if not data or "name" not in data:
|
||
|
|
return jsonify({"error": "name is required"}), 400
|
||
|
|
|
||
|
|
name = data["name"].strip()
|
||
|
|
if not name:
|
||
|
|
return jsonify({"error": "name cannot be empty"}), 400
|
||
|
|
|
||
|
|
if Sensor.query.filter_by(name=name).first():
|
||
|
|
return jsonify({"error": "sensor already exists"}), 409
|
||
|
|
|
||
|
|
sensor = Sensor(name=name)
|
||
|
|
db.session.add(sensor)
|
||
|
|
db.session.commit()
|
||
|
|
return jsonify({"id": sensor.id, "name": sensor.name}), 201
|
||
|
|
|
||
|
|
@app.route("/api/sensors/<int:sensor_id>/readouts", methods=["POST"])
|
||
|
|
def api_create_readout(sensor_id):
|
||
|
|
sensor = db.session.get(Sensor, sensor_id)
|
||
|
|
if not sensor:
|
||
|
|
return jsonify({"error": "sensor not found"}), 404
|
||
|
|
|
||
|
|
data = request.get_json(silent=True)
|
||
|
|
if not data:
|
||
|
|
return jsonify({"error": "request body is required"}), 400
|
||
|
|
|
||
|
|
missing = [f for f in ("type", "value") if f not in data]
|
||
|
|
if missing:
|
||
|
|
return jsonify({"error": f"missing fields: {', '.join(missing)}"}), 400
|
||
|
|
|
||
|
|
try:
|
||
|
|
value = float(data["value"])
|
||
|
|
except (TypeError, ValueError):
|
||
|
|
return jsonify({"error": "value must be a number"}), 400
|
||
|
|
|
||
|
|
readout = SensorReadout(
|
||
|
|
sensor_id=sensor_id,
|
||
|
|
type=str(data["type"]).strip(),
|
||
|
|
value=value,
|
||
|
|
)
|
||
|
|
db.session.add(readout)
|
||
|
|
db.session.commit()
|
||
|
|
return jsonify(
|
||
|
|
{
|
||
|
|
"id": readout.id,
|
||
|
|
"sensor_id": readout.sensor_id,
|
||
|
|
"type": readout.type,
|
||
|
|
"value": readout.value,
|
||
|
|
"timestamp": readout.timestamp.isoformat(),
|
||
|
|
}
|
||
|
|
), 201
|
||
|
|
|
||
|
|
return app
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
app = create_app()
|
||
|
|
app.run(debug=True, host='0.0.0.0')
|