first commint
This commit is contained in:
100
detect_filled_cells.py
Normal file
100
detect_filled_cells.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import csv
|
||||
from openpyxl import load_workbook
|
||||
from openpyxl.styles import PatternFill
|
||||
|
||||
|
||||
def resolve_color_hex_from_colorobj(color):
|
||||
"""Try to resolve an openpyxl Color object to ARGB hex string.
|
||||
Returns None if cannot resolve.
|
||||
"""
|
||||
if color is None:
|
||||
return None
|
||||
|
||||
# direct rgb (may be 'FF00B050' or '00B050')
|
||||
rgb = getattr(color, "rgb", None)
|
||||
if rgb:
|
||||
s = str(rgb)
|
||||
if len(s) == 6:
|
||||
s = "FF" + s
|
||||
return s.upper()
|
||||
|
||||
# try indexed (some workbooks may use indexed colors)
|
||||
idx = getattr(color, "indexed", None) or getattr(color, "index", None)
|
||||
if idx is not None:
|
||||
try:
|
||||
# openpyxl does not always expose a global list here; fallback to None
|
||||
return f"INDEXED{int(idx)}"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
theme = getattr(color, "theme", None)
|
||||
if theme is not None:
|
||||
try:
|
||||
return f"THEME{int(theme)}"
|
||||
except Exception:
|
||||
return str(theme)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def detect_colored_cells(xlsx_path, sheet_name=None, write_csv=None):
|
||||
"""Detect cells with fills (pattern fills) and return list of dicts:
|
||||
[{'coordinate':'A1','row':1,'col':1,'value':..., 'color': 'FF00B050', 'fill_type': 'solid'}]
|
||||
|
||||
If write_csv is provided (path), write the results to that CSV.
|
||||
"""
|
||||
wb = load_workbook(xlsx_path, data_only=False)
|
||||
ws = wb[sheet_name] if sheet_name else wb.active
|
||||
|
||||
results = []
|
||||
min_row = ws.min_row or 1
|
||||
max_row = ws.max_row or 1
|
||||
min_col = ws.min_column or 1
|
||||
max_col = ws.max_column or 1
|
||||
|
||||
for row in ws.iter_rows(min_row=min_row, max_row=max_row, min_col=min_col, max_col=max_col):
|
||||
for cell in row:
|
||||
fill = cell.fill
|
||||
if fill is None:
|
||||
continue
|
||||
# patternType for PatternFill is fill.fill_type or fill.patternType depending on openpyxl version
|
||||
pattern = getattr(fill, "patternType", None) or getattr(fill, "fill_type", None)
|
||||
if pattern in (None, 'none'):
|
||||
continue
|
||||
|
||||
fg = getattr(fill, 'fgColor', None) or getattr(fill, 'start_color', None) or getattr(fill, 'bgColor', None)
|
||||
color = resolve_color_hex_from_colorobj(fg)
|
||||
if color is None or color.startswith("FF") is False:
|
||||
continue
|
||||
|
||||
results.append({
|
||||
'coordinate': cell.coordinate,
|
||||
'row': cell.row,
|
||||
'col': cell.column,
|
||||
'value': cell.value,
|
||||
'color': color,
|
||||
'fill_type': pattern,
|
||||
})
|
||||
|
||||
if write_csv:
|
||||
keys = ['coordinate', 'row', 'col', 'value', 'color', 'fill_type']
|
||||
with open(write_csv, 'w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.DictWriter(f, fieldnames=keys)
|
||||
writer.writeheader()
|
||||
for r in results:
|
||||
writer.writerow(r)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--xlsx', '-x', required=True, help='Path to xlsx file')
|
||||
parser.add_argument('--sheet', '-s', default=None, help='Sheet name (optional)')
|
||||
parser.add_argument('--csv', '-c', default=None, help='Write results to CSV')
|
||||
args = parser.parse_args()
|
||||
|
||||
res = detect_colored_cells(args.xlsx, sheet_name=args.sheet, write_csv=args.csv)
|
||||
for r in res:
|
||||
print(r)
|
||||
Reference in New Issue
Block a user