rating routes added

This commit is contained in:
DmitryGantimurov 2023-08-06 23:59:33 +03:00
parent cc414e38bd
commit 5fcee1157e
10 changed files with 223 additions and 59 deletions

View File

@ -1,8 +1,8 @@
from typing import AsyncGenerator from typing import AsyncGenerator
from sqlalchemy import create_engine, select from sqlalchemy import create_engine, select, MetaData
# from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine # from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import sessionmaker, Session from sqlalchemy.orm import sessionmaker, Session, DeclarativeBase
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from fastapi import Depends from fastapi import Depends
@ -18,4 +18,18 @@ engine = create_engine(
SessionLocal = sessionmaker(bind=engine, autoflush=True, autocommit=False) SessionLocal = sessionmaker(bind=engine, autoflush=True, autocommit=False)
database = SessionLocal() database = SessionLocal()
Base = declarative_base() # Base = declarative_base()
# add your model's MetaData object here
# for 'autogenerate' support
# in your application's model:
class Base(DeclarativeBase):
metadata = MetaData(naming_convention={
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_`%(constraint_name)s`",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s"
})

View File

@ -2,4 +2,5 @@ from sqlalchemy import Table, MetaData
from .db import engine from .db import engine
tbl = Table('UserDatabase', MetaData(), autoload_with=engine) tbl = Table('UserDatabase', MetaData(), autoload_with=engine)
tbl.drop(engine, checkfirst=False) tbl.drop(engine, checkfirst=False)
a = input()

View File

@ -19,7 +19,7 @@ import shutil
import os import os
from .db import Base, engine, SessionLocal, database from .db import Base, engine, SessionLocal, database
from .service import add_poems_to_db, generate_poem from .service import add_poems_to_db, generate_poem, check_obsolete
from . import schemas, models, utils from . import schemas, models, utils
Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=engine)
@ -36,32 +36,37 @@ app.mount("/uploads", StaticFiles(directory = "./uploads"))
# # Записываем стихи в базу данных, если их еще нет (запускать только если стихов в базе нет). # # Записываем стихи в базу данных, если их еще нет (запускать только если стихов в базе нет).
# add_poems_to_db(database) # add_poems_to_db(database)
@app.get("/api/announcements")#адрес объявлений @app.get("/api/announcements", response_model=List[schemas.Announcement]) #адрес объявлений
def annoncements_list(owner_id: int = None, metro: str = None, category: str = None, booked_by: int = 0): def annoncements_list(params_to_sort: schemas.SortAnnouncements):
# Считываем данные из Body и отображаем их на странице. # Считываем данные из Body и отображаем их на странице.
# В последствии будем вставлять данные в html-форму # В последствии будем вставлять данные в html-форму
a = database.query(models.Announcement) # Фильтруем по сроку годности
b = database.query(models.Announcement) not_expired = check_obsolete(current_date=datetime.date.today())
c = database.query(models.Announcement) # Фильтруем по другим параметрам и делаем пересечение с not_expired
d = database.query(models.Announcement) result = not_expired.intersect(get_query_results(params_to_sort))
e = database.query(models.Announcement)
if owner_id != None:
b = a.filter(models.Announcement.owner_id == owner_id)
if metro != None: # a = database.query(models.Announcement)
c = a.filter(models.Announcement.metro == metro) # b = database.query(models.Announcement)
# c = database.query(models.Announcement)
# d = database.query(models.Announcement)
# e = database.query(models.Announcement)
if category != None: # if owner_id != None:
d = a.filter(models.Announcement.category == category) # b = a.filter(models.Announcement.owner_id == owner_id)
if not any([category, owner_id, metro]) and booked_by == -1: # if metro != None:
result = a.all() # c = a.filter(models.Announcement.metro == metro)
# if category != None:
# d = a.filter(models.Announcement.category == category)
# if not any([category, owner_id, metro]) and booked_by == 0:
# result = a.all()
else: # else:
result = b.intersect(c, d, e).all() # result = b.intersect(c, d, e).all()
return {"Success" : True, "list_of_announcements": result, "poem": generate_poem(database)} return {"Success" : True, "list_of_announcements": result, "poem": generate_poem(database)}
@ -179,6 +184,25 @@ async def read_own_items(
return [{"Current user name": current_user.name, "Current user surname": current_user.surname}] return [{"Current user name": current_user.name, "Current user surname": current_user.surname}]
# начисляем баллы пользователю
@app.post("/api/user/rating")
def add_points(data: schemas.AddPoints):
user = utils.get_user(data.user_id)
if not user:
raise HTTPException(status_code=404, detail="Item not found")
user.rating += data.rate
return {"Success": True}
# получаем данные о баллах пользователя
@app.get("/api/user/rating")
def add_points(user_id: int):
user = utils.get_user(user_id)
if not user:
raise HTTPException(status_code=404, detail="Item not found")
return {"rating": user.rating}
@app.get("/api/trashbox", response_model=List[schemas.TrashboxResponse]) @app.get("/api/trashbox", response_model=List[schemas.TrashboxResponse])
def get_trashboxes(lat:float, lng:float):#крутая функция для работы с api def get_trashboxes(lat:float, lng:float):#крутая функция для работы с api
BASE_URL='https://geointelect2.gate.petersburg.ru'#адрес сайта и мой токин BASE_URL='https://geointelect2.gate.petersburg.ru'#адрес сайта и мой токин
@ -214,3 +238,8 @@ def get_trashboxes(lat:float, lng:float):#крутая функция для р
@app.get("/{rest_of_path:path}") @app.get("/{rest_of_path:path}")
async def react_app(req: Request, rest_of_path: str): async def react_app(req: Request, rest_of_path: str):
return templates.TemplateResponse('index.html', { 'request': req }) return templates.TemplateResponse('index.html', { 'request': req })
@app.post("api/announcement/dispose")
def dispose(despose_data: schemas.DisposeData, current_user: Annotated[schemas.User, Depends(utils.get_current_user)]):
current_user.rating += 60

View File

@ -1,12 +1,17 @@
from sqlalchemy import Column, Integer, String, Boolean, Float, DateTime, Date, ForeignKey from sqlalchemy import Column, Integer, String, Boolean, Float, DateTime, Date, ForeignKey, Enum, UniqueConstraint
from fastapi import Depends from fastapi import Depends
from .db import Base, engine from .db import Base, engine
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
# class User(SQLAlchemyBaseUserTableUUID, Base): # импортируем модуль для создания перечислений
# name = Column(String, nullable=True)#имя пользователя import enum
# # класс, необходимый для создания перечисления
# class State(enum.Enum):
# published = 1
# taken = 2
# obsolete = 3
class User(Base):#класс пользователя class User(Base):#класс пользователя
@ -18,30 +23,34 @@ class User(Base):#класс пользователя
name = Column(String, nullable=True)#имя пользователя name = Column(String, nullable=True)#имя пользователя
surname = Column(String)#фамилия пользователя surname = Column(String)#фамилия пользователя
disabled = Column(Boolean, default=False) disabled = Column(Boolean, default=False)
rating = Column(Integer, default=0) #баллы пользователя
reg_date = Column(Date) # дата регистрации
items = relationship("Announcement", back_populates="owner") items = relationship("Announcement", back_populates="users")
class Announcement(Base): #класс объявления class Announcement(Base): #класс объявления
__tablename__ = "announcements" __tablename__ = "announcements"
id = Column(Integer, primary_key=True, index=True)#айди объявления id = Column(Integer, primary_key=True, index=True) # айди объявления
owner_id = Column(Integer, ForeignKey("users.id"))#айди создателя объявления user_id = Column(Integer, ForeignKey("users.id", name="fk_users_id")) # айди создателя объявления
name = Column(String) # название объявления name = Column(String) # название объявления
category = Column(String)#категория продукта из объявления category = Column(String) #категория продукта из объявления
best_by = Column(Date)#срок годности продукта из объявления best_by = Column(Date) #срок годности продукта из объявления
address = Column(String) address = Column(String)
longtitude = Column(Integer) longtitude = Column(Integer)
latitude = Column(Integer) latitude = Column(Integer)
description = Column(String)#описание продукта в объявлении description = Column(String) #описание продукта в объявлении
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)#статус бронирования (либо -1, либо айди бронирующего) booked_by = Column(Integer) #статус бронирования (либо -1, либо айди бронирующего)
# state = Column(Enum(State), default=State.published) # состояние объявления (опубликовано, забронировано, устарело)
obsolete = Column(Boolean, default=False) # состояние объявления (по-умолчанию считаем его актуальным)
owner = relationship("User", back_populates="items") owner = relationship("User", back_populates="items")
class Trashbox(Base):#класс мусорных баков class Trashbox(Base): #класс мусорных баков
__tablename__ = "trashboxes" __tablename__ = "trashboxes"
id = Column(Integer, primary_key=True, index=True)#айди id = Column(Integer, primary_key=True, index=True)#айди
@ -49,7 +58,7 @@ class Trashbox(Base):#класс мусорных баков
address = Column(String) address = Column(String)
latitude = Column(Integer) latitude = Column(Integer)
longtitude = Column(Integer) longtitude = Column(Integer)
category = Column(String)#категория продукта из объявления category = Column(String) #категория продукта из объявления
class Poems(Base):#класс поэзии class Poems(Base):#класс поэзии

View File

@ -71,3 +71,21 @@ class TrashboxRequest(TrashboxBase):
class DisposeRequest(BaseModel): class DisposeRequest(BaseModel):
ann_id: int ann_id: int
trashbox: TrashboxRequest trashbox: TrashboxRequest
class SortAnnouncements(BaseModel):
expired: Union[int, None] = False
user_id: Union[int, None] = None
metro: Union[str, None] = None
category: Union[str, None] = None
# booked_by: Union[int, None] = None
class DisposeData(BaseModel):
ann_id: int # id объявления
trash_id: int # id мусорки
trash_category: str # категория мусора
# схема для начисления баллов
class AddPoints(BaseModel):
user_id: int
rate: int

View File

@ -1,6 +1,9 @@
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from .models import Poems from typing import Annotated, Union
from fastapi import Depends
from . import models, schemas, utils
import random import random
import datetime
# Загружаем стихи # Загружаем стихи
@ -31,20 +34,34 @@ def add_poems_to_db(db: Session):
def generate_poem(db: Session): def generate_poem(db: Session):
# генерируем 1 случайное id и выбираем объект бд с этим id # генерируем 1 случайное id и выбираем объект бд с этим id
rand_id = random.randint(1, 102) rand_id = random.randint(1, 102)
poem = db.query(Poems).filter(Poems.id == rand_id).first() poem = db.query(models.Poems).filter(models.Poems.id == rand_id).first()
# возвращаем название и текст стихотворения # возвращаем название и текст стихотворения
return {"name": poem.poem_name, "text": poem.poem_text, "author":""} # добавить поле author в Poems return {"name": poem.poem_name, "text": poem.poem_text, "author":""} # добавить поле author в Poems
# Функция, создающая сессию БД при каждом запросе к нашему API. def get_query_results(db: Annotated[Session, Depends(utils.get_db)], schema: schemas.SortAnnouncements):
# Срабатывает до запуска остальных функций. """Функция для последовательного применения различных фильтров (через схему SortAnnouncements)"""
# Всегда закрывает сессию при окончании работы с ней res = db.query(models.Announcement)
# @app.middleware("http") fields = schema.__dict__ # параметры передоваемой схемы SortAnnouncements (ключи и значения)
# async def db_session_middleware(request: Request, call_next): for name, filt in fields.items():
# response = Response("Internal server error", status_code=500) if filt is not None:
# try: d = {name: filt}
# request.state.db = SessionLocal() res = res.filter_by(**d)
# response = await call_next(request) return res.all()
# finally:
# request.state.db.close()
# return response def check_obsolete(db: Annotated[Session, Depends(utils.get_db)], current_date: datetime.date):
"""
Возвращаем список объектов базы данных типа Announcement
, удовлетворяющих условию obsolete == True
"""
list_to_return = []
not_obsolete = database.query(models.Announcement).filter(models.Announcement.obsolete == True).all()
for ann in not_obsolete:
if ann.best_by < current_date:
ann.obsolete = False
else:
list_to_return.append(ann)
return list_to_return

View File

@ -36,15 +36,15 @@ def get_password_hash(password):
return pwd_context.hash(password) return pwd_context.hash(password)
def get_user(db: Session, user_id: int): def get_user(db: Annotated[Session, Depends(get_db)], user_id: int):
user_with_required_id = db.query(models.User).filter(models.User.id == user_id).first() user_with_required_id = db.query(models.User).filter(models.User.id == user_id).first()
if user_with_required_id: if user_with_required_id:
return user_with_required_id return user_with_required_id
return None return None
def authenticate_user(db: Session, email: str, password: str): def authenticate_user(db: Annotated[Session, Depends(get_db)], email: str, password: str):
user = get_user(db, user_id) user = get_user(user_id = user_id)
if not user: if not user:
return False return False
if not verify_password(password, user.hashed_password): if not verify_password(password, user.hashed_password):
@ -81,7 +81,6 @@ async def get_current_user(db: Annotated[Session, Depends(get_db)], token: Annot
if user is None: if user is None:
raise credentials_exception raise credentials_exception
return schemas.User(id=user.id, email=user.email, name=user.name, surname=user.surname, disabled=user.disabled, items=user.items) return schemas.User(id=user.id, email=user.email, name=user.name, surname=user.surname, disabled=user.disabled, items=user.items)
# return user
async def get_current_active_user( async def get_current_active_user(

View File

@ -17,12 +17,7 @@ config = context.config
if config.config_file_name is not None: if config.config_file_name is not None:
fileConfig(config.config_file_name) fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = base.Base.metadata target_metadata = base.Base.metadata
# target_metadata = None
# other values from the config, defined by the needs of env.py, # other values from the config, defined by the needs of env.py,
# can be acquired: # can be acquired:

View File

@ -0,0 +1,48 @@
"""points added to user model; owner_id -> user_id; added state(announcement table)
Revision ID: 4e4d30fd58fc
Revises: 00529d20660b
Create Date: 2023-08-05 09:38:03.284306
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '4e4d30fd58fc'
down_revision = '00529d20660b'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('announcements', schema=None) as batch_op:
batch_op.add_column(sa.Column('user_id', sa.Integer(), nullable=True))
batch_op.add_column(sa.Column('state', sa.Enum('published', 'taken', 'obsolete', name='state'), nullable=True))
# batch_op.drop_constraint(None, type_='foreignkey')
batch_op.create_foreign_key('fk_users_id', 'users', ['user_id'], ['id'])
batch_op.drop_column('owner_id')
with op.batch_alter_table('users', schema=None) as batch_op:
batch_op.add_column(sa.Column('rating', sa.Integer(), nullable=True))
batch_op.add_column(sa.Column('reg_date', sa.Date(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('users', schema=None) as batch_op:
batch_op.drop_column('reg_date')
batch_op.drop_column('rating')
with op.batch_alter_table('announcements', schema=None) as batch_op:
batch_op.add_column(sa.Column('owner_id', sa.INTEGER(), nullable=True))
# batch_op.drop_constraint('fk_users_id', type_='foreignkey')
batch_op.create_foreign_key(None, 'users', ['owner_id'], ['id'])
batch_op.drop_column('state')
batch_op.drop_column('user_id')
# ### end Alembic commands ###

View File

@ -0,0 +1,34 @@
"""state(enum) -> obsolete(Boolean)
Revision ID: faecbd04e5eb
Revises: 4e4d30fd58fc
Create Date: 2023-08-05 23:57:31.784676
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'faecbd04e5eb'
down_revision = '4e4d30fd58fc'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('announcements', schema=None) as batch_op:
batch_op.add_column(sa.Column('obsolete', sa.Boolean(), nullable=True))
batch_op.drop_column('state')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('announcements', schema=None) as batch_op:
batch_op.add_column(sa.Column('state', sa.VARCHAR(length=9), nullable=True))
batch_op.drop_column('obsolete')
# ### end Alembic commands ###