From ff8a13295b39565e5c71fe983b37e21e00686fb8 Mon Sep 17 00:00:00 2001
From: Antoine Gaudron-desjardins <antoine.gaudrondesjardins@student-cs.fr>
Date: Mon, 4 Jul 2022 15:29:24 +0200
Subject: [PATCH] creation and connexion of db with the app

---
 .gitignore                                    |   1 +
 backend/.env.template                         |   9 +++
 backend/{app/api.py => app.py}                |  59 +++++++++++++-----
 .../{app/model => assets}/keras_metadata.pb   |   0
 backend/{app/model => assets}/saved_model.pb  | Bin
 .../variables/variables.data-00000-of-00001   | Bin
 .../variables/variables.index                 | Bin
 backend/{app => db}/__init__.py               |   0
 backend/db/crud.py                            |  20 ++++++
 backend/db/database.py                        |  18 ++++++
 backend/db/models.py                          |  17 +++++
 backend/db/schemas.py                         |  21 +++++++
 backend/docker-compose.yml                    |  11 ++++
 backend/main.py                               |   6 +-
 backend/utils/__init__.py                     |   0
 backend/{app => }/utils/preprocessing.py      |   0
 16 files changed, 146 insertions(+), 16 deletions(-)
 create mode 100644 backend/.env.template
 rename backend/{app/api.py => app.py} (54%)
 rename backend/{app/model => assets}/keras_metadata.pb (100%)
 rename backend/{app/model => assets}/saved_model.pb (100%)
 rename backend/{app/model => assets}/variables/variables.data-00000-of-00001 (100%)
 rename backend/{app/model => assets}/variables/variables.index (100%)
 rename backend/{app => db}/__init__.py (100%)
 create mode 100644 backend/db/crud.py
 create mode 100644 backend/db/database.py
 create mode 100644 backend/db/models.py
 create mode 100644 backend/db/schemas.py
 create mode 100644 backend/docker-compose.yml
 create mode 100644 backend/utils/__init__.py
 rename backend/{app => }/utils/preprocessing.py (100%)

diff --git a/.gitignore b/.gitignore
index ad6020b..de88a2b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
 node_modules/
 build/
 env/
+.env
 __pycache__
\ No newline at end of file
diff --git a/backend/.env.template b/backend/.env.template
new file mode 100644
index 0000000..87b242d
--- /dev/null
+++ b/backend/.env.template
@@ -0,0 +1,9 @@
+MYSQL_DATABASE=eatfast
+MYSQL_USER=user
+MYSQL_PASSWORD=password
+MYSQL_ROOT_PASSWORD=rootpassword
+
+DB_HOST=localhost
+DB_PORT=3306
+
+WEB_ROOT=http://localhost:8000
\ No newline at end of file
diff --git a/backend/app/api.py b/backend/app.py
similarity index 54%
rename from backend/app/api.py
rename to backend/app.py
index 6082912..24562b5 100644
--- a/backend/app/api.py
+++ b/backend/app.py
@@ -1,29 +1,57 @@
-import cv2
-import numpy as np
-import keras
-
-from fastapi import FastAPI
+from typing import List
+from fastapi import Body, Depends, FastAPI
 from fastapi.middleware.cors import CORSMiddleware
+from sqlalchemy.orm import Session
+from dotenv import load_dotenv
+import os
 
-from utils.preprocessing import fix_singular_shape, norm_by_imagenet
+from db import crud, schemas, database
 
 
-app = FastAPI()
+# load environment variables
+load_dotenv()
+
+app = FastAPI(docs_url="/api/docs", openapi_url="/api/openapi.json")
 
 origins = [
-    "http://localhost:3000",
-    "localhost:3000"
+    os.getenv('WEB_ROOT'),
 ]
 
-
 app.add_middleware(
-        CORSMiddleware,
-        allow_origins=origins,
-        allow_credentials=True,
-        allow_methods=["*"],
-        allow_headers=["*"]
+    CORSMiddleware,
+    allow_origins=origins,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"]
 )
 
+def get_db():
+    """Create a database session."""
+    db = database.SessionLocal()
+    try:
+        yield db
+    finally:
+        db.close()
+
+
+@app.get('/api/{place}', response_model=List[schemas.Record])
+async def eatfast(place: str, db: Session = Depends(get_db)):
+    return crud.get_records(place, db)
+
+
+@app.post('/api/create', response_model=schemas.Record)
+async def post(record: schemas.RecordBase = Body(...), db: Session = Depends(get_db)):
+    return crud.create_record(record, db)
+
+
+"""
+import cv2
+import numpy as np
+import keras
+
+from utils.preprocessing import fix_singular_shape, norm_by_imagenet
+
+
 model = keras.models.load_model('model')
 
 # contours of the zone of a picture that should be analyzed by the model
@@ -49,3 +77,4 @@ async def estimate_(id: str) -> float:
     pred_map = np.squeeze(model.predict(input_image))
     count_prediction = np.sum(pred_map)
     return count_prediction
+"""
\ No newline at end of file
diff --git a/backend/app/model/keras_metadata.pb b/backend/assets/keras_metadata.pb
similarity index 100%
rename from backend/app/model/keras_metadata.pb
rename to backend/assets/keras_metadata.pb
diff --git a/backend/app/model/saved_model.pb b/backend/assets/saved_model.pb
similarity index 100%
rename from backend/app/model/saved_model.pb
rename to backend/assets/saved_model.pb
diff --git a/backend/app/model/variables/variables.data-00000-of-00001 b/backend/assets/variables/variables.data-00000-of-00001
similarity index 100%
rename from backend/app/model/variables/variables.data-00000-of-00001
rename to backend/assets/variables/variables.data-00000-of-00001
diff --git a/backend/app/model/variables/variables.index b/backend/assets/variables/variables.index
similarity index 100%
rename from backend/app/model/variables/variables.index
rename to backend/assets/variables/variables.index
diff --git a/backend/app/__init__.py b/backend/db/__init__.py
similarity index 100%
rename from backend/app/__init__.py
rename to backend/db/__init__.py
diff --git a/backend/db/crud.py b/backend/db/crud.py
new file mode 100644
index 0000000..b90c047
--- /dev/null
+++ b/backend/db/crud.py
@@ -0,0 +1,20 @@
+"""
+Module to interact with the database
+"""
+from sqlalchemy.orm import Session
+from db import models, schemas
+
+
+def get_records(place: str, db: Session):
+    """ Get all the records for the given place """
+    records = db.query(models.Records).filter(models.Records.place == place).order_by(models.Records.date.desc()).all()
+    return records
+
+
+def create_record(new_record: schemas.RecordBase, db: Session):
+    """ Add a new record to the database """
+    db_record = models.Records(**new_record.dict())
+    db.add(db_record)
+    db.commit()
+    db.refresh(db_record)
+    return db_record
\ No newline at end of file
diff --git a/backend/db/database.py b/backend/db/database.py
new file mode 100644
index 0000000..6f99502
--- /dev/null
+++ b/backend/db/database.py
@@ -0,0 +1,18 @@
+"""
+Database connection
+"""
+from dotenv import load_dotenv
+from sqlalchemy import create_engine
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+import os
+
+# load environment variables
+load_dotenv()
+SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{os.getenv('MYSQL_USER')}:{os.getenv('MYSQL_PASSWORD')}@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('MYSQL_DATABASE')}?charset=utf8"
+
+engine = create_engine(SQLALCHEMY_DATABASE_URL)
+SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+
+Base = declarative_base()
+
diff --git a/backend/db/models.py b/backend/db/models.py
new file mode 100644
index 0000000..55820de
--- /dev/null
+++ b/backend/db/models.py
@@ -0,0 +1,17 @@
+"""
+Models of the database for magasin app
+"""
+from sqlalchemy import Column, Integer, DateTime, Float, Interval, String
+
+from db.database import Base
+
+
+class Records(Base):
+    """Records sql table model"""
+    __tablename__ = "records"
+
+    id = Column(Integer, primary_key=True, index=True)
+    place = Column(String(10))
+    date = Column(DateTime)
+    density = Column(Float)
+    waiting_time = Column(Interval)
\ No newline at end of file
diff --git a/backend/db/schemas.py b/backend/db/schemas.py
new file mode 100644
index 0000000..600efc7
--- /dev/null
+++ b/backend/db/schemas.py
@@ -0,0 +1,21 @@
+"""
+Pydantic schemas for the magasin app
+"""
+from typing import Optional
+from datetime import datetime, timedelta
+from pydantic import BaseModel, Field
+
+
+class RecordBase(BaseModel):
+    """Records base schema"""
+    place: str = Field(..., title="Name of the RU corresponding the given record")
+    date: datetime = Field(..., title="Date of the record")
+    density: float = Field(..., title="Estimated density of people")
+    waiting_time: Optional[timedelta] = Field(title="Estimated waiting time for people coming at this date")
+    
+class Record(RecordBase):
+    """Database records schema"""
+    id: int
+
+    class Config:
+        orm_mode = True
\ No newline at end of file
diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml
new file mode 100644
index 0000000..abb808e
--- /dev/null
+++ b/backend/docker-compose.yml
@@ -0,0 +1,11 @@
+version: "3.3"
+
+services:
+  db:
+    image: mysql
+    container_name: "db"
+    restart: always
+    env_file: .env
+    command: ["mysqld", "--authentication-policy=mysql_native_password"]
+    ports:
+      - "3306:3306"
\ No newline at end of file
diff --git a/backend/main.py b/backend/main.py
index 1994264..9ad9db2 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -1,5 +1,9 @@
 import uvicorn
+from db import models, database
 
 
+# Database creation
+models.Base.metadata.create_all(bind=database.engine)
+
 if __name__ == "__main__":
-    uvicorn.run("app.api:app", host="0.0.0.0", port=8000, reload=True)
+    uvicorn.run("app:app", host="0.0.0.0", port=3000, reload=True)
diff --git a/backend/utils/__init__.py b/backend/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/app/utils/preprocessing.py b/backend/utils/preprocessing.py
similarity index 100%
rename from backend/app/utils/preprocessing.py
rename to backend/utils/preprocessing.py
-- 
GitLab