2 Commits

Author SHA1 Message Date
22a6cf8028 Ratings table added 2024-09-04 20:32:49 +03:00
0094b18ddc Endpoint cancelling a booking added 2024-08-17 22:13:49 +03:00
6 changed files with 151 additions and 58 deletions

View File

@ -57,39 +57,6 @@ async def filter_ann(schema: pydantic_schemas.SortAnnouncements, db: AsyncSessio
filter_query = await db.execute(select(orm_models.Announcement).where(literal_column(f"announcements.{name}") == filt_val)) filter_query = await db.execute(select(orm_models.Announcement).where(literal_column(f"announcements.{name}") == filt_val))
filtered = set(filter_query.scalars().all()) filtered = set(filter_query.scalars().all())
res = res.intersection(filtered) res = res.intersection(filtered)
# # отфильтровываем подходящие объявления
# res = await db.execute(
# select(orm_models.Announcement).where(
# ((schema.obsolete == None) | ((schema.obsolete != None) & (orm_models.Announcement.obsolete == schema.obsolete)))
# & ((schema.user_id == None) | ((schema.user_id != None) & (orm_models.Announcement.user_id == schema.user_id)))
# & ((schema.metro == None) | ((schema.metro != None) & (orm_models.Announcement.metro == schema.metro)))
# & ((schema.category == None) | ((schema.category != None) & (orm_models.Announcement.category == schema.category)))
# )
# )
# .where(schema.user_id != None and orm_models.Announcement.user_id == schema.user_id)
# .where(schema.metro != None and orm_models.Announcement.metro == schema.metro)
# .where(schema.category != None and orm_models.Announcement.category == schema.category)
# statement = text("SELECT * FROM announcements "
# "WHERE announcements.obsolete = :obsolete "
# "INTERSECT"
# "SELECT * FROM announcements "
# "WHERE announcements.user_id == :user_id "
# "INTERSECT"
# "SELECT * FROM announcements "
# "WHERE announcements.metro == :metro "
# "INTERSECT"
# "SELECT * FROM announcements "
# "WHERE announcements.category == :category")
# res = await db.execute(statement,
# {"obsolete": schema.obsolete,
# "user_id": schema.user_id,
# "metro": schema.metro,
# "category": schema.category}
# )
# возвращаем все подходящие объявления # возвращаем все подходящие объявления
return res return res

View File

@ -43,33 +43,66 @@ if not os.path.exists("./uploads"):
# создаем эндпоинт для хранения файлов пользователя # создаем эндпоинт для хранения файлов пользователя
app.mount("/uploads", StaticFiles(directory = "./uploads")) app.mount("/uploads", StaticFiles(directory = "./uploads"))
# эндпоинт для возвращения согласия в pdf # эндпоинт для возвращения согласия в pdf
@app.get("/privacy_policy.pdf") @app.get("/privacy_policy.pdf")
async def privacy_policy(): async def privacy_policy():
return FileResponse("./privacy_policy.pdf") return FileResponse("./privacy_policy.pdf")
# получение списка объявлений # получение списка объявлений
@app.get("/api/announcements", response_model=List[pydantic_schemas.Announcement])#адрес объявлений @app.get("/api/announcements", response_model=List[pydantic_schemas.Announcement])
async def announcements_list(db: Annotated[Session, Depends(auth_utils.get_session)], obsolete: Union[bool, None] = False, user_id: Union[int, None] = None, async def announcements_list(db: Annotated[Session, Depends(auth_utils.get_session)],
metro: Union[str, None] = None,category: Union[str, None] = None): current_user: Annotated[pydantic_schemas.User, Depends(auth_utils.get_current_active_user)],
obsolete: Union[bool, None] = False, user_id: Union[int, None] = None, metro: Union[str, None] = None,
category: Union[str, None] = None):
# параметры для сортировки (схема pydantic schemas.SortAnnouncements) # параметры для сортировки (схема pydantic schemas.SortAnnouncements)
params_to_sort = pydantic_schemas.SortAnnouncements(obsolete=obsolete, user_id=user_id, metro=metro, category=category) params_to_sort = pydantic_schemas.SortAnnouncements(obsolete=obsolete, user_id=user_id, metro=metro, category=category)
# получаем результат # получаем результат (значения с определенными полями obsolete, user_id, metro, category).
# user_id - id пользователя, которому принадлежат объявления
result = await add_poems_and_filters.filter_ann(db=db, schema=params_to_sort) result = await add_poems_and_filters.filter_ann(db=db, schema=params_to_sort)
return result # для каждого отфильтрованного объявления проверяем, забронировано ли оно текущим пользователем и в зависимости
# от этого флаг booked_by_current_user устанавливаем в 0 или в 1
check_if_booked = [pydantic_schemas.Announcement.from_orm(elem) for elem in result]
for an in check_if_booked:
# ищем пару с заданными id объявления и id текущего пользователя
query = await db.execute(select(orm_models.AnnouncementUser).where(
orm_models.AnnouncementUser.announcement_id == an.id).where(
orm_models.AnnouncementUser.booking_user_id == current_user.id))
pair_found = query.scalars().first()
if pair_found:
an.booked_by_current_user = True
else:
an.booked_by_current_user = False
return check_if_booked
# получаем данные одного объявления # получаем данные одного объявления
@app.get("/api/announcement", response_model=pydantic_schemas.AnnResponce) @app.get("/api/announcement", response_model=pydantic_schemas.AnnResponce)
async def single_announcement(ann_id:int, db: Annotated[Session, Depends(auth_utils.get_session)]): # передаем индекс обявления async def single_announcement(ann_id:int, db: Annotated[Session, Depends(auth_utils.get_session)],
current_user: Annotated[pydantic_schemas.User, Depends(auth_utils.get_current_active_user)]):
# Считываем данные из Body и отображаем их на странице. # Считываем данные из Body и отображаем их на странице.
# В последствии будем вставлять данные в html-форму # В последствии будем вставлять данные в html-форму
announcement = await db.get(orm_models.Announcement, ann_id) announcement = await db.get(orm_models.Announcement, ann_id)
#announcement = await db.execute(select(orm_models.Announcement)).scalars().all() #announcement = await db.execute(select(orm_models.Announcement)).scalars().all()
if not announcement: if not announcement:
raise HTTPException(status_code=404, detail="Item not found") raise HTTPException(status_code=404, detail="Item not found")
return announcement
# создаем форму pydantic_schemas.AnnResponce из ORM-объекта announcement
announcement_model = pydantic_schemas.AnnResponce.from_orm(announcement)
# ищем пару с заданными id объявления и id текущего пользователя
query = await db.execute(select(orm_models.AnnouncementUser).where(
orm_models.AnnouncementUser.announcement_id == announcement.id).where(
orm_models.AnnouncementUser.booking_user_id == current_user.id))
pair_found = query.scalars().first()
# если такая пара найдена, записываем в поле booked_by_current_user True
if pair_found:
announcement_model.booked_by_current_user = True
return announcement_model
# Занести объявление в базу данных # Занести объявление в базу данных
@ -124,11 +157,11 @@ async def delete_from_db(announcement: pydantic_schemas.DelAnnouncement, db: Ann
return {"Success": True} return {"Success": True}
except: except:
raise HTTPException(status_code=500, detail="Problem with adding to database") raise HTTPException(status_code=500, detail="Problem with deleteng the announcement from the database")
# Забронировать объявление # Забронировать объявление
@app.post("/api/book") @app.put("/api/book")
async def change_book_status(data: pydantic_schemas.Book, current_user: Annotated[pydantic_schemas.User, Depends(auth_utils.get_current_user)], async def change_book_status(data: pydantic_schemas.Book, current_user: Annotated[pydantic_schemas.User, Depends(auth_utils.get_current_user)],
db: Annotated[Session, Depends(auth_utils.get_session)]): db: Annotated[Session, Depends(auth_utils.get_session)]):
# Находим объявление по данному id # Находим объявление по данному id
@ -153,7 +186,7 @@ async def change_book_status(data: pydantic_schemas.Book, current_user: Annotate
new_pair = orm_models.AnnouncementUser(announcement_to_change.id, current_user.id) new_pair = orm_models.AnnouncementUser(announcement_to_change.id, current_user.id)
# Инкрементируем поле booked_counter на 1 # Инкрементируем поле booked_counter на 1
announcement_to_change.booked_counter += 1 announcement_to_change.booked_counter += 1
# вставляем индекс забронировавшего пользователя в поле booked_by # добавляем запись в таблицу announcementuser
db.add(new_pair) db.add(new_pair)
# фиксируем изменения в бд # фиксируем изменения в бд
@ -163,7 +196,31 @@ async def change_book_status(data: pydantic_schemas.Book, current_user: Annotate
raise HTTPException(status_code=403, detail="The announcement is already booked by this user") raise HTTPException(status_code=403, detail="The announcement is already booked by this user")
# reginstration # Отмена брони
@app.delete("/api/book")
async def cancel_booking(data: pydantic_schemas.CancelBooking, current_user: Annotated[pydantic_schemas.User, Depends(auth_utils.get_current_user)],
db: Annotated[Session, Depends(auth_utils.get_session)]):
# ищем пару с заданными id объявления и пользователя
query = await db.execute(select(orm_models.AnnouncementUser).where(
orm_models.AnnouncementUser.announcement_id == data.announcement_id).where(
orm_models.AnnouncementUser.booking_user_id == current_user.id))
pair_found = query.scalars().first()
# если не найдена
if not pair_found:
raise HTTPException(status_code=404, detail="Item not found")
# если найдена пытаемся удалить из бд
else:
try:
await db.delete(pair_found) # удаление из БД
await db.commit() # сохраняем изменения
return {"Success": True}
except:
raise HTTPException(status_code=500, detail="Problem with deleteng pair 'booked_announcement_id:user_id' from the database")
# Регистрация
@app.post("/api/signup") @app.post("/api/signup")
async def create_user(nickname: Annotated[str, Form()], password: Annotated[str, Form()], db: Annotated[Session, Depends(auth_utils.get_session)], async def create_user(nickname: Annotated[str, Form()], password: Annotated[str, Form()], db: Annotated[Session, Depends(auth_utils.get_session)],
name: Annotated[str, Form()]=None, surname: Annotated[str, Form()]=None, avatar: Annotated[UploadFile, Form()]=None): name: Annotated[str, Form()]=None, surname: Annotated[str, Form()]=None, avatar: Annotated[UploadFile, Form()]=None):

View File

@ -37,17 +37,11 @@ class Announcement(Base): #класс объявления
src = Column(String, nullable=True) #изображение продукта в объявлении src = Column(String, nullable=True) #изображение продукта в объявлении
metro = Column(String) #ближайщее метро от адреса нахождения продукта metro = Column(String) #ближайщее метро от адреса нахождения продукта
trashId = Column(Integer, nullable=True) trashId = Column(Integer, nullable=True)
booked_by = Column(Integer, nullable=True) # id пользователя, забронировавшего объявление
booked_counter = Column(Integer, nullable=True) #количество забронировавших (0 - никто не забронировал) booked_counter = Column(Integer, nullable=True) #количество забронировавших (0 - никто не забронировал)
obsolete = Column(Boolean, default=False) # состояние объявления (по-умолчанию считаем его актуальным) obsolete = Column(Boolean, default=False) # состояние объявления (по-умолчанию считаем его актуальным)
user = relationship("User", secondary="announcementuser", back_populates="announcements") user = relationship("User", secondary="announcementuser", back_populates="announcements")
# Класс пары, хранящей id объявления и id забронировавшего юзера
@dataclasses.dataclass
class UserAnouncementPair:
announcement_id: int
booking_user_id: int
class AnnouncementUser(Base): class AnnouncementUser(Base):
__tablename__ = "announcementuser" __tablename__ = "announcementuser"
@ -61,11 +55,17 @@ class AnnouncementUser(Base):
# class Ratings(Base): class Ratings(Base):
# __tablename__ = "ratings" __tablename__ = "ratings"
# rated_user_id = Column(Integer, primary_key=True) # id пользователя, оставившего оценку def __init__(self, rated_user_id, user_giving_rating_id, rating_val):
# rating_value = Column(Integer, primary_key=True) # оценка self.rated_user_id = rated_user_id
self.user_giving_rating_id = user_giving_rating_id
self.rating_value = rating_val
rated_user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) # id пользователя, оставившего оценку
user_giving_rating_id = Column(Integer, ForeignKey("users.id"), primary_key=True) # id оцениваемого пользователя
rating_value = Column(Integer, primary_key=True) # оценка
class Trashbox(Base): #класс мусорных баков class Trashbox(Base): #класс мусорных баков

View File

@ -1,13 +1,15 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import Annotated, Union from typing import Union
from datetime import date from datetime import date
from typing import List from typing import List
from fastapi import UploadFile, Form
class Book(BaseModel): class Book(BaseModel):
id: int id: int
class CancelBooking(BaseModel):
announcement_id: int
class DelAnnouncement(BaseModel): class DelAnnouncement(BaseModel):
id: int id: int
@ -25,7 +27,7 @@ class Announcement(BaseModel):
src: Union[str, None] = None #изображение продукта в объявлении src: Union[str, None] = None #изображение продукта в объявлении
metro: str #ближайщее метро от адреса нахождения продукта metro: str #ближайщее метро от адреса нахождения продукта
trashId: Union[int, None] = None trashId: Union[int, None] = None
booked_by: Union[int, None] = 0 #статус бронирования (либо 0, либо айди бронирующего) booked_by_current_user: Union[bool, None] = False
obsolete: bool obsolete: bool
class Config: class Config:
@ -47,7 +49,7 @@ class AnnResponce(BaseModel):
src: Union[str, None] = None #изображение продукта в объявлении src: Union[str, None] = None #изображение продукта в объявлении
metro: str #ближайщее метро от адреса нахождения продукта metro: str #ближайщее метро от адреса нахождения продукта
trashId: Union[int, None] = None trashId: Union[int, None] = None
booked_by: Union[int, None] = 0 #статус бронирования (либо 0, либо айди бронирующего) booked_by_current_user: Union[bool, None] = False
class Config: class Config:
orm_mode = True orm_mode = True

View File

@ -0,0 +1,37 @@
"""Many to many relationship Rating added
Revision ID: a309e6ee6307
Revises: cf0525fd49a8
Create Date: 2024-08-28 22:08:08.399530
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'a309e6ee6307'
down_revision: Union[str, None] = 'cf0525fd49a8'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('ratings',
sa.Column('rated_user_id', sa.Integer(), nullable=False),
sa.Column('user_giving_rating_id', sa.Integer(), nullable=False),
sa.Column('rating_value', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['rated_user_id'], ['users.id'], ),
sa.ForeignKeyConstraint(['user_giving_rating_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('rated_user_id', 'user_giving_rating_id', 'rating_value')
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('ratings')
# ### end Alembic commands ###

View File

@ -0,0 +1,30 @@
"""booked_by column removed in Announcements table
Revision ID: cf0525fd49a8
Revises: 19dbd9793f11
Create Date: 2024-08-28 21:59:23.787732
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'cf0525fd49a8'
down_revision: Union[str, None] = '19dbd9793f11'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('announcements', 'booked_by')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('announcements', sa.Column('booked_by', sa.INTEGER(), autoincrement=False, nullable=True))
# ### end Alembic commands ###