v0.1
This commit is contained in:
		
							
								
								
									
										3
									
								
								constants.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								constants.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
allowed_telegram_ids = [
 | 
			
		||||
    454777987,
 | 
			
		||||
]
 | 
			
		||||
@@ -37,10 +37,11 @@ class Deal(BaseModel):
 | 
			
		||||
    products = relationship('DealProduct', back_populates='deal')
 | 
			
		||||
 | 
			
		||||
    # TODO remake with sequence
 | 
			
		||||
    rank = Column(String, nullable=False, comment='Lexorank')
 | 
			
		||||
    lexorank = Column(String, nullable=False, comment='Lexorank', index=True)
 | 
			
		||||
 | 
			
		||||
    comment = Column(String, nullable=False, server_default='', comment='Коментарий к заданию')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DealStatusHistory(BaseModel):
 | 
			
		||||
    __tablename__ = 'deals_status_history'
 | 
			
		||||
    id = Column(Integer, autoincrement=True, primary_key=True, index=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,8 @@ from services.client import ClientService
 | 
			
		||||
 | 
			
		||||
client_router = APIRouter(
 | 
			
		||||
    prefix="/client",
 | 
			
		||||
    tags=['client']
 | 
			
		||||
    tags=['client'],
 | 
			
		||||
    dependencies=[Depends(get_current_user)]
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,8 @@ from services.deal import DealService
 | 
			
		||||
 | 
			
		||||
deal_router = APIRouter(
 | 
			
		||||
    prefix='/deal',
 | 
			
		||||
    tags=['deal']
 | 
			
		||||
    tags=['deal'],
 | 
			
		||||
    dependencies=[Depends(get_current_user)]
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -24,6 +25,18 @@ async def create(
 | 
			
		||||
    return await DealService(session).create(request, user)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@deal_router.post(
 | 
			
		||||
    '/delete',
 | 
			
		||||
    response_model=DealDeleteResponse,
 | 
			
		||||
    operation_id='deleteDeal'
 | 
			
		||||
)
 | 
			
		||||
async def delete(
 | 
			
		||||
        request: DealDeleteRequest,
 | 
			
		||||
        session: Annotated[AsyncSession, Depends(get_session)]
 | 
			
		||||
):
 | 
			
		||||
    return await DealService(session).delete(request)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@deal_router.post('/quickCreate', response_model=DealQuickCreateResponse)
 | 
			
		||||
async def quick_create(
 | 
			
		||||
        request: DealQuickCreateRequest,
 | 
			
		||||
@@ -39,7 +52,7 @@ async def change_status(
 | 
			
		||||
        session: Annotated[AsyncSession, Depends(get_session)],
 | 
			
		||||
        user: Annotated[User, Depends(get_current_user)]
 | 
			
		||||
):
 | 
			
		||||
    return await DealService(session).change_status(request, user)
 | 
			
		||||
    return await DealService(session).change_status_manual(request, user)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@deal_router.get('/summaries',
 | 
			
		||||
@@ -51,9 +64,10 @@ async def get_summary(
 | 
			
		||||
):
 | 
			
		||||
    return await DealService(session).get_summary()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@deal_router.post(
 | 
			
		||||
    '/summaries/reorder',
 | 
			
		||||
    response_model=DealSummaryReorderResponse,
 | 
			
		||||
    response_model=DealSummaryResponse,
 | 
			
		||||
    operation_id='reorderDealSummaries'
 | 
			
		||||
)
 | 
			
		||||
async def reorder(
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,13 @@ import utils.dependecies
 | 
			
		||||
from backend.session import get_session
 | 
			
		||||
from schemas.base import PaginationSchema
 | 
			
		||||
from schemas.product import *
 | 
			
		||||
from services.auth import get_current_user
 | 
			
		||||
from services.product import ProductService
 | 
			
		||||
 | 
			
		||||
product_router = APIRouter(
 | 
			
		||||
    prefix="/product",
 | 
			
		||||
    tags=["product"]
 | 
			
		||||
    tags=["product"],
 | 
			
		||||
    dependencies=[Depends(get_current_user)]
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -98,6 +100,7 @@ async def exists_product_barcode(
 | 
			
		||||
):
 | 
			
		||||
    return await ProductService(session).exists_barcode(product_id, barcode)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@product_router.post(
 | 
			
		||||
    '/barcode/generate',
 | 
			
		||||
    response_model=ProductGenerateBarcodeResponse,
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,13 @@ from sqlalchemy.ext.asyncio import AsyncSession
 | 
			
		||||
 | 
			
		||||
from backend.session import get_session
 | 
			
		||||
from schemas.service import *
 | 
			
		||||
from services.auth import get_current_user
 | 
			
		||||
from services.service import ServiceService
 | 
			
		||||
 | 
			
		||||
service_router = APIRouter(
 | 
			
		||||
    prefix="/service",
 | 
			
		||||
    tags=['service']
 | 
			
		||||
    tags=['service'],
 | 
			
		||||
    dependencies=[Depends(get_current_user)]
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ class DealSummary(CustomModelCamel):
 | 
			
		||||
    name: str
 | 
			
		||||
    client_name: str
 | 
			
		||||
    changed_at: datetime.datetime
 | 
			
		||||
    deadline: datetime.datetime
 | 
			
		||||
    status: int
 | 
			
		||||
    total_price: int
 | 
			
		||||
    rank: int
 | 
			
		||||
@@ -41,7 +42,8 @@ class DealStatusHistorySchema(CustomModelCamel):
 | 
			
		||||
    changed_at: datetime.datetime
 | 
			
		||||
    from_status: int
 | 
			
		||||
    to_status: int
 | 
			
		||||
    next_status_deadline: datetime.datetime
 | 
			
		||||
    next_status_deadline: datetime.datetime | None
 | 
			
		||||
    comment: str | None = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DealSchema(CustomModelCamel):
 | 
			
		||||
@@ -146,12 +148,16 @@ class DealUpdateGeneralInfoRequest(CustomModelCamel):
 | 
			
		||||
 | 
			
		||||
class DealSummaryReorderRequest(CustomModelCamel):
 | 
			
		||||
    deal_id: int
 | 
			
		||||
    new_status: int
 | 
			
		||||
    rank: int
 | 
			
		||||
    next_status_deadline: datetime.datetime
 | 
			
		||||
    status: int
 | 
			
		||||
    index: int
 | 
			
		||||
    deadline: datetime.datetime
 | 
			
		||||
    comment: str
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DealDeleteRequest(CustomModelCamel):
 | 
			
		||||
    deal_id: int
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# endregion Requests
 | 
			
		||||
 | 
			
		||||
# region Responses
 | 
			
		||||
@@ -219,4 +225,8 @@ class DealUpdateGeneralInfoResponse(OkMessageSchema):
 | 
			
		||||
 | 
			
		||||
class DealSummaryReorderResponse(OkMessageSchema):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DealDeleteResponse(OkMessageSchema):
 | 
			
		||||
    pass
 | 
			
		||||
# endregion Responses
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
 | 
			
		||||
from starlette import status
 | 
			
		||||
 | 
			
		||||
import backend.config
 | 
			
		||||
import constants
 | 
			
		||||
from backend.session import get_session
 | 
			
		||||
from models import User
 | 
			
		||||
from schemas.auth import *
 | 
			
		||||
@@ -46,6 +47,9 @@ class AuthService(BaseService):
 | 
			
		||||
        return jwt.encode(payload, backend.config.SECRET_KEY, algorithm=algorithm)
 | 
			
		||||
 | 
			
		||||
    async def authenticate(self, request: AuthLoginRequest):
 | 
			
		||||
        if request.id not in constants.allowed_telegram_ids:
 | 
			
		||||
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid credentials')
 | 
			
		||||
 | 
			
		||||
        user: Union[User, None] = await self.session.scalar(select(User).where(User.telegram_id == request.id))
 | 
			
		||||
        if not user:
 | 
			
		||||
            user = User(telegram_id=request.id,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										125
									
								
								services/deal.py
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								services/deal.py
									
									
									
									
									
								
							@@ -21,38 +21,47 @@ 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 _get_rank_for_deal(self, deal_status: DealStatus) -> int:
 | 
			
		||||
        deal_query = await self.session.execute(
 | 
			
		||||
            select(Deal).where(Deal.current_status == deal_status).order_by(Deal.lexorank.desc()).limit(1))
 | 
			
		||||
        deal = deal_query.scalar_one_or_none()
 | 
			
		||||
        if not deal:
 | 
			
		||||
            prev = lexorank.middle(lexorank.Bucket.BUCEKT_0)
 | 
			
		||||
            return str(prev.next())
 | 
			
		||||
        return str(lexorank.parse(deal.lexorank).next())
 | 
			
		||||
 | 
			
		||||
    async def change_status(self, deal: Deal,
 | 
			
		||||
                            status: DealStatus,
 | 
			
		||||
                            user: User,
 | 
			
		||||
                            deadline: datetime.datetime = None) -> DealStatusHistory:
 | 
			
		||||
        deadline = deadline
 | 
			
		||||
        status_change = DealStatusHistory(
 | 
			
		||||
            deal_id=deal.id,
 | 
			
		||||
            user_id=user.id,
 | 
			
		||||
            changed_at=datetime.datetime.now(),
 | 
			
		||||
            from_status=deal.current_status,
 | 
			
		||||
            to_status=status,
 | 
			
		||||
            next_status_deadline=deadline
 | 
			
		||||
        )
 | 
			
		||||
        self.session.add(status_change)
 | 
			
		||||
                            deadline: datetime.datetime = None,
 | 
			
		||||
                            rank=None,
 | 
			
		||||
                            comment: str = '') -> DealStatusHistory:
 | 
			
		||||
        if not deal.current_status == status:
 | 
			
		||||
            deadline = deadline
 | 
			
		||||
            status_change = DealStatusHistory(
 | 
			
		||||
                deal_id=deal.id,
 | 
			
		||||
                user_id=user.id,
 | 
			
		||||
                changed_at=datetime.datetime.now(),
 | 
			
		||||
                from_status=deal.current_status,
 | 
			
		||||
                to_status=status,
 | 
			
		||||
                next_status_deadline=deadline,
 | 
			
		||||
                comment=comment
 | 
			
		||||
            )
 | 
			
		||||
            self.session.add(status_change)
 | 
			
		||||
            deal.current_status = status
 | 
			
		||||
            if not rank:
 | 
			
		||||
                rank = await self._get_rank_for_deal(status)
 | 
			
		||||
        if rank:
 | 
			
		||||
            deal.lexorank = rank
 | 
			
		||||
        await self.session.flush()
 | 
			
		||||
        return status_change
 | 
			
		||||
 | 
			
		||||
    async def create(self, request: DealCreateRequest, user: User) -> DealCreateResponse:
 | 
			
		||||
        rank = await self._get_rank()
 | 
			
		||||
        rank = self._get_rank_for_deal(DealStatus.CREATED)
 | 
			
		||||
        deal = Deal(
 | 
			
		||||
            name=request.name,
 | 
			
		||||
            created_at=datetime.datetime.now(),
 | 
			
		||||
            current_status=DealStatus.CREATED,
 | 
			
		||||
            rank=rank
 | 
			
		||||
            lexorank=rank
 | 
			
		||||
        )
 | 
			
		||||
        self.session.add(deal)
 | 
			
		||||
        await self.session.flush()
 | 
			
		||||
@@ -63,6 +72,14 @@ class DealService(BaseService):
 | 
			
		||||
        await self.session.commit()
 | 
			
		||||
        return DealCreateResponse(ok=True)
 | 
			
		||||
 | 
			
		||||
    async def delete(self, request: DealDeleteRequest) -> DealDeleteResponse:
 | 
			
		||||
        deal = await self._get_deal_by_id(request.deal_id)
 | 
			
		||||
        if not deal:
 | 
			
		||||
            return DealDeleteResponse(ok=False, message="Сделка не найдена")
 | 
			
		||||
        deal.is_deleted = True
 | 
			
		||||
        await self.session.commit()
 | 
			
		||||
        return DealDeleteResponse(ok=True, message="Сделка успешно удалена")
 | 
			
		||||
 | 
			
		||||
    async def quick_create(self, request: DealQuickCreateRequest, user: User) -> DealQuickCreateResponse:
 | 
			
		||||
        client_service = ClientService(self.session)
 | 
			
		||||
        client = await client_service.get_by_name(request.client_name)
 | 
			
		||||
@@ -72,13 +89,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()
 | 
			
		||||
        rank = await self._get_rank_for_deal(DealStatus.CREATED)
 | 
			
		||||
        deal = Deal(
 | 
			
		||||
            name=request.name,
 | 
			
		||||
            created_at=datetime.datetime.now(),
 | 
			
		||||
            client_id=client.id,
 | 
			
		||||
            current_status=DealStatus.CREATED,
 | 
			
		||||
            rank=rank
 | 
			
		||||
            lexorank=rank
 | 
			
		||||
        )
 | 
			
		||||
        self.session.add(deal)
 | 
			
		||||
        await self.session.flush()
 | 
			
		||||
@@ -108,10 +125,11 @@ class DealService(BaseService):
 | 
			
		||||
        q = (
 | 
			
		||||
            select(
 | 
			
		||||
                Deal,
 | 
			
		||||
                func.coalesce(service_subquery.c.total_price, 0)
 | 
			
		||||
            )
 | 
			
		||||
            .order_by(
 | 
			
		||||
                Deal.rank.desc()
 | 
			
		||||
                func.coalesce(service_subquery.c.total_price, 0),
 | 
			
		||||
                func.row_number().over(
 | 
			
		||||
                    partition_by=Deal.current_status,
 | 
			
		||||
                    order_by=Deal.lexorank
 | 
			
		||||
                ).label('rank')
 | 
			
		||||
            )
 | 
			
		||||
            .options(
 | 
			
		||||
                selectinload(Deal.status_history),
 | 
			
		||||
@@ -126,18 +144,20 @@ class DealService(BaseService):
 | 
			
		||||
        )
 | 
			
		||||
        deals_query = await self.session.execute(q)
 | 
			
		||||
        summaries = []
 | 
			
		||||
        for deal, total_price in deals_query.all():
 | 
			
		||||
        for deal, total_price, rank in deals_query.all():
 | 
			
		||||
            deal: Deal
 | 
			
		||||
            last_status: DealStatusHistory = max(deal.status_history, key=lambda status: status.changed_at)
 | 
			
		||||
            deadline = last_status.next_status_deadline
 | 
			
		||||
            summaries.append(
 | 
			
		||||
                DealSummary(
 | 
			
		||||
                    id=deal.id,
 | 
			
		||||
                    client_name=deal.client.name,
 | 
			
		||||
                    name=deal.name,
 | 
			
		||||
                    changed_at=last_status.changed_at,
 | 
			
		||||
                    deadline=deadline,
 | 
			
		||||
                    status=last_status.to_status,
 | 
			
		||||
                    total_price=total_price,
 | 
			
		||||
                    rank=deal.rank
 | 
			
		||||
                    rank=rank
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        return DealSummaryResponse(summaries=summaries)
 | 
			
		||||
@@ -190,8 +210,51 @@ class DealService(BaseService):
 | 
			
		||||
            await self.session.rollback()
 | 
			
		||||
            return DealUpdateGeneralInfoResponse(ok=False, message=str(e))
 | 
			
		||||
 | 
			
		||||
    async def reorder(self, request: DealSummaryReorderRequest, user: User) -> DealSummaryReorderResponse:
 | 
			
		||||
        pass
 | 
			
		||||
    async def reorder(self, request: DealSummaryReorderRequest, user: User) -> DealSummaryResponse:
 | 
			
		||||
        deal: Deal = await self.session.scalar(select(Deal).where(Deal.id == request.deal_id))
 | 
			
		||||
        if request.index == 1:
 | 
			
		||||
            request.index = 0
 | 
			
		||||
        is_first = request.index == 0
 | 
			
		||||
        stmt = (
 | 
			
		||||
            select(Deal)
 | 
			
		||||
            .where(Deal.current_status == request.status,
 | 
			
		||||
                   Deal.id != request.deal_id)
 | 
			
		||||
            .order_by(Deal.lexorank)
 | 
			
		||||
            .offset(max([request.index - 2, 0]))
 | 
			
		||||
            .limit(2 if not is_first else 1)
 | 
			
		||||
 | 
			
		||||
        )
 | 
			
		||||
        query = await self.session.execute(stmt)
 | 
			
		||||
        boundaries = query.scalars().all()
 | 
			
		||||
        top_boundary: Union[Deal, None] = boundaries[0] if not is_first else None
 | 
			
		||||
        bottom_boundary: Union[Deal, None] = boundaries[1] if len(boundaries) == 2 else None
 | 
			
		||||
        if top_boundary:
 | 
			
		||||
            print(top_boundary.name)
 | 
			
		||||
        if bottom_boundary:
 | 
			
		||||
            print(bottom_boundary.name)
 | 
			
		||||
        # working when between two elements
 | 
			
		||||
        if top_boundary and bottom_boundary:
 | 
			
		||||
            top_lexorank = lexorank.parse(top_boundary.lexorank)
 | 
			
		||||
            bottom_lexorank = lexorank.parse(bottom_boundary.lexorank)
 | 
			
		||||
            new_rank = lexorank.between(top_lexorank, bottom_lexorank)
 | 
			
		||||
 | 
			
		||||
        # working when at the bottom
 | 
			
		||||
        elif top_boundary and not bottom_boundary:
 | 
			
		||||
            new_rank = lexorank.parse(top_boundary.lexorank).next()
 | 
			
		||||
        # working when at the top
 | 
			
		||||
        elif bottom_boundary and not top_boundary:
 | 
			
		||||
            new_rank = lexorank.parse(bottom_boundary.lexorank).prev()
 | 
			
		||||
        elif not top_boundary and not bottom_boundary and len(boundaries) > 0:
 | 
			
		||||
            new_rank = lexorank.parse(boundaries[0].lexorank).prev()
 | 
			
		||||
        else:
 | 
			
		||||
            new_rank = lexorank.middle(lexorank.Bucket.BUCEKT_0)
 | 
			
		||||
 | 
			
		||||
        await self.change_status(deal, request.status, user,
 | 
			
		||||
                                 deadline=request.deadline,
 | 
			
		||||
                                 comment=request.comment,
 | 
			
		||||
                                 rank=str(new_rank))
 | 
			
		||||
        await self.session.commit()
 | 
			
		||||
        return await self.get_summary()
 | 
			
		||||
 | 
			
		||||
    # endregion
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,16 @@
 | 
			
		||||
import string
 | 
			
		||||
from pprint import pprint
 | 
			
		||||
 | 
			
		||||
import lexorank
 | 
			
		||||
from lexorank import Bucket, middle
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    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()
 | 
			
		||||
prev = middle(Bucket.BUCEKT_0)
 | 
			
		||||
ranks = [prev]
 | 
			
		||||
for _ in range(9):
 | 
			
		||||
    ranks.append(prev.next())
 | 
			
		||||
    prev = ranks[-1]
 | 
			
		||||
middle_ranks = lexorank.between(ranks[1], ranks[2])
 | 
			
		||||
pprint(ranks)
 | 
			
		||||
print('----------')
 | 
			
		||||
ranks.append(middle_ranks)
 | 
			
		||||
ranks = sorted(ranks)
 | 
			
		||||
pprint(ranks)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user