diff --git a/migrations/env.py b/migrations/env.py index cdbcaed..b194424 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -29,6 +29,10 @@ target_metadata = BaseModel.metadata # can be acquired: # my_important_option = config.get_main_option("my_important_option") # ... etc. +def include_object(object, name, type_, reflected, compare_to): + print(f"{type_}: {name}") + return True # Temporarily return True to debug all objects + def get_url(): url = config.get_main_option("sqlalchemy.url").format( @@ -65,7 +69,11 @@ def run_migrations_offline() -> None: def do_run_migrations(connection: Connection) -> None: - context.configure(connection=connection, target_metadata=target_metadata) + context.configure(connection=connection, + target_metadata=target_metadata, + include_schemas=True, + include_object=include_object, + ) with context.begin_transaction(): context.run_migrations() diff --git a/models/__init__.py b/models/__init__.py index 3fcbaca..cc7220d 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -6,5 +6,4 @@ from .client import * from .service import * from .product import * from .secondary import * - configure_mappers() diff --git a/models/deal.py b/models/deal.py index 0996ce2..820d730 100644 --- a/models/deal.py +++ b/models/deal.py @@ -1,9 +1,10 @@ from enum import IntEnum, unique -from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean +from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean, Sequence from sqlalchemy.orm import relationship -from models.base import BaseModel +from models.base import BaseModel, metadata +from models.utils import add_sequence_to_model @unique @@ -35,6 +36,10 @@ class Deal(BaseModel): services = relationship('DealService', back_populates='deal') products = relationship('DealProduct', back_populates='deal') + # TODO remake with sequence + rank = Column(String, nullable=False, comment='Lexorank') + + comment = Column(String, nullable=False, server_default='', comment='Коментарий к заданию') class DealStatusHistory(BaseModel): __tablename__ = 'deals_status_history' @@ -52,3 +57,4 @@ class DealStatusHistory(BaseModel): next_status_deadline = Column(DateTime, comment='Дедлайн до которого сделку нужно перевести на следующий этап') + comment = Column(String, nullable=False, comment='Коментарий', server_default='') diff --git a/models/product.py b/models/product.py index 1994849..ca9d2af 100644 --- a/models/product.py +++ b/models/product.py @@ -1,7 +1,11 @@ -from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy import Column, Integer, String, ForeignKey, Sequence from sqlalchemy.orm import relationship -from models import BaseModel +from models import BaseModel, metadata + +deal_rank_seq = Sequence('test_ochko', start=1, increment=1, metadata=metadata) + +sequence = Sequence('my_sequence_name') class Product(BaseModel): @@ -13,6 +17,7 @@ class Product(BaseModel): client_id = Column(Integer, ForeignKey('clients.id'), nullable=False, comment='ID сделки') client = relationship('Client', back_populates='products') barcodes = relationship('ProductBarcode', back_populates='product', cascade="all, delete-orphan") + my_column = Column(Integer, sequence) class ProductBarcode(BaseModel): diff --git a/models/sequences.py b/models/sequences.py new file mode 100644 index 0000000..9642fdf --- /dev/null +++ b/models/sequences.py @@ -0,0 +1,4 @@ +from sqlalchemy import Sequence + +from models import BaseModel + diff --git a/models/utils.py b/models/utils.py new file mode 100644 index 0000000..40891c9 --- /dev/null +++ b/models/utils.py @@ -0,0 +1,4 @@ +def add_sequence_to_model(sequence, metadata): + metadata.info.setdefault("sequences", set()).add( + (sequence.schema, sequence.name) + ) diff --git a/requirements.txt b/requirements.txt index c55941f..d1526f9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,4 +17,5 @@ alembic python-dotenv aiohttp aiohttp[speedups] -openpyxl \ No newline at end of file +openpyxl +lexorank-py \ No newline at end of file diff --git a/routers/deal.py b/routers/deal.py index 3826df8..f76aa45 100644 --- a/routers/deal.py +++ b/routers/deal.py @@ -51,6 +51,18 @@ async def get_summary( ): return await DealService(session).get_summary() +@deal_router.post( + '/summaries/reorder', + response_model=DealSummaryReorderResponse, + operation_id='reorderDealSummaries' +) +async def reorder( + session: Annotated[AsyncSession, Depends(get_session)], + request: DealSummaryReorderRequest, + user: Annotated[User, Depends(get_current_user)] +): + return await DealService(session).reorder(request, user) + @deal_router.get( '/get-all', diff --git a/schemas/deal.py b/schemas/deal.py index 112cddf..08b12f1 100644 --- a/schemas/deal.py +++ b/schemas/deal.py @@ -23,6 +23,7 @@ class DealSummary(CustomModelCamel): changed_at: datetime.datetime status: int total_price: int + rank: int class DealServiceSchema(CustomModelCamel): @@ -55,12 +56,14 @@ class DealSchema(CustomModelCamel): is_deleted: bool is_completed: bool client: ClientSchema + comment: str class DealGeneralInfoSchema(CustomModelCamel): name: str is_deleted: bool is_completed: bool + comment: str # endregion Entities @@ -141,6 +144,14 @@ class DealUpdateGeneralInfoRequest(CustomModelCamel): data: DealGeneralInfoSchema +class DealSummaryReorderRequest(CustomModelCamel): + deal_id: int + new_status: int + rank: int + next_status_deadline: datetime.datetime + comment: str + + # endregion Requests # region Responses @@ -204,4 +215,8 @@ class DealAddProductResponse(OkMessageSchema): class DealUpdateGeneralInfoResponse(OkMessageSchema): pass + + +class DealSummaryReorderResponse(OkMessageSchema): + pass # endregion Responses diff --git a/services/deal.py b/services/deal.py index 3db3632..db475ee 100644 --- a/services/deal.py +++ b/services/deal.py @@ -1,3 +1,5 @@ +import lexorank + import models.secondary from typing import Union import models.deal @@ -19,6 +21,14 @@ class DealService(BaseService): async def _get_deal_by_id(self, deal_id) -> Union[Deal, None]: return await self.session.get(Deal, deal_id) + async def _get_rank(self): + query = await self.session.execute(select(Deal.rank).order_by(Deal.rank.desc())) + result = query.scalar_one_or_none() + if not result: + return str(lexorank.Bucket.BUCEKT_0.next()) + rank = lexorank.parse(result) + return str(rank.next()) + async def change_status(self, deal: Deal, status: DealStatus, user: User, @@ -37,10 +47,12 @@ class DealService(BaseService): return status_change async def create(self, request: DealCreateRequest, user: User) -> DealCreateResponse: + rank = await self._get_rank() deal = Deal( name=request.name, created_at=datetime.datetime.now(), - current_status=DealStatus.CREATED + current_status=DealStatus.CREATED, + rank=rank ) self.session.add(deal) await self.session.flush() @@ -60,11 +72,13 @@ class DealService(BaseService): request.client_name, ClientDetailsSchema(address=request.client_address)) await client_service.update_details(user, client, ClientDetailsSchema(address=request.client_address)) + rank = await self._get_rank() deal = Deal( name=request.name, created_at=datetime.datetime.now(), client_id=client.id, - current_status=DealStatus.CREATED + current_status=DealStatus.CREATED, + rank=rank ) self.session.add(deal) await self.session.flush() @@ -91,15 +105,25 @@ class DealService(BaseService): .group_by(models.secondary.DealService.deal_id) .subquery() ) - q = (select( - Deal, - func.coalesce(service_subquery.c.total_price, 0) + q = ( + select( + Deal, + func.coalesce(service_subquery.c.total_price, 0) + ) + .order_by( + Deal.rank.desc() + ) + .options( + selectinload(Deal.status_history), + joinedload(Deal.client) + ) + .outerjoin( + service_subquery, Deal.id == service_subquery.c.deal_id) + .where( + Deal.is_deleted == False, + Deal.is_completed == False + ) ) - .options(selectinload(Deal.status_history), - joinedload(Deal.client)) - .outerjoin(service_subquery, Deal.id == service_subquery.c.deal_id) - .where(Deal.is_deleted == False, - Deal.is_completed == False)) deals_query = await self.session.execute(q) summaries = [] for deal, total_price in deals_query.all(): @@ -112,7 +136,8 @@ class DealService(BaseService): name=deal.name, changed_at=last_status.changed_at, status=last_status.to_status, - total_price=total_price + total_price=total_price, + rank=deal.rank ) ) return DealSummaryResponse(summaries=summaries) @@ -156,6 +181,7 @@ class DealService(BaseService): if not deal: raise HTTPException(status_code=404, detail="Сделка не найдена") deal.name = request.data.name + deal.comment = request.data.comment deal.is_deleted = request.data.is_deleted deal.is_completed = request.data.is_completed await self.session.commit() @@ -164,6 +190,9 @@ class DealService(BaseService): await self.session.rollback() return DealUpdateGeneralInfoResponse(ok=False, message=str(e)) + async def reorder(self, request: DealSummaryReorderRequest, user: User) -> DealSummaryReorderResponse: + pass + # endregion # region Deal services diff --git a/test/ockho.py b/test/ockho.py index a868159..ead2b70 100644 --- a/test/ockho.py +++ b/test/ockho.py @@ -1,8 +1,17 @@ import string +import lexorank + def main(): - pass - + prev = lexorank.middle(lexorank.Bucket.BUCEKT_0) + ranks = [prev] + for _ in range(1_000_000): + ranks.append(prev.next()) + prev = ranks[-1] + print('generated') + sorted_ranks = list(sorted(ranks)) + for idx in range(len(sorted_ranks)): + print(sorted_ranks[idx], ' ', ranks[idx]) if __name__ == '__main__': main() diff --git a/test/test.py b/test/test.py index a7a9cc7..d165328 100644 --- a/test/test.py +++ b/test/test.py @@ -3,29 +3,12 @@ import asyncio from sqlalchemy.ext.asyncio import AsyncSession from backend.session import session_maker +from migrations.env import run_async_migrations from models import Product, ProductBarcode async def main(session: AsyncSession): - client_ids = [8, 18] - for client_id in client_ids: - for i in range(1, 500 + 1): - product = Product( - name=f"Товар №{i}", - article=f"Ариткул товара №{i}", - client_id=client_id - ) - session.add(product) - await session.flush() - for j in range(1, 5 + 1): - barcode = ProductBarcode( - barcode=f"Штрихкод №{j} для товара №{i}", - product_id=product.id - ) - session.add(barcode) - await session.flush() - await session.commit() - + await run_async_migrations() async def preload(): async with session_maker() as session: