Coverage for src\genesis\parsers\table_extractor.py: 100%

24 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-26 14:20 +0800

1from __future__ import annotations 

2 

3from typing import Any 

4 

5from genesis.data_models import ( 

6 CellFormatting, CellValue, ExcelTable, ExtractionMethod, Provenance, SheetType, 

7) 

8 

9 

10def column_letter(index: int) -> str: 

11 """1 → A、27 → AA。""" 

12 letters = "" 

13 while index > 0: 

14 index, rem = divmod(index - 1, 26) 

15 letters = chr(65 + rem) + letters 

16 return letters 

17 

18 

19def extract_table( 

20 sheet_name: str, 

21 matrix: list[list[Any]], 

22 file_name: str, 

23 detected_type: SheetType, 

24 header_row: int = 0, 

25 formatting_map: dict[tuple[int, int], CellFormatting] | None = None, 

26) -> ExcelTable: 

27 """从矩阵提取表格:首行视为表头,其后为数据行。 

28 

29 formatting_map:矩阵坐标 (row, col)(0 起)→ CellFormatting,用于还原取消线/背景色等样式。 

30 """ 

31 if not matrix: 

32 return ExcelTable( 

33 name=sheet_name, detected_type=detected_type, 

34 extraction_method=ExtractionMethod.OPENPYXL.value, headers=[], rows=[], 

35 ) 

36 if header_row < 0 or header_row >= len(matrix): 

37 header_row = 0 

38 fmt_map = formatting_map or {} 

39 headers = [str(c) if c is not None else "" for c in matrix[header_row]] 

40 rows = [] 

41 for r in range(header_row + 1, len(matrix)): 

42 row_dict = {} 

43 for c, h in enumerate(headers): 

44 raw = matrix[r][c] if c < len(matrix[r]) else None 

45 row_dict[h] = CellValue( 

46 value=raw, 

47 provenance=Provenance( 

48 file_name=file_name, 

49 sheet_name=sheet_name, 

50 row=r - header_row, # 数据行号从 1 开始 

51 column=column_letter(c + 1), 

52 column_header=h, 

53 ), 

54 formatting=fmt_map.get((r, c)), 

55 ) 

56 rows.append(row_dict) 

57 return ExcelTable( 

58 name=sheet_name, detected_type=detected_type, 

59 extraction_method=ExtractionMethod.OPENPYXL.value, headers=headers, rows=rows, 

60 )