Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • raptornythorink/eatfast-website
1 result
Select Git revision
Loading items
Show changes
Commits on Source (40)
Showing
with 654 additions and 303 deletions
MYSQL_DATABASE=eatfast
MYSQL_USER=user
MYSQL_PASSWORD=password
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=
MYSQL_USER=
MYSQL_PASSWORD=
MYSQL_ROOT_PASSWORD=
DB_HOST=localhost
DB_PORT=3306
CLIENT_ID=
CLIENT_SECRET=
API_ROOT=http://localhost:3001/api
WEB_ROOT=http://localhost:3000
AUTH_ROOT=https://auth.viarezo.fr
CAM_NUMBER=1
CAM_0_PLACE=local
CAM_0_IP=
CAM_0_USER=
CAM_0_PASSWORD=
CAM_0_STREAM=stream1
CAM_0_A_FACTOR=30
CAM_0_B_FACTOR=120
CAM_0_FRAMEGAP=150
CAM_0_POINTS_NB=7
CAM_0_POINT_0=70, 370
CAM_0_POINT_1=420, 720
CAM_0_POINT_2=1280, 720
CAM_0_POINT_3=1280, 250
CAM_0_POINT_4=930, 215
CAM_0_POINT_5=450, 550
CAM_0_POINT_6=130, 350
\ No newline at end of file
......@@ -4,6 +4,8 @@ Module to interact with the database
from datetime import date, datetime, time, timedelta
from sqlalchemy.orm import Session
from sqlalchemy.sql import func
from uuid import uuid4
import secrets
import pytz
from db import models, schemas
......@@ -15,36 +17,50 @@ def get_waiting_time(place: str, db: Session):
""" Get the last estimated waiting time for the given place """
current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
weekday, current_time = current_date.weekday(), current_date.time()
data = {"status": False, "waiting_time": None, "next_timetable": None}
first_timeslot = get_timeslot(place, weekday, True, db)
if first_timeslot and current_time < first_timeslot[0]:
return first_timeslot[0].hour, first_timeslot[0].minute
data["next_timetable"] = "{:d}h{:02d}".format(
first_timeslot[0].hour, first_timeslot[0].minute)
return data
elif first_timeslot and current_time <= first_timeslot[1]:
waiting_time = db.query(
last_record = db.query(
models.Records.waiting_time
).filter(
models.Records.place == place
).order_by(
models.Records.date.desc()
).first()
waiting_time_minutes = round(waiting_time[0].total_seconds() / 60)
return waiting_time_minutes, None
if last_record:
waiting_time = last_record.waiting_time
waiting_time = round(waiting_time.total_seconds() / 60)
data["status"] = True
data["waiting_time"] = waiting_time
return data
second_timeslot = get_timeslot(place, weekday, False, db)
if second_timeslot and current_time < second_timeslot[0]:
return second_timeslot[0].hour, second_timeslot[0].minute
data["next_timetable"] = "{:d}h{:02d}".format(
second_timeslot[0].hour, second_timeslot[0].minute)
return data
elif second_timeslot and current_time <= second_timeslot[1]:
waiting_time = db.query(
last_record = db.query(
models.Records.waiting_time
).filter(
models.Records.place == place
).order_by(
models.Records.date.desc()
).first()
waiting_time_minutes = round(waiting_time[0].total_seconds() / 60)
return waiting_time_minutes, None
return None, None
if last_record:
waiting_time = last_record.waiting_time
waiting_time = round(waiting_time.total_seconds() / 60)
data["status"] = True
data["waiting_time"] = waiting_time
return data
return data
def get_avg_graph_points(place: str, weekday: int, min_time: time, max_time: time, interval: timedelta, db: Session):
def get_avg_graph_points(place: str, weekday: int, min_time: time,
max_time: time, interval: timedelta, db: Session):
""" Get the average waiting time for each interval between two time steps """
def shift_time(t: time, delta: timedelta):
......@@ -75,7 +91,7 @@ def get_avg_graph_points(place: str, weekday: int, min_time: time, max_time: tim
def add_slot(slots_list, start_time, end_time):
average_waiting_time = avg_time_query(start_time, end_time)
if average_waiting_time:
name = f'{start_time.hour:02}h{start_time.minute:02}'
name = 60 * start_time.hour + start_time.minute
slots_list.append({'name': name, 'time': average_waiting_time})
stats = []
......@@ -94,14 +110,16 @@ def get_avg_graph(place: str, db: Session):
weekday, current_time = current_date.weekday(), current_date.time()
first_timeslot = get_timeslot(place, weekday, True, db)
if first_timeslot and current_time <= first_timeslot[1]:
return get_avg_graph_points(place, weekday, first_timeslot[0], first_timeslot[1], timedelta(minutes=5), db)
return get_avg_graph_points(
place, weekday, first_timeslot[0], first_timeslot[1], timedelta(minutes=5), db)
second_timeslot = get_timeslot(place, weekday, False, db)
if second_timeslot and current_time <= second_timeslot[1]:
return get_avg_graph_points(place, weekday, second_timeslot[0], second_timeslot[1], timedelta(minutes=5), db)
return None
return []
def get_current_graph_points(place: str, current_date: date, min_time: time, max_time: time, interval: timedelta, db: Session):
def get_current_graph_points(place: str, current_date: date,
min_time: time, max_time: time, interval: timedelta, db: Session):
""" Get the waiting time for each interval between two time steps for the current timeslot """
def shift_time(t: time, delta: timedelta):
......@@ -134,7 +152,7 @@ def get_current_graph_points(place: str, current_date: date, min_time: time, max
def add_slot(slots_list, start_time, end_time):
average_waiting_time = avg_time_query(start_time, end_time)
if average_waiting_time:
name = f'{start_time.hour:02}h{start_time.minute:02}'
name = 60 * start_time.hour + start_time.minute
slots_list.append({'name': name, 'time': average_waiting_time})
stats = []
......@@ -149,48 +167,60 @@ def get_current_graph_points(place: str, current_date: date, min_time: time, max
def get_current_graph(place: str, db: Session):
""" Get the waiting_time_graph for the current timeslot"""
current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
weekday, day, current_time = current_date.weekday(), current_date.date(), current_date.time()
weekday, day, current_time = current_date.weekday(
), current_date.date(), current_date.time()
first_timeslot = get_timeslot(place, weekday, True, db)
if first_timeslot and current_time <= first_timeslot[0]:
return None
return [], None, None
elif first_timeslot and current_time <= first_timeslot[1]:
return get_current_graph_points(place, day, first_timeslot[0], current_time, timedelta(minutes=5), db)
points = get_current_graph_points(place, day, first_timeslot[0], current_time, timedelta(minutes=5), db)
start_time = 60 * first_timeslot[0].hour + first_timeslot[0].minute
end_time = 60 * first_timeslot[1].hour + first_timeslot[1].minute
return points, start_time, end_time
second_timeslot = get_timeslot(place, weekday, False, db)
if second_timeslot and current_time <= second_timeslot[0]:
return None
return [], None, None
elif second_timeslot and current_time <= second_timeslot[1]:
return get_current_graph_points(place, day, second_timeslot[0], current_time, timedelta(minutes=5), db)
return None
points = get_current_graph_points(place, day, second_timeslot[0], current_time, timedelta(minutes=5), db)
start_time = 60 * second_timeslot[0].hour + second_timeslot[0].minute
end_time = 60 * second_timeslot[1].hour + second_timeslot[1].minute
return points, start_time, end_time
return [], None, None
# Define CRUD operation for the comments
def get_comments(place: str, page: int, db: Session):
""" Get the 10 last comments for the given place """
""" Get the 20 last comments for the given place """
if page == 0:
comments = db.query(models.Comments).order_by(models.Comments.published_at.desc(), models.Comments.id.desc()).all()
comments = db.query(
models.Comments).order_by(
models.Comments.published_at.desc(),
models.Comments.id.desc()).all()
else:
comments = db.query(
models.Comments).filter(
models.Comments,
models.Users.username).join(
models.Users).filter(
models.Comments.place == place).order_by(
models.Comments.published_at.desc(),
models.Comments.id.desc()).slice(
(page -
1) *
10,
20,
page *
10).all()
return comments
20).all()
return list(schemas.Comment(**comment.__dict__, username=username) for comment, username in comments)
def create_comment(place: str, new_comments: schemas.CommentBase, db: Session):
def create_comment(user: schemas.User, place: str, new_comments: schemas.CommentBase, db: Session):
""" Add a new comment to the database """
date = datetime.now(tz=pytz.timezone("Europe/Paris"))
db_comment = models.Comments(**new_comments.dict(), published_at=date, place=place)
db_comment = models.Comments(**new_comments.dict(), published_at=date, place=place, user_id=user.id)
db.add(db_comment)
db.commit()
db.refresh(db_comment)
return db_comment
return schemas.Comment(**db_comment.__dict__, username=user.username)
def delete_comment(id: int, db: Session):
......@@ -206,7 +236,10 @@ def delete_comment(id: int, db: Session):
def get_news(place: str, db: Session):
""" Get the news for the given place """
news = db.query(models.News).filter(models.News.place == place).order_by(models.News.published_at.desc()).all()
news = db.query(
models.News).filter(
models.News.place == place).order_by(
models.News.published_at.desc()).all()
return news
......@@ -234,10 +267,7 @@ def delete_news(id: int, db: Session):
def get_opening_hours(place: str, db: Session):
""" Get the opening hours for the given place """
opening_hours = db.query(
models.OpeningHours.day,
models.OpeningHours.timeslot,
models.OpeningHours.open_time,
models.OpeningHours.close_time,
models.OpeningHours
).filter(
models.OpeningHours.place == place
).order_by(
......@@ -259,7 +289,8 @@ def get_timeslot(place: str, day: int, timeslot: bool, db: Session):
return opening_hours
def create_opening_hours(new_opening_hours: schemas.OpeningHoursBase, db: Session):
def create_opening_hours(
new_opening_hours: schemas.OpeningHoursBase, db: Session):
""" Add opening hours to the database """
db_opening_hours = models.OpeningHours(**new_opening_hours.dict())
db.add(db_opening_hours)
......@@ -273,7 +304,9 @@ def delete_opening_hours(id: int, db: Session):
if id == 0:
db.query(models.OpeningHours).delete()
else:
db.query(models.OpeningHours).filter(models.OpeningHours.id == id).delete()
db.query(
models.OpeningHours).filter(
models.OpeningHours.id == id).delete()
db.commit()
......@@ -282,7 +315,9 @@ def delete_opening_hours(id: int, db: Session):
def get_restaurants(db: Session):
current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
weekday, current_time = current_date.weekday(), current_date.time()
restaurant_names = [r.place for r in db.query(models.OpeningHours.place).distinct()]
restaurant_names = [
r.place for r in db.query(
models.OpeningHours.place).distinct()]
restaurants = []
for name in restaurant_names:
......@@ -311,18 +346,80 @@ def get_restaurants(db: Session):
restaurant["timeslot"] = "-"
if restaurant["status"]:
waiting_time = db.query(
models.Records.waiting_time
last_record = db.query(
models.Records
).filter(
models.Records.place == name
).order_by(
models.Records.date.desc()
).first()
waiting_time_minutes = round(waiting_time[0].total_seconds() / 60)
restaurant["waiting_time"] = waiting_time_minutes
if last_record:
waiting_time = last_record.waiting_time
restaurant["waiting_time"] = round(
waiting_time.total_seconds() / 60)
else:
restaurant["waiting_time"] = None
else:
restaurant["waiting_time"] = None
restaurants.append(restaurant)
return restaurants
# Define CRUD operation for the authentication
def init_user(db: Session):
""" Add a news to the database """
cookie = uuid4()
state = secrets.token_urlsafe(30)
expiration_date = datetime.now(tz=pytz.timezone("Europe/Paris")) + timedelta(minutes=10)
db_user = models.Users(state=state, cookie=cookie, expiration_date=expiration_date)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
def get_user(cookie: str, db: Session):
""" Get user infos """
user = db.query(models.Users).filter(models.Users.cookie == cookie).one()
if pytz.timezone("Europe/Paris").localize(user.expiration_date) < datetime.now(tz=pytz.timezone("Europe/Paris")):
return
return user
def delete_state(user: schemas.User, db: Session):
""" Delete the state of a user """
user.state = None
db.add(user)
db.commit()
def update_user(user: schemas.User, user_info: dict, db: Session):
full_name = f"{user_info['firstName']} {user_info['lastName']}"
expiration_date = datetime.now(tz=pytz.timezone("Europe/Paris")) + timedelta(days=3)
existing_user = db.query(models.Users).filter(models.Users.username == full_name).first()
if existing_user:
existing_user.cookie = user.cookie
existing_user.expiration_date = expiration_date
db.delete(user)
db.add(existing_user)
db.commit()
db.refresh(existing_user)
return existing_user
else:
user.username = full_name
user.expiration_date = expiration_date
db.add(user)
db.commit()
db.refresh(user)
return user
def end_session(cookie: str, db: Session):
user = db.query(models.Users).filter(models.Users.cookie == cookie).one()
user.expiration_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
db.add(user)
db.commit()
return
"""
Models of the database for magasin app
"""
from sqlalchemy import Column, Integer, DateTime, Float, Interval, String, Text, Boolean, Time
from sqlalchemy import Column, ForeignKey, Integer, DateTime, Float, Interval, String, Text, Boolean, Time
from sqlalchemy.orm import relationship
from db.database import Base
......@@ -22,6 +23,7 @@ class Comments(Base):
__tablename__ = "comments"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"))
content = Column(Text)
published_at = Column(DateTime)
place = Column(String(30))
......@@ -49,3 +51,15 @@ class OpeningHours(Base):
timeslot = Column(Boolean)
open_time = Column(Time)
close_time = Column(Time)
class Users(Base):
""" User sql table model """
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
state = Column(String(50))
username = Column(String(50))
cookie = Column(String(50))
expiration_date = Column(DateTime)
comments = relationship("Comments")
......@@ -8,10 +8,12 @@ from pydantic import BaseModel, Field
class RecordBase(BaseModel):
"""Records base schema"""
place: str = Field(..., title="Name of the RU corresponding the given record")
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")
waiting_time: Optional[timedelta] = Field(
title="Estimated waiting time for people coming at this date")
class Record(RecordBase):
......@@ -28,8 +30,9 @@ class CommentBase(BaseModel):
class Comment(CommentBase):
"""Database comments base schema"""
"""Comments reading schema"""
id: int
username: str = Field(..., title="Name of the user posting the comment")
published_at: datetime = Field(..., title="Publication date of the comment")
place: str = Field(..., title="Name of the RU corresponding the comment")
......@@ -56,9 +59,11 @@ class News(NewsBase):
class OpeningHoursBase(BaseModel):
"""Database opening_hours base schema"""
place: str = Field(..., title="Name of the RU corresponding the given record")
place: str = Field(...,
title="Name of the RU corresponding the given record")
day: int = Field(..., title="Day of the week")
timeslot: bool = Field(..., title="Service slot (True for midday, False for evening)")
timeslot: bool = Field(...,
title="Service slot (True for midday, False for evening)")
open_time: time = Field(..., title="Opening time")
close_time: time = Field(..., title="Closing time")
......@@ -69,3 +74,12 @@ class OpeningHours(OpeningHoursBase):
class Config:
orm_mode = True
class User(BaseModel):
"""Database user base schema"""
id: int
state: str
username: str
cookie: str
expiration_date: datetime
......@@ -7,9 +7,11 @@ from fastapi import Depends
from sqlalchemy.orm import Session
from db import schemas
from typing import List
from threading import Thread
from db import database, models
from routers import stats, comments, news
from routers import *
from video_capture import handle_cameras
app = FastAPI(docs_url="/api/docs", openapi_url="/api/openapi.json")
......@@ -30,24 +32,28 @@ app.add_middleware(
@app.on_event("startup")
def on_startup():
async def on_startup():
# Database creation
models.Base.metadata.create_all(bind=database.engine)
t = Thread(target=handle_cameras)
t.start()
# Integration of routers
app.include_router(stats.router)
app.include_router(comments.router)
app.include_router(news.router)
app.include_router(authentication.router)
@app.get('/api/records', response_model=List[schemas.Record])
async def get_records(place: str, db: Session = Depends(get_db)):
return db.query(models.Records).filter(models.Records == place).order_by(models.Records.date.desc()).all()
return db.query(models.Records).filter(models.Records.place ==
place).order_by(models.Records.date.desc()).all()
@app.post('/api/records', response_model=schemas.Record)
async def stats(record: schemas.RecordBase, db: Session = Depends(get_db)):
async def post_records(record: schemas.RecordBase, db: Session = Depends(get_db)):
db_record = models.Records(**record.dict())
db.add(db_record)
db.commit()
......@@ -56,46 +62,10 @@ async def stats(record: schemas.RecordBase, db: Session = Depends(get_db)):
@app.delete('/api/records', response_model=None)
async def stats(id: str, db: Session = Depends(get_db)):
async def del_records(id: int, db: Session = Depends(get_db)):
if id == 0:
db.query(models.Records).delete()
else:
db.query(models.Records).filter(models.Records.id == id).delete()
db.commit()
return
"""
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
contours = {
'eiffel': [[70, 370], [420, 720], [1280, 720], [1280, 250], [930, 215], [450, 550], [130, 350]]
}
masks = {}
for key, polygon in contours.items():
mask = np.zeros((1280, 720, 3), dtype=np.unit8)
cv2.fillPoly(mask, [polygon], (255, 255, 255))
masks[key] = mask
@app.get("/estimate/{id}")
async def estimate_(id: str) -> float:
# img = fetch(...)
img = np.zeros((1280, 720, 3))
resized_img = cv2.cvtColor(cv2.resize(img, (1280, 720)), cv2.COLOR_BGR2RGB).astype(np.float32)
masked_img = cv2.bitwise_and(resized_img, mask[id])
treated_img = fix_singular_shape(masked_img, 16)
input_image = np.expand_dims(np.squeeze(norm_by_imagenet([treated_img])), axis=0)
pred_map = np.squeeze(model.predict(input_image))
count_prediction = np.sum(pred_map)
return count_prediction
"""
from . import authentication
from . import comments
from . import news
from . import opening_hours
from . import stats
from fastapi import APIRouter, Cookie, HTTPException, Depends
from fastapi.responses import RedirectResponse
from sqlalchemy.orm import Session
from typing import Optional, Union
from requests import get, post
from urllib.parse import urlencode
from dotenv import load_dotenv
import os
from db.database import get_db
from db import crud
# load environment variables
load_dotenv("../.env")
router = APIRouter(prefix="/api/auth", tags=["auth"])
@router.get("/")
async def whoami(connect_id: str = Cookie(...), db: Session = Depends(get_db)):
user = crud.get_user(connect_id, db)
return user.username
@router.get("/login")
async def login(code: Optional[str] = None, state: Optional[str] = None, connect_id: Union[str, None] = Cookie(default=None), db: Session = Depends(get_db)):
if not code:
init_user = crud.init_user(db)
params = urlencode({
"client_id": os.getenv("CLIENT_ID"),
"redirect_uri": f"{os.getenv('API_ROOT')}/auth/login",
"response_type": "code",
"state": init_user.state,
"scope": "default"})
response = RedirectResponse(f"{os.getenv('AUTH_ROOT')}/oauth/authorize?{params}")
response.set_cookie(key="connect_id", value=init_user.cookie)
return response
if not connect_id or not state:
raise HTTPException(status_code=403, detail="Cookie Invalid")
user = crud.get_user(connect_id, db)
if not user:
raise HTTPException(status_code=599, detail="Timeout error")
if user.state != state:
raise HTTPException(status_code=403, detail="State Invalid")
crud.delete_state(user, db)
headers = {"content-type": "application/x-www-form-urlencoded"}
data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": f"{os.getenv('API_ROOT')}/auth/login",
"client_id": os.getenv("CLIENT_ID"),
"client_secret": os.getenv("CLIENT_SECRET"),
}
token_response = post(
f"{os.getenv('AUTH_ROOT')}/oauth/token",
data=data,
headers=headers
)
access_token = token_response.json()["access_token"]
user_info = get(
f"{os.getenv('AUTH_ROOT')}/api/user/show/me",
headers={"Authorization": f"Bearer {access_token}"}
)
user = crud.update_user(user, user_info.json(), db)
return RedirectResponse(f"{os.getenv('WEB_ROOT')}?connected=true")
@router.get("/logout")
async def delete_session(connect_id: str = Cookie(...), db: Session = Depends(get_db)):
response = RedirectResponse(f"{os.getenv('AUTH_ROOT')}/logout?{urlencode({'redirect_logout': os.getenv('WEB_ROOT')})}")
response.delete_cookie(key="connect_id")
crud.end_session(connect_id, db)
return response
from fastapi import APIRouter, Body, Depends
from fastapi import APIRouter, Body, Cookie, Depends
from sqlalchemy.orm import Session
from typing import List
......@@ -15,8 +15,12 @@ async def get_comments(place: str, page: int = 1, db: Session = Depends(get_db))
@router.post('/{place}/comments', response_model=schemas.Comment)
async def create_comment(place: str, comment: schemas.CommentBase = Body(...), db: Session = Depends(get_db)):
return crud.create_comment(place, comment, db)
async def create_comment(place: str, connect_id: str = Cookie(...), comment: schemas.CommentBase = Body(...), db: Session = Depends(get_db)):
user = crud.get_user(connect_id, db)
if user:
return crud.create_comment(user, place, comment, db)
else:
raise Exception
@router.delete('/comments/{id}', response_model=None)
......
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from typing import List
from db import schemas, crud
from db.database import get_db
router = APIRouter(prefix="/api", tags=["opening_hours"])
@router.get('/{place}/opening_hours', response_model=List[schemas.OpeningHours])
async def get_opening_hours(place: str, db: Session = Depends(get_db)):
return crud.get_opening_hours(place, db)
@router.get('/{place}/opening_hours/{day}/{timeslot}', response_model=List[schemas.OpeningHours])
async def get_timeslot(place: str, day: int, timeslot: bool, db: Session = Depends(get_db)):
return crud.get_timeslot(place, day, timeslot, db)
@router.post('/opening_hours', response_model=schemas.OpeningHours)
async def create_opening_hours(opening_hours: schemas.OpeningHoursBase, db: Session = Depends(get_db)):
return crud.create_opening_hours(opening_hours, db)
@router.delete('/opening_hours/{id}', response_model=None)
async def delete_opening_hours(id: int, db: Session = Depends(get_db)):
return crud.delete_opening_hours(id, db)
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from typing import List
from typing import List, Tuple
from db import schemas, crud
from db.database import get_db
......@@ -9,7 +9,7 @@ from db.database import get_db
router = APIRouter(prefix="/api", tags=["stats"])
@router.get('/{place}/waiting_time', response_model=tuple)
@router.get('/{place}/waiting_time', response_model=dict)
async def waiting_time(place: str, db: Session = Depends(get_db)):
return crud.get_waiting_time(place, db)
......@@ -19,17 +19,19 @@ async def stats(place: str, db: Session = Depends(get_db)):
return crud.get_avg_graph(place, db)
@router.get('/{place}/stats/current_graph', response_model=list)
@router.get('/{place}/stats/current_graph', response_model=Tuple[list, int, int])
async def stats(place: str, db: Session = Depends(get_db)):
return crud.get_current_graph(place, db)
@router.get('/{place}/opening_hours', response_model=List[schemas.OpeningHours])
@router.get('/{place}/opening_hours',
response_model=List[schemas.OpeningHours])
async def get_opening_hours(place: str, db: Session = Depends(get_db)):
return crud.get_opening_hours(place, db)
@router.get('/{place}/opening_hours/{day}/{timeslot}', response_model=List[schemas.OpeningHours])
@router.get('/{place}/opening_hours/{day}/{timeslot}',
response_model=List[schemas.OpeningHours])
async def get_timeslot(place: str, day: int, timeslot: bool, db: Session = Depends(get_db)):
return crud.get_timeslot(place, day, timeslot, db)
......
import cv2
from datetime import datetime, timedelta
import numpy as np
import keras
from utils.preprocessing import fix_singular_shape, norm_by_imagenet
from db import models
from dotenv import load_dotenv
import os
from db.database import SessionLocal
def handle_cameras():
model = keras.models.load_model('assets', compile=False)
db = SessionLocal()
load_dotenv()
camera_number = int(os.getenv('CAM_NUMBER'))
cameras = []
for i in range(camera_number):
camera = {}
camera["place"] = os.getenv(f'CAM_{i}_PLACE')
camera["IP"] = os.getenv(f'CAM_{i}_IP')
camera["user"] = os.getenv(f'CAM_{i}_USER')
camera["password"] = os.getenv(f'CAM_{i}_PASSWORD')
camera["stream"] = os.getenv(f'CAM_{i}_STREAM')
camera["a_factor"] = int(os.getenv(f'CAM_{i}_A_FACTOR'))
camera["b_factor"] = int(os.getenv(f'CAM_{i}_B_FACTOR'))
camera["framegap"] = int(os.getenv(f'CAM_{i}_FRAMEGAP'))
camera["count"] = 0
camera["cap"] = cv2.VideoCapture(
f'rtsp://{camera["user"]}:{camera["password"]}@{camera["IP"]}/{camera["stream"]}')
mask_length = int(os.getenv(f'CAM_{i}_POINTS_NB'))
mask_points = []
for j in range(mask_length):
point = os.getenv(f'CAM_{i}_POINT_{j}')
mask_points.append(list(map(int, point.split(','))))
mask = np.zeros((720, 1280, 3), dtype=np.float32)
cv2.fillPoly(mask, [np.array(mask_points)], (255, 255, 255))
camera["mask"] = mask
cameras.append(camera)
while True:
for camera in cameras:
if camera['cap'].isOpened():
ret, frame = camera['cap'].read()
if ret and camera['count'] % camera['framegap'] == 0:
current_time = datetime.now()
masked_img = cv2.bitwise_and(
frame.astype(np.float32), camera["mask"])
treated_img = fix_singular_shape(masked_img, 16)
input_image = np.expand_dims(
np.squeeze(
norm_by_imagenet(
np.array(
[treated_img]))),
axis=0)
pred_map = np.squeeze(model.predict(input_image))
count_prediction = np.sum(pred_map)
print(count_prediction)
waiting_time = timedelta(
seconds=camera['b_factor'] +
int(count_prediction) *
camera['a_factor'])
db_record = models.Records(
place=camera['place'],
date=current_time,
density=int(count_prediction),
waiting_time=waiting_time)
db.add(db_record)
db.commit()
db.refresh(db_record)
camera['count'] += 1
else:
camera["cap"] = cv2.VideoCapture(
f"rtsp://{camera['user']}:{camera['password']}@{camera['IP']}/{camera['stream']}")
print("tentative de reconnexion")
REACT_APP_BASE_URL_BACK=http://localhost:3001/api
\ No newline at end of file
REACT_APP_BASE_URL_FRONT=http://localhost:3000
\ No newline at end of file
import React, { useEffect, useRef, useState } from "react";
import React, { useContext, useEffect, useRef, useState } from "react";
import axios from "axios";
import { AiOutlineInfoCircle } from "react-icons/ai";
import { BiSend } from "react-icons/bi";
import { BsChatText } from "react-icons/bs";
import { User } from "../index";
import { getSiblings } from "../utils";
import "../styles/Comments.css";
export default function Messages({ place, infos }) {
const [user] = useContext(User);
const [messages, setMessages] = useState([]);
const [newComment, setNewComment] = useState("");
const [loading, setLoading] = useState(true);
......@@ -22,9 +24,11 @@ export default function Messages({ place, infos }) {
if (newComment.replace(/\s/g, "").length) {
ev.preventDefault();
axios
.post(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/comments`, {
content: newComment,
})
.post(
`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/comments`,
{ content: newComment },
{ withCredentials: true },
)
.then((res) => {
if (messages.length) {
let update = messages.map((_, index) => (index ? messages[index - 1] : res.data));
......@@ -88,6 +92,10 @@ export default function Messages({ place, infos }) {
.then((res) => {
setMessages(res.data);
setLoading(false);
if (!width && infos && res.data.length) {
let otherIcon = document.getElementById("comments-icon-left");
otherIcon.style.cssText = "border: 1px solid red";
}
})
.catch((e) => {
console.log(e);
......@@ -144,6 +152,9 @@ export default function Messages({ place, infos }) {
let [year, month, day] = date.split("-");
return (
<div key={index} className="comment">
<div className={`comment-title${infos ? "-infos" : ""}`}>
{infos ? message.title : message.username}
</div>
<div className="comment-content">{message.content}</div>
<div className="comment-date">
{${hour.substring(0, 5)} le ${day}/${month}/${year}`}
......@@ -153,7 +164,7 @@ export default function Messages({ place, infos }) {
})
)}
</div>
{!infos && (
{!infos && user && (
<div className="comment-input-container">
<textarea
className="comments-input"
......
import React from "react";
import axios from "axios";
import {
AreaChart,
Line,
Area,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
ComposedChart,
} from "recharts";
import "../styles/Graph.css";
export default function Graph({ place, type }) {
const [data, setData] = React.useState(null);
const CustomTooltip = ({ active, payload, label }) => {
return (
<>
{active && payload && payload.length && (
<div className="custom-tooltip">
<p className="label">{`${formatXAxis(label)}`}</p>
<p className="label">{`Temps d'attente : ${payload[0].value} minutes`}</p>
</div>
)}
</>
);
};
function formatXAxis(value) {
if (value == 0) return "";
value = Math.round(value);
return Math.floor(value / 60).toString() + "h" + (value % 60).toString().padStart(2, "0");
}
export default function Graph({ place }) {
const [checked, setChecked] = React.useState(false);
const [currentData, setCurrentData] = React.useState([[], 0, 0]);
React.useEffect(() => {
axios
.get(
`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(
place,
)}/stats/${encodeURIComponent(type)}_graph`,
`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/stats/current_graph`,
)
.then((response) => {
setData(response.data);
setCurrentData(response.data);
});
}, []);
if (!data) return null;
const CustomTooltip = ({ active, payload }) => {
if (active && payload && payload.length) {
return (
<div className="custom-tooltip">
<p className="label">{`Temps d'attente : ${payload[0].value} minutes`}</p>
</div>
);
}
return null;
};
const [avgData, setAvgData] = React.useState([[], 0, 0]);
React.useEffect(() => {
axios
.get(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/stats/avg_graph`)
.then((response) => {
setAvgData(response.data);
});
}, []);
return (
<>
{!!currentData[0].length && (
<div style={{ height: "60%", padding: "3rem" }}>
<div className="graph">
<ResponsiveContainer width="100%" height="100%">
<AreaChart
data={data}
<ComposedChart
margin={{
top: 5,
right: 30,
left: 20,
left: -10,
bottom: 5,
}}
>
{checked ? (
<Line
data={avgData}
type="monotone"
dataKey="time"
stroke="#FF0000"
strokeWidth={2}
dot={false}
/>
) : (
<div />
)}
<defs>
<linearGradient id="colorGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="${2 * temp}%" stopColor="#ff0000" stopOpacity={0.55} />
<stop offset="10%" stopColor="#ff0000" stopOpacity={0.55} />
<stop offset="50%" stopColor="#fff200" stopOpacity={0.5} />
<stop offset="90%" stopColor="#1e9600" stopOpacity={0.35} />
</linearGradient>
......@@ -62,7 +94,14 @@ export default function Graph({ place, type }) {
axisLine={false}
tickLine={false}
tick={{ fill: "#FFFFFF", fontSize: "18" }}
ticks={[...Array(4).keys()].map(
(i) => currentData[1] + (i * (currentData[2] - currentData[1])) / 3,
)}
dataKey="name"
type="number"
interval="preserveStartEnd"
domain={[currentData[1], currentData[2]]}
tickFormatter={formatXAxis}
/>
<YAxis
axisLine={false}
......@@ -71,24 +110,32 @@ export default function Graph({ place, type }) {
tickInt
tickCount={10}
dataKey="time"
type="number"
domain={[0, (dataMax) => 10 * Math.floor((dataMax + 10) / 10)]}
allowDecimals={false}
name="Temps d'attente"
/>
<Tooltip content={<CustomTooltip />} />
<Area
data={currentData[0]}
type="monotone"
dataKey="time"
stroke="#FFFFFF"
strokeWidth={1}
strokeWidth={2}
fillOpacity={1}
fill="url(#colorGradient)"
dot={{ stroke: "#0967D2", strokeWidth: 2, fill: "#fff" }}
/>
</AreaChart>
</ComposedChart>
</ResponsiveContainer>
</div>
<div className="graph-title">Temps d&apos;attente estimé depuis l&apos;ouverture</div>
<div className="graph-title">
Temps d&apos;attente estimé depuis l&apos;ouverture (en minutes)
</div>
<button id="graph-avg-graph" onClick={() => setChecked(!checked)}>
{checked ? "Retirer le temps d'attente moyen" : "Afficher le temps d'attente moyen"}
</button>
</div>
)}
</>
);
}
import React from "react";
import React, { useContext, useEffect } from "react";
import { Link } from "react-router-dom";
import { BiLogOutCircle, BiLogInCircle } from "react-icons/bi";
import "../styles/Header.css";
import { User } from "../index";
import axios from "axios";
export default function Header({ selection, setSelection }) {
const [user, setUser] = useContext(User);
const connected = new URLSearchParams(location.search).get("connected");
let width = window.innerWidth > 0 ? window.innerWidth : screen.width;
width = width > 600;
useEffect(() => {
if (connected) {
axios
.get(`${process.env.REACT_APP_BASE_URL_BACK}/auth`, { withCredentials: true })
.then((res) => {
setUser(res.data);
localStorage.setItem("user", res.data);
})
.catch((e) => console.log(e));
}
}, [connected]);
return (
<div id="header-container">
<div id="header-container" style={!selection ? { flexDirection: "row" } : {}}>
<div id="header-restaurant-status">
{!selection
{width &&
(!selection
? "Accueil"
: `${selection.name} : actuellement ${selection.status ? "ouvert" : "fermé"}`}
: `${selection.name} : actuellement ${selection.status ? "ouvert" : "fermé"}`)}
</div>
<Link id="header-home-link" to="/" onClick={() => setSelection(null)}>
<h2>{width || !selection ? "Eatfast" : selection.name}</h2>
</Link>
<div id="header-timetable">{selection && `horaires : ${selection.timetable}`}</div>
<div id="header-timetable">
{selection ? (
width ? (
`Horaires : ${selection.timetable}`
) : (
selection.timetable
)
) : user ? (
<BiLogOutCircle
id="header-button"
title="Déconnexion"
onClick={() => {
localStorage.removeItem("user");
window.location.assign(`${process.env.REACT_APP_BASE_URL_BACK}/auth/logout`);
}}
/>
) : (
<BiLogInCircle
id="header-button"
title="Connexion"
onClick={() => {
window.location.assign(`${process.env.REACT_APP_BASE_URL_BACK}/auth/login`);
}}
/>
)}
</div>
</div>
);
}
......@@ -4,33 +4,35 @@ import axios from "axios";
import "../styles/WaitingTime.css";
export default function WaitingTime({ place }) {
const [post, setPost] = useState([null, null]);
const [data, setData] = useState({ status: true, waiting_time: null, next_timetable: null });
React.useEffect(() => {
axios
.get(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/waiting_time`)
.then((res) => {
setPost(res.data);
setData(res.data);
});
}, []);
return (
<div id="waiting-time-parent">
{post[1] ? (
{data.status ? (
data.waiting_time || data.waiting_time == 0 ? (
<div id="waiting-time-display">
Le RU ouvre aujourd&apos;hui à :
Le temps d&apos;attente est estimé à
<div className="waiting-time-minutes">
<b id="waiting-time-number">
{String(post[0]).padStart(2, "0")}h{String(post[1]).padStart(2, "0")}
</b>
<b id="waiting-time-number">{data.waiting_time}</b> minutes
</div>
</div>
) : post[0] ? (
) : (
<div className="waiting-time-minutes">Pas de données</div>
)
) : data.next_timetable ? (
<div id="waiting-time-display">
Le temps d&apos;attente est estimé à :
<div className="waiting-time-minutes">
<b id="waiting-time-number">{post[0]}</b> minutes
</div>
Le RU ouvre aujourd&apos;hui à
<span className="waiting-time-minutes">
<b id="waiting-time-number">{data.next_timetable}</b>
</span>
</div>
) : (
<div className="waiting-time-minutes">Le RU est fermé pour aujourd&apos;hui.</div>
......
import React from "react";
export default function gradient() {
return (
<defs>
<linearGradient id="colorGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="10%" stopColor="#ff0000" stopOpacity={0.55} />
<stop offset="50%" stopColor="#fff200" stopOpacity={0.55} />
<stop offset="90%" stopColor="#1e9600" stopOpacity={0.55} />
</linearGradient>
</defs>
);
}
import React, { useEffect, useState } from "react";
import React, { createContext, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import axios from "axios";
......@@ -9,10 +9,13 @@ import { HomePage, RestaurantPage, NotFoundPage, DetailsPage } from "./views";
import "bootstrap/dist/css/bootstrap.min.css";
import "./styles/index.css";
export const User = createContext(null);
export default function App() {
const [restaurantsList, setRestaurantsList] = useState([]);
const [selection, setSelection] = useState(null);
const [loading, setLoading] = useState(true);
const [user, setUser] = useState(localStorage.getItem("user"));
useEffect(() => {
axios
......@@ -40,6 +43,7 @@ export default function App() {
return (
<div className="app">
<User.Provider value={[user, setUser]}>
<Router>
<Header {...{ selection, setSelection }} />
<div className="page">
......@@ -60,6 +64,7 @@ export default function App() {
</div>
</Router>
<Footer />
</User.Provider>
</div>
);
}
......