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

40 statements  

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

1from __future__ import annotations 

2 

3from genesis.data_models import CellComment, CellFormatting 

4from genesis.parsers.provenance import build_source_uri 

5 

6_BLACK_RGB = ("00000000", "FF000000") 

7 

8 

9def _to_rgb_hex(color) -> str | None: 

10 """将 openpyxl Color 转为 RGB 十六进制;非 RGB 主题色/默认色返回 None""" 

11 if color is None: 

12 return None 

13 try: 

14 value = str(color.rgb) 

15 except Exception: 

16 return None 

17 if not value or value in _BLACK_RGB: 

18 return None 

19 if len(value) not in (6, 8): 

20 return None 

21 if not all(ch in "0123456789ABCDEFabcdef" for ch in value): 

22 return None 

23 return value 

24 

25 

26def cell_formatting(cell) -> CellFormatting | None: 

27 strike = bool(cell.font.strike) 

28 font_color = _to_rgb_hex(getattr(cell.font, "color", None)) 

29 bg_color = None 

30 fill = cell.fill 

31 if fill is not None: 

32 bg_color = _to_rgb_hex(getattr(fill, "fgColor", None)) 

33 if strike or font_color or bg_color: 

34 return CellFormatting( 

35 strikethrough=strike, font_color=font_color, bg_color=bg_color, 

36 ) 

37 return None 

38 

39 

40def cell_comment(cell, file_name: str) -> CellComment | None: 

41 if cell.comment is None: 

42 return None 

43 return CellComment( 

44 author=cell.comment.author or "", 

45 text=cell.comment.text or "", 

46 source_uri=build_source_uri(file_name, cell.parent.title, cell.coordinate), 

47 ) 

48 

49 

50def collect_comments(ws, file_name: str) -> list[CellComment]: 

51 result = [] 

52 for row in ws.iter_rows(): 

53 for cell in row: 

54 cm = cell_comment(cell, file_name) 

55 if cm is not None: 

56 result.append(cm) 

57 return result