import copy from openpyxl import Workbook from django.http import HttpResponse from openpyxl.writer.excel import save_virtual_workbook from openpyxl.utils import get_column_letter from BaseModels.mailSender import techSendMail from openpyxl.styles import PatternFill, Font, Alignment from openpyxl.styles.borders import Border, Side from openpyxl.styles.numbers import BUILTIN_FORMATS from colorsys import rgb_to_hls def pairwise(iterable): a = iter(iterable) return zip(a, a) options_params_splitter = '<|>' def set_col_options(ws, row, col, rows_len, options): if type(options) == str: options = dict(item.split("=")[::1] for item in options.split('&')) cols_set = 1 if options: exists_group_option_for_column = False for key in options.keys(): if key.startswith('g_col_'): exists_group_option_for_column = True break if 'cols_merge' in options and options['cols_merge']: cols_set = int(options['cols_merge']) if cols_set > 1: ws.merge_cells(start_row=row, start_column=col, end_row=row, end_column=col + cols_set - 1) if exists_group_option_for_column: g_col_back_color = None if 'g_col_back_color' in options and options['g_col_back_color']: g_col_back_color = options['g_col_back_color'] g_col_num_w_sep = None if 'g_col_num_w_sep' in options and options['g_col_num_w_sep']: g_col_num_w_sep = options['g_col_num_w_sep'] cur_col = col while cur_col < col + cols_set: cur_row = row while cur_row < rows_len: if g_col_back_color: ws.cell(row=cur_row, column=cur_col).fill = PatternFill('solid', fgColor=g_col_back_color) if g_col_num_w_sep: ws.cell(row=cur_row, column=cur_col).number_format = '#,##0.00' cur_row += 1 cur_col += 1 if 'col_show_total' in options and options['col_show_total']: ws.cell(row=rows_len, column=col).font = Font(bold=True) ws.cell(row=rows_len, column=col).value = "=SUM({0}{1}:{0}{2})".format( get_column_letter(col), row + 1, rows_len - 1 ) ws.cell(row=rows_len, column=col).number_format = '#,##0.00' if 'back_color' in options and options['back_color']: ws.cell(row=row, column=col).fill = PatternFill('solid', fgColor=options['back_color']) if 'bold' in options and options['bold']: ws.cell(row=row, column=col).font = Font(bold=True) if 'col_bold' in options and options['col_bold']: cur_col = col while cur_col < col + cols_set: cur_row = row while cur_row < rows_len: ws.cell(row=cur_row, column=cur_col).font = Font(bold=True) cur_row += 1 cur_col += 1 return cols_set def add_table_in_workbook(work_sheet, data, convert_minus_to_null=False, headers_rows_count=0): thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin')) r = 1 for row in data: try: c = 1 cols = row if type(data) == dict: cols = row.values() for val in cols: options = None inc_c = 1 work_sheet.cell(row=r, column=c).border = thin_border # получаем опции if type(val) == str: val_w_options = val.split(options_params_splitter) if len(val_w_options) > 1: val = val_w_options[0] # применяем опции inc_c = set_col_options(work_sheet, row=r, col=c, rows_len=len(data) + 1, options=val_w_options[1]) elif type(val) == dict: inc_c = set_col_options(work_sheet, row=r, col=c, rows_len=len(data) + 1, options=val) val = val['val'] # если стоит опция "минусовые значения преобразовывать в нулевые" if convert_minus_to_null: try: if val < 0: val = 0 except: pass try: work_sheet.cell(row=r, column=c).value = val except: work_sheet.cell(row=r, column=c).value = str(val) c += inc_c except Exception as e: msg = f'add_table_in_workbook in row {str(r)} ERROR = {str(e)}' print(msg) r += 1 try: dims = {} row_c = 0 for row in work_sheet.rows: # не подгоняем данные под надписи в хэдере if row_c < headers_rows_count: row_c += 1 continue for cell in row: if cell.value: dims[cell.column] = max((dims.get(cell.column, 0), len(str(cell.value)))) row_c += 1 for col, value in dims.items(): if value > 150: value = 150 if value < 3: value = 3 work_sheet.column_dimensions[get_column_letter(col)].width = value except Exception as e: msg = f'add_table_in_workbook in sets width ERROR = {str(e)}' print(msg) return def get_xls_file_by_data_list(data, convert_minus_to_null=False): try: wb = Workbook() ws = wb.active if type(data) == list and len(data) and type(data[0]) == dict: i = 0 for page in data: title = None if 'title' in page: title = page['title'] # если первая страница - она уже создана, просто переименовываем if i == 0: if title: ws.title = title else: ws = wb.create_sheet(title) headers_rows_count = 0 if 'headers_rows_count' in page: headers_rows_count = page['headers_rows_count'] add_table_in_workbook(ws, page['table'], convert_minus_to_null, headers_rows_count) i += 1 else: add_table_in_workbook(ws, data, convert_minus_to_null) xls_file = save_virtual_workbook(wb) return xls_file except Exception as e: msg = str(e) print(msg) return msg