diff --git a/webservice/README.md b/webservice/README.md new file mode 100644 index 0000000..98c0229 --- /dev/null +++ b/webservice/README.md @@ -0,0 +1,8 @@ +# Webservice + +Developer Mode starten: fastapi dev webservice.py + + +## Error Table +| Errorcode | Type | Definition | +| --- | --- | --- | diff --git a/webservice/dbfunctions.py b/webservice/dbfunctions.py index de349b1..f672341 100644 --- a/webservice/dbfunctions.py +++ b/webservice/dbfunctions.py @@ -1,35 +1,23 @@ -import psycopg2 -from psycopg2.extras import execute_values import os +from sqlmodel import create_engine, Session from dotenv import load_dotenv +from models import SensorData, Client +# Lade Umgebungsvariablen load_dotenv() +DB_CONNECTION_STRING = os.getenv("DB_CONNECTION_STRING", "postgresql://user:password@localhost/sensordb") -# Create a database connection -def connectDatabase(): - conn = psycopg2.connect( - host=os.getenv('DBHOST'), - database=os.getenv('DBNAME'), - user=os.getenv('DBUSER'), - password=os.getenv('DBPASSWORD'), - port=os.getenv('DBPORT'),) - conn.autocommit = True - cursor = conn.cursor() - return cursor +# SQLAlchemy Setup +engine = create_engine(DB_CONNECTION_STRING, echo=True) -# Execute database query -def executeQuery(query): - conn = connectDatabase() - conn.execute(query) - result = conn.fetchall() - return result +# Funktion zum Speichern von Sensordaten +def save_sensor_data(db: Session, sensor_data: SensorData): + db.add(sensor_data) # Das Pydantic-Modell kann direkt hinzugefügt werden + db.commit() + db.refresh(sensor_data) # Holt die letzten Informationen des hinzugefügten Eintrags + return sensor_data -# Execute database query without fetching data -def executeWithoutFetch(query): - conn = connectDatabase() - conn.execute(query) - return None - -def bulkInsert(query, data): - conn = connectDatabase() - return execute_values(conn, query, data) \ No newline at end of file +# dbfunctions.py +def get_client_id_by_name(db: Session, client_name: str): + client = db.query(Client).filter(Client.name == client_name).first() + return client.id if client else None # Gibt die clientid zurück oder None, wenn nicht gefunden \ No newline at end of file diff --git a/webservice/models.py b/webservice/models.py new file mode 100644 index 0000000..656cacc --- /dev/null +++ b/webservice/models.py @@ -0,0 +1,33 @@ +from datetime import datetime +from pydantic import BaseModel, Field +from typing import Optional +from sqlmodel import SQLModel, Field + +class MessageOnly(BaseModel): + message: str + timestamp: datetime = Field(default_factory=datetime.now) + +class Client(SQLModel, table=True): + __tablename__ = "clients" # Definiere den Tabellennamen für Clients + id: int = Field(default=None, primary_key=True) + name: str = Field(max_length=50) # Stelle sicher, dass der Name der DB-Struktur entspricht + +class SensorDataIn(BaseModel): # Klasse für den Input (d.h., die API-Dokumentation) + timestamp: datetime = Field(default_factory=datetime.now) + temperature: float = Field(default=None, nullable=True) + humidity: float = Field(default=None, nullable=True) + pressure: float = Field(default=None, nullable=True) + voc: float = Field(default=None, nullable=True) + gas: float = Field(default=None, nullable=True) + + +class SensorData(SQLModel, table=True): + __tablename__ = "sensor_data" + id: int = Field(default=None, primary_key=True) + timestamp: datetime = Field(default_factory=datetime.now) + temperature: float = Field(default=None, nullable=True) + humidity: float = Field(default=None, nullable=True) + pressure: float = Field(default=None, nullable=True) + voc: float = Field(default=None, nullable=True) + gas: float = Field(default=None, nullable=True) + clientid: int = Field(default=None, nullable=True) \ No newline at end of file diff --git a/webservice/requirements.txt b/webservice/requirements.txt index 098b87b..fe47b19 100644 --- a/webservice/requirements.txt +++ b/webservice/requirements.txt @@ -4,4 +4,5 @@ sqlalchemy psycopg2-binary psycopg2-binary==2.9.9 python-dotenv==1.0.0 -requests \ No newline at end of file +requests +sqlmodel \ No newline at end of file diff --git a/webservice/webservice.py b/webservice/webservice.py index 5e3bf83..fb47e28 100644 --- a/webservice/webservice.py +++ b/webservice/webservice.py @@ -1,47 +1,60 @@ # Webservice # INP21b - Timo Weber & Michael von Ah -from fastapi import FastAPI, HTTPException -from pydantic import BaseModel -import psycopg2 -import dbfunctions as db -import requests, secrets, hashlib, re, string, random, os -from datetime import datetime, timedelta, date, time -import json -from datetime import datetime +################ IMPORTS ################ +from fastapi import FastAPI, Depends, HTTPException +from sqlmodel import Session +from dbfunctions import save_sensor_data, get_client_id_by_name, engine +from models import SensorDataIn, SensorData, MessageOnly + -DATABASE_URL = os.getenv("DB_CONNECTION_STRING", "postgresql://user:password@localhost/sensordb") - -""" class apiFunctions: - def __init__(self) -> None: - self.alternativeImage = "https://cdn.ep.neodym.dev/media/20240505-halloween.jpeg" - - def getAttractionList(self): - query = 'SELECT "name", "id", "imageurl", "description" FROM "attraction" ORDER BY "name";' - attractions = db.executeQuery(query) - if attractions: - return attractions - else: - raise Exception("Cannot generate list of attractions. Request invalid") """ - - -#### API #### +################ API ################ app = FastAPI( - title="M241-M245-BBZW-Horizion", + title="M241-M245-BBZW-Horizon", description="BBZW-Horizon", summary="BBZW-Horizon", version="0.0.1" ) -class Session(BaseModel): - username: str = None - token: str = None - message: str = None - timestamp: datetime = datetime.now() - -@app.post("/account/new-session", tags=["account"]) -async def initNewSessionApi(username: str, password: str) -> Session: +# DB Session +def get_db(): + db = Session(bind=engine) try: - return Session(username="username", token="sessionToken", message="Session initiated successfully") + yield db + finally: + db.close() + +# class Session(BaseModel): +# username: str = None +# token: str = None +# message: str = None +# timestamp: datetime = datetime.now() + + + + +# @app.post("/account/new-session", tags=["account"]) +# async def initNewSessionApi(username: str, password: str) -> Session: +# try: +# return Session(username="username", token="sessionToken", message="Session initiated successfully") +# except Exception as error: +# raise HTTPException(status_code=401, detail=f"{error}") + +@app.post("/sensors/push-data", response_model=MessageOnly, tags=["sensors"]) +async def saveNewSensorData(token: str, client: str, data: SensorDataIn, db: Session = Depends(get_db)): + try: + # Ermittle die clientid basierend auf dem Client-Namen + client_id = get_client_id_by_name(db, client) + if client_id is None: + raise HTTPException(status_code=404, detail="Client not found") + + # Erstelle ein SensorData-Objekt für die Datenbank + sensor_data = SensorData(**data.dict()) + sensor_data.clientid = client_id # Setze die clientid aus der DB + + # Speichern der Sensordaten in der Datenbank + save_sensor_data(db, sensor_data) + + return MessageOnly(message="Sensor data saved successfully.") except Exception as error: - raise HTTPException(status_code=401, detail=f"{error}") \ No newline at end of file + raise HTTPException(status_code=500, detail=str(error)) \ No newline at end of file