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")
# ... etc.
def include_object(object, name, type_, reflected, compare_to):
print(f"{type_}: {name}")
return True # Temporarily return True to debug all objects

View File

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

View File

@@ -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):

View File

@@ -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'

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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

View File

@@ -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}")

View File

@@ -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