diff --git a/backend/db/crud.py b/backend/db/crud.py
index 12b744fbaf93d49dd858b89ec2aa52830fd0068e..c0b31deeb2425cf8965ac5eece72a9313d3f1eaa 100644
--- a/backend/db/crud.py
+++ b/backend/db/crud.py
@@ -4,6 +4,7 @@ Module to interact with the database
 from datetime import date, datetime, time, timedelta
 from sqlalchemy.orm import Session
 from sqlalchemy.sql import func
+from sqlalchemy import Time, Date, cast
 from uuid import uuid4
 import secrets
 import pytz
@@ -17,87 +18,59 @@ 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]:
-        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]:
-        last_record = db.query(
-            models.Records.waiting_time
-        ).filter(
-            models.Records.place == place
-        ).order_by(
-            models.Records.date.desc()
-        ).first()
-        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]:
-        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]:
-        last_record = db.query(
-            models.Records.waiting_time
-        ).filter(
-            models.Records.place == place
-        ).order_by(
-            models.Records.date.desc()
-        ).first()
-        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
+    opening_hours = db.query(models.OpeningHours.open_time, models.OpeningHours.close_time).filter(
+        models.OpeningHours.place == place, models.OpeningHours.day == weekday).all()
+    for time_slot in opening_hours:
+        if current_time < time_slot.open_time:
+            return schemas.WaitingTime(next_timetable=time_slot.open_time.strftime('%Hh%M'))
+        elif current_time <= time_slot.close_time:
+            limit = datetime.combine(date.today(), time_slot.open_time)
+            last_record = db.query(models.Records.waiting_time).filter(models.Records.place == place).filter(
+                models.Records.date >= limit).order_by(models.Records.date.desc()).first()
+            waiting_time = None
+            if last_record:
+                waiting_time = round(
+                    last_record.waiting_time.total_seconds() / 60)
+            return schemas.WaitingTime(status=True, waiting_time=waiting_time)
+    return schemas.WaitingTime()
+
+
+## Define some utils function
+def shift_time(t: time, delta: timedelta):
+    return (datetime.combine(date(1, 1, 1), t) + delta).time()
+
+def add_slot(slots_list, start_time, end_time, function):
+    average_waiting_time = function(start_time, end_time)
+    if average_waiting_time:
+        name = 60 * start_time.hour + start_time.minute
+        slots_list.append(schemas.RecordRead(name=name, time=average_waiting_time))
 
 
 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):
-        return (datetime.combine(date(1, 1, 1), t) + delta).time()
-
     def avg_time_query(start_time, end_time):
         records = db.query(
-            (func.round(
+            func.round(
                 func.avg(
-                    3600 * func.extract('HOUR', models.Records.waiting_time) +
-                    60 * func.extract('MINUTE', models.Records.waiting_time) +
-                    func.extract('SECOND', models.Records.waiting_time))
-            )) / 60
+                    60 * func.extract('HOUR', models.Records.waiting_time) +
+                    func.extract('MINUTE', models.Records.waiting_time))
+            )
         ).filter(
             models.Records.place == place,
             func.weekday(models.Records.date) == weekday,
-                (func.extract('HOUR', models.Records.date) > start_time.hour) |
-            ((func.extract('HOUR', models.Records.date) == start_time.hour) &
-             (func.extract('MINUTE', models.Records.date) >= start_time.minute)),
-            (func.extract('HOUR', models.Records.date) < end_time.hour) |
-            ((func.extract('HOUR', models.Records.date) == end_time.hour) &
-             (func.extract('MINUTE', models.Records.date) < end_time.minute)),
-        ).one()
-        if records[0]:
+            cast(models.Records.date, Time) >= start_time,
+            cast(models.Records.date, Time) <= end_time,
+        ).first()
+        if records[0] or records[0] == 0:
             return int(records[0])
         return None
 
-    def add_slot(slots_list, start_time, end_time):
-        average_waiting_time = avg_time_query(start_time, end_time)
-        if average_waiting_time:
-            name = 60 * start_time.hour + start_time.minute
-            slots_list.append({'name': name, 'time': average_waiting_time})
-
     stats = []
     start_time, end_time = min_time, shift_time(min_time, interval)
     while start_time < max_time:
-        add_slot(stats, start_time, end_time)
+        add_slot(stats, start_time, end_time, avg_time_query)
         start_time, end_time = end_time, shift_time(end_time, interval)
 
     return stats
@@ -108,13 +81,12 @@ def get_avg_graph(place: str, db: Session):
     for the current or next available timeslot"""
     current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
     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)
-    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)
+    opening_hours = db.query(models.OpeningHours.open_time, models.OpeningHours.close_time).filter(
+        models.OpeningHours.place == place, models.OpeningHours.day == weekday).order_by(models.OpeningHours.open_time).all()
+
+    for time_slot in opening_hours:
+        if current_time <= time_slot.close_time:
+            return get_avg_graph_points(place, weekday, time_slot.open_time, time_slot.close_time, timedelta(minutes=5), db)
     return []
 
 
@@ -122,43 +94,27 @@ 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):
-        return (datetime.combine(date(1, 1, 1), t) + delta).time()
-
-    def avg_time_query(start_time, end_time):
+    def current_time_query(start_time, end_time):
         records = db.query(
-            (func.round(
+            func.round(
                 func.avg(
-                    3600 * func.extract('HOUR', models.Records.waiting_time) +
-                    60 * func.extract('MINUTE', models.Records.waiting_time) +
-                    func.extract('SECOND', models.Records.waiting_time))
-            )) / 60
+                    60 * func.extract('HOUR', models.Records.waiting_time) +
+                    func.extract('MINUTE', models.Records.waiting_time))
+            )
         ).filter(
             models.Records.place == place,
-            func.extract('YEAR', models.Records.date) == current_date.year,
-            func.extract('MONTH', models.Records.date) == current_date.month,
-            func.extract('DAY', models.Records.date) == current_date.day,
-                (func.extract('HOUR', models.Records.date) > start_time.hour) |
-            ((func.extract('HOUR', models.Records.date) == start_time.hour) &
-             (func.extract('MINUTE', models.Records.date) >= start_time.minute)),
-            (func.extract('HOUR', models.Records.date) < end_time.hour) |
-            ((func.extract('HOUR', models.Records.date) == end_time.hour) &
-             (func.extract('MINUTE', models.Records.date) < end_time.minute)),
-        ).one()
-        if records[0]:
+            cast(models.Records.date, Date) == current_date,
+            cast(models.Records.date, Time) >= start_time,
+            cast(models.Records.date, Time) <= end_time
+        ).first()
+        if records[0] or records[0] == 0:
             return int(records[0])
         return None
 
-    def add_slot(slots_list, start_time, end_time):
-        average_waiting_time = avg_time_query(start_time, end_time)
-        if average_waiting_time:
-            name = 60 * start_time.hour + start_time.minute
-            slots_list.append({'name': name, 'time': average_waiting_time})
-
     stats = []
     start_time, end_time = min_time, shift_time(min_time, interval)
     while start_time < max_time:
-        add_slot(stats, start_time, end_time)
+        add_slot(stats, start_time, end_time, current_time_query)
         start_time, end_time = end_time, shift_time(end_time, interval)
 
     return stats
@@ -169,23 +125,17 @@ def get_current_graph(place: str, db: Session):
     current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
     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, None
-    elif first_timeslot and current_time <= first_timeslot[1]:
-        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, None
-    elif second_timeslot and current_time <= second_timeslot[1]:
-        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
+    opening_hours = db.query(models.OpeningHours.open_time, models.OpeningHours.close_time).filter(
+        models.OpeningHours.place == place, models.OpeningHours.day == weekday).all()
+
+    for time_slot in opening_hours:
+        if time_slot.open_time <= current_time <= time_slot.close_time:
+            points = get_current_graph_points(
+                place, day, time_slot.open_time, current_time, timedelta(minutes=5), db)
+            start_time = 60 * time_slot.open_time.hour + time_slot.open_time.minute
+            end_time = 60 * time_slot.close_time.hour + time_slot.close_time.minute
+            return schemas.Graph(data=points, start=start_time, end=end_time)
+    return schemas.Graph(data=[])
 
 
 # Define CRUD operation for the comments
@@ -210,7 +160,8 @@ def get_comments(place: str, page: int, db: Session):
                     20,
                     page *
             20).all()
-    comments_list = list(schemas.Comment(**comment.__dict__, username=username) for comment, username in comments)
+    comments_list = list(schemas.Comment(
+        **comment.__dict__, username=username) for comment, username in comments)
     comments_list.reverse()
     return comments_list
 
@@ -218,7 +169,8 @@ def get_comments(place: str, page: int, 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, user_id=user.id)
+    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)
@@ -273,24 +225,11 @@ def get_opening_hours(place: str, db: Session):
     ).filter(
         models.OpeningHours.place == place
     ).order_by(
-        models.OpeningHours.day, models.OpeningHours.timeslot.desc()
+        models.OpeningHours.day, models.OpeningHours.open_time
     ).all()
     return opening_hours
 
 
-def get_timeslot(place: str, day: int, timeslot: bool, db: Session):
-    """ Get the opening hours for the given place and timeslot"""
-    opening_hours = db.query(
-        models.OpeningHours.open_time,
-        models.OpeningHours.close_time,
-    ).filter(
-        models.OpeningHours.place == place,
-        models.OpeningHours.day == day,
-        models.OpeningHours.timeslot == timeslot
-    ).first()
-    return opening_hours
-
-
 def create_opening_hours(
         new_opening_hours: schemas.OpeningHoursBase, db: Session):
     """ Add opening hours to the database """
@@ -315,56 +254,19 @@ def delete_opening_hours(id: int, db: Session):
 # Restaurants information
 
 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()]
+    weekday = datetime.now(tz=pytz.timezone("Europe/Paris")).weekday()
+    places = db.query(models.OpeningHours.place).distinct()
     restaurants = []
 
-    for name in restaurant_names:
-        restaurant = {}
-        restaurant["name"] = name
-        first_timeslot = get_timeslot(name, weekday, True, db)
-        second_timeslot = get_timeslot(name, weekday, False, db)
-
-        if (first_timeslot and first_timeslot[0] <= current_time < first_timeslot[1]) or (
-                second_timeslot and second_timeslot[0] <= current_time < second_timeslot[1]):
-            restaurant["status"] = True
-        else:
-            restaurant["status"] = False
-
-        if first_timeslot and second_timeslot:
-            restaurant["timetable"] = (
-                f"{first_timeslot[0].hour:{'0'}{2}}h{first_timeslot[0].minute:{'0'}{2}}-"
-                f"{first_timeslot[1].hour:{'0'}{2}}h{first_timeslot[1].minute:{'0'}{2}} / "
-                f"{second_timeslot[0].hour:{'0'}{2}}h{second_timeslot[0].minute:{'0'}{2}}-"
-                f"{second_timeslot[1].hour:{'0'}{2}}h{second_timeslot[1].minute:{'0'}{2}}")
-        elif first_timeslot:
-            restaurant["timetable"] = (
-                f"{first_timeslot[0].hour:{'0'}{2}}h{first_timeslot[0].minute:{'0'}{2}}-"
-                f"{first_timeslot[1].hour:{'0'}{2}}h{first_timeslot[1].minute:{'0'}{2}}")
-        else:
-            restaurant["timeslot"] = "-"
-
-        if restaurant["status"]:
-            last_record = db.query(
-                models.Records
-            ).filter(
-                models.Records.place == name
-            ).order_by(
-                models.Records.date.desc()
-            ).first()
-            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)
+    for place in places:
+        opening_hours = db.query(models.OpeningHours).filter(
+            models.OpeningHours.place == place.place, models.OpeningHours.day == weekday).all()
+        opening_hours_formated = [
+            f"{row.open_time.strftime('%Hh%M')}-{row.close_time.strftime('%Hh%M')}" for row in opening_hours]
+        timetable = "/".join(opening_hours_formated)
+        infos = get_waiting_time(place.place, db)
+        restaurants.append(schemas.Restaurant(
+            **infos.dict(), name=place.place, timetable=timetable))
 
     return restaurants
 
@@ -375,8 +277,10 @@ 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)
+    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)
@@ -400,8 +304,10 @@ def delete_state(user: schemas.User, db: Session):
 
 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()
+    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
diff --git a/backend/db/models.py b/backend/db/models.py
index ff985da4d23f28c5b5868fb0bd7ded7aedcb53dc..a03399ebe7678f792a6aad4185ec53a33eef8e17 100644
--- a/backend/db/models.py
+++ b/backend/db/models.py
@@ -48,7 +48,6 @@ class OpeningHours(Base):
     id = Column(Integer, primary_key=True, index=True)
     place = Column(String(30))
     day = Column(Integer)
-    timeslot = Column(Boolean)
     open_time = Column(Time)
     close_time = Column(Time)
 
diff --git a/backend/db/schemas.py b/backend/db/schemas.py
index 3a170468277526f19c502167aa4caa1d7cc802fd..991d910e96f3721e693910558d6a33b4c1511aca 100644
--- a/backend/db/schemas.py
+++ b/backend/db/schemas.py
@@ -1,11 +1,12 @@
 """
 Pydantic schemas for the magasin app
 """
-from typing import Optional
+from typing import List, Optional
 from datetime import datetime, timedelta, time
 from pydantic import BaseModel, Field
 
 
+# Records data structure
 class RecordBase(BaseModel):
     """Records base schema"""
     place: str = Field(...,
@@ -24,6 +25,13 @@ class Record(RecordBase):
         orm_mode = True
 
 
+class RecordRead(BaseModel):
+    """ Data structure for record in graph """
+    name: int
+    time: int
+
+
+# Comments Data structure
 class CommentBase(BaseModel):
     """Comments base schema"""
     content: str = Field(..., title="Content of the comment posted")
@@ -40,6 +48,7 @@ class Comment(CommentBase):
         orm_mode = True
 
 
+# News data structure
 class NewsBase(BaseModel):
     """News sql table model"""
     title: str = Field(..., title="Title of the news")
@@ -57,13 +66,19 @@ class News(NewsBase):
         orm_mode = True
 
 
+# Stats data structure
+class WaitingTime(BaseModel):
+    """Waiting time schema for reading"""
+    status: bool = Field(default=False, title="Status of the restaurant for the current hour")
+    waiting_time: Optional[timedelta] = Field(default=None, title="Waiting time for the restaurant")
+    next_timetable: Optional[str] = Field(default=None, title="Next time the restaurant will be open")
+
+
 class OpeningHoursBase(BaseModel):
     """Database opening_hours base schema"""
     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)")
     open_time: time = Field(..., title="Opening time")
     close_time: time = Field(..., title="Closing time")
 
@@ -76,6 +91,22 @@ class OpeningHours(OpeningHoursBase):
         orm_mode = True
 
 
+class Restaurant(BaseModel):
+    """Restaurant schema for reading"""
+    name: str
+    status: bool
+    waiting_time: Optional[timedelta] = Field(default=None, title="Waiting time for the restaurant")
+    timetable: str
+
+
+class Graph(BaseModel):
+    """ Data structure for current graph display """
+    data: List[RecordRead] = Field(title="Last records list for the restaurant")
+    start: Optional[int] = Field(default=None, title="Opening of the RU")
+    end: Optional[int] = Field(default=None, title="Closure of the RU")
+
+
+# User data structure
 class User(BaseModel):
     """Database user base schema"""
     id: int
diff --git a/backend/main.py b/backend/main.py
index 8aa44d91b3ac105d5d226debef40ad13fbb6ed79..ce0938157c9ad0872df0693744cab36fb523f231 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -2,14 +2,14 @@ from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware
 from dotenv import load_dotenv
 import os
-from db.database import get_db
 from fastapi import Depends
 from sqlalchemy.orm import Session
-from db import schemas
 from typing import List
 from threading import Thread
+import json
 
-from db import database, models
+from db import database, models, schemas
+from db.database import get_db
 from routers import *
 from video_capture import handle_cameras
 
@@ -59,6 +59,7 @@ async def post_records(record: schemas.RecordBase, db: Session = Depends(get_db)
     db.add(db_record)
     db.commit()
     db.refresh(db_record)
+    await websocket.manager.broadcast(json.dumps({"type": "data"}))
     return db_record
 
 
diff --git a/backend/routers/__init__.py b/backend/routers/__init__.py
index 091fddcb03a0abf8097c5bdd36461a30dd173e1a..745afdca4556b73e7a36084eca5963eadfbfa487 100644
--- a/backend/routers/__init__.py
+++ b/backend/routers/__init__.py
@@ -1,6 +1,5 @@
 from . import authentication
 from . import comments
 from . import news
-from . import opening_hours
 from . import stats
 from . import websocket
diff --git a/backend/routers/authentication.py b/backend/routers/authentication.py
index df409dfcfe6f9caa9e336ae1a0dea47744f67f4f..32ab07f5d4969da1bfb9ad9f3d22dff5498f963f 100644
--- a/backend/routers/authentication.py
+++ b/backend/routers/authentication.py
@@ -74,6 +74,8 @@ async def login(code: Optional[str] = None, state: Optional[str] = None, connect
 @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
+    try:
+        response.delete_cookie(key="connect_id")
+        crud.end_session(connect_id, db)
+    finally:
+        return response
diff --git a/backend/routers/news.py b/backend/routers/news.py
index b76cdc55dbfe26020d06fa67c0ef4199c410d750..7c4b8136fda0bd567297b83c6c41d859399452b6 100644
--- a/backend/routers/news.py
+++ b/backend/routers/news.py
@@ -1,9 +1,11 @@
 from fastapi import APIRouter, Depends
 from sqlalchemy.orm import Session
 from typing import List
+import json
 
 from db import schemas, crud
 from db.database import get_db
+from .websocket import manager
 
 
 router = APIRouter(prefix="/api", tags=["news"])
@@ -16,7 +18,9 @@ async def get_news(place: str, db: Session = Depends(get_db)):
 
 @router.post('/news', response_model=schemas.News)
 async def create_news(news: schemas.NewsBase, db: Session = Depends(get_db)):
-    return crud.create_news(news, db)
+    saved_news = crud.create_news(news, db)
+    await manager.broadcast(json.dumps({"type": "news", "comment": saved_news.__dict__}, default=str))
+    return saved_news
 
 
 @router.delete('/news/{id}', response_model=None)
diff --git a/backend/routers/stats.py b/backend/routers/stats.py
index 066bd7681327a979c025b66690616742d5f168ec..ffe229fdeffc4a85d48b24c6625e23ebdf084a8d 100644
--- a/backend/routers/stats.py
+++ b/backend/routers/stats.py
@@ -1,51 +1,45 @@
 from fastapi import APIRouter, Depends
 from sqlalchemy.orm import Session
-from typing import List, Tuple
+from typing import List
 
 from db import schemas, crud
 from db.database import get_db
 
 
-router = APIRouter(prefix="/api", tags=["stats"])
+router = APIRouter(prefix="/api")
 
 
-@router.get('/{place}/waiting_time', response_model=dict)
+@router.get('/{place}/waiting_time', response_model=schemas.WaitingTime, tags=["stats"])
 async def waiting_time(place: str, db: Session = Depends(get_db)):
     return crud.get_waiting_time(place, db)
 
 
-@router.get('/{place}/stats/avg_graph', response_model=list)
+@router.get('/{place}/stats/avg_graph', response_model=list, tags=["stats"])
 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=Tuple[list, int, int])
+@router.get('/{place}/stats/current_graph', response_model=schemas.Graph, tags=["stats"])
 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])
+            response_model=List[schemas.OpeningHours], tags=["timetable"])
 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)
+@router.post('/opening_hours', response_model=schemas.OpeningHours, tags=["timetable"])
 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)
+@router.delete('/opening_hours/{id}', response_model=None, tags=["timetable"])
 async def delete_opening_hours(id: int, db: Session = Depends(get_db)):
     return crud.delete_opening_hours(id, db)
 
 
-@router.get('/restaurants', response_model=List[dict])
+@router.get('/restaurants', response_model=List[schemas.Restaurant], tags=["timetable"])
 async def get_restaurants(db: Session = Depends(get_db)):
     return crud.get_restaurants(db)
diff --git a/backend/video_capture.py b/backend/video_capture.py
index 96afe5a8d8795b13600286463e4add0348025c4f..9806146f1aad3258b8e71d350cf91b4c6093d04f 100644
--- a/backend/video_capture.py
+++ b/backend/video_capture.py
@@ -3,11 +3,13 @@ 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 json
 import os
 
+from db import models
 from db.database import SessionLocal
+from routers.websocket import manager
 
 
 def handle_cameras():
@@ -56,7 +58,6 @@ def handle_cameras():
                         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) *
@@ -68,7 +69,7 @@ def handle_cameras():
                         waiting_time=waiting_time)
                     db.add(db_record)
                     db.commit()
-                    db.refresh(db_record)
+                    manager.broadcast(json.dumps({"type": "data"}))
                 camera['count'] += 1
             else:
                 camera["cap"] = cv2.VideoCapture(
diff --git a/frontend/src/components/Comments.js b/frontend/src/components/Comments.js
index 714bec6aeb12f3d677c47252e4a619ec7f8b07a1..1f7975a963cc88c874bada47d9431c0e63e3b09a 100644
--- a/frontend/src/components/Comments.js
+++ b/frontend/src/components/Comments.js
@@ -30,10 +30,7 @@ export default function Messages({ place, infos, lastMessage }) {
           { withCredentials: true },
         )
         .then((res) => {
-          setMessages((messages) => {
-            messages.push(res.data);
-            return messages;
-          });
+          setMessages((old_messages) => [...old_messages, res.data]);
           updateValue("");
         })
         .catch((e) => {
@@ -102,8 +99,12 @@ export default function Messages({ place, infos, lastMessage }) {
 
   useEffect(() => {
     if (chat.current) {
-      let position = chat.current.scrollHeight - chat.current.clientHeight;
-      chat.current.scrollTop = position;
+      if (infos) {
+        chat.current.scrollTop = 0;
+      } else {
+        let position = chat.current.scrollHeight - chat.current.clientHeight;
+        chat.current.scrollTop = position;
+      }
     }
   }, [chat.current, messages.length]);
 
@@ -117,11 +118,11 @@ export default function Messages({ place, infos, lastMessage }) {
   useEffect(() => {
     if (lastMessage?.data) {
       let new_message = JSON.parse(lastMessage.data);
-      if (new_message.type == "comment" && new_message.comment.username != user) {
-        setMessages((messages) => {
-          messages.push(new_message.comment);
-          return messages;
-        });
+      if (
+        (new_message.type == "news" && infos) ||
+        (new_message.type == "comment" && !infos && new_message.comment.username != user)
+      ) {
+        setMessages((old_messages) => [...old_messages, new_message.comment]);
       }
     }
   }, [lastMessage]);
diff --git a/frontend/src/components/Graph.js b/frontend/src/components/Graph.js
index 8a4acc1860f11c67bb95f6a060f1d6b0a86c4d5c..5aca5c7becd299d5044001ef27531efc5ac9fb59 100644
--- a/frontend/src/components/Graph.js
+++ b/frontend/src/components/Graph.js
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useEffect, useState } from "react";
 import axios from "axios";
 import {
   Line,
@@ -32,33 +32,44 @@ function formatXAxis(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]);
+export default function Graph({ place, lastMessage }) {
+  const [checked, setChecked] = useState(false);
+  const [currentData, setCurrentData] = useState({ data: [], start: 0, end: 0 });
+  const [avgData, setAvgData] = useState([]);
+  const [reload, setReload] = useState(true);
 
-  React.useEffect(() => {
-    axios
-      .get(
-        `${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/stats/current_graph`,
-      )
-      .then((response) => {
-        setCurrentData(response.data);
-      });
-  }, []);
+  useEffect(() => {
+    if (reload) {
+      axios
+        .get(
+          `${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/stats/current_graph`,
+        )
+        .then((response) => {
+          setCurrentData(response.data);
+        });
 
-  const [avgData, setAvgData] = React.useState([[], 0, 0]);
+      axios
+        .get(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/stats/avg_graph`)
+        .then((response) => {
+          setAvgData(response.data);
+        });
 
-  React.useEffect(() => {
-    axios
-      .get(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/stats/avg_graph`)
-      .then((response) => {
-        setAvgData(response.data);
-      });
-  }, []);
+      setReload(false);
+    }
+  }, [reload]);
+
+  useEffect(() => {
+    if (lastMessage?.data) {
+      let new_message = JSON.parse(lastMessage.data);
+      if (new_message.type == "data") {
+        setReload(true);
+      }
+    }
+  }, [lastMessage]);
 
   return (
     <>
-      {!!currentData[0].length && (
+      {!!currentData?.data?.length && (
         <div style={{ height: "60%", padding: "3rem" }}>
           <div className="graph">
             <ResponsiveContainer width="100%" height="100%">
@@ -95,12 +106,12 @@ export default function Graph({ place }) {
                   tickLine={false}
                   tick={{ fill: "#FFFFFF", fontSize: "18" }}
                   ticks={[...Array(4).keys()].map(
-                    (i) => currentData[1] + (i * (currentData[2] - currentData[1])) / 3,
+                    (i) => currentData.start + (i * (currentData.end - currentData.start)) / 3,
                   )}
                   dataKey="name"
                   type="number"
                   interval="preserveStartEnd"
-                  domain={[currentData[1], currentData[2]]}
+                  domain={[currentData.start, currentData.end]}
                   tickFormatter={formatXAxis}
                 />
                 <YAxis
@@ -117,7 +128,7 @@ export default function Graph({ place }) {
                 />
                 <Tooltip content={<CustomTooltip />} />
                 <Area
-                  data={currentData[0]}
+                  data={currentData.data}
                   type="monotone"
                   dataKey="time"
                   stroke="#FFFFFF"
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index c7eeadea94d9f9dd54a4f600bd7a0bdf99218153..b3ab6f54adf318f7a5b1ddd484107eec09de05d8 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -39,7 +39,7 @@ export default function Header({ selection, setSelection }) {
       <div id="header-timetable">
         {selection ? (
           width ? (
-            `Horaires : ${selection.timetable}`
+            selection.timetable && `Horaires : ${selection.timetable}`
           ) : (
             selection.timetable
           )
diff --git a/frontend/src/components/WaitingTime.js b/frontend/src/components/WaitingTime.js
index 7af4d42ce100a9915adf04e972f4716e5f10141f..7e12c19bd80563c942a30ad8f46559c0c6c5ae76 100644
--- a/frontend/src/components/WaitingTime.js
+++ b/frontend/src/components/WaitingTime.js
@@ -1,18 +1,31 @@
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
 import axios from "axios";
 
 import "../styles/WaitingTime.css";
 
-export default function WaitingTime({ place }) {
+export default function WaitingTime({ place, lastMessage }) {
   const [data, setData] = useState({ status: true, waiting_time: null, next_timetable: null });
+  const [reload, setReload] = useState(true);
 
-  React.useEffect(() => {
-    axios
-      .get(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/waiting_time`)
-      .then((res) => {
-        setData(res.data);
-      });
-  }, []);
+  useEffect(() => {
+    if (reload) {
+      axios
+        .get(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/waiting_time`)
+        .then((res) => {
+          setData(res.data);
+        });
+      setReload(false);
+    }
+  }, [reload]);
+
+  useEffect(() => {
+    if (lastMessage?.data) {
+      let new_message = JSON.parse(lastMessage.data);
+      if (new_message.type == "data") {
+        setReload(true);
+      }
+    }
+  }, [lastMessage]);
 
   return (
     <div id="waiting-time-parent">
diff --git a/frontend/src/index.js b/frontend/src/index.js
index c793ad7462c0020ac6ad20348c92f0186743150d..28592a4fe01255010457d4e46c7edf453e19adee 100644
--- a/frontend/src/index.js
+++ b/frontend/src/index.js
@@ -5,7 +5,7 @@ import useWebSocket from "react-use-websocket";
 import axios from "axios";
 
 import { Footer, Header } from "./components";
-import { HomePage, RestaurantPage, NotFoundPage, DetailsPage } from "./views";
+import { HomePage, RestaurantPage, NotFoundPage } from "./views";
 
 import "bootstrap/dist/css/bootstrap.min.css";
 import "./styles/index.css";
@@ -17,23 +17,28 @@ export default function App() {
   const [restaurantsList, setRestaurantsList] = useState([]);
   const [selection, setSelection] = useState(null);
   const [loading, setLoading] = useState(true);
+  const [reload, setReload] = useState(true);
   const [user, setUser] = useState(localStorage.getItem("user"));
   const { lastMessage } = useWebSocket(socketUrl, {
     shouldReconnect: () => true,
   });
 
   useEffect(() => {
-    axios
-      .get(`${process.env.REACT_APP_BASE_URL_BACK}/restaurants`)
-      .then((res) => {
-        setRestaurantsList(res.data);
-        setLoading(false);
-      })
-      .catch((e) => {
-        console.log(e);
-        setLoading(false);
-      });
-  }, []);
+    if (reload) {
+      axios
+        .get(`${process.env.REACT_APP_BASE_URL_BACK}/restaurants`)
+        .then((res) => {
+          setRestaurantsList(res.data);
+          setLoading(false);
+          setReload(false);
+        })
+        .catch((e) => {
+          console.log(e);
+          setLoading(false);
+          setReload(false);
+        });
+    }
+  }, [reload]);
 
   useEffect(() => {
     let path = window.location.pathname.split("/");
@@ -46,6 +51,15 @@ export default function App() {
     }
   }, [restaurantsList]);
 
+  useEffect(() => {
+    if (lastMessage?.data) {
+      let new_message = JSON.parse(lastMessage.data);
+      if (new_message.type == "data") {
+        setReload(true);
+      }
+    }
+  }, [lastMessage]);
+
   return (
     <div className="app">
       <User.Provider value={[user, setUser]}>
@@ -61,9 +75,7 @@ export default function App() {
               <Route
                 path="/:restaurant"
                 element={<RestaurantPage {...{ selection, setSelection, lastMessage }} />}
-              >
-                <Route path="details" element={<DetailsPage selection={selection} />} />
-              </Route>
+              />
               <Route path="*" element={<NotFoundPage />} />
             </Routes>
           </div>
diff --git a/frontend/src/views/Details.js b/frontend/src/views/Details.js
deleted file mode 100644
index fe564b8827e7c68c69c4733330a3766d30989454..0000000000000000000000000000000000000000
--- a/frontend/src/views/Details.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import React from "react";
-
-export default function Details() {
-  return <div>details page</div>;
-}
diff --git a/frontend/src/views/Restaurant.js b/frontend/src/views/Restaurant.js
index 5b0d73f7ce1cd7e907ce13a6164b21daa6af5486..a309473c4e7a71292f16dbf7341955ff3e078bd0 100644
--- a/frontend/src/views/Restaurant.js
+++ b/frontend/src/views/Restaurant.js
@@ -9,7 +9,7 @@ export default function RestaurantPage({ selection, lastMessage }) {
     <>
       {selection && (
         <div className="restaurant-container">
-          <Comments place={selection.name} infos />
+          <Comments place={selection.name} lastMessage={lastMessage} infos />
           <div className="restaurant-container" id="restaurant-main-page">
             <WaitingTime place={selection.name} lastMessage={lastMessage} />
             <Graph place={selection.name} type="current" lastMessage={lastMessage} />
diff --git a/frontend/src/views/index.js b/frontend/src/views/index.js
index 82ebf8645a723d6d8d63880bf45fe1351a82a6e1..77ab82fcd6c65ec32cf59292d0e7eea5cb8681cc 100644
--- a/frontend/src/views/index.js
+++ b/frontend/src/views/index.js
@@ -1,4 +1,3 @@
 export { default as HomePage } from "./HomePage";
 export { default as RestaurantPage } from "./Restaurant";
 export { default as NotFoundPage } from "./NotFoundPage";
-export { default as DetailsPage } from "./Details.js";