From 8075ce04b046753a8aba848e6b71510137def39e Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 11 Oct 2024 20:02:04 +0300 Subject: [PATCH] feat: services excel --- .../services_excel_generator/__init__.py | 1 + generators/services_excel_generator/core.py | 183 ++++++++++++++++++ static/excel/services.xlsx | Bin 0 -> 23411 bytes 3 files changed, 184 insertions(+) create mode 100644 generators/services_excel_generator/__init__.py create mode 100644 generators/services_excel_generator/core.py create mode 100644 static/excel/services.xlsx diff --git a/generators/services_excel_generator/__init__.py b/generators/services_excel_generator/__init__.py new file mode 100644 index 0000000..d840b3a --- /dev/null +++ b/generators/services_excel_generator/__init__.py @@ -0,0 +1 @@ +from .core import ServiceExcelExporter diff --git a/generators/services_excel_generator/core.py b/generators/services_excel_generator/core.py new file mode 100644 index 0000000..5f79ea9 --- /dev/null +++ b/generators/services_excel_generator/core.py @@ -0,0 +1,183 @@ +import time +from collections import defaultdict +from pathlib import Path + +import openpyxl +import openpyxl.styles +from six import BytesIO +from sqlalchemy import select +from sqlalchemy.orm import joinedload + +from constants import APP_PATH +from enums.service import ServiceType +from models import Service, ServiceCategory + + +class ServiceExcelExporter: + SERVICE_TYPE_FONT = openpyxl.styles.Font(bold=True, size=14, name='Calibri') + SERVICE_TYPE_FILL = openpyxl.styles.PatternFill(start_color='0000FF', end_color='0000FF', fill_type='solid') + SERVICE_TYPE_ALIGNMENT = openpyxl.styles.Alignment(horizontal='center') + SERVICE_TYPE_BORDER = openpyxl.styles.Border( + left=openpyxl.styles.Side(style='medium'), + right=openpyxl.styles.Side(style='medium'), + top=openpyxl.styles.Side(style='medium'), + bottom=openpyxl.styles.Side(style='medium') + ) + + CATEGORY_FONT = openpyxl.styles.Font(bold=True, size=12, name='Calibri') + CATEGORY_FILL = openpyxl.styles.PatternFill(start_color='DBEEF4', end_color='DBEEF4', fill_type='solid') + CATEGORY_ALIGNMENT = openpyxl.styles.Alignment(horizontal='center') + CATEGORY_BORDER = SERVICE_TYPE_BORDER # Same as service type border + + EVEN_ROW_FILL = openpyxl.styles.PatternFill(start_color='EBF1DE', end_color='EBF1DE', fill_type='solid') + + def __init__(self, session): + self.session = session + self.template_path = Path(APP_PATH) / 'static' / 'excel' / 'services.xlsx' + self.workbook = None + self.worksheet = None + self.start_row = 12 + self.name_column = 'A' + self.price_column = 'B' + self.categories_dict = {} + self.service_type_dict = { + ServiceType.DEAL_SERVICE: 'Общие услуги', + ServiceType.PRODUCT_SERVICE: 'Услуги фулфилмента' + } + + async def get_services(self): + """Fetch and organize services from the database.""" + services = (await self.session.scalars( + select(Service) + .options(joinedload(Service.category)) + .filter(Service.is_deleted == False) + .order_by(Service.rank) + )).all() + + intermediate_result = defaultdict(lambda: defaultdict(list)) + for service in services: + intermediate_result[service.service_type][service.category_id].append(service) + + final_result = defaultdict(dict) + for service_type, categories_dict in intermediate_result.items(): + categories = {service.category for services in categories_dict.values() for service in services} + + def category_sort_key(category): + return category.deal_service_rank if service_type == ServiceType.DEAL_SERVICE else category.product_service_rank + + sorted_categories = sorted(categories, key=category_sort_key) + sorted_categories_dict = {category.id: categories_dict[category.id] for category in sorted_categories} + final_result[service_type] = sorted_categories_dict + final_final_result = {} + for service_type in [ServiceType.DEAL_SERVICE, ServiceType.PRODUCT_SERVICE]: + final_final_result[service_type] = final_result[service_type] + return dict(final_final_result) + + async def get_categories(self): + """Fetch categories from the database.""" + categories = (await self.session.scalars( + select(ServiceCategory) + .filter(ServiceCategory.is_deleted == False) + )).all() + self.categories_dict = {category.id: category for category in categories} + + @staticmethod + def format_ruble_number(number: int): + """Format a number with spaces as thousand separators.""" + return f'{number:,}'.replace(',', ' ') + + def get_price_value(self, service: Service): + """Get the price value string for a service.""" + if service.price_ranges: + price_ranges_length = len(service.price_ranges) + if price_ranges_length == 1: + price = self.format_ruble_number(int(service.price_ranges[0].price)) + return f'{price} ₽' + + result = [] + for idx, price_range in enumerate(service.price_ranges): + price = self.format_ruble_number(int(price_range.price)) + if idx == price_ranges_length - 1: + result.append(f'от {price_range.from_quantity} шт: {price} ₽') + else: + result.append(f'{price_range.from_quantity} шт - {price_range.to_quantity} шт: {price} ₽') + return '\n'.join(result) + else: + price = self.format_ruble_number(int(service.price)) + return f'{price} ₽' + + def load_template(self): + """Load the Excel template.""" + self.workbook = openpyxl.load_workbook(self.template_path) + self.worksheet = self.workbook.active + + def format_service_type_cell(self, cell): + """Apply formatting to a service type cell.""" + cell.font = self.SERVICE_TYPE_FONT + cell.fill = self.SERVICE_TYPE_FILL + cell.alignment = self.SERVICE_TYPE_ALIGNMENT + cell.border = self.SERVICE_TYPE_BORDER + + def format_category_cell(self, cell): + """Apply formatting to a category cell.""" + cell.font = self.CATEGORY_FONT + cell.fill = self.CATEGORY_FILL + cell.alignment = self.CATEGORY_ALIGNMENT + cell.border = self.CATEGORY_BORDER + + def format_service_row(self, name_cell, price_cell, is_even_row): + """Apply formatting to a service row.""" + name_cell.alignment = openpyxl.styles.Alignment(wrap_text=True, vertical='center') + price_cell.alignment = openpyxl.styles.Alignment(wrap_text=True, horizontal='right', vertical="center") + if is_even_row: + name_cell.fill = self.EVEN_ROW_FILL + price_cell.fill = self.EVEN_ROW_FILL + + def write_service_type_row(self, service_type): + """Write a service type row to the worksheet.""" + row = self.start_row + cell = self.worksheet[f'{self.name_column}{row}'] + cell.value = self.service_type_dict[service_type] + self.worksheet.merge_cells(f'{self.name_column}{row}:{self.price_column}{row}') + self.format_service_type_cell(cell) + self.start_row += 1 + + def write_category_row(self, category_name): + """Write a category row to the worksheet.""" + row = self.start_row + cell = self.worksheet[f'{self.name_column}{row}'] + cell.value = category_name + self.worksheet.merge_cells(f'{self.name_column}{row}:{self.price_column}{row}') + self.format_category_cell(cell) + self.start_row += 1 + + def write_service_row(self, service, counter): + """Write a service row to the worksheet.""" + row = self.start_row + name_cell = self.worksheet[f'{self.name_column}{row}'] + price_cell = self.worksheet[f'{self.price_column}{row}'] + name_cell.value = service.name + price_cell.value = self.get_price_value(service) + self.format_service_row(name_cell, price_cell, counter % 2 == 0) + self.start_row += 1 + + async def generate_excel(self): + """Generate the Excel file with services and categories.""" + start = time.time() + await self.get_categories() + services = await self.get_services() + self.load_template() + + for service_type, categories in services.items(): + self.write_service_type_row(service_type) + for category_id, services_list in categories.items(): + category_name = self.categories_dict[category_id].name + self.write_category_row(category_name) + for idx, service in enumerate(services_list): + self.write_service_row(service, idx) + + result = BytesIO() + self.workbook.save(result) + result.seek(0) + print('Elapsed time:', time.time() - start) + return result diff --git a/static/excel/services.xlsx b/static/excel/services.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1898763fbd23a1cf1a94556904ad555ffe52bf46 GIT binary patch literal 23411 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