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
  • main
1 result

Target

Select target project
No results found
Select Git revision
  • main
1 result
Show changes

Commits on Source 40

25 files
+ 698
310
Compare changes
  • Side-by-side
  • Inline

Files

Original line number Original line Diff line number Diff line
MYSQL_DATABASE=eatfast
MYSQL_DATABASE=
MYSQL_USER=user
MYSQL_USER=
MYSQL_PASSWORD=password
MYSQL_PASSWORD=
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_ROOT_PASSWORD=


DB_HOST=localhost
DB_HOST=localhost
DB_PORT=3306
DB_PORT=3306


CLIENT_ID=
CLIENT_SECRET=

API_ROOT=http://localhost:3001/api
WEB_ROOT=http://localhost:3000
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
+144 −47
Original line number Original line Diff line number Diff line
@@ -4,6 +4,8 @@ Module to interact with the database
from datetime import date, datetime, time, timedelta
from datetime import date, datetime, time, timedelta
from sqlalchemy.orm import Session
from sqlalchemy.orm import Session
from sqlalchemy.sql import func
from sqlalchemy.sql import func
from uuid import uuid4
import secrets
import pytz
import pytz


from db import models, schemas
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 """
    """ Get the last estimated waiting time for the given place """
    current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
    current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
    weekday, current_time = current_date.weekday(), current_date.time()
    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)
    first_timeslot = get_timeslot(place, weekday, True, db)
    if first_timeslot and current_time < first_timeslot[0]:
    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]:
    elif first_timeslot and current_time <= first_timeslot[1]:
        waiting_time = db.query(
        last_record = db.query(
            models.Records.waiting_time
            models.Records.waiting_time
        ).filter(
        ).filter(
            models.Records.place == place
            models.Records.place == place
        ).order_by(
        ).order_by(
            models.Records.date.desc()
            models.Records.date.desc()
        ).first()
        ).first()
        waiting_time_minutes = round(waiting_time[0].total_seconds() / 60)
        if last_record:
        return waiting_time_minutes, None
            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)
    second_timeslot = get_timeslot(place, weekday, False, db)
    if second_timeslot and current_time < second_timeslot[0]:
    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]:
    elif second_timeslot and current_time <= second_timeslot[1]:
        waiting_time = db.query(
        last_record = db.query(
            models.Records.waiting_time
            models.Records.waiting_time
        ).filter(
        ).filter(
            models.Records.place == place
            models.Records.place == place
        ).order_by(
        ).order_by(
            models.Records.date.desc()
            models.Records.date.desc()
        ).first()
        ).first()
        waiting_time_minutes = round(waiting_time[0].total_seconds() / 60)
        if last_record:
        return waiting_time_minutes, None
            waiting_time = last_record.waiting_time
    return None, None
            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 """
    """ Get the average waiting time for each interval between two time steps """


    def shift_time(t: time, delta: timedelta):
    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):
    def add_slot(slots_list, start_time, end_time):
        average_waiting_time = avg_time_query(start_time, end_time)
        average_waiting_time = avg_time_query(start_time, end_time)
        if average_waiting_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})
            slots_list.append({'name': name, 'time': average_waiting_time})


    stats = []
    stats = []
@@ -94,14 +110,16 @@ def get_avg_graph(place: str, db: Session):
    weekday, current_time = current_date.weekday(), current_date.time()
    weekday, current_time = current_date.weekday(), current_date.time()
    first_timeslot = get_timeslot(place, weekday, True, db)
    first_timeslot = get_timeslot(place, weekday, True, db)
    if first_timeslot and current_time <= first_timeslot[1]:
    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)
    second_timeslot = get_timeslot(place, weekday, False, db)
    if second_timeslot and current_time <= second_timeslot[1]:
    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 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 """
    """ Get the waiting time for each interval between two time steps for the current timeslot """


    def shift_time(t: time, delta: timedelta):
    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):
    def add_slot(slots_list, start_time, end_time):
        average_waiting_time = avg_time_query(start_time, end_time)
        average_waiting_time = avg_time_query(start_time, end_time)
        if average_waiting_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})
            slots_list.append({'name': name, 'time': average_waiting_time})


    stats = []
    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):
def get_current_graph(place: str, db: Session):
    """ Get the waiting_time_graph for the current timeslot"""
    """ Get the waiting_time_graph for the current timeslot"""
    current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
    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)
    first_timeslot = get_timeslot(place, weekday, True, db)
    if first_timeslot and current_time <= first_timeslot[0]:
    if first_timeslot and current_time <= first_timeslot[0]:
        return None
        return [], None, None
    elif first_timeslot and current_time <= first_timeslot[1]:
    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)
    second_timeslot = get_timeslot(place, weekday, False, db)
    if second_timeslot and current_time <= second_timeslot[0]:
    if second_timeslot and current_time <= second_timeslot[0]:
        return None
        return [], None, None
    elif second_timeslot and current_time <= second_timeslot[1]:
    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)
        points = get_current_graph_points(place, day, second_timeslot[0], current_time, timedelta(minutes=5), db)
    return None
        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
# Define CRUD operation for the comments


def get_comments(place: str, page: int, db: Session):
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:
    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:
    else:
        comments = db.query(
        comments = db.query(
            models.Comments).filter(
            models.Comments,
            models.Users.username).join(
            models.Users).filter(
            models.Comments.place == place).order_by(
            models.Comments.place == place).order_by(
                models.Comments.published_at.desc(),
                models.Comments.published_at.desc(),
                models.Comments.id.desc()).slice(
                models.Comments.id.desc()).slice(
                    (page -
                    (page -
                     1) *
                     1) *
                10,
                    20,
                    page *
                    page *
            10).all()
            20).all()
    return comments
    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 """
    """ Add a new comment to the database """
    date = datetime.now(tz=pytz.timezone("Europe/Paris"))
    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.add(db_comment)
    db.commit()
    db.commit()
    db.refresh(db_comment)
    db.refresh(db_comment)
    return db_comment
    return schemas.Comment(**db_comment.__dict__, username=user.username)




def delete_comment(id: int, db: Session):
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):
def get_news(place: str, db: Session):
    """ Get the news for the given place """
    """ 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
    return news




@@ -234,10 +267,7 @@ def delete_news(id: int, db: Session):
def get_opening_hours(place: str, db: Session):
def get_opening_hours(place: str, db: Session):
    """ Get the opening hours for the given place """
    """ Get the opening hours for the given place """
    opening_hours = db.query(
    opening_hours = db.query(
        models.OpeningHours.day,
        models.OpeningHours
        models.OpeningHours.timeslot,
        models.OpeningHours.open_time,
        models.OpeningHours.close_time,
    ).filter(
    ).filter(
        models.OpeningHours.place == place
        models.OpeningHours.place == place
    ).order_by(
    ).order_by(
@@ -259,7 +289,8 @@ def get_timeslot(place: str, day: int, timeslot: bool, db: Session):
    return opening_hours
    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 """
    """ Add opening hours to the database """
    db_opening_hours = models.OpeningHours(**new_opening_hours.dict())
    db_opening_hours = models.OpeningHours(**new_opening_hours.dict())
    db.add(db_opening_hours)
    db.add(db_opening_hours)
@@ -273,7 +304,9 @@ def delete_opening_hours(id: int, db: Session):
    if id == 0:
    if id == 0:
        db.query(models.OpeningHours).delete()
        db.query(models.OpeningHours).delete()
    else:
    else:
        db.query(models.OpeningHours).filter(models.OpeningHours.id == id).delete()
        db.query(
            models.OpeningHours).filter(
            models.OpeningHours.id == id).delete()
    db.commit()
    db.commit()




@@ -282,7 +315,9 @@ def delete_opening_hours(id: int, db: Session):
def get_restaurants(db: Session):
def get_restaurants(db: Session):
    current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
    current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
    weekday, current_time = current_date.weekday(), current_date.time()
    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 = []
    restaurants = []


    for name in restaurant_names:
    for name in restaurant_names:
@@ -311,18 +346,80 @@ def get_restaurants(db: Session):
            restaurant["timeslot"] = "-"
            restaurant["timeslot"] = "-"


        if restaurant["status"]:
        if restaurant["status"]:
            waiting_time = db.query(
            last_record = db.query(
                models.Records.waiting_time
                models.Records
            ).filter(
            ).filter(
                models.Records.place == name
                models.Records.place == name
            ).order_by(
            ).order_by(
                models.Records.date.desc()
                models.Records.date.desc()
            ).first()
            ).first()
            waiting_time_minutes = round(waiting_time[0].total_seconds() / 60)
            if last_record:
            restaurant["waiting_time"] = waiting_time_minutes
                waiting_time = last_record.waiting_time
                restaurant["waiting_time"] = round(
                    waiting_time.total_seconds() / 60)
            else:
                restaurant["waiting_time"] = None
        else:
        else:
            restaurant["waiting_time"] = None
            restaurant["waiting_time"] = None


        restaurants.append(restaurant)
        restaurants.append(restaurant)


    return restaurants
    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
Original line number Original line Diff line number Diff line
"""
"""
Models of the database for magasin app
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
from db.database import Base


@@ -22,6 +23,7 @@ class Comments(Base):
    __tablename__ = "comments"
    __tablename__ = "comments"


    id = Column(Integer, primary_key=True, index=True)
    id = Column(Integer, primary_key=True, index=True)
    user_id = Column(Integer, ForeignKey("users.id"))
    content = Column(Text)
    content = Column(Text)
    published_at = Column(DateTime)
    published_at = Column(DateTime)
    place = Column(String(30))
    place = Column(String(30))
@@ -49,3 +51,15 @@ class OpeningHours(Base):
    timeslot = Column(Boolean)
    timeslot = Column(Boolean)
    open_time = Column(Time)
    open_time = Column(Time)
    close_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")
Original line number Original line Diff line number Diff line
@@ -8,10 +8,12 @@ from pydantic import BaseModel, Field


class RecordBase(BaseModel):
class RecordBase(BaseModel):
    """Records base schema"""
    """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")
    date: datetime = Field(..., title="Date of the record")
    density: float = Field(..., title="Estimated density of people")
    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):
class Record(RecordBase):
@@ -28,8 +30,9 @@ class CommentBase(BaseModel):




class Comment(CommentBase):
class Comment(CommentBase):
    """Database comments base schema"""
    """Comments reading schema"""
    id: int
    id: int
    username: str = Field(..., title="Name of the user posting the comment")
    published_at: datetime = Field(..., title="Publication date of the comment")
    published_at: datetime = Field(..., title="Publication date of the comment")
    place: str = Field(..., title="Name of the RU corresponding the comment")
    place: str = Field(..., title="Name of the RU corresponding the comment")


@@ -56,9 +59,11 @@ class News(NewsBase):


class OpeningHoursBase(BaseModel):
class OpeningHoursBase(BaseModel):
    """Database opening_hours base schema"""
    """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")
    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")
    open_time: time = Field(..., title="Opening time")
    close_time: time = Field(..., title="Closing time")
    close_time: time = Field(..., title="Closing time")


@@ -69,3 +74,12 @@ class OpeningHours(OpeningHoursBase):


    class Config:
    class Config:
        orm_mode = True
        orm_mode = True


class User(BaseModel):
    """Database user base schema"""
    id: int
    state: str
    username: str
    cookie: str
    expiration_date: datetime
+11 −41
Original line number Original line Diff line number Diff line
@@ -7,9 +7,11 @@ from fastapi import Depends
from sqlalchemy.orm import Session
from sqlalchemy.orm import Session
from db import schemas
from db import schemas
from typing import List
from typing import List
from threading import Thread


from db import database, models
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")
app = FastAPI(docs_url="/api/docs", openapi_url="/api/openapi.json")


@@ -30,24 +32,28 @@ app.add_middleware(




@app.on_event("startup")
@app.on_event("startup")
def on_startup():
async def on_startup():
    # Database creation
    # Database creation
    models.Base.metadata.create_all(bind=database.engine)
    models.Base.metadata.create_all(bind=database.engine)
    t = Thread(target=handle_cameras)
    t.start()




# Integration of routers
# Integration of routers
app.include_router(stats.router)
app.include_router(stats.router)
app.include_router(comments.router)
app.include_router(comments.router)
app.include_router(news.router)
app.include_router(news.router)
app.include_router(authentication.router)




@app.get('/api/records', response_model=List[schemas.Record])
@app.get('/api/records', response_model=List[schemas.Record])
async def get_records(place: str, db: Session = Depends(get_db)):
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)
@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_record = models.Records(**record.dict())
    db.add(db_record)
    db.add(db_record)
    db.commit()
    db.commit()
@@ -56,46 +62,10 @@ async def stats(record: schemas.RecordBase, db: Session = Depends(get_db)):




@app.delete('/api/records', response_model=None)
@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:
    if id == 0:
        db.query(models.Records).delete()
        db.query(models.Records).delete()
    else:
    else:
        db.query(models.Records).filter(models.Records.id == id).delete()
        db.query(models.Records).filter(models.Records.id == id).delete()
    db.commit()
    db.commit()
    return
    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
"""
Original line number Original line Diff line number Diff line
from . import authentication
from . import comments
from . import news
from . import opening_hours
from . import stats
+79 −0
Original line number Original line Diff line number Diff line
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
Original line number Original line Diff line number Diff line
from fastapi import APIRouter, Body, Depends
from fastapi import APIRouter, Body, Cookie, Depends
from sqlalchemy.orm import Session
from sqlalchemy.orm import Session
from typing import List
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)
@router.post('/{place}/comments', response_model=schemas.Comment)
async def create_comment(place: str, comment: schemas.CommentBase = Body(...), db: Session = Depends(get_db)):
async def create_comment(place: str, connect_id: str = Cookie(...), comment: schemas.CommentBase = Body(...), db: Session = Depends(get_db)):
    return crud.create_comment(place, comment, 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)
@router.delete('/comments/{id}', response_model=None)

backend/routers/opening_hours.py

deleted100644 → 0
+0 −29
Original line number Original line Diff line number Diff line
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)
Original line number Original line Diff line number Diff line
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from sqlalchemy.orm import Session
from typing import List
from typing import List, Tuple


from db import schemas, crud
from db import schemas, crud
from db.database import get_db
from db.database import get_db
@@ -9,7 +9,7 @@ from db.database import get_db
router = APIRouter(prefix="/api", tags=["stats"])
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)):
async def waiting_time(place: str, db: Session = Depends(get_db)):
    return crud.get_waiting_time(place, 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)
    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)):
async def stats(place: str, db: Session = Depends(get_db)):
    return crud.get_current_graph(place, 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)):
async def get_opening_hours(place: str, db: Session = Depends(get_db)):
    return crud.get_opening_hours(place, 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)):
async def get_timeslot(place: str, day: int, timeslot: bool, db: Session = Depends(get_db)):
    return crud.get_timeslot(place, day, timeslot, db)
    return crud.get_timeslot(place, day, timeslot, db)


+76 −0
Original line number Original line Diff line number Diff line
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")
Original line number Original line Diff line number Diff line
REACT_APP_BASE_URL_BACK=http://localhost:3001/api
REACT_APP_BASE_URL_BACK=http://localhost:3001/api
REACT_APP_BASE_URL_FRONT=http://localhost:3000
 No newline at end of file
Original line number Original line Diff line number Diff line
import React, { useEffect, useRef, useState } from "react";
import React, { useContext, useEffect, useRef, useState } from "react";
import axios from "axios";
import axios from "axios";
import { AiOutlineInfoCircle } from "react-icons/ai";
import { AiOutlineInfoCircle } from "react-icons/ai";
import { BiSend } from "react-icons/bi";
import { BiSend } from "react-icons/bi";
import { BsChatText } from "react-icons/bs";
import { BsChatText } from "react-icons/bs";


import { User } from "../index";
import { getSiblings } from "../utils";
import { getSiblings } from "../utils";


import "../styles/Comments.css";
import "../styles/Comments.css";


export default function Messages({ place, infos }) {
export default function Messages({ place, infos }) {
  const [user] = useContext(User);
  const [messages, setMessages] = useState([]);
  const [messages, setMessages] = useState([]);
  const [newComment, setNewComment] = useState("");
  const [newComment, setNewComment] = useState("");
  const [loading, setLoading] = useState(true);
  const [loading, setLoading] = useState(true);
@@ -22,9 +24,11 @@ export default function Messages({ place, infos }) {
    if (newComment.replace(/\s/g, "").length) {
    if (newComment.replace(/\s/g, "").length) {
      ev.preventDefault();
      ev.preventDefault();
      axios
      axios
        .post(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/comments`, {
        .post(
          content: newComment,
          `${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/comments`,
        })
          { content: newComment },
          { withCredentials: true },
        )
        .then((res) => {
        .then((res) => {
          if (messages.length) {
          if (messages.length) {
            let update = messages.map((_, index) => (index ? messages[index - 1] : res.data));
            let update = messages.map((_, index) => (index ? messages[index - 1] : res.data));
@@ -88,6 +92,10 @@ export default function Messages({ place, infos }) {
      .then((res) => {
      .then((res) => {
        setMessages(res.data);
        setMessages(res.data);
        setLoading(false);
        setLoading(false);
        if (!width && infos && res.data.length) {
          let otherIcon = document.getElementById("comments-icon-left");
          otherIcon.style.cssText = "border: 1px solid red";
        }
      })
      })
      .catch((e) => {
      .catch((e) => {
        console.log(e);
        console.log(e);
@@ -144,6 +152,9 @@ export default function Messages({ place, infos }) {
            let [year, month, day] = date.split("-");
            let [year, month, day] = date.split("-");
            return (
            return (
              <div key={index} className="comment">
              <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-content">{message.content}</div>
                <div className="comment-date">
                <div className="comment-date">
                  {${hour.substring(0, 5)} le ${day}/${month}/${year}`}
                  {${hour.substring(0, 5)} le ${day}/${month}/${year}`}
@@ -153,7 +164,7 @@ export default function Messages({ place, infos }) {
          })
          })
        )}
        )}
      </div>
      </div>
      {!infos && (
      {!infos && user && (
        <div className="comment-input-container">
        <div className="comment-input-container">
          <textarea
          <textarea
            className="comments-input"
            className="comments-input"
Original line number Original line Diff line number Diff line
import React from "react";
import React from "react";
import axios from "axios";
import axios from "axios";
import {
import {
  AreaChart,
  Line,
  Area,
  Area,
  XAxis,
  XAxis,
  YAxis,
  YAxis,
  CartesianGrid,
  CartesianGrid,
  Tooltip,
  Tooltip,
  ResponsiveContainer,
  ResponsiveContainer,
  ComposedChart,
} from "recharts";
} from "recharts";

import "../styles/Graph.css";
import "../styles/Graph.css";


export default function Graph({ place, type }) {
const CustomTooltip = ({ active, payload, label }) => {
  const [data, setData] = React.useState(null);
  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(() => {
  React.useEffect(() => {
    axios
    axios
      .get(
      .get(
        `${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(
        `${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/stats/current_graph`,
          place,
        )}/stats/${encodeURIComponent(type)}_graph`,
      )
      )
      .then((response) => {
      .then((response) => {
        setData(response.data);
        setCurrentData(response.data);
      });
      });
  }, []);
  }, []);
  if (!data) return null;


  const CustomTooltip = ({ active, payload }) => {
  const [avgData, setAvgData] = React.useState([[], 0, 0]);
    if (active && payload && payload.length) {

      return (
  React.useEffect(() => {
        <div className="custom-tooltip">
    axios
          <p className="label">{`Temps d'attente : ${payload[0].value} minutes`}</p>
      .get(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/stats/avg_graph`)
        </div>
      .then((response) => {
      );
        setAvgData(response.data);
    }
      });
    return null;
  }, []);
  };


  return (
  return (
    <>
      {!!currentData[0].length && (
        <div style={{ height: "60%", padding: "3rem" }}>
        <div style={{ height: "60%", padding: "3rem" }}>
          <div className="graph">
          <div className="graph">
            <ResponsiveContainer width="100%" height="100%">
            <ResponsiveContainer width="100%" height="100%">
          <AreaChart
              <ComposedChart
            data={data}
                margin={{
                margin={{
                  top: 5,
                  top: 5,
                  right: 30,
                  right: 30,
              left: 20,
                  left: -10,
                  bottom: 5,
                  bottom: 5,
                }}
                }}
              >
              >
                {checked ? (
                  <Line
                    data={avgData}
                    type="monotone"
                    dataKey="time"
                    stroke="#FF0000"
                    strokeWidth={2}
                    dot={false}
                  />
                ) : (
                  <div />
                )}
                <defs>
                <defs>
                  <linearGradient id="colorGradient" x1="0" y1="0" x2="0" y2="1">
                  <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="50%" stopColor="#fff200" stopOpacity={0.5} />
                    <stop offset="90%" stopColor="#1e9600" stopOpacity={0.35} />
                    <stop offset="90%" stopColor="#1e9600" stopOpacity={0.35} />
                  </linearGradient>
                  </linearGradient>
@@ -62,7 +94,14 @@ export default function Graph({ place, type }) {
                  axisLine={false}
                  axisLine={false}
                  tickLine={false}
                  tickLine={false}
                  tick={{ fill: "#FFFFFF", fontSize: "18" }}
                  tick={{ fill: "#FFFFFF", fontSize: "18" }}
                  ticks={[...Array(4).keys()].map(
                    (i) => currentData[1] + (i * (currentData[2] - currentData[1])) / 3,
                  )}
                  dataKey="name"
                  dataKey="name"
                  type="number"
                  interval="preserveStartEnd"
                  domain={[currentData[1], currentData[2]]}
                  tickFormatter={formatXAxis}
                />
                />
                <YAxis
                <YAxis
                  axisLine={false}
                  axisLine={false}
@@ -71,24 +110,32 @@ export default function Graph({ place, type }) {
                  tickInt
                  tickInt
                  tickCount={10}
                  tickCount={10}
                  dataKey="time"
                  dataKey="time"
                  type="number"
                  domain={[0, (dataMax) => 10 * Math.floor((dataMax + 10) / 10)]}
                  domain={[0, (dataMax) => 10 * Math.floor((dataMax + 10) / 10)]}
                  allowDecimals={false}
                  allowDecimals={false}
                  name="Temps d'attente"
                  name="Temps d'attente"
                />
                />
                <Tooltip content={<CustomTooltip />} />
                <Tooltip content={<CustomTooltip />} />
                <Area
                <Area
                  data={currentData[0]}
                  type="monotone"
                  type="monotone"
                  dataKey="time"
                  dataKey="time"
                  stroke="#FFFFFF"
                  stroke="#FFFFFF"
              strokeWidth={1}
                  strokeWidth={2}
                  fillOpacity={1}
                  fillOpacity={1}
                  fill="url(#colorGradient)"
                  fill="url(#colorGradient)"
              dot={{ stroke: "#0967D2", strokeWidth: 2, fill: "#fff" }}
                />
                />
          </AreaChart>
              </ComposedChart>
            </ResponsiveContainer>
            </ResponsiveContainer>
          </div>
          </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>
        </div>
      )}
    </>
  );
  );
}
}
Original line number Original line Diff line number Diff line
import React from "react";
import React, { useContext, useEffect } from "react";
import { Link } from "react-router-dom";
import { Link } from "react-router-dom";
import { BiLogOutCircle, BiLogInCircle } from "react-icons/bi";


import "../styles/Header.css";
import "../styles/Header.css";
import { User } from "../index";
import axios from "axios";


export default function Header({ selection, setSelection }) {
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;
  let width = window.innerWidth > 0 ? window.innerWidth : screen.width;
  width = width > 600;
  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 (
  return (
    <div id="header-container">
    <div id="header-container" style={!selection ? { flexDirection: "row" } : {}}>
      <div id="header-restaurant-status">
      <div id="header-restaurant-status">
        {!selection
        {width &&
          (!selection
            ? "Accueil"
            ? "Accueil"
          : `${selection.name} : actuellement ${selection.status ? "ouvert" : "fermé"}`}
            : `${selection.name} : actuellement ${selection.status ? "ouvert" : "fermé"}`)}
      </div>
      </div>
      <Link id="header-home-link" to="/" onClick={() => setSelection(null)}>
      <Link id="header-home-link" to="/" onClick={() => setSelection(null)}>
        <h2>{width || !selection ? "Eatfast" : selection.name}</h2>
        <h2>{width || !selection ? "Eatfast" : selection.name}</h2>
      </Link>
      </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>
    </div>
  );
  );
}
}
Original line number Original line Diff line number Diff line
@@ -4,33 +4,35 @@ import axios from "axios";
import "../styles/WaitingTime.css";
import "../styles/WaitingTime.css";


export default function WaitingTime({ place }) {
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(() => {
  React.useEffect(() => {
    axios
    axios
      .get(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/waiting_time`)
      .get(`${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/waiting_time`)
      .then((res) => {
      .then((res) => {
        setPost(res.data);
        setData(res.data);
      });
      });
  }, []);
  }, []);


  return (
  return (
    <div id="waiting-time-parent">
    <div id="waiting-time-parent">
      {post[1] ? (
      {data.status ? (
        data.waiting_time || data.waiting_time == 0 ? (
          <div id="waiting-time-display">
          <div id="waiting-time-display">
          Le RU ouvre aujourd&apos;hui à :
            Le temps d&apos;attente est estimé à
            <div className="waiting-time-minutes">
            <div className="waiting-time-minutes">
            <b id="waiting-time-number">
              <b id="waiting-time-number">{data.waiting_time}</b> minutes
              {String(post[0]).padStart(2, "0")}h{String(post[1]).padStart(2, "0")}
            </b>
            </div>
            </div>
          </div>
          </div>
      ) : post[0] ? (
        ) : (
          <div className="waiting-time-minutes">Pas de données</div>
        )
      ) : data.next_timetable ? (
        <div id="waiting-time-display">
        <div id="waiting-time-display">
          Le temps d&apos;attente est estimé à :
          Le RU ouvre aujourd&apos;hui à
          <div className="waiting-time-minutes">
          <span className="waiting-time-minutes">
            <b id="waiting-time-number">{post[0]}</b> minutes
            <b id="waiting-time-number">{data.next_timetable}</b>
          </div>
          </span>
        </div>
        </div>
      ) : (
      ) : (
        <div className="waiting-time-minutes">Le RU est fermé pour aujourd&apos;hui.</div>
        <div className="waiting-time-minutes">Le RU est fermé pour aujourd&apos;hui.</div>
+0 −13
Original line number Original line Diff line number Diff line
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>
  );
}

frontend/src/global.css

deleted100644 → 0
+0 −0
Original line number Original line Diff line number Diff line
Original line number Original line Diff line number Diff line
import React, { useEffect, useState } from "react";
import React, { createContext, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import { createRoot } from "react-dom/client";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import axios from "axios";
import axios from "axios";
@@ -9,10 +9,13 @@ import { HomePage, RestaurantPage, NotFoundPage, DetailsPage } from "./views";
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/css/bootstrap.min.css";
import "./styles/index.css";
import "./styles/index.css";


export const User = createContext(null);

export default function App() {
export default function App() {
  const [restaurantsList, setRestaurantsList] = useState([]);
  const [restaurantsList, setRestaurantsList] = useState([]);
  const [selection, setSelection] = useState(null);
  const [selection, setSelection] = useState(null);
  const [loading, setLoading] = useState(true);
  const [loading, setLoading] = useState(true);
  const [user, setUser] = useState(localStorage.getItem("user"));


  useEffect(() => {
  useEffect(() => {
    axios
    axios
@@ -40,6 +43,7 @@ export default function App() {


  return (
  return (
    <div className="app">
    <div className="app">
      <User.Provider value={[user, setUser]}>
        <Router>
        <Router>
          <Header {...{ selection, setSelection }} />
          <Header {...{ selection, setSelection }} />
          <div className="page">
          <div className="page">
@@ -60,6 +64,7 @@ export default function App() {
          </div>
          </div>
        </Router>
        </Router>
        <Footer />
        <Footer />
      </User.Provider>
    </div>
    </div>
  );
  );
}
}
Original line number Original line Diff line number Diff line
@@ -111,6 +111,15 @@
    margin-right: 0.5rem;
    margin-right: 0.5rem;
}
}


.comment-title { 
    font-size: 0.8rem;
    text-align: right;
}

.comment-title-infos { 
    font-size : 1.2rem;
}

@media only screen and (max-width: 600px) {
@media only screen and (max-width: 600px) {
    .comments-side-bar {
    .comments-side-bar {
        width: 0px;
        width: 0px;
@@ -145,4 +154,8 @@
    .comment-input-container {
    .comment-input-container {
        padding-left: 0rem;
        padding-left: 0rem;
    }
    }

    .comments-input {
        max-height: 5rem;
    }
}
}
 No newline at end of file
Original line number Original line Diff line number Diff line
@@ -8,3 +8,17 @@
    width: 100%;
    width: 100%;
    display: inline-block;
    display: inline-block;
}
}

#graph-avg-graph {
    border: none;
    background: none;
    color: inherit;
    font-style: italic;
    font-size: 0.8rem;
    font-weight: lighter;
    margin-top: -1.5rem;
}

#graph-avg-graph:hover {
    text-decoration-line: underline;
}
 No newline at end of file
Original line number Original line Diff line number Diff line
#header-container {
#header-container {
    display: flex;
    display: flex;
    justify-content: space-between;
    justify-content: space-between;
    align-items: baseline;
    align-items: center;
    padding-left: 1rem;
    padding-left: 1rem;
    padding-right: 1rem;
    padding-right: 1rem;
    background-color: rgb(33, 37, 41);
    background-color: rgb(33, 37, 41);
@@ -24,15 +24,20 @@
    font-weight: lighter;
    font-weight: lighter;
}
}


#header-button {
    height: 2rem;
    width: 2rem;
}

#header-button:hover {
    cursor: pointer;
}

@media only screen and (max-width: 600px) {
@media only screen and (max-width: 600px) {
    #header-home-link > h2 {
    #header-home-link > h2 {
        font-size: 2rem;
        font-size: 2rem;
    }
    }


    #header-restaurant-status {
        display: none;
    }

    #header-container {
    #header-container {
        flex-direction: column;
        flex-direction: column;
        align-items: center;
        align-items: center;
Original line number Original line Diff line number Diff line
@@ -10,6 +10,8 @@
#home-selection-title {
#home-selection-title {
    margin-bottom: 0;
    margin-bottom: 0;
    padding-bottom: 2%;
    padding-bottom: 2%;
    padding-left: 5%;
    padding-right: 5%;
}
}


#home-table {
#home-table {
@@ -72,9 +74,11 @@


    .home-restaurant-status {
    .home-restaurant-status {
        display: none;
        display: none;
        font-size: 1.5rem;
    }
    }


    .home-restaurant-name {
    .home-restaurant-name {
        padding-right: 2rem;
        padding-right: 2rem;
        font-size: 1.5rem;
    }
    }
}
}
Original line number Original line Diff line number Diff line
@@ -8,14 +8,15 @@


#waiting-time-display {
#waiting-time-display {
    font-size: 1.4rem;
    font-size: 1.4rem;
    padding-left: 3rem;
    padding-right: 3rem;
}
}


.waiting-time-minutes {
.waiting-time-minutes {
    font-size: 1.8rem;
    font-size: 1.8rem;
    display: inline;
    display: inline;
    vertical-align: baseline;
    vertical-align: baseline;
    padding-left: 3rem;
    margin-left: 0.5rem;
    padding-right: 3rem;
}
}


#waiting-time-number {
#waiting-time-number {