""" Post-process Finanzplan Excel exports: attach charts to the Dashboard sheet and move Dashboard to be the first tab. Run after export-finanzplan-excel.ts: python3 scripts/add-charts.py """ from __future__ import annotations import sys from pathlib import Path from openpyxl import load_workbook from openpyxl.chart import BarChart, LineChart, Reference from openpyxl.chart.label import DataLabelList from openpyxl.utils import get_column_letter EXPORTS_DIR = Path(__file__).resolve().parent.parent / "exports" def column_count_until_empty(ws, start_col: int, header_row: int) -> int: """Count consecutive non-empty cells starting at (header_row, start_col).""" c = start_col while ws.cell(row=header_row, column=c).value not in (None, ""): c += 1 return c - start_col def add_charts_to_workbook(path: Path) -> None: wb = load_workbook(path) if "Dashboard" not in wb.sheetnames: print(f" skip {path.name}: no Dashboard tab") return ws = wb["Dashboard"] # --- Chart 1: YoY Revenue / Material / Personnel / EBIT --- # Source: rows 3..9 (3=year headers, rows 4=Umsatz, 5=Material, 6=Personal, 9=EBIT) # categories: B3:F3 (years), data: rows 4,5,6,9 cat_ref = Reference(ws, min_col=2, max_col=6, min_row=3, max_row=3) chart1 = BarChart() chart1.type = "col" chart1.style = 11 chart1.title = "Umsatz / Material / Personal / EBIT — YoY" chart1.y_axis.title = "EUR" chart1.x_axis.title = "Jahr" for r in (4, 5, 6, 9): data_ref = Reference(ws, min_col=1, max_col=6, min_row=r, max_row=r) chart1.add_data(data_ref, titles_from_data=True, from_rows=True) chart1.set_categories(cat_ref) chart1.height = 9 chart1.width = 18 ws.add_chart(chart1, "H3") # --- Chart 2: Jahresueberschuss YoY (row 11) --- chart2 = BarChart() chart2.type = "col" chart2.style = 13 chart2.title = "Jahresüberschuss — YoY" chart2.y_axis.title = "EUR" chart2.x_axis.title = "Jahr" data_ref = Reference(ws, min_col=1, max_col=6, min_row=11, max_row=11) chart2.add_data(data_ref, titles_from_data=True, from_rows=True) chart2.set_categories(cat_ref) chart2.height = 9 chart2.width = 18 chart2.dataLabels = DataLabelList(showVal=True) ws.add_chart(chart2, "H22") # --- Chart 3: Liquidity monthly (row 16, months from col B) --- # Determine the last month column by scanning row 15 (month labels) n_months = column_count_until_empty(ws, 2, 15) last_col = 1 + n_months # col 2..(1+n_months) chart3 = LineChart() chart3.title = "Liquidität (monatlich)" chart3.y_axis.title = "EUR" chart3.x_axis.title = "Monat" chart3.style = 12 cat_months = Reference(ws, min_col=2, max_col=last_col, min_row=15, max_row=15) data_liq = Reference(ws, min_col=1, max_col=last_col, min_row=16, max_row=16) chart3.add_data(data_liq, titles_from_data=True, from_rows=True) chart3.set_categories(cat_months) chart3.height = 9 chart3.width = 24 ws.add_chart(chart3, "H41") # --- Chart 4: Headcount (row 20) --- chart4 = LineChart() chart4.title = "Headcount (monatlich)" chart4.y_axis.title = "Personen" chart4.x_axis.title = "Monat" chart4.style = 10 data_hc = Reference(ws, min_col=1, max_col=last_col, min_row=20, max_row=20) chart4.add_data(data_hc, titles_from_data=True, from_rows=True) chart4.set_categories(cat_months) chart4.height = 9 chart4.width = 24 ws.add_chart(chart4, "H60") # --- Chart 5: Personalkosten total monthly (row 24) --- chart5 = LineChart() chart5.title = "Personalkosten total (monatlich)" chart5.y_axis.title = "EUR" chart5.x_axis.title = "Monat" chart5.style = 13 data_pers = Reference(ws, min_col=1, max_col=last_col, min_row=24, max_row=24) chart5.add_data(data_pers, titles_from_data=True, from_rows=True) chart5.set_categories(cat_months) chart5.height = 9 chart5.width = 24 ws.add_chart(chart5, "H79") # --- Move Dashboard to be the first sheet --- idx = wb.sheetnames.index("Dashboard") if idx != 0: # openpyxl uses _sheets internal list; reorder by index. wb._sheets.insert(0, wb._sheets.pop(idx)) # Also put the Formelübersicht (docs) tab at the end if present for docs_name in ("Formelübersicht", "Formulas"): if docs_name in wb.sheetnames: fidx = wb.sheetnames.index(docs_name) wb._sheets.append(wb._sheets.pop(fidx)) break # Make Dashboard the active sheet on open wb.active = 0 wb.save(path) print(f" charts added to {path.name}") def main() -> None: if not EXPORTS_DIR.exists(): print(f"no exports dir at {EXPORTS_DIR}") sys.exit(1) files = sorted(EXPORTS_DIR.glob("Finanzplan-*.xlsx")) if not files: print("no Finanzplan-*.xlsx files found in exports/") sys.exit(1) for f in files: print(f"Processing {f.name}") add_charts_to_workbook(f) if __name__ == "__main__": main()