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)