#!/usr/bin/env python3
"""
Excelファイル処理ユーティリティ
Excelファイルの検索と処理を行う関数を提供します。
"""
from pathlib import Path
from typing import Optional
import openpyxl
class ExcelFileResolver:
"""様々な場所からExcelファイルのパスを解決するクラス"""
SEARCH_DIRECTORIES = [
Path("/app/sample"),
Path("/app/data"),
]
@classmethod
def locate_file(cls, file_path: str) -> Path:
"""
設定された検索ディレクトリ内でExcelファイルを検索します。
Args:
file_path: Excelファイルへの相対パスまたは絶対パス
Returns:
解決されたPathオブジェクト
Raises:
FileNotFoundError: ファイルが見つからない場合
"""
path = Path(file_path)
# 絶対パスの場合はそのまま使用
if path.is_absolute():
if not path.exists():
raise FileNotFoundError(f"Excel file not found: {file_path}")
return path
# 設定されたディレクトリ内を検索
for base_dir in cls.SEARCH_DIRECTORIES:
candidate = base_dir / path
if candidate.exists():
return candidate
# 最後の手段として絶対パスとして試行
if path.exists():
return path
raise FileNotFoundError(f"Excel file not found: {file_path}")
class WorkbookParser:
"""Excelワークブックを解析し、シートデータを抽出するクラス"""
@staticmethod
def extract_all_sheets(workbook_path: Path) -> dict[str, list[list[str]]]:
"""
ワークブック内のすべてのシートからデータを抽出します。
Args:
workbook_path: Excelワークブックへのパス
Returns:
シート名をキー、行データを値とする辞書
"""
return WorkbookParser._process_workbook(
workbook_path,
target_sheets=None,
sheet_position=None
)
@staticmethod
def extract_sheet_by_name(
workbook_path: Path,
sheet_name: str
) -> dict[str, list[list[str]]]:
"""
指定されたシート名のシートからデータを抽出します。
Args:
workbook_path: Excelワークブックへのパス
sheet_name: 対象シートの名前
Returns:
シート名をキー、行データを値とする辞書
"""
return WorkbookParser._process_workbook(
workbook_path,
target_sheets=[sheet_name],
sheet_position=None
)
@staticmethod
def extract_sheet_by_position(
workbook_path: Path,
sheet_position: int
) -> dict[str, list[list[str]]]:
"""
指定された位置のシートからデータを抽出します。
Args:
workbook_path: Excelワークブックへのパス
sheet_position: 対象シートのゼロベースインデックス
Returns:
シート名をキー、行データを値とする辞書
"""
return WorkbookParser._process_workbook(
workbook_path,
target_sheets=None,
sheet_position=sheet_position
)
@staticmethod
def _process_workbook(
workbook_path: Path,
target_sheets: Optional[list[str]] = None,
sheet_position: Optional[int] = None
) -> dict[str, list[list[str]]]:
"""
ワークブックを処理し、シートデータを抽出する内部メソッド。
Args:
workbook_path: Excelワークブックへのパス
target_sheets: 抽出するシート名のリスト(オプション)
sheet_position: 抽出するシートのインデックス(オプション)
Returns:
シート名をキー、行データを値とする辞書
"""
try:
workbook = openpyxl.load_workbook(workbook_path, data_only=True)
result = {}
# 処理するシートを決定
if target_sheets:
sheets_to_process = [
name for name in target_sheets
if name in workbook.sheetnames
]
if not sheets_to_process:
raise ValueError(
f"None of the specified sheets found: {target_sheets}"
)
elif sheet_position is not None:
if sheet_position < 0 or sheet_position >= len(workbook.sheetnames):
raise IndexError(
f"Sheet position {sheet_position} is out of range. "
f"Available sheets: {len(workbook.sheetnames)}"
)
sheets_to_process = [workbook.sheetnames[sheet_position]]
else:
sheets_to_process = workbook.sheetnames
# 各シートからデータを抽出
for sheet_name in sheets_to_process:
worksheet = workbook[sheet_name]
sheet_data = []
for row in worksheet.iter_rows(values_only=True):
# セルの値を文字列に正規化
normalized_row = [
str(cell_value) if cell_value is not None else ""
for cell_value in row
]
sheet_data.append(normalized_row)
result[sheet_name] = sheet_data
return result
except openpyxl.utils.exceptions.InvalidFileException as e:
raise ValueError(f"Invalid Excel file format: {str(e)}")
except Exception as e:
raise RuntimeError(f"Failed to process workbook: {str(e)}")