feat: crappy reordering
This commit is contained in:
@@ -30,7 +30,6 @@ target_metadata = BaseModel.metadata
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
@@ -12,5 +12,6 @@ from .marketplace import *
|
||||
from .payroll import *
|
||||
from .billing import *
|
||||
from .marketplace_products import *
|
||||
# from .deal_group import *
|
||||
|
||||
configure_mappers()
|
||||
|
||||
@@ -10,7 +10,9 @@ from .marketplace import BaseMarketplace
|
||||
from .shipping_warehouse import ShippingWarehouse
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import DealBillRequest, ServicePriceCategory
|
||||
from . import (DealBillRequest, ServicePriceCategory,
|
||||
# DealGroup
|
||||
)
|
||||
|
||||
|
||||
# @unique
|
||||
@@ -89,6 +91,13 @@ class Deal(BaseModel):
|
||||
category: Mapped[Optional["ServicePriceCategory"]] = relationship('ServicePriceCategory',
|
||||
secondary=DealPriceCategory.__table__,
|
||||
lazy='joined')
|
||||
# group: Mapped[Optional["DealGroup"]] = relationship(
|
||||
# 'DealGroup',
|
||||
# secondary='deal_relations',
|
||||
# lazy='joined',
|
||||
# uselist=False,
|
||||
# back_populates='deals'
|
||||
# )
|
||||
|
||||
|
||||
class DealStatusHistory(BaseModel):
|
||||
|
||||
@@ -46,6 +46,11 @@ class Service(BaseModel):
|
||||
back_populates='service',
|
||||
lazy='selectin',
|
||||
cascade="all, delete-orphan")
|
||||
rank: Mapped[str] = mapped_column(
|
||||
nullable=False,
|
||||
server_default='',
|
||||
comment='Ранг услуги'
|
||||
)
|
||||
|
||||
|
||||
class ServicePriceRange(BaseModel):
|
||||
@@ -80,6 +85,17 @@ class ServiceCategory(BaseModel):
|
||||
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
|
||||
name = Column(String, nullable=False)
|
||||
|
||||
deal_service_rank: Mapped[str] = mapped_column(
|
||||
nullable=False,
|
||||
server_default='',
|
||||
comment='Ранг услуги для сделки'
|
||||
)
|
||||
product_service_rank: Mapped[str] = mapped_column(
|
||||
nullable=False,
|
||||
server_default='',
|
||||
comment='Ранг услуги для товара'
|
||||
)
|
||||
|
||||
|
||||
class ServicesKit(BaseModel):
|
||||
__tablename__ = 'services_kits'
|
||||
|
||||
@@ -70,6 +70,19 @@ async def delete(
|
||||
return await ServiceService(session).delete(request)
|
||||
|
||||
|
||||
@service_router.post(
|
||||
'/reorder',
|
||||
response_model=ServiceReorderResponse,
|
||||
operation_id="reorder_service",
|
||||
|
||||
)
|
||||
async def reorder(
|
||||
session: Annotated[AsyncSession, Depends(get_session)],
|
||||
request: ServiceReorderRequest
|
||||
):
|
||||
return await ServiceService(session).reorder(request)
|
||||
|
||||
|
||||
# endregion
|
||||
|
||||
# region Categories
|
||||
@@ -99,6 +112,19 @@ async def create_category(
|
||||
return await ServiceService(session).create_category(request)
|
||||
|
||||
|
||||
@service_router.post(
|
||||
'/categories/reorder',
|
||||
response_model=ServiceCategoryReorderResponse,
|
||||
operation_id="reorder_service_category",
|
||||
dependencies=[Depends(authorized_user)]
|
||||
)
|
||||
async def reorder_category(
|
||||
session: Annotated[AsyncSession, Depends(get_session)],
|
||||
request: ServiceCategoryReorderRequest
|
||||
):
|
||||
return await ServiceService(session).reorder_category(request)
|
||||
|
||||
|
||||
# endregion
|
||||
|
||||
# region Types
|
||||
|
||||
@@ -23,6 +23,12 @@ class FastDeal(BaseSchema):
|
||||
acceptance_date: datetime.datetime
|
||||
|
||||
|
||||
class DealGroupSchema(BaseSchema):
|
||||
id: int
|
||||
name: Optional[str] = None
|
||||
lexorank: str
|
||||
|
||||
|
||||
class DealSummary(BaseSchema):
|
||||
id: int
|
||||
name: str
|
||||
@@ -42,6 +48,7 @@ class DealSummary(BaseSchema):
|
||||
delivery_date: Optional[datetime.datetime] = None
|
||||
receiving_slot_date: Optional[datetime.datetime] = None
|
||||
bill_request: Optional[DealBillRequestSchema] = None
|
||||
# group: Optional[DealGroupSchema] = None
|
||||
|
||||
|
||||
class DealServiceSchema(BaseSchema):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from schemas.base import BaseSchema, OkMessageSchema, BaseEnumSchema
|
||||
from schemas.base import BaseSchema, OkMessageSchema
|
||||
|
||||
|
||||
# region Entities
|
||||
@@ -16,6 +16,8 @@ class ServicePriceRangeSchema(BaseSchema):
|
||||
class ServiceCategorySchema(BaseSchema):
|
||||
id: int
|
||||
name: str
|
||||
deal_service_rank: str
|
||||
product_service_rank: str
|
||||
|
||||
|
||||
class ServicePriceCategorySchema(BaseSchema):
|
||||
@@ -37,6 +39,7 @@ class ServiceSchema(BaseSchema):
|
||||
price_ranges: List[ServicePriceRangeSchema]
|
||||
category_prices: List[ServiceCategoryPriceSchema]
|
||||
cost: Optional[int]
|
||||
rank: str
|
||||
|
||||
|
||||
# endregion
|
||||
@@ -69,7 +72,6 @@ class UpdateServiceKitSchema(BaseServiceKitSchema):
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region Requests
|
||||
class ServiceCreateRequest(BaseSchema):
|
||||
service: ServiceSchema
|
||||
@@ -108,9 +110,20 @@ class DeletePriceCategoryRequest(BaseSchema):
|
||||
id: int
|
||||
|
||||
|
||||
# endregion
|
||||
class ServiceReorderRequest(BaseSchema):
|
||||
draining_service_id: int
|
||||
hovered_service_id: int
|
||||
|
||||
|
||||
class ServiceCategoryReorderRequest(BaseSchema):
|
||||
move_down: bool
|
||||
move_up: bool
|
||||
category_id: int
|
||||
service_type: int
|
||||
|
||||
|
||||
# endregion
|
||||
|
||||
# region Responses
|
||||
class ServiceGetAllResponse(BaseSchema):
|
||||
services: List[ServiceSchema]
|
||||
@@ -163,4 +176,11 @@ class UpdatePriceCategoryResponse(OkMessageSchema):
|
||||
class DeletePriceCategoryResponse(OkMessageSchema):
|
||||
pass
|
||||
|
||||
|
||||
class ServiceReorderResponse(OkMessageSchema):
|
||||
pass
|
||||
|
||||
|
||||
class ServiceCategoryReorderResponse(OkMessageSchema):
|
||||
pass
|
||||
# endregion
|
||||
|
||||
@@ -668,9 +668,15 @@ class DealService(BaseService):
|
||||
models.DealProductService.deal_id == request.deal_id,
|
||||
)
|
||||
)
|
||||
deal_product_services: list[models.DealProductService] = (
|
||||
await self.session.scalars(source_services_stmt)).all()
|
||||
# source_services: list[models.Service] = [dpc.service for dpc in deal_product_services]
|
||||
deal_product_services = (
|
||||
(
|
||||
await self.session.scalars(
|
||||
source_services_stmt
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
|
||||
destination_deal_products_stmt = (
|
||||
select(
|
||||
models.DealProduct
|
||||
|
||||
@@ -1,25 +1,40 @@
|
||||
from typing import Union
|
||||
|
||||
from sqlalchemy import select, update, insert, delete
|
||||
import lexorank
|
||||
from lexorank import Bucket, middle
|
||||
from sqlalchemy import select, update, insert, delete, desc
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from enums.service import ServiceType
|
||||
from models import Service, ServiceCategory, ServicePriceRange, ServicesKit, services_kit_services, \
|
||||
ServiceCategoryPrice, ServicePriceCategory
|
||||
from services.base import BaseService
|
||||
from schemas.service import *
|
||||
from services.base import BaseService
|
||||
from utils.list_utils import previous_current_next
|
||||
|
||||
|
||||
class ServiceService(BaseService):
|
||||
async def get_all(self) -> ServiceGetAllResponse:
|
||||
query = await (self.session
|
||||
.scalars(select(Service)
|
||||
.options(joinedload(Service.category))
|
||||
.order_by(Service.category_id, Service.id)))
|
||||
query = await (
|
||||
self.session
|
||||
.scalars(select(Service)
|
||||
.options(joinedload(Service.category))
|
||||
.order_by(Service.rank)
|
||||
)
|
||||
)
|
||||
services = []
|
||||
for service in query.all():
|
||||
services.append(ServiceSchema.model_validate(service))
|
||||
return ServiceGetAllResponse(services=services)
|
||||
|
||||
async def get_latest_rank_in_category(self, category_id: int, service_type: ServiceType) -> str:
|
||||
stmt = (
|
||||
select(Service.rank)
|
||||
.where(Service.category_id == category_id)
|
||||
.where(Service.service_type == service_type)
|
||||
.order_by(desc(Service.rank))
|
||||
.limit(1)
|
||||
)
|
||||
return await self.session.scalar(stmt) or ''
|
||||
|
||||
async def create(self, request: ServiceCreateRequest) -> ServiceCreateResponse:
|
||||
try:
|
||||
raw_service = request.service
|
||||
@@ -29,6 +44,16 @@ class ServiceService(BaseService):
|
||||
del service_dict['category']
|
||||
del service_dict['price_ranges']
|
||||
del service_dict['category_prices']
|
||||
service_type = ServiceType(raw_service.service_type)
|
||||
category_id = raw_service.category.id
|
||||
|
||||
latest_rank = await self.get_latest_rank_in_category(category_id, service_type)
|
||||
if not latest_rank:
|
||||
latest_rank = middle(Bucket.BUCEKT_0)
|
||||
else:
|
||||
latest_rank = lexorank.parse(latest_rank)
|
||||
service_rank = str(latest_rank.next())
|
||||
service_dict['rank'] = service_rank
|
||||
service = Service(**service_dict)
|
||||
self.session.add(service)
|
||||
await self.session.flush()
|
||||
@@ -293,3 +318,64 @@ class ServiceService(BaseService):
|
||||
return ServiceDeleteResponse(ok=True, message="Категория цен успешно удалена")
|
||||
except Exception as e:
|
||||
return ServiceDeleteResponse(ok=False, message=f"Неудалось удалить категорию цен, ошибка: {e}")
|
||||
|
||||
async def reorder(self, request: ServiceReorderRequest) -> ServiceReorderResponse:
|
||||
try:
|
||||
draining_service = await (self.session.get(Service, request.draining_service_id))
|
||||
hovered_service = await (self.session.get(Service, request.hovered_service_id))
|
||||
if not draining_service or not hovered_service:
|
||||
return ServiceReorderResponse(ok=False, message="Услуга не найдена")
|
||||
if draining_service.category_id == hovered_service.category_id:
|
||||
temp = draining_service.rank
|
||||
draining_service.rank = hovered_service.rank
|
||||
hovered_service.rank = temp
|
||||
else:
|
||||
return ServiceReorderResponse(ok=False, message="Нельзя перемещать услуги между категориями")
|
||||
await self.session.commit()
|
||||
return ServiceReorderResponse(ok=True, message="Услуги успешно пересортированы")
|
||||
except Exception as e:
|
||||
return ServiceReorderResponse(ok=False, message=f"Неудалось найти услугу, ошибка: {e}")
|
||||
|
||||
async def reorder_category(self, request: ServiceCategoryReorderRequest) -> ServiceCategoryReorderResponse:
|
||||
try:
|
||||
if request.move_down and request.move_up:
|
||||
return ServiceCategoryReorderResponse(ok=False,
|
||||
message="Невозможно выполнить два действия одновременно")
|
||||
if not request.move_down and not request.move_up:
|
||||
return ServiceCategoryReorderResponse(ok=False, message="Не указано действие")
|
||||
if request.service_type == ServiceType.DEAL_SERVICE:
|
||||
order_by = ServiceCategory.deal_service_rank
|
||||
rank_field = 'deal_service_rank'
|
||||
else:
|
||||
order_by = ServiceCategory.product_service_rank
|
||||
rank_field = 'product_service_rank'
|
||||
service_categories = await (
|
||||
self.session
|
||||
.scalars(select(ServiceCategory)
|
||||
.order_by(order_by))
|
||||
)
|
||||
service_categories = service_categories.all()
|
||||
for prv, cur, nxt in previous_current_next(service_categories):
|
||||
if request.category_id == cur.id:
|
||||
if request.move_down:
|
||||
if nxt:
|
||||
temp = getattr(cur, rank_field)
|
||||
# cur.rank = nxt.rank
|
||||
setattr(cur, rank_field, getattr(nxt, rank_field))
|
||||
# nxt.rank = temp
|
||||
setattr(nxt, rank_field, temp)
|
||||
else:
|
||||
return ServiceCategoryReorderResponse(ok=False, message="Невозможно переместить вниз")
|
||||
elif request.move_up:
|
||||
if prv:
|
||||
temp = getattr(cur, rank_field)
|
||||
# cur.rank = prv.rank
|
||||
setattr(cur, rank_field, getattr(prv, rank_field))
|
||||
# prv.rank = temp
|
||||
setattr(prv, rank_field, temp)
|
||||
else:
|
||||
return ServiceCategoryReorderResponse(ok=False, message="Невозможно переместить вверх")
|
||||
await self.session.commit()
|
||||
return ServiceCategoryReorderResponse(ok=True, message="Категории успешно пересортированы")
|
||||
except Exception as e:
|
||||
return ServiceCategoryReorderResponse(ok=False, message=f"Неудалось пересортировать категорию, ошибка: {e}")
|
||||
|
||||
@@ -7,5 +7,26 @@ def compile_query_to_plain_sql(query) -> str:
|
||||
return query.compile(compile_kwargs={
|
||||
'literal_binds': True
|
||||
})
|
||||
|
||||
|
||||
def to_locale_number(value):
|
||||
return '{:,}'.format(value).replace(',', ' ')
|
||||
return '{:,}'.format(value).replace(',', ' ')
|
||||
|
||||
|
||||
def previous_current_next(iterable):
|
||||
"""Make an iterator that yields a (previous, current, next) tuple per element.
|
||||
|
||||
Returns None if the value does not make sense (i.e. previous before
|
||||
first and next after last).
|
||||
"""
|
||||
iterable = iter(iterable)
|
||||
prv = None
|
||||
cur = next(iterable)
|
||||
try:
|
||||
while True:
|
||||
nxt = next(iterable)
|
||||
yield prv, cur, nxt
|
||||
prv = cur
|
||||
cur = nxt
|
||||
except StopIteration:
|
||||
yield prv, cur, None
|
||||
|
||||
Reference in New Issue
Block a user