From f15c17a17e0baaafb19183d03355a491c8b1ce33 Mon Sep 17 00:00:00 2001 From: DmitryGantimurov Date: Mon, 7 Aug 2023 22:27:23 +0300 Subject: [PATCH] get_query_results works correctly. relationships between models changed --- back/db.py | 24 +++---- back/main.py | 40 +++++++----- back/models.py | 7 +- back/scheduler.py | 13 ++++ back/schemas.py | 6 +- back/service.py | 27 ++++---- ...8512_user_model_got_a_new_field_num_of_.py | 64 +++++++++++++++++++ ...aecbd04e5eb_state_enum_obsolete_boolean.py | 34 ---------- 8 files changed, 135 insertions(+), 80 deletions(-) create mode 100644 back/scheduler.py create mode 100644 migrations/versions/9c19b9658512_user_model_got_a_new_field_num_of_.py delete mode 100644 migrations/versions/faecbd04e5eb_state_enum_obsolete_boolean.py diff --git a/back/db.py b/back/db.py index 9c7f1b4..d4d7872 100644 --- a/back/db.py +++ b/back/db.py @@ -18,18 +18,18 @@ engine = create_engine( SessionLocal = sessionmaker(bind=engine, autoflush=True, autocommit=False) database = SessionLocal() -# Base = declarative_base() +Base = declarative_base() -# add your model's MetaData object here -# for 'autogenerate' support -# in your application's model: +# # 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" - }) \ No newline at end of file +# 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" +# }) \ No newline at end of file diff --git a/back/main.py b/back/main.py index eb22a1f..451ee8a 100644 --- a/back/main.py +++ b/back/main.py @@ -7,12 +7,13 @@ from fastapi.templating import Jinja2Templates from fastapi.requests import Request from pydantic import json -from typing import Any, Annotated, List +from typing import Any, Annotated, List, Union from starlette.staticfiles import StaticFiles import requests from uuid import uuid4 import random +import datetime import ast import pathlib @@ -20,7 +21,8 @@ import shutil import os from .db import Base, engine, SessionLocal, database -from .service import add_poems_to_db, generate_poem, check_obsolete +from .service import add_poems_to_db, generate_poem, check_obsolete, get_query_results +from .scheduler import app as app_rocketry from . import schemas, models, utils Base.metadata.create_all(bind=engine) @@ -38,17 +40,23 @@ app.mount("/uploads", StaticFiles(directory = "./uploads")) # add_poems_to_db(database) -@app.get("/api/announcements")#адрес объявлений -def annoncements_list(owner_id: int = None, metro: str = None, category: str = None, booked_by: int = 0): +# Каждый день в 00.00 проверяем все объявления (сравниваем текущую дату и срок годности) +# session_rocketry = app_rocketry.session + + +@app.get("/api/announcements", response_model=List[schemas.Announcement])#адрес объявлений +def annoncements_list(obsolete: Union[bool, None] = False, user_id: Union[int, None] = None, metro: Union[str, None] = None, + category: Union[str, None] = None): # Считываем данные из Body и отображаем их на странице. # В последствии будем вставлять данные в html-форму - + params_to_sort = schemas.SortAnnouncements(obsolete=obsolete, user_id=user_id, metro=metro, category=category) # Фильтруем по сроку годности - not_expired = check_obsolete(current_date=datetime.date.today()) + # not_expired = check_obsolete(current_date=datetime.date.today()) # Фильтруем по другим параметрам и делаем пересечение с not_expired - result = not_expired.intersect(get_query_results(params_to_sort)) + # result = not_expired.intersect(get_query_results(params_to_sort)) + result = get_query_results(db=database, schema=params_to_sort) - return {"Success" : True, "list_of_announcements": result} + return result @app.get("/api/announcement")#адрес объявления @@ -68,7 +76,7 @@ def single_annoncement(user_id:int): # Занести объявление в базу данных @app.put("/api/announcement")#адрес объявлений -def put_in_db(name: Annotated[str, Form()], category: Annotated[str, Form()], bestBy: Annotated[int, Form()], +def put_in_db(name: Annotated[str, Form()], category: Annotated[str, Form()], bestBy: Annotated[datetime.date, Form()], address: Annotated[str, Form()], longtitude: Annotated[float, Form()], latitude: Annotated[float, Form()], description: Annotated[str, Form()], src: UploadFile, metro: Annotated[str, Form()], trashId: Annotated[int, Form()] = None): @@ -167,12 +175,14 @@ async def read_own_items( # начисляем баллы пользователю @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} +def add_points(data: schemas.AddPoints, current_user: Annotated[schemas.User, Depends(utils.get_current_user)]): + if current_user.id != data.user_id: + user = utils.get_user(data.user_id) + if not user: + raise HTTPException(status_code=404, detail="Item not found") + user.num_of_ratings += 1 + user.rating = (user.rating + data.rate)/user.num_of_ratings + return {"Success": True} # получаем данные о баллах пользователя diff --git a/back/models.py b/back/models.py index 8909ab9..623f7ea 100644 --- a/back/models.py +++ b/back/models.py @@ -24,15 +24,16 @@ class User(Base):#класс пользователя surname = Column(String)#фамилия пользователя disabled = Column(Boolean, default=False) rating = Column(Integer, default=0) #баллы пользователя + num_of_ratings = Column(Integer, default=0) # количество оценок (т.е. то, сколько раз другие пользователи оценили текущего) reg_date = Column(Date) # дата регистрации - items = relationship("Announcement", back_populates="users") + announcements = relationship("Announcement", back_populates="user") class Announcement(Base): #класс объявления __tablename__ = "announcements" id = Column(Integer, primary_key=True, index=True) # айди объявления - user_id = Column(Integer, ForeignKey("users.id", name="fk_users_id")) # айди создателя объявления + user_id = Column(Integer, ForeignKey("users.id")) # айди создателя объявления name = Column(String) # название объявления category = Column(String) #категория продукта из объявления best_by = Column(Date) #срок годности продукта из объявления @@ -47,7 +48,7 @@ class Announcement(Base): #класс объявления # state = Column(Enum(State), default=State.published) # состояние объявления (опубликовано, забронировано, устарело) obsolete = Column(Boolean, default=False) # состояние объявления (по-умолчанию считаем его актуальным) - owner = relationship("User", back_populates="items") + user = relationship("User", back_populates="announcements") class Trashbox(Base): #класс мусорных баков diff --git a/back/scheduler.py b/back/scheduler.py new file mode 100644 index 0000000..0a279e3 --- /dev/null +++ b/back/scheduler.py @@ -0,0 +1,13 @@ +from . import service +from rocketry import Rocketry +from rocketry.conds import daily +import datetime + +app = Rocketry() + +# Create some tasks: + +@app.task("daily") +def daily_check(): + service.check_obsolete(current_date=datetime.date.today()) + diff --git a/back/schemas.py b/back/schemas.py index 14d085e..25ebc85 100644 --- a/back/schemas.py +++ b/back/schemas.py @@ -26,6 +26,10 @@ class Announcement(BaseModel): trashId: Union[int, None] = None booked_by: int #статус бронирования (либо -1, либо айди бронирующего) + class Config: + orm_mode = True + arbitrary_types_allowed=True + class Token(BaseModel): access_token: str @@ -73,7 +77,7 @@ class DisposeRequest(BaseModel): trashbox: TrashboxRequest class SortAnnouncements(BaseModel): - expired: Union[int, None] = False + obsolete: Union[int, None] = False user_id: Union[int, None] = None metro: Union[str, None] = None category: Union[str, None] = None diff --git a/back/service.py b/back/service.py index 549bc4c..d5032b2 100644 --- a/back/service.py +++ b/back/service.py @@ -41,12 +41,12 @@ def add_poems_to_db(db: Session): f1.close() -# def generate_poem(db: Session): -# # генерируем 1 случайное id и выбираем объект бд с этим id -# rand_id = random.randint(1, 102) -# poem = db.query(models.Poems).filter(models.Poems.id == rand_id).first() -# # возвращаем название и текст стихотворения -# return {"name": poem.title, "text": poem.poem_text, "author":""} # добавить поле author в Poems +def generate_poem(db: Session): + # генерируем 1 случайное id и выбираем объект бд с этим id + rand_id = random.randint(1, 102) + poem = db.query(models.Poems).filter(models.Poems.id == rand_id).first() + # возвращаем название и текст стихотворения + return {"name": poem.title, "text": poem.poem_text, "author":""} # добавить поле author в Poems #Вова тестирует получение поэм, Димоны, помогите пж # def poems_to_front(db: Annotated[Session, Depends(utils.get_db)]): # kolvo_stixov = 109 # пока количество стихотворений = 101 @@ -58,7 +58,7 @@ def add_poems_to_db(db: Session): -def get_query_results(db: Annotated[Session, Depends(utils.get_db)], schema: schemas.SortAnnouncements): +def get_query_results(schema: schemas.SortAnnouncements, db: Annotated[Session, Depends(utils.get_db)]): """Функция для последовательного применения различных фильтров (через схему SortAnnouncements)""" res = db.query(models.Announcement) fields = schema.__dict__ # параметры передоваемой схемы SortAnnouncements (ключи и значения) @@ -71,16 +71,13 @@ def get_query_results(db: Annotated[Session, Depends(utils.get_db)], schema: sch def check_obsolete(db: Annotated[Session, Depends(utils.get_db)], current_date: datetime.date): """ - Возвращаем список объектов базы данных типа Announcement - , удовлетворяющих условию obsolete == True + Обновляем поле obsolete у всех объявлений раз в сутки """ - list_to_return = [] - not_obsolete = database.query(models.Announcement).filter(models.Announcement.obsolete == True).all() - for ann in not_obsolete: + announcements = db.query(models.Announcement).all() + for ann in announcements: if ann.best_by < current_date: ann.obsolete = False else: - list_to_return.append(ann) - - return list_to_return + ann.obsolete = True + diff --git a/migrations/versions/9c19b9658512_user_model_got_a_new_field_num_of_.py b/migrations/versions/9c19b9658512_user_model_got_a_new_field_num_of_.py new file mode 100644 index 0000000..a4ea8d5 --- /dev/null +++ b/migrations/versions/9c19b9658512_user_model_got_a_new_field_num_of_.py @@ -0,0 +1,64 @@ +"""User model got a new field: num_of_ratings; delete name of ForeignKey; relationships changed + +Revision ID: 9c19b9658512 +Revises: 4e4d30fd58fc +Create Date: 2023-08-07 18:16:32.386940 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '9c19b9658512' +down_revision = '4e4d30fd58fc' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('_alembic_tmp_users') + 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') + + with op.batch_alter_table('poems', schema=None) as batch_op: + batch_op.add_column(sa.Column('title', sa.String(), nullable=True)) + batch_op.add_column(sa.Column('text', sa.String(), nullable=True)) + batch_op.add_column(sa.Column('author', sa.String(), nullable=True)) + batch_op.drop_column('poem_text') + batch_op.drop_column('poem_name') + + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.add_column(sa.Column('num_of_ratings', sa.Integer(), 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('num_of_ratings') + + with op.batch_alter_table('poems', schema=None) as batch_op: + batch_op.add_column(sa.Column('poem_name', sa.VARCHAR(), nullable=True)) + batch_op.add_column(sa.Column('poem_text', sa.VARCHAR(), nullable=True)) + batch_op.drop_column('author') + batch_op.drop_column('text') + batch_op.drop_column('title') + + 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') + + op.create_table('_alembic_tmp_users', + sa.Column('id', sa.INTEGER(), nullable=False), + sa.Column('email', sa.VARCHAR(), nullable=True), + sa.Column('hashed_password', sa.VARCHAR(), nullable=True), + sa.Column('name', sa.VARCHAR(), nullable=True), + sa.Column('surname', sa.VARCHAR(), nullable=True), + sa.Column('disabled', sa.BOOLEAN(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### diff --git a/migrations/versions/faecbd04e5eb_state_enum_obsolete_boolean.py b/migrations/versions/faecbd04e5eb_state_enum_obsolete_boolean.py deleted file mode 100644 index da76452..0000000 --- a/migrations/versions/faecbd04e5eb_state_enum_obsolete_boolean.py +++ /dev/null @@ -1,34 +0,0 @@ -"""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 ###