🐱

yaml形式のパラメータシートをExcelに変換するpythonスクリプト(ChatGPT製)

2023/10/29に公開

概要

例えばEC2のパラメータを以下のようにyaml形式で作成したとする。

webサーバ:
  概要:
    インスタンス名: web-server-01
    インスタンスID: 自動生成
  OSイメージ:
    AMI: ami-xxxxxxxxxxxx
  高度なネットワーク設定:
    ネットワークインターフェース1:
      サブネット: web-server-subnet-1a
      セキュリティグループ: web-server-secg-01
      プライマリIP: 172.16.xx.xx
~略~
---
appサーバ:
  概要:
    インスタンス名: app-server-01
    インスタンスID: 自動生成
  OSイメージ:
    AMI: ami-xxxxxxxxxxxx
  高度なネットワーク設定:
    ネットワークインターフェース1:
      サブネット: app-server-subnet-1a
      セキュリティグループ: app-server-secg-01
      プライマリIP: 172.16.xx.xx
~略~

世に数多あるExcel形式のパラメータシートは修正時の変更差分を見るのに苦労するが(セルの色を変える、目grepなど)、
yaml形式であればgit管理でプルリクなり、VScodeの機能なり、何ならWinMergeなどで変更差分を一発でバシッと簡単に見ることができるので、レビューの負担が下がる。
ただし、yaml形式のままだと納品時などで都合が悪いので、Excel形式に変換する。

yamlファイル内で---のセパレータで区切るとシートを分けて作成される。
セルはすべて左揃え、格子をかけた状態で作成される。
python xxxxx.py yyyyy.yaml の形で実行する。
Excelファイルはyamlファイルと同じ名前で作成される。

コード

import yaml
from openpyxl import Workbook
from openpyxl.styles import PatternFill, Border, Side, Alignment
import sys
import os

def yaml_to_excel(input_file, output_file):
    with open(input_file, 'r', encoding='utf-8') as f:
        documents = list(yaml.safe_load_all(f))

    workbook = Workbook()
    workbook.remove(workbook.active)  # Remove default sheet

    def max_depth(d, depth=0):
        """Returns the maximum depth of a dictionary."""
        if not isinstance(d, dict):
            return depth
        return max(max_depth(v, depth + 1) for v in d.values())

    depth = max(max_depth(doc) for doc in documents)

    def write_data(d, sheet, start_row=2, start_col=1):
        """Writes the data recursively into the Excel sheet."""
        row = start_row
        for key, value in d.items():
            if isinstance(value, dict):
                cell = sheet.cell(row=row, column=start_col, value=key)
                cell.alignment = Alignment(horizontal="left")
                row = write_data(value, sheet, row, start_col + 1)
            else:
                key_cell = sheet.cell(row=row, column=start_col, value=key)
                value_cell = sheet.cell(row=row, column=depth, value=value)
                key_cell.alignment = Alignment(horizontal="left")
                value_cell.alignment = Alignment(horizontal="left")
                row += 1
        return row

    for content in documents:
        for sheet_name, data in content.items():
            current_sheet = workbook.create_sheet(title=sheet_name)
            current_sheet.cell(row=1, column=1, value="設定項目").alignment = Alignment(horizontal="left")
            current_sheet.cell(row=1, column=depth, value="設定値").alignment = Alignment(horizontal="left")

            # Fill cells with light blue color
            blue_fill = PatternFill(start_color="D9EBF5", end_color="D9EBF5", fill_type="solid")
            for col in range(1, depth + 1):
                current_sheet.cell(row=1, column=col).fill = blue_fill

            write_data(data, current_sheet)

    # Adding borders and adjusting the width of the columns
    thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))
    for sheet in workbook.worksheets:
        for row in sheet.iter_rows():
            for cell in row:
                cell.border = thin_border

        for column in sheet.columns:
            max_length = 0
            column = [cell for cell in column]
            for cell in column:
                try:
                    if len(str(cell.value)) > max_length:
                        max_length = len(str(cell.value))
                except:
                    pass
            # adding some extra padding and a correction factor for width
            adjusted_width = (max_length + 8) * 1.4
            sheet.column_dimensions[column[0].column_letter].width = adjusted_width

    workbook.save(output_file)

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print("Usage: python script_name.py <input_yaml_file>")
        sys.exit(1)

    input_filename = sys.argv[1]
    output_filename = os.path.splitext(input_filename)[0] + '.xlsx'
    yaml_to_excel(input_filename, output_filename)

生成されたExcelファイルのイメージ

感想

  • ChatGPTスゴイ(語彙力)
  • クリーンなコードにするとかエラーハンドリングとかはあまり考慮してません

Discussion