Files
sensor-web-v2/server-python/app.py
Marco Crapts 4d3644e437
All checks were successful
continuous-integration/drone/push Build is passing
use Pydantic settings
2024-09-06 23:12:31 +03:00

135 lines
3.6 KiB
Python

import json
import os
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from typing import Literal
import motor.motor_asyncio
from bson import json_util
from bson.objectid import ObjectId
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from prettytable import PrettyTable
from pydantic import BaseModel, Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file="../.env")
is_prod: bool = Field(default=False)
mongo_server: str = Field(default="")
mongo_db: str = Field(default="")
class Document(BaseModel):
date: int
value: float
@asynccontextmanager
async def lifespan(app: FastAPI):
table = PrettyTable()
table.field_names = ["VARIABLE", "VALUE"]
table.add_rows(Settings().__dict__.items())
print(table)
yield
app = FastAPI(
openapi_url=("/openapi.json" if not Settings().is_prod else ""), lifespan=lifespan
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
client = motor.motor_asyncio.AsyncIOMotorClient(Settings().mongo_server, 27017)
db = client[Settings().mongo_db]
def object_id_from_date(date: int) -> ObjectId:
timestamp = datetime.fromtimestamp(date, tz=timezone.utc)
object_id = ObjectId.from_datetime(timestamp)
return object_id
@app.get(
"/type/{type}/startDate/{start_date}/endDate/{end_date}/sample/{sample}",
tags=["Retrieve sensor data"],
)
async def get_sensor_data(
type: Literal["temperature", "humidity"],
start_date: int,
end_date: int,
sample: int,
) -> list[Document]:
collection = db["dht22"]
pipeline = [
{
"$match": {
"$and": [
{
"_id": {
"$gt": object_id_from_date(start_date),
"$lt": object_id_from_date(end_date),
}
},
{"type": type},
{"value": {"$ne": float("nan")}},
]
}
},
{
"$group": {
"_id": {
"$toDate": {
"$subtract": [
{"$toLong": {"$toDate": "$_id"}},
{
"$mod": [
{"$toLong": {"$toDate": "$_id"}},
1000 * 60 * sample,
]
},
]
}
},
"value": {"$avg": "$value"},
"date": {"$min": {"$toDate": "$_id"}},
}
},
{
"$project": {
"_id": 0,
"value": {"$round": ["$value", 1]},
"date": {"$toLong": "$date"},
}
},
{"$sort": {"date": 1}},
]
docs = [doc async for doc in collection.aggregate(pipeline)]
return json.loads(json_util.dumps(docs))
index = FileResponse(path="../dist/index.html", headers={"Cache-Control": "no-cache"})
@app.middleware("http")
async def add_default_404(request: Request, call_next):
response = await call_next(request)
if response.status_code == 404:
return index
else:
return response
app.mount("/", StaticFiles(directory="../dist", html=True), name="dist")