← Files Public Equity InvestingARCHIVED FILE

shared/workbook_inspection.py

5.28 KB · Oct 2, 2026 · 00:03 UTC

↓ Download file

"""Workbook inspection gates for Public Equity Investing formula workbooks."""

from __future__ import annotations

import zipfile
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Sequence
from xml.etree import ElementTree as ET

NS_MAIN = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
NS_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"


@dataclass(frozen=True)
class WorkbookInspectionPolicy:
    workbook_type: str
    minimum_formula_count: int
    required_formula_sheets: tuple[str, ...]


POLICIES: dict[str, WorkbookInspectionPolicy] = {
    "dcf": WorkbookInspectionPolicy(
        "dcf",
        800,
        (
            "Revenue Build",
            "Margin Cost Build",
            "Working Capital",
            "Capex D&A",
            "Tax Schedule",
            "Unlevered FCF",
            "WACC",
            "Terminal Value",
            "DCF Valuation",
            "Sensitivities",
            "Checks",
        ),
    ),
    "three_statement": WorkbookInspectionPolicy(
        "three_statement",
        1100,
        (
            "Revenue Build",
            "Expense Build",
            "Income Statement",
            "Working Capital",
            "PP&E D&A",
            "Debt Interest",
            "Tax",
            "Balance Sheet",
            "Cash Flow Statement",
            "Scenarios",
            "Checks",
        ),
    ),
}


def workbook_sheet_paths(path: str | Path) -> tuple[dict[str, str], dict[str, str]]:
    with zipfile.ZipFile(path) as zf:
        workbook = ET.fromstring(zf.read("xl/workbook.xml"))
        rels = ET.fromstring(zf.read("xl/_rels/workbook.xml.rels"))
        relmap = {rel.attrib["Id"]: rel.attrib["Target"] for rel in rels}
        sheets = workbook.find(f"{{{NS_MAIN}}}sheets")
        if sheets is None:
            return {}, {}
        paths: dict[str, str] = {}
        states: dict[str, str] = {}
        for sheet in sheets:
            rid = sheet.attrib[f"{{{NS_REL}}}id"]
            target = relmap[rid].lstrip("/")
            name = sheet.attrib["name"]
            paths[name] = target if target.startswith("xl/") else f"xl/{target}"
            states[name] = sheet.attrib.get("state", "visible")
        return paths, states


def defined_names(path: str | Path) -> list[str]:
    with zipfile.ZipFile(path) as zf:
        workbook = ET.fromstring(zf.read("xl/workbook.xml"))
    node = workbook.find(f"{{{NS_MAIN}}}definedNames")
    if node is None:
        return []
    return [child.attrib["name"] for child in node if child.attrib.get("name")]


def inspect_formula_workbook(
    path: str | Path,
    *,
    required_sheets: Sequence[str],
    workbook_type: str,
    minimum_formula_count: int | None = None,
    required_formula_sheets: Sequence[str] | None = None,
) -> dict[str, Any]:
    policy = POLICIES.get(workbook_type)
    min_formulas = (
        minimum_formula_count
        if minimum_formula_count is not None
        else (policy.minimum_formula_count if policy else 100)
    )
    formula_sheet_requirements = tuple(
        required_formula_sheets or (policy.required_formula_sheets if policy else ())
    )
    with zipfile.ZipFile(path) as zf:
        names = set(zf.namelist())
        sheet_paths, sheet_states = workbook_sheet_paths(path)
        formula_counts_by_sheet: dict[str, int] = {}
        formula_count = 0
        for sheet_name, sheet_path in sheet_paths.items():
            root = ET.fromstring(zf.read(sheet_path))
            count = len(root.findall(f".//{{{NS_MAIN}}}f"))
            formula_counts_by_sheet[sheet_name] = count
            formula_count += count
        external_links = sorted(name for name in names if name.startswith("xl/externalLinks/"))
        has_styles = "xl/styles.xml" in names and len(zf.read("xl/styles.xml")) > 100
    sheet_names = list(sheet_paths.keys())
    visible = [name for name in sheet_names if sheet_states.get(name, "visible") == "visible"]
    names_defined = defined_names(path)
    missing_required = [sheet for sheet in required_sheets if sheet not in sheet_paths]
    missing_formula_sheets = [
        sheet for sheet in formula_sheet_requirements if formula_counts_by_sheet.get(sheet, 0) <= 0
    ]
    return {
        "workbook_type": workbook_type,
        "sheet_names": sheet_names,
        "sheet_states": sheet_states,
        "first_visible_sheet": visible[0] if visible else "",
        "cover_first": bool(visible and visible[0] == "Cover"),
        "required_sheets_present": not missing_required,
        "missing_required_sheets": missing_required,
        "formula_count": formula_count,
        "minimum_formula_count": min_formulas,
        "formula_count_passes_threshold": formula_count >= min_formulas,
        "formula_counts_by_sheet": formula_counts_by_sheet,
        "formula_sheets": sum(1 for count in formula_counts_by_sheet.values() if count > 0),
        "required_formula_sheets": list(formula_sheet_requirements),
        "missing_formula_sheets": missing_formula_sheets,
        "required_formula_sheets_populated": not missing_formula_sheets,
        "defined_names": names_defined,
        "named_ranges_count": len(names_defined),
        "has_named_ranges": bool(names_defined),
        "has_styles": has_styles,
        "external_links": external_links,
        "no_external_links": not external_links,
    }

SHA-256: e494a22b5be1c165bff932c5cda16a56de26be1f1861b139a5b05d8e88eef319