feat: crappy reordering

This commit is contained in:
2024-10-07 23:42:37 +03:00
parent e153d4a1c0
commit aae7e96001
10 changed files with 208 additions and 17 deletions

View File

@@ -30,7 +30,6 @@ target_metadata = BaseModel.metadata
# my_important_option = config.get_main_option("my_important_option") # my_important_option = config.get_main_option("my_important_option")
# ... etc. # ... etc.
def include_object(object, name, type_, reflected, compare_to): def include_object(object, name, type_, reflected, compare_to):
print(f"{type_}: {name}")
return True # Temporarily return True to debug all objects return True # Temporarily return True to debug all objects

View File

@@ -12,5 +12,6 @@ from .marketplace import *
from .payroll import * from .payroll import *
from .billing import * from .billing import *
from .marketplace_products import * from .marketplace_products import *
# from .deal_group import *
configure_mappers() configure_mappers()

View File

@@ -10,7 +10,9 @@ from .marketplace import BaseMarketplace
from .shipping_warehouse import ShippingWarehouse from .shipping_warehouse import ShippingWarehouse
if TYPE_CHECKING: if TYPE_CHECKING:
from . import DealBillRequest, ServicePriceCategory from . import (DealBillRequest, ServicePriceCategory,
# DealGroup
)
# @unique # @unique
@@ -89,6 +91,13 @@ class Deal(BaseModel):
category: Mapped[Optional["ServicePriceCategory"]] = relationship('ServicePriceCategory', category: Mapped[Optional["ServicePriceCategory"]] = relationship('ServicePriceCategory',
secondary=DealPriceCategory.__table__, secondary=DealPriceCategory.__table__,
lazy='joined') lazy='joined')
# group: Mapped[Optional["DealGroup"]] = relationship(
# 'DealGroup',
# secondary='deal_relations',
# lazy='joined',
# uselist=False,
# back_populates='deals'
# )
class DealStatusHistory(BaseModel): class DealStatusHistory(BaseModel):

View File

@@ -46,6 +46,11 @@ class Service(BaseModel):
back_populates='service', back_populates='service',
lazy='selectin', lazy='selectin',
cascade="all, delete-orphan") cascade="all, delete-orphan")
rank: Mapped[str] = mapped_column(
nullable=False,
server_default='',
comment='Ранг услуги'
)
class ServicePriceRange(BaseModel): class ServicePriceRange(BaseModel):
@@ -80,6 +85,17 @@ class ServiceCategory(BaseModel):
id = Column(Integer, autoincrement=True, primary_key=True, index=True) id = Column(Integer, autoincrement=True, primary_key=True, index=True)
name = Column(String, nullable=False) 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): class ServicesKit(BaseModel):
__tablename__ = 'services_kits' __tablename__ = 'services_kits'

View File

@@ -70,6 +70,19 @@ async def delete(
return await ServiceService(session).delete(request) 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 # endregion
# region Categories # region Categories
@@ -99,6 +112,19 @@ async def create_category(
return await ServiceService(session).create_category(request) 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 # endregion
# region Types # region Types

View File

@@ -23,6 +23,12 @@ class FastDeal(BaseSchema):
acceptance_date: datetime.datetime acceptance_date: datetime.datetime
class DealGroupSchema(BaseSchema):
id: int
name: Optional[str] = None
lexorank: str
class DealSummary(BaseSchema): class DealSummary(BaseSchema):
id: int id: int
name: str name: str
@@ -42,6 +48,7 @@ class DealSummary(BaseSchema):
delivery_date: Optional[datetime.datetime] = None delivery_date: Optional[datetime.datetime] = None
receiving_slot_date: Optional[datetime.datetime] = None receiving_slot_date: Optional[datetime.datetime] = None
bill_request: Optional[DealBillRequestSchema] = None bill_request: Optional[DealBillRequestSchema] = None
# group: Optional[DealGroupSchema] = None
class DealServiceSchema(BaseSchema): class DealServiceSchema(BaseSchema):

View File

@@ -1,6 +1,6 @@
from typing import List, Optional from typing import List, Optional
from schemas.base import BaseSchema, OkMessageSchema, BaseEnumSchema from schemas.base import BaseSchema, OkMessageSchema
# region Entities # region Entities
@@ -16,6 +16,8 @@ class ServicePriceRangeSchema(BaseSchema):
class ServiceCategorySchema(BaseSchema): class ServiceCategorySchema(BaseSchema):
id: int id: int
name: str name: str
deal_service_rank: str
product_service_rank: str
class ServicePriceCategorySchema(BaseSchema): class ServicePriceCategorySchema(BaseSchema):
@@ -37,6 +39,7 @@ class ServiceSchema(BaseSchema):
price_ranges: List[ServicePriceRangeSchema] price_ranges: List[ServicePriceRangeSchema]
category_prices: List[ServiceCategoryPriceSchema] category_prices: List[ServiceCategoryPriceSchema]
cost: Optional[int] cost: Optional[int]
rank: str
# endregion # endregion
@@ -69,7 +72,6 @@ class UpdateServiceKitSchema(BaseServiceKitSchema):
# endregion # endregion
# region Requests # region Requests
class ServiceCreateRequest(BaseSchema): class ServiceCreateRequest(BaseSchema):
service: ServiceSchema service: ServiceSchema
@@ -108,9 +110,20 @@ class DeletePriceCategoryRequest(BaseSchema):
id: int 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 # region Responses
class ServiceGetAllResponse(BaseSchema): class ServiceGetAllResponse(BaseSchema):
services: List[ServiceSchema] services: List[ServiceSchema]
@@ -163,4 +176,11 @@ class UpdatePriceCategoryResponse(OkMessageSchema):
class DeletePriceCategoryResponse(OkMessageSchema): class DeletePriceCategoryResponse(OkMessageSchema):
pass pass
class ServiceReorderResponse(OkMessageSchema):
pass
class ServiceCategoryReorderResponse(OkMessageSchema):
pass
# endregion # endregion

View File

@@ -668,9 +668,15 @@ class DealService(BaseService):
models.DealProductService.deal_id == request.deal_id, models.DealProductService.deal_id == request.deal_id,
) )
) )
deal_product_services: list[models.DealProductService] = ( deal_product_services = (
await self.session.scalars(source_services_stmt)).all() (
# source_services: list[models.Service] = [dpc.service for dpc in deal_product_services] await self.session.scalars(
source_services_stmt
)
)
.all()
)
destination_deal_products_stmt = ( destination_deal_products_stmt = (
select( select(
models.DealProduct models.DealProduct

View File

@@ -1,25 +1,40 @@
from typing import Union import lexorank
from lexorank import Bucket, middle
from sqlalchemy import select, update, insert, delete from sqlalchemy import select, update, insert, delete, desc
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from enums.service import ServiceType
from models import Service, ServiceCategory, ServicePriceRange, ServicesKit, services_kit_services, \ from models import Service, ServiceCategory, ServicePriceRange, ServicesKit, services_kit_services, \
ServiceCategoryPrice, ServicePriceCategory ServiceCategoryPrice, ServicePriceCategory
from services.base import BaseService
from schemas.service import * from schemas.service import *
from services.base import BaseService
from utils.list_utils import previous_current_next
class ServiceService(BaseService): class ServiceService(BaseService):
async def get_all(self) -> ServiceGetAllResponse: async def get_all(self) -> ServiceGetAllResponse:
query = await (self.session query = await (
.scalars(select(Service) self.session
.options(joinedload(Service.category)) .scalars(select(Service)
.order_by(Service.category_id, Service.id))) .options(joinedload(Service.category))
.order_by(Service.rank)
)
)
services = [] services = []
for service in query.all(): for service in query.all():
services.append(ServiceSchema.model_validate(service)) services.append(ServiceSchema.model_validate(service))
return ServiceGetAllResponse(services=services) 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: async def create(self, request: ServiceCreateRequest) -> ServiceCreateResponse:
try: try:
raw_service = request.service raw_service = request.service
@@ -29,6 +44,16 @@ class ServiceService(BaseService):
del service_dict['category'] del service_dict['category']
del service_dict['price_ranges'] del service_dict['price_ranges']
del service_dict['category_prices'] 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) service = Service(**service_dict)
self.session.add(service) self.session.add(service)
await self.session.flush() await self.session.flush()
@@ -293,3 +318,64 @@ class ServiceService(BaseService):
return ServiceDeleteResponse(ok=True, message="Категория цен успешно удалена") return ServiceDeleteResponse(ok=True, message="Категория цен успешно удалена")
except Exception as e: except Exception as e:
return ServiceDeleteResponse(ok=False, message=f"Неудалось удалить категорию цен, ошибка: {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}")

View File

@@ -7,5 +7,26 @@ def compile_query_to_plain_sql(query) -> str:
return query.compile(compile_kwargs={ return query.compile(compile_kwargs={
'literal_binds': True 'literal_binds': True
}) })
def to_locale_number(value): 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