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