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 dotenv import find_dotenv, load_dotenv 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 env_file = find_dotenv(".env") load_dotenv("../.env") IS_PROD = os.getenv("PRODUCTION", "") == "true" class Config: MONGO_SERVER: str = os.environ.get("MONGO_SERVER", "") MONGO_DB: str = os.environ.get("MONGO_DB", "") class Document(BaseModel): date: int value: float @asynccontextmanager async def lifespan(app: FastAPI): table = PrettyTable() table.field_names = ["VARIABLE", "VALUE"] table.add_rows( [ [env_variable, os.environ.get(env_variable)] for env_variable in ["MONGO_SERVER", "MONGO_DB"] ] ) print(table) yield app = FastAPI(openapi_url=("/openapi.json" if not IS_PROD else ""), lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) client = motor.motor_asyncio.AsyncIOMotorClient(Config.MONGO_SERVER, 27017) db = client[Config.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": "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")