Coverage for graphica/core/excel_utils.py: 92.6%

36 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-12 16:38 +0000

1"""Excel の読み込みの補助。 

2 

3pandas は数式セルの計算済みの値を読むので、Excel 以外で書いたファイルや手動計算のまま保存したファイルでは 

4値が無く空になる。欠損に見えて気づきにくいので、読む前に見つけて知らせる。 

5""" 

6import logging 

7 

8import openpyxl 

9 

10logger = logging.getLogger(__name__) 

11 

12 

13def find_unevaluated_formula_cells(file_path: str, sheet_name: str | None = None, max_examples: int = 5, 

14 max_scan_cells: int = 200_000) -> tuple[bool, list[str], bool]: 

15 """数式なのに計算済みの値を持たないセルを探す。(見つかったか, 例のリスト, 全部を見たか)。 

16 

17 大きなファイルで重くならないよう max_scan_cells で打ち切る。 

18 """ 

19 # .xls は openpyxl で開けず、xlrd は計算済みの値しか持たないので確かめようがない 

20 if not str(file_path).lower().endswith('.xlsx'): 

21 return False, [], True 

22 

23 wb_formulas = None 

24 wb_values = None 

25 try: 

26 # 検査に失敗したら「見つからなかった」扱いにする約束なので、開くところから try に入れる 

27 wb_formulas = openpyxl.load_workbook(file_path, data_only=False, read_only=True) 

28 wb_values = openpyxl.load_workbook(file_path, data_only=True, read_only=True) 

29 

30 sheets = [sheet_name] if sheet_name else wb_formulas.sheetnames 

31 examples = [] 

32 scanned = 0 

33 

34 for sname in sheets: 

35 if sname not in wb_formulas.sheetnames or sname not in wb_values.sheetnames: 35 ↛ 36line 35 didn't jump to line 36 because the condition on line 35 was never true

36 continue 

37 ws_formulas = wb_formulas[sname] 

38 ws_values = wb_values[sname] 

39 

40 for row_f, row_v in zip(ws_formulas.iter_rows(), ws_values.iter_rows()): 

41 for cell_f, cell_v in zip(row_f, row_v): 

42 scanned += 1 

43 if cell_f.data_type == 'f' and cell_v.value is None: 

44 examples.append(f"{sname}!{cell_f.coordinate}") 

45 if len(examples) >= max_examples: 

46 return True, examples, (scanned < max_scan_cells) 

47 if scanned >= max_scan_cells: 47 ↛ 48line 47 didn't jump to line 48 because the condition on line 47 was never true

48 return bool(examples), examples, False 

49 

50 return bool(examples), examples, True 

51 except Exception: 

52 logger.exception("数式セルの検査中にエラーが発生しました: %s", file_path) 

53 return False, [], True 

54 finally: 

55 if wb_formulas is not None: 

56 wb_formulas.close() 

57 if wb_values is not None: 

58 wb_values.close()