diff --git a/backend/db/crud.py b/backend/db/crud.py index 2ba72873ea5b8afefb49e20733bd696bb81e348c..fd699fca902b8f440954db14ab60b38fdd69e0d0 100644 --- a/backend/db/crud.py +++ b/backend/db/crud.py @@ -1,130 +1,171 @@ -""" -Module to interact with the database -""" -from datetime import date, datetime, time, timedelta -from sqlalchemy.orm import Session -from sqlalchemy.sql import func -import pytz - -from db import models, schemas - - -# Define CRUD operation to collect the statistics - -def get_waiting_time(place: str, db: Session): - """ Get the last estimated waiting time for the given place """ - db_record = db.query(models.Records).filter(models.Records.place == place).order_by(models.Records.date.desc()).first() - if db_record.waiting_time is not None: - return db_record.waiting_time - else: - raise Exception - - -def get_stats(place: str, weekday: int, min_time_hour: int, min_time_mn: int, max_time_hour: int, max_time_mn: int, 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.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 - ).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]: - 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 = f'{start_time.hour:02}h{start_time.minute:02}' - slots_list.append({'name': name, 'time': average_waiting_time}) - - min_time, max_time = time(min_time_hour, min_time_mn), time(max_time_hour, max_time_mn) - stats = [] - start_time, end_time = min_time, shift_time(min_time, interval) - while start_time < max_time: - add_slot(stats, start_time, end_time) - start_time, end_time = end_time, shift_time(end_time, interval) - - return stats - - -# Define CRUD operation for the comments - -def get_comments(place: str, page: int, db: Session): - """ Get the 10 last comments for the given place """ - if page == 0: - comments = db.query(models.Comments).order_by(models.Comments.published_at.desc(), models.Comments.id.desc()).all() - else: - comments = db.query( - models.Comments).filter( - models.Comments.place == place).order_by( - models.Comments.published_at.desc(), - models.Comments.id.desc()).slice( - (page - - 1) * - 10, - page * - 10).all() - return comments - - -def create_comment(place: str, new_comments: schemas.CommentBase, db: Session): - """ Add a new comment to the database """ - date = datetime.now(tz=pytz.timezone("Europe/Paris")) - db_comment = models.Comments(**new_comments.dict(), published_at=date, place=place) - db.add(db_comment) - db.commit() - db.refresh(db_comment) - return db_comment - - -def delete_comment(id: int, db: Session): - """ Delete the comment with the matching id """ - if id == 0: - db.query(models.Comments).delete() - else: - db.query(models.Comments).filter(models.Comments.id == id).delete() - db.commit() - - -# Define CRUD operation for the news - -def get_news(place: str, db: Session): - """ Get the news for the given place """ - news = db.query(models.News).filter(models.News.place == place).order_by(models.News.published_at.desc()).all() - return news - - -def create_news(new_news: schemas.NewsBase, db: Session): - """ Add a news to the database """ - date = datetime.now(tz=pytz.timezone("Europe/Paris")) - db_news = models.News(**new_news.dict(), published_at=date) - db.add(db_news) - db.commit() - db.refresh(db_news) - return db_news - - -def delete_news(id: int, db: Session): - """ Delete the news with the matching id """ - if id == 0: - db.query(models.News).delete() - else: - db.query(models.News).filter(models.News.id == id).delete() - db.commit() +""" +Module to interact with the database +""" +from datetime import date, datetime, time, timedelta +from sqlalchemy.orm import Session +from sqlalchemy.sql import func +import pytz + +from db import models, schemas + + +# Define CRUD operation to collect the statistics + +def get_waiting_time(place: str, db: Session): + """ Get the last estimated waiting time for the given place """ + db_record = db.query(models.Records).filter(models.Records.place == place).order_by(models.Records.date.desc()).first() + if db_record.waiting_time is not None: + return db_record.waiting_time + else: + raise Exception + + +def get_stats(place: str, weekday: int, min_time_hour: int, min_time_mn: int, max_time_hour: int, max_time_mn: int, 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.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 + ).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]: + 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 = f'{start_time.hour:02}h{start_time.minute:02}' + slots_list.append({'name': name, 'time': average_waiting_time}) + + min_time, max_time = time(min_time_hour, min_time_mn), time(max_time_hour, max_time_mn) + stats = [] + start_time, end_time = min_time, shift_time(min_time, interval) + while start_time < max_time: + add_slot(stats, start_time, end_time) + start_time, end_time = end_time, shift_time(end_time, interval) + + return stats + + +# Define CRUD operation for the comments + +def get_comments(place: str, page: int, db: Session): + """ Get the 10 last comments for the given place """ + if page == 0: + comments = db.query(models.Comments).order_by(models.Comments.published_at.desc(), models.Comments.id.desc()).all() + else: + comments = db.query( + models.Comments).filter( + models.Comments.place == place).order_by( + models.Comments.published_at.desc(), + models.Comments.id.desc()).slice( + (page - + 1) * + 10, + page * + 10).all() + return comments + + +def create_comment(place: str, new_comments: schemas.CommentBase, db: Session): + """ Add a new comment to the database """ + date = datetime.now(tz=pytz.timezone("Europe/Paris")) + db_comment = models.Comments(**new_comments.dict(), published_at=date, place=place) + db.add(db_comment) + db.commit() + db.refresh(db_comment) + return db_comment + + +def delete_comment(id: int, db: Session): + """ Delete the comment with the matching id """ + if id == 0: + db.query(models.Comments).delete() + else: + db.query(models.Comments).filter(models.Comments.id == id).delete() + db.commit() + + +# Define CRUD operation for the news + +def get_news(place: str, db: Session): + """ Get the news for the given place """ + news = db.query(models.News).filter(models.News.place == place).order_by(models.News.published_at.desc()).all() + return news + + +def create_news(new_news: schemas.NewsBase, db: Session): + """ Add a news to the database """ + date = datetime.now(tz=pytz.timezone("Europe/Paris")) + db_news = models.News(**new_news.dict(), published_at=date) + db.add(db_news) + db.commit() + db.refresh(db_news) + return db_news + + +def delete_news(id: int, db: Session): + """ Delete the news with the matching id """ + if id == 0: + db.query(models.News).delete() + else: + db.query(models.News).filter(models.News.id == id).delete() + db.commit() + + +# Define CRUD operation for the opening hours + +def get_opening_hours(place: str, db: Session): + """ Get the opening hours for the given place """ + opening_hours = db.query( + models.OpeningHours + ).filter( + models.OpeningHours.place == place + ).order_by( + models.OpeningHours.day, models.OpeningHours.timeslot.desc() + ).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).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 """ + db_opening_hours = models.News(**new_opening_hours.dict()) + db.add(db_opening_hours) + db.commit() + db.refresh(db_opening_hours) + return db_opening_hours + + +def delete_opening_hours(id: int, db: Session): + """ Delete the opening hours with the matching id """ + if id == 0: + db.query(models.OpeningHours).delete() + else: + db.query(models.OpeningHours).filter(models.OpeningHours.id == id).delete() + db.commit() diff --git a/backend/db/models.py b/backend/db/models.py index 1a39110dec496e6dddcf945637598659526d1627..635abd73968e7c712c97f4760bfb9dfa58441007 100644 --- a/backend/db/models.py +++ b/backend/db/models.py @@ -1,3 +1,4 @@ +<<<<<<< HEAD """ Models of the database for magasin app """ @@ -37,3 +38,58 @@ class News(Base): published_at = Column(DateTime) end_date = Column(DateTime) place = Column(String(10)) +======= +""" +Models of the database for magasin app +""" +from sqlalchemy import Column, Integer, DateTime, Float, Interval, String, Text, Boolean + +from db.database import Base + + +class Records(Base): + """Records sql table model""" + __tablename__ = "records" + + id = Column(Integer, primary_key=True, index=True) + place = Column(String(10)) + date = Column(DateTime) + density = Column(Float) + waiting_time = Column(Interval) + + +class Comments(Base): + """Comments sql table model""" + __tablename__ = "comments" + + id = Column(Integer, primary_key=True, index=True) + comment = Column(Text) + date = Column(DateTime) + place = Column(String(10)) + + +class News(Base): + """News sql table model""" + __tablename__ = "news" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String(50)) + content = Column(Text) + published_at = Column(DateTime) + end_date = Column(DateTime) + place = Column(String(10)) + + +class OpeningHours(Base): + """OpeningHours sql table model""" + __tablename__ = "opening_hours" + + id = Column(Integer, primary_key=True, index=True) + place = Column(String(10)) + day= Column(Integer) + timeslot = Column(Boolean) + open_hour = Column(Integer) + open_minute = Column(Integer) + close_hour = Column(Integer) + close_minute = Column(Integer) +>>>>>>> db and back routes for restaurants opening hours diff --git a/backend/db/schemas.py b/backend/db/schemas.py index 8811cdc25234b6018982fe9daecb33f8d1e6366b..a915d3035b72aff4f72558b3ec5f5d37ce5d66d1 100644 --- a/backend/db/schemas.py +++ b/backend/db/schemas.py @@ -1,3 +1,4 @@ +<<<<<<< HEAD """ Pydantic schemas for the magasin app """ @@ -52,3 +53,78 @@ class News(NewsBase): class Config: orm_mode = True +======= +""" +Pydantic schemas for the magasin app +""" +from typing import Optional +from datetime import datetime, timedelta +from pydantic import BaseModel, Field + + +class RecordBase(BaseModel): + """Records base schema""" + place: str = Field(..., title="Name of the RU corresponding the given record") + date: datetime = Field(..., title="Date of the record") + density: float = Field(..., title="Estimated density of people") + waiting_time: Optional[timedelta] = Field(title="Estimated waiting time for people coming at this date") + + +class Record(RecordBase): + """Database records base schema""" + id: int + + class Config: + orm_mode = True + + +class CommentBase(BaseModel): + """Comments base schema""" + comment: str = Field(..., title="Content of the comment posted") + + +class Comment(CommentBase): + """Database comments base schema""" + id: int + date: datetime = Field(..., title="Publication date of the comment") + place: str = Field(..., title="Name of the RU corresponding the comment") + + class Config: + orm_mode = True + + +class NewsBase(BaseModel): + """News sql table model""" + title: str = Field(..., title="Title of the news") + content: str = Field(..., title="Content of the news") + end_date: datetime = Field(..., title="End date to display the news") + place: str = Field(..., title="Name of the RU corresponding the news") + + +class News(NewsBase): + """Database news base schema""" + id: int + published_at: datetime = Field(..., title="Publication date of the news") + + class Config: + orm_mode = True + + +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_hour: int = Field(..., title="Hour of the opening time") + open_minute: int = Field(..., title="Minute of the opening time") + close_hour: int = Field(..., title="Hour of the closing time") + close_minute: int = Field(..., title="Minute of the closing time") + + +class OpeningHours(OpeningHoursBase): + """Database opening_hours base schema""" + id: int + + class Config: + orm_mode = True +>>>>>>> db and back routes for restaurants opening hours diff --git a/backend/main.py b/backend/main.py index d4d2ed49d8b5d2e69c4a45a7bcc8b1006f389a8b..705da50639356f7b9c1ef11196c6590c926158db 100644 --- a/backend/main.py +++ b/backend/main.py @@ -4,7 +4,7 @@ from dotenv import load_dotenv import os from db import database, models -from routers import stats, comments, news +from routers import stats, comments, news, opening_hours app = FastAPI(docs_url="/api/docs", openapi_url="/api/openapi.json") @@ -34,6 +34,7 @@ def on_startup(): app.include_router(stats.router) app.include_router(comments.router) app.include_router(news.router) +app.include_router(opening_hours.router) """ diff --git a/backend/routers/opening_hours.py b/backend/routers/opening_hours.py new file mode 100644 index 0000000000000000000000000000000000000000..5f2725e27dc411a1cf2ca7b7afc1950941537303 --- /dev/null +++ b/backend/routers/opening_hours.py @@ -0,0 +1,29 @@ +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/opening_hours", tags=["opening_hours"]) + + +@router.get('/{place}', response_model=List[schemas.OpeningHours]) +async def get_opening_hours(place: str, page: int = 1, db: Session = Depends(get_db)): + return crud.get_opening_hours(place, page, db) + + +@router.get('/{place}/{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('/{place}', response_model=schemas.OpeningHours) +async def create_opening_hours(place: str, opening_hours: schemas.OpeningHoursBase, db: Session = Depends(get_db)): + return crud.create_opening_hours(place, opening_hours, db) + + +@router.delete('/{id}', response_model=None) +async def delete_opening_hours(id: int, db: Session = Depends(get_db)): + return crud.delete_opening_hours(id, db)