From 6e472cf63495751b98c020a65188d725e87dce35 Mon Sep 17 00:00:00 2001 From: hayano Date: Wed, 6 Nov 2024 18:26:16 +0000 Subject: [PATCH] Generate Excel dev stege final --- SumasenLibs/excel_lib/requirements.txt | 1 + .../excel_lib/sumaexcel/.sumaexcel.py.swp | Bin 45056 -> 0 bytes SumasenLibs/excel_lib/sumaexcel/sumaexcel.py | 407 +++++++++++++++--- .../excel_lib/testdata/certificate_5033.xlsx | Bin 6982 -> 42190 bytes .../testdata/certificate_template.xlsx | Bin 11138 -> 11217 bytes SumasenLibs/excel_lib/testdata/test.ini | 10 +- 6 files changed, 355 insertions(+), 63 deletions(-) delete mode 100644 SumasenLibs/excel_lib/sumaexcel/.sumaexcel.py.swp diff --git a/SumasenLibs/excel_lib/requirements.txt b/SumasenLibs/excel_lib/requirements.txt index 8938e66..60c70be 100644 --- a/SumasenLibs/excel_lib/requirements.txt +++ b/SumasenLibs/excel_lib/requirements.txt @@ -3,3 +3,4 @@ pandas>=1.0.0 pillow>=8.0.0 configparser>=5.0.0 psycopg2-binary==2.9.9 +requests diff --git a/SumasenLibs/excel_lib/sumaexcel/.sumaexcel.py.swp b/SumasenLibs/excel_lib/sumaexcel/.sumaexcel.py.swp deleted file mode 100644 index d9fd27f3a8bf2135535a23158010b715f7d885b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45056 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 diff --git a/SumasenLibs/excel_lib/sumaexcel/sumaexcel.py b/SumasenLibs/excel_lib/sumaexcel/sumaexcel.py index c977537..3db4b40 100644 --- a/SumasenLibs/excel_lib/sumaexcel/sumaexcel.py +++ b/SumasenLibs/excel_lib/sumaexcel/sumaexcel.py @@ -24,6 +24,14 @@ from .config_handler import ConfigHandler # ini file のロード #from .conditional import ConditionalFormatManager from .page import PageManager, PaperSizes +from datetime import datetime +import pytz +from urllib.parse import urlparse +import requests +from io import BytesIO +from PIL import Image +import re + import logging logging.basicConfig( @@ -165,9 +173,18 @@ class SumasenExcel: return {"status": False, "message": f"Error no template sheet found: {template_sheet_name}"} # シートの名前を設定 - new_sheet = self.workbook.create_sheet(title=section_config.get("sheet_name", section)) - self.worksheet = new_sheet + new_sheet_name = section_config.get("sheet_name", section) + # Create new sheet with template name if it doesn't exist + if new_sheet_name not in self.workbook.sheetnames: + self.current_sheet = self.workbook.create_sheet(new_sheet_name) + else: + self.current_sheet = self.workbook[new_sheet_name] + + # Remove default sheet if it exists + if 'Sheet' in self.workbook.sheetnames: + del self.workbook['Sheet'] + self.dbname=variables.get('db') self.user=variables.get('username') self.password=variables.get('password') @@ -195,12 +212,17 @@ class SumasenExcel: # シートの幅を設定 fit_to_width = section_config.get("fit_to_width") if fit_to_width: - new_sheet.sheet_view.zoomScaleNormal = float(fit_to_width) + self.current_sheet.sheet_view.zoomScaleNormal = float(fit_to_width) # シートの向きを設定 orientation = section_config.get("orientation") - new_sheet.sheet_view.orientation = orientation if orientation else "portrait" - self.current_worksheet = new_sheet + self.current_sheet.sheet_view.orientation = orientation if orientation else "portrait" + + max_col = self.basic.get("maxcol",20) + if not max_col: + return {"status": False, "message": f"Error no maxcol found: basic"} + #self.set_column_width(1,int(max_col)) + self.set_column_width_from_config(self.current_sheet) # グループ定義を取得 groups = section_config.get("groups") @@ -253,6 +275,78 @@ class SumasenExcel: logging.error(f"Error in proceed_group: {str(e)}") return {"status": False, "message": f"Exception in proceed_group : Error generating report: {str(e)}"} + def format_cell_value(self,field_value, cell): + """セルの値を適切な形式に変換する + + Args: + field_value: DBから取得した値 + cell: 対象のExcelセル + + Returns: + 変換後の値 + """ + # Noneの場合は空文字を返す + if field_value is None: + return "" + + # 真偽値の場合 + if isinstance(field_value, bool): + return "OK" if field_value else "-" + + # 日時型の場合 + if isinstance(field_value, datetime): + jst = pytz.timezone('Asia/Tokyo') + # UTC -> JST変換 + jst_time = field_value.astimezone(jst) + return jst_time.strftime('%H:%M:%S') + + # 文字列の場合、URLかどうかをチェック + if isinstance(field_value, str): + # URL形式かどうかチェック + try: + result = urlparse(field_value) + # URLで、かつ画像ファイルの拡張子を持つ場合 + if all([result.scheme, result.netloc]) and \ + re.search(r'\.(jpg|jpeg|png|gif|bmp)$', result.path, re.I): + try: + # 画像をダウンロード + response = requests.get(field_value) + img = Image.open(BytesIO(response.content)) + + # セルの大きさを取得(ピクセル単位) + cell_width = cell.column_dimensions.width * 6 # 概算のピクセル変換 + cell_height = cell.row_dimensions.height + + # アスペクト比を保持しながらリサイズ + img_aspect = img.width / img.height + cell_aspect = cell_width / cell_height + + if img_aspect > cell_aspect: + width = int(cell_width) + height = int(width / img_aspect) + else: + height = int(cell_height) + width = int(height * img_aspect) + + # 画像をリサイズしてセルに追加 + img = img.resize((width, height)) + + # 画像をセルに追加(実装方法はExcelライブラリに依存) + if hasattr(cell, 'add_image'): # 使用するライブラリによって適切なメソッドを使用 + cell.add_image(img) + return "" # 画像を追加したので、テキスト値は空にする + except Exception as e: + logging.warning(f"画像の処理に失敗: {str(e)}") + return field_value # エラーの場合はURLをそのまま返す + except Exception as e: + logging.warning(f"形式変換の処理に失敗: {str(e)}") + return field_value # エラーの場合はURLをそのまま返す + + # その他の場合は文字列に変換 + return str(field_value) + + + def proceed_one_record(self,table:str,where:str,group_range:str,variables: Dict[str, Any]): """1レコードのデータを取得してシートの値を置き換える @@ -268,6 +362,7 @@ class SumasenExcel: # まずself.template_sheetの指定範囲のセルをself.current_sheetにコピーする。 self.copy_template_to_current(group_range,group_range) + print(f"step-1") cursor = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) @@ -280,7 +375,7 @@ class SumasenExcel: print(f"record={record}") if record: # group_rangeの範囲内のセルを走査 - for row in self.current_worksheet: + for row in self.current_sheet: for cell in row: if cell.value and isinstance(cell.value, str): # [field_name]形式の文字列を検索 @@ -292,12 +387,14 @@ class SumasenExcel: new_value = cell.value for field_name in matches: if field_name in record: + # 新しい形式変換関数を使用 + formatted_value = self.format_cell_value(record[field_name], cell) new_value = new_value.replace( - f'[{field_name}]', - str(record[field_name]) - ) + f'[{field_name}]', + formatted_value + ) cell.value = new_value - + cursor.close() return {"status": True, "message": f"Success generating group: "} @@ -378,7 +475,7 @@ class SumasenExcel: # コピーした範囲内のセルを走査して値を置換 for row in range(current_row, current_row + template_rows): - for cell in self.current_worksheet[row]: + for cell in self.current_sheet[row]: if cell.value and isinstance(cell.value, str): # [field_name]形式の文字列を検索 import re @@ -389,9 +486,11 @@ class SumasenExcel: new_value = cell.value for field_name in matches: if field_name in record: + # 新しい形式変換関数を使用 + formatted_value = self.format_cell_value(record[field_name], cell) new_value = new_value.replace( - f'[{field_name}]', - str(record[field_name]) + f'[{field_name}]', + formatted_value ) cell.value = new_value @@ -405,40 +504,224 @@ class SumasenExcel: logging.error(f"Error in proceed_all_record: {str(e)}") return {"status": False, "message": f"Exception in proceed_all_record:Error processing records: {str(e)}"} + + def set_column_width_from_config(self, worksheet): + """ + INIファイルの設定に基づいて列幅を設定する + + Parameters: + worksheet: 設定対象のワークシート + + Returns: + dict: 処理結果を示す辞書 + """ + try: + section_config = self.conf.get_section('basic') + + # basic セクションから column_width を読み取る + if not section_config.get('column_width'): + raise ValueError("column_width setting not found in [basic] section") + + # カンマ区切りの文字列を数値のリストに変換 + width_str = section_config.get('column_width') + widths = [float(w.strip()) for w in width_str.split(',')] + + # 各列に幅を設定 + for col_index, width in enumerate(widths, start=1): + col_letter = get_column_letter(col_index) + worksheet.column_dimensions[col_letter].width = width + logging.info(f"Set column {col_letter} width to {width}") + + return { + "status": True, + "message": "Column widths set successfully from config", + "details": { + "num_columns": len(widths), + "widths": widths + } + } + + except ValueError as ve: + logging.error(f"Invalid column width value in config: {str(ve)}") + return { + "status": False, + "message": f"Invalid column width configuration: {str(ve)}" + } + except Exception as e: + logging.error(f"Error setting column widths: {str(e)}") + return { + "status": False, + "message": f"Error setting column widths: {str(e)}" + } + + def verify_column_widths(self, worksheet, expected_widths=None): + """ + 列幅が正しく設定されているか検証する + + Parameters: + worksheet: 検証対象のワークシート + expected_widths: 期待される幅のリスト(指定がない場合はINIファイルから読み取り) + + Returns: + list: 検証結果のリスト + """ + if expected_widths is None: + width_str = self.config.get('basic', 'column_width') + expected_widths = [float(w.strip()) for w in width_str.split(',')] + + verification_results = [] + for col_index, expected_width in enumerate(expected_widths, start=1): + col_letter = get_column_letter(col_index) + actual_width = worksheet.column_dimensions[col_letter].width + + matches = abs(float(actual_width) - float(expected_width)) < 0.01 + verification_results.append({ + 'column': col_letter, + 'expected_width': expected_width, + 'actual_width': actual_width, + 'matches': matches + }) + + if not matches: + logging.warning( + f"Column {col_letter} width mismatch: " + f"expected={expected_width}, actual={actual_width}" + ) + + return verification_results + + def copy_template_range(self, orig_min_row, orig_min_col, orig_max_row, orig_max_col, target_min_row): + """ + テンプレートの範囲をコピーし、マージセルの罫線を正しく設定する + """ + try: + # マージセルの情報を保存 + merged_ranges = [] + + # マージセルをコピー + for merged_range in self.template_sheet.merged_cells: + min_col, min_row, max_col, max_row = range_boundaries(str(merged_range)) + + # コピー範囲と重なるマージセルをチェック + if (min_col >= orig_min_col and max_col <= orig_max_col and + min_row >= orig_min_row and max_row <= orig_max_row): + # ターゲットのマージ範囲を計算 + target_merge_min_row = target_min_row + (min_row - orig_min_row) + target_merge_max_row = target_min_row + (max_row - orig_min_row) + target_merge_range = f"{get_column_letter(min_col)}{target_merge_min_row}:" \ + f"{get_column_letter(max_col)}{target_merge_max_row}" + + # マージ情報を保存 + merged_ranges.append({ + 'range': target_merge_range, + 'min_col': min_col, + 'max_col': max_col, + 'min_row': target_merge_min_row, + 'max_row': target_merge_max_row, + 'source_cell': self.template_sheet.cell(row=min_row, column=min_col) + }) + + # セルをマージ + self.current_sheet.merge_cells(target_merge_range) + + # セルの内容とスタイルをコピー + row_offset = target_min_row - orig_min_row + for row in range(orig_min_row, orig_max_row + 1): + for col in range(orig_min_col, orig_max_col + 1): + source_cell = self.template_sheet.cell(row=row, column=col) + target_cell = self.current_sheet.cell(row=row+row_offset, column=col) + + # 値とスタイルをコピー + if source_cell.value is not None: + target_cell.value = source_cell.value + + if source_cell.has_style: + target_cell.font = copy(source_cell.font) + target_cell.fill = copy(source_cell.fill) + target_cell.number_format = source_cell.number_format + target_cell.protection = copy(source_cell.protection) + target_cell.alignment = copy(source_cell.alignment) + + # マージセルの罫線を設定 + for merge_info in merged_ranges: + source_cell = merge_info['source_cell'] + source_border = source_cell.border + + # マージ範囲内の各セルに罫線を設定 + for row in range(merge_info['min_row'], merge_info['max_row'] + 1): + for col in range(merge_info['min_col'], merge_info['max_col'] + 1): + target_cell = self.current_sheet.cell(row=row, column=col) + + # 新しい罫線スタイルを作成 + new_border = copy(source_border) + + # 範囲の端のセルの場合のみ、対応する辺の罫線を設定 + if col == merge_info['min_col']: # 左端 + new_border.left = copy(source_border.left) + if col == merge_info['max_col']: # 右端 + new_border.right = copy(source_border.right) + if row == merge_info['min_row']: # 上端 + new_border.top = copy(source_border.top) + if row == merge_info['max_row']: # 下端 + new_border.bottom = copy(source_border.bottom) + + target_cell.border = new_border + + return {"status": True, "message": "Range copied successfully with merged cell borders"} + + except Exception as e: + logging.error(f"Error copying template range: {str(e)}") + return {"status": False, "message": f"Error copying template range: {str(e)}"} + + def verify_merged_cell_borders(self, worksheet, merge_range): + """ + マージセルの罫線が正しく設定されているか検証する + """ + try: + min_col, min_row, max_col, max_row = range_boundaries(merge_range) + + border_status = { + 'top': [], + 'bottom': [], + 'left': [], + 'right': [] + } + + # 各辺の罫線をチェック + for row in range(min_row, max_row + 1): + for col in range(min_col, max_col + 1): + cell = worksheet.cell(row=row, column=col) + + if col == min_col: # 左辺 + border_status['left'].append(cell.border.left) + if col == max_col: # 右辺 + border_status['right'].append(cell.border.right) + if row == min_row: # 上辺 + border_status['top'].append(cell.border.top) + if row == max_row: # 下辺 + border_status['bottom'].append(cell.border.bottom) + + return border_status + + except Exception as e: + logging.error(f"Error verifying merged cell borders: {str(e)}") + return None + + + def copy_template_to_current(self, orig_range, target_range): try: - print(f"orig_rage={orig_range},target_range={target_range}") + #print(f"copy_template_to_current : orig_range={orig_range},target_range={target_range}") # 範囲をパースする orig_min_col, orig_min_row, orig_max_col, orig_max_row = range_boundaries(orig_range) target_min_col, target_min_row, target_max_col, target_max_row = range_boundaries(target_range) - print(f"min_col, min_row, max_col, max_row = {orig_min_col}, {orig_min_row}, {orig_max_col}, {orig_max_row}") - - print(f"min_col, min_row, max_col, max_row = {target_min_col}, {target_min_row}, {target_max_col}, {target_max_row}") + #print(f"min_col, min_row, max_col, max_row = {orig_min_col}, {orig_min_row}, {orig_max_col}, {orig_max_row}") # Get template sheet name from ini file section_config = self.conf.get_section(self.section_list[0]) # 現在の処理中のセクション template_sheet_name = section_config.get("template_sheet") - - if not template_sheet_name: - raise ValueError("Template sheet name not found in configuration") - - # Create new sheet with template name if it doesn't exist - if template_sheet_name not in self.workbook.sheetnames: - self.current_sheet = self.workbook.create_sheet(template_sheet_name) - else: - self.current_sheet = self.workbook[template_sheet_name] - - # Remove default sheet if it exists - if 'Sheet' in self.workbook.sheetnames: - del self.workbook['Sheet'] - - # Copy column widths - for col in range(orig_min_col, orig_max_col + 1): - col_letter = get_column_letter(col) - if col_letter in self.template_sheet.column_dimensions: - self.current_sheet.column_dimensions[col_letter].width = \ - self.template_sheet.column_dimensions[col_letter].width + new_sheet_name = section_config.get("sheet_name",template_sheet_name) # Copy row heights for row in range(orig_min_row, orig_max_row + 1): @@ -449,19 +732,24 @@ class SumasenExcel: if target_row not in self.current_sheet.row_dimensions: self.current_sheet.row_dimensions[target_row] = openpyxl.worksheet.dimensions.RowDimension(target_row) self.current_sheet.row_dimensions[target_row].height = source_height + #print(f"Row(target_row).height = {source_height}") # Copy merged cells - for merged_range in self.template_sheet.merged_cells: - min_col, min_row, max_col, max_row = range_boundaries(str(merged_range)) - # Check if merge range intersects with our copy range - if (min_col >= orig_min_col and max_col <= orig_max_col and - min_row >= orig_min_row and max_row <= orig_max_row): - # Calculate target merge range - target_merge_min_row = target_min_row + (min_row - orig_min_row) - target_merge_max_row = target_min_row + (max_row - orig_min_row) - target_merge_range = f"{get_column_letter(min_col)}{target_merge_min_row}:" \ - f"{get_column_letter(max_col)}{target_merge_max_row}" - self.current_sheet.merge_cells(target_merge_range) + self.copy_template_range(orig_min_row, orig_min_col, orig_max_row, orig_max_col, target_min_row) + + #for merged_range in self.template_sheet.merged_cells: + # min_col, min_row, max_col, max_row = range_boundaries(str(merged_range)) + # #print(f"Merge cell: min_col, min_row, max_col, max_row = {min_col}, {min_row}, {max_col}, {max_row}") + # # Check if merge range intersects with our copy range + # if (min_col >= orig_min_col and max_col <= orig_max_col and + # min_row >= orig_min_row and max_row <= orig_max_row): + # # Calculate target merge range + # target_merge_min_row = target_min_row + (min_row - orig_min_row) + # target_merge_max_row = target_min_row + (max_row - orig_min_row) + # target_merge_range = f"{get_column_letter(min_col)}{target_merge_min_row}:" \ + # f"{get_column_letter(max_col)}{target_merge_max_row}" + # self.current_sheet.merge_cells(target_merge_range) + # #print(f"Merge : {target_merge_range}") # Copy cell contents and styles row_offset = target_min_row - orig_min_row @@ -470,17 +758,18 @@ class SumasenExcel: source_cell = self.template_sheet.cell(row=row, column=col) target_cell = self.current_sheet.cell(row=row+row_offset, column=col) - # Copy value - target_cell.value = source_cell.value - - # Copy styles - if source_cell.has_style: - target_cell.font = copy(source_cell.font) - target_cell.border = copy(source_cell.border) - target_cell.fill = copy(source_cell.fill) - target_cell.number_format = source_cell.number_format - target_cell.protection = copy(source_cell.protection) - target_cell.alignment = copy(source_cell.alignment) + if source_cell.value: + # Copy value + target_cell.value = source_cell.value + #print(f"({col},{row}) : {target_cell.value}") + # Copy styles + if source_cell.has_style: + target_cell.font = copy(source_cell.font) + target_cell.border = copy(source_cell.border) + target_cell.fill = copy(source_cell.fill) + target_cell.number_format = source_cell.number_format + target_cell.protection = copy(source_cell.protection) + target_cell.alignment = copy(source_cell.alignment) # Copy page setup target_page_setup = self.current_sheet.page_setup diff --git a/SumasenLibs/excel_lib/testdata/certificate_5033.xlsx b/SumasenLibs/excel_lib/testdata/certificate_5033.xlsx index 492d82a85d53385f89e763249716d6dc94921d7d..1a14c2b5b8572c7031fe0cf07d05890228a8bb9a 100644 GIT binary patch delta 40219 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 Gul^5f5yb5P delta 4684 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 diff --git a/SumasenLibs/excel_lib/testdata/certificate_template.xlsx b/SumasenLibs/excel_lib/testdata/certificate_template.xlsx index a5f7d3f521d020345d15fd3905c49cf923cc5a7b..5c966953d07f76b7bd7c474385477de36669a541 100644 GIT binary patch delta 5244 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 delta 5190 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 NvXYk-cc1GY?LW|2q!9oB diff --git a/SumasenLibs/excel_lib/testdata/test.ini b/SumasenLibs/excel_lib/testdata/test.ini index 9392f46..e59f05a 100644 --- a/SumasenLibs/excel_lib/testdata/test.ini +++ b/SumasenLibs/excel_lib/testdata/test.ini @@ -2,10 +2,12 @@ template_file=certificate_template.xlsx doc_file=certificate_[zekken_number].xlsx sections=section1 -maxcol=8 +maxcol=10 +column_width=3,5,16,16,16,16,16,8,8,12,3 [section1] template_sheet=certificate +sheet_name=certificate groups=group1,group2 fit_to_width=1 orientation=portrait @@ -13,12 +15,12 @@ orientation=portrait [section1.group1] table_name=mv_entry_details where=zekken_number='[zekken_number]' and event_name='[event_code]' -group_range=A1:H11 +group_range=A1:J12 [section1.group2] -table_name=gps_checkins +table_name=v_checkins_locations where=zekken_number='[zekken_number]' and event_code='[event_code]' sort=path_order -group_range=A12:H12 +group_range=A13:J13