diff --git a/StreamServer/src/config.py b/StreamServer/src/config.py index c7306ea..0746d60 100644 --- a/StreamServer/src/config.py +++ b/StreamServer/src/config.py @@ -28,4 +28,7 @@ CONFIG_FILE = config('CONFIG_FILE') YOLO_WEIGHT_FILE = config('YOLO_WEIGHT_FILE') SPPE_WEIGHT_FILE = config('SPPE_WEIGHT_FILE') TSSTG_WEIGHT_FILE = config('TSSTG_WEIGHT_FILE') -LINE_NOTIFY_TOKEN = config('LINE_NOTIFY_TOKEN') \ No newline at end of file +LINE_NOTIFY_TOKEN = config('LINE_NOTIFY_TOKEN') +WEATHER_API_KEY = config('WEATHER_API_KEY') +LAT = config('LAT') +LON = config('LON') \ No newline at end of file diff --git a/StreamServer/src/query/prediction.py b/StreamServer/src/query/prediction.py new file mode 100644 index 0000000..9f13d2b --- /dev/null +++ b/StreamServer/src/query/prediction.py @@ -0,0 +1,33 @@ +from datetime import datetime, timedelta + +from sqlalchemy import func +from sqlalchemy.orm import Session + +from models import PredictionData + + +def get_temp_prediction_data(session: Session): + """Get indoor temperature prediction for the next five days""" + current_time = datetime.now() + limit_day = current_time + timedelta(days=5) + + return session.query( + PredictionData.timestamp, PredictionData.indoor_temp + ).filter( + PredictionData.timestamp >= current_time, + PredictionData.timestamp < limit_day + ).order_by(PredictionData.timestamp.desc()).all() + + +def get_feature_prediction_data(session: Session): + """Get all features used to predict indoor temperature for the next five days""" + current_time = datetime.now() + limit_day = current_time + timedelta(days=5) + + return session.query( + PredictionData.timestamp, PredictionData.outdoor_temp, PredictionData.outdoor_feels_like, + PredictionData.outdoor_pressure, PredictionData.outdoor_humidity + ).filter( + PredictionData.timestamp >= current_time, + PredictionData.timestamp < limit_day + ).order_by(PredictionData.timestamp.desc()).all() \ No newline at end of file diff --git a/StreamServer/src/routers/prediction.py b/StreamServer/src/routers/prediction.py new file mode 100644 index 0000000..382a330 --- /dev/null +++ b/StreamServer/src/routers/prediction.py @@ -0,0 +1,78 @@ +import httpx + +from datetime import datetime + +from sqlalchemy import func +from sqlalchemy.orm import Session + +from fastapi import APIRouter, Depends, HTTPException, status + +from analytic.health.indoor_model import XgboostIndoorModel +from config import WEATHER_API_KEY, LAT, LON +from database import SessionLocal +from scheme import IndoorTemperature +from models import PredictionData +from query.prediction import get_temp_prediction_data, get_feature_prediction_data + +router = APIRouter() + + +#Dependency +def get_db(): + db = SessionLocal() + try : + yield db + finally: + db.close() + +# Load prediction data -> FOUND -> Return +# NOT FOUND +# Try load data from database -> Load future data from database -> predict -> RETURN +# -> Not found -> Load data from api -> Predict/Save to database -> RETURN + + +def _fetch_data_from_api() -> dict: + # api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key} + url = f"https://api.openweathermap.org/data/2.5/forecast?lat={LAT}&lon={LON}&units=metric&appid={WEATHER_API_KEY}" + response = httpx.get(url) + return response.json() + + +@router.get("/indoor/predict/", response_model=list[IndoorTemperature]) +async def get_tomorrow_indoor_temp(db: Session = Depends(get_db)): + result = get_temp_prediction_data(db) + if not result: + features = get_feature_prediction_data(db) + xgboost = XgboostIndoorModel() + if not features: + datas = _fetch_data_from_api() + # Save to database + if datas: + results = [] + for data in datas['list']: + ts = datetime.fromtimestamp(data['dt']) + data = data['main'] + features = [data['temp'], data['feels_like'], data['pressure'], data['humidity']] + result = xgboost.predict(features) + results.append(IndoorTemperature(indoor_temp=result, timestamp=ts)) + new_data_entry = PredictionData( + timestamp=ts, + indoor_temp=result, + outdoor_temp=data['temp'], + outdoor_feels_like=data['feels_like'], + outdoor_pressure=data['pressure'], + outdoor_humidity=data['humidity'] + ) + db.add(new_data_entry) + db.commit() + result = results + else: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to fetch data from API" + ) + else: + for i in features: + indoor = xgboost.predict([i.outdoor_temp, i.outdoor_feels_like, i.outdoor_pressure, i.outdoor_humidity]) + result.append(IndoorTemperature(timestamp=i.timestamp, indoor_temp=indoor)) + return result