DEV Community

Cover image for How to Build Executive-Ready Excel Reports Directly in Python Using openpyxl
Vasile Bratu
Vasile Bratu

Posted on

How to Build Executive-Ready Excel Reports Directly in Python Using openpyxl

Stop sending ugly, unformatted CSV files. Impress your B2B clients with professional design systems, airy layouts, and dynamic Excel charts built programmatically.


Corporate Steel Blue Executive Dashboard

Every freelance developer has done it: you build a high-performance web scraper or data pipeline, extract thousands of rows of valuable leads, and deliver them to your client as a raw, unformatted .csv file.

To you, it’s a job well done. But to a business stakeholder, a raw CSV file feels messy, hard to read, and cheap.

If you want to command high-ticket pricing on freelance platforms like Upwork and Fiverr, you must shift your perspective. You are not just a scraper developer; you are a data solutions architect.

In this guide, we will explore the 6 visual design laws of corporate spreadsheets and build a robust, ready-to-run Python script using openpyxl that generates breathtaking, interactive executive dashboards automatically.


The 6 Spacing & Design Laws for Professional Spreadsheets

Before writing a single line of code, we must establish a visual system. Corporate managers and executives scan spreadsheets for key insights. To make scanning frictionless, we follow six core principles:

1. Activating Gridlines (The Golden Rule)

By default, when you apply custom fills to cell blocks, Excel may hide the surrounding gridlines. This makes the spreadsheet look incomplete and floating. We must explicitly force gridlines to be visible:

ws.views.sheetView[0].showGridLines = True
Enter fullscreen mode Exit fullscreen mode

2. Deep Spacing & Airy Heights

Normal spreadsheets use a tight row height of 15pt, packing text together and causing visual fatigue.

  • Header Rows: Expand to 28-30pt to give categories breathing room.
  • Data Rows: Expand to 32-35pt, creating a spacious, clean feel.
  • Padding: Never let text touch borders. We use center and left alignments with cell indentations.

3. The Interactive Hyperlink Pattern

E-commerce URLs can be 150+ characters long. If you paste them directly into a cell, they wrap and stretch the row vertically, completely ruining the symmetrical grid.
Instead, we programmatically inject native Excel =HYPERLINK formulas:

  • E-commerce URLs: =HYPERLINK("https://...", "View Product ↗") (Styled in sleek Corporate Slate Blue #1A365D with a clean single underline).
  • Product Images: =HYPERLINK("https://...", "View Image 📷") (Allows quick image validation without cluttering).

4. Harmonious Palettes (Ditch the Neon Colors)

Never use default Excel primary green, red, or yellow. They look aggressive and amateur. Instead, use soft HSL/Hex corporate palettes:

  • Corporate Steel Blue: Header fill #1A365D (white text), with zebra striping #F7FAFC (light grey-blue).
  • Zebra Striping: Alternating white and light-tinted rows to guide the reader's eye across long rows.

5. The Accounting Double Underline

The final summary row must adhere to traditional GAAP accounting standards: a thin top border (representing a calculation is starting) and a double bottom border (representing a final total).

6. Programmatic Charts

An executive should never have to manually build a chart. We inject a native, interactive Excel chart directly next to our dataset so key trends are visible the second the file is opened.


The Complete Python Implementation

Here is a fully functional, self-contained Python script using openpyxl. It takes raw, unstructured dictionary data (like what you'd scrape from an e-commerce store) and turns it into a pixel-perfect, interactive corporate dashboard.

Ensure you have openpyxl installed:

pip install openpyxl
Enter fullscreen mode Exit fullscreen mode

Now, create a file named excel_designer.py and paste the following code:

import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
from openpyxl.formatting.rule import CellIsRule
from openpyxl.chart import BarChart, Reference

def generate_premium_report(data, output_path="premium_report.xlsx"):
    # 1. Initialize Workbook & Worksheet
    wb = openpyxl.Workbook()
    ws = wb.active
    ws.title = "Executive Lead & Product Report"

    # CRITICAL: Always ensure gridlines are visible
    ws.views.sheetView[0].showGridLines = True

    # 2. Setup Column Headers
    headers = ["Product / Lead Name", "Price (£)", "Rating (1-5)", "In Stock", "Product Link", "Image Link"]
    ws.append(headers)

    # 3. Populate Rows with Data & Native Hyperlinks
    for item in data:
        prod_url = item.get("Product URL", "")
        img_url = item.get("Image URL", "")

        row_data = [
            item.get("Title", ""),
            item.get("Price", 0.0),
            item.get("Rating", 0),
            item.get("In Stock", "No"),
            "View Product ↗" if prod_url else "N/A",
            "View Image 📷" if img_url else "N/A"
        ]
        ws.append(row_data)

        # Inject programmatic hyperlink coordinates
        current_row = ws.max_row
        if prod_url:
            ws.cell(row=current_row, column=5).hyperlink = prod_url
        if img_url:
            ws.cell(row=current_row, column=6).hyperlink = img_url

    # 4. Design System Tokens (Corporate Steel Blue)
    header_fill = PatternFill(start_color="1A365D", end_color="1A365D", fill_type="solid") # Dark Blue
    header_font = Font(name="Segoe UI", size=11, bold=True, color="FFFFFF")

    zebra_fill = PatternFill(start_color="F7FAFC", end_color="F7FAFC", fill_type="solid") # Very Light Grey-Blue
    normal_fill = PatternFill(start_color="FFFFFF", end_color="FFFFFF", fill_type="solid")

    border_color = Side(border_style="thin", color="CBD5E0") # Light Slate Border
    cell_border = Border(left=border_color, right=border_color, top=border_color, bottom=border_color)

    align_left = Alignment(horizontal="left", vertical="center", wrap_text=False)
    align_right = Alignment(horizontal="right", vertical="center")
    align_center = Alignment(horizontal="center", vertical="center")

    num_rows = len(data)
    start_row = 2
    end_row = num_rows + 1

    # 5. Apply Row Heights & Typography
    # Header Row Height = 28pt
    ws.row_dimensions[1].height = 28
    for col_idx in range(1, len(headers) + 1):
        cell = ws.cell(row=1, column=col_idx)
        cell.fill = header_fill
        cell.font = header_font
        cell.alignment = align_center if col_idx != 1 else align_left
        cell.border = cell_border

    # Data Rows Height = 32pt (Spacious & Aerat)
    for r_idx in range(start_row, end_row + 1):
        ws.row_dimensions[r_idx].height = 32
        row_fill = zebra_fill if r_idx % 2 == 0 else normal_fill

        for c_idx in range(1, len(headers) + 1):
            cell = ws.cell(row=r_idx, column=c_idx)
            cell.fill = row_fill
            cell.border = cell_border

            # Format and align by Column Types
            if c_idx == 1: # Title
                cell.font = Font(name="Segoe UI", size=10)
                cell.alignment = align_left
            elif c_idx == 2: # Price
                cell.font = Font(name="Segoe UI", size=10)
                cell.alignment = align_right
                cell.number_format = '£#,##0.00' # Proper currency format
            elif c_idx == 3: # Rating
                cell.font = Font(name="Segoe UI", size=10)
                cell.alignment = align_center
                cell.number_format = '0'
            elif c_idx == 4: # Availability
                cell.font = Font(name="Segoe UI", size=10)
                cell.alignment = align_center
            else: # Links (Col 5 & 6)
                cell.alignment = align_center
                if cell.value == "N/A":
                    cell.font = Font(name="Segoe UI", size=10, italic=True, color="A0AEC0")
                else:
                    # Classic elegant blue underlined link style
                    cell.font = Font(name="Segoe UI", size=10, underline="single", color="1A365D")

    # 6. Setup Summary & Accounting Row
    summary_row = end_row + 1
    ws.row_dimensions[summary_row].height = 24

    summary_fill = PatternFill(start_color="E2E8F0", end_color="E2E8F0", fill_type="solid")
    summary_font = Font(name="Segoe UI", size=10, bold=True, color="1A365D")

    # GAAP Standard: Thin top line, double bottom line
    double_bottom_border = Border(
        top=Side(border_style="thin", color="718096"),
        bottom=Side(border_style="double", color="1A365D"),
        left=border_color,
        right=border_color
    )

    # Injecting standard Excel uppercase formulas
    ws.cell(row=summary_row, column=1, value="Average / Totals").alignment = align_left
    ws.cell(row=summary_row, column=2, value=f"=AVERAGE(B{start_row}:B{end_row})").number_format = '£#,##0.00'
    ws.cell(row=summary_row, column=3, value=f"=AVERAGE(C{start_row}:C{end_row})").number_format = '0.0'
    ws.cell(row=summary_row, column=4, value=f'=COUNTIF(D{start_row}:D{end_row}, "Yes")').number_format = '0'
    ws.cell(row=summary_row, column=5, value="Total Scraped").alignment = align_right
    ws.cell(row=summary_row, column=6, value=f"=COUNTA(A{start_row}:A{end_row})").number_format = '0'

    for c_idx in range(1, len(headers) + 1):
        cell = ws.cell(row=summary_row, column=c_idx)
        cell.fill = summary_fill
        cell.font = summary_font
        cell.border = double_bottom_border
        if c_idx in [2, 3, 4, 6]:
            cell.alignment = align_center if c_idx in [3, 4] else align_right

    # 7. Apply Conditional Formatting (e.g. highlights)
    green_fill = PatternFill(start_color="C6F6D5", end_color="C6F6D5", fill_type="solid")
    green_font = Font(color="22543D", bold=True)
    red_fill = PatternFill(start_color="FED7D7", end_color="FED7D7", fill_type="solid")
    red_font = Font(color="742A2A", bold=True)

    # Highlight High Ratings (Equal to 5) in Green
    ws.conditional_formatting.add(
        f"C{start_row}:C{end_row}",
        CellIsRule(operator="equal", formula=["5"], fill=green_fill, font=green_font)
    )

    # Highlight Out of Stock in Soft Red
    ws.conditional_formatting.add(
        f"D{start_row}:D{end_row}",
        CellIsRule(operator="equal", formula=['"No"'], fill=red_fill, font=red_font)
    )

    # 8. Embed a Beautiful Bar Chart
    chart = BarChart()
    chart.type = "col"
    chart.style = 10
    chart.title = "Price Analysis (Top 5 Items)"
    chart.y_axis.title = "Price (£)"
    chart.x_axis.title = "Product Name"

    data_ref = Reference(ws, min_col=2, min_row=1, max_row=min(end_row, 6))
    cats_ref = Reference(ws, min_col=1, min_row=2, max_row=min(end_row, 6))

    chart.add_data(data_ref, titles_from_data=True)
    chart.set_categories(cats_ref)
    chart.legend = None # Single-series, no legend needed

    chart.width = 16
    chart.height = 10
    ws.add_chart(chart, "H2") # Position at column H

    # 9. Dynamic Auto-Fitting Columns with Comfort Offsets
    for col in ws.columns:
        col_letter = get_column_letter(col[0].column)
        max_len = 0
        for cell in col:
            val = str(cell.value or '')
            if val.startswith('='):
                val = "Average / Totals"
            max_len = max(max_len, len(val))

        # Title column max width restriction to prevent runaway spans
        if col[0].column == 1:
            ws.column_dimensions[col_letter].width = min(max(max_len + 4, 18), 40)
        else:
            ws.column_dimensions[col_letter].width = max(max_len + 4, 14)

    # Save output
    wb.save(output_path)
    print(f"Premium Excel dashboard successfully created at: {output_path}")

# Run demo
if __name__ == "__main__":
    demo_dataset = [
        {"Title": "ASUS ROG Strix G16", "Price": 1249.99, "Rating": 5, "In Stock": "Yes", "Product URL": "https://example.com/rog", "Image URL": "https://example.com/rog_img"},
        {"Title": "HP Victus 15.6 Gaming", "Price": 799.00, "Rating": 4, "In Stock": "Yes", "Product URL": "https://example.com/victus", "Image URL": "https://example.com/victus_img"},
        {"Title": "Lenovo Legion 5 Pro", "Price": 1499.50, "Rating": 5, "In Stock": "No", "Product URL": "https://example.com/legion", "Image URL": "https://example.com/legion_img"},
        {"Title": "Acer Nitro V 15 Laptop", "Price": 689.00, "Rating": 2, "In Stock": "Yes", "Product URL": "https://example.com/nitro", "Image URL": "https://example.com/nitro_img"},
        {"Title": "Dell G15 Gaming Engine", "Price": 950.00, "Rating": 4, "In Stock": "Yes", "Product URL": "https://example.com/dell", "Image URL": "https://example.com/dell_img"},
    ]
    generate_premium_report(demo_dataset)
Enter fullscreen mode Exit fullscreen mode

Code Deep Dive: Why This Works Programmatically

Gridlines & Typography

Setting gridlines explicitly is a major step. By default, Excel uses a standard font like Arial or Calibri. We upgraded our sheet to Segoe UI, which is Microsoft’s premium UI typeface, rendering clean, modern character edges.

The Spacious 32pt Spacing

We programmatically iterate through all populated data rows and set row_dimensions[idx].height = 32. When cell borders don't squeeze data, columns look clean, organized, and feel highly deliberate.

Safe Excel Formulas

Notice that the formula names like =AVERAGE() and =COUNTA() are strictly capitalized. Excel's calculation engine can throw errors if formulas are injected in lower-case letters depending on the user's localized Windows regional settings.

The Hyperlink Trick

Normally, cell text overrides anything in it. By setting:

ws.cell(row=r, column=col).hyperlink = "http..."
Enter fullscreen mode Exit fullscreen mode

and then configuring the font with underline="single" and color="1A365D", we emulate Excel’s internal stylesheet properties.


Elevate Your Freelance Brand & Get Hired

When you present client deliverables like this, you instantly separate yourself from 99% of developers on Fiverr and Upwork. You prove that you understand data presentation, business intelligence, and stakeholder needs.

Clients are glad to pay a premium when their automated tools produce results they can immediately paste into an executive slides presentation.


Need to automate your business data pipelines?
I design and build robust, enterprise-grade data engines, custom web scrapers, and beautifully formatted analytical dashboards.

Top comments (0)