Metadata-Version: 2.4
Name: spreadsheet-analyser
Version: 0.2.0
Summary: Formula-logic analysis for spreadsheets (.xlsx, .xlsm) — the lens-family member that reads the workbook's reasoning, not its data
Project-URL: Homepage, https://github.com/michael-borck/spreadsheet-analyser
Project-URL: Repository, https://github.com/michael-borck/spreadsheet-analyser
Project-URL: Issues, https://github.com/michael-borck/spreadsheet-analyser/issues
Author-email: Michael Borck <michael.borck@curtin.edu.au>
License: MIT
License-File: LICENSE
Keywords: analysis,excel,formula,lens,openpyxl,spreadsheet,xlsx
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.11
Requires-Dist: fastapi>=0.109.0
Requires-Dist: lens-contract>=0.2.0
Requires-Dist: openpyxl>=3.1.0
Requires-Dist: python-multipart>=0.0.9
Requires-Dist: rich>=13.7.0
Requires-Dist: uvicorn[standard]>=0.27.0
Provides-Extra: dev
Requires-Dist: httpx>=0.27.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Description-Content-Type: text/markdown

# spreadsheet-analyser

**Formula-logic analysis for spreadsheets** — the [lens-family](https://github.com/michael-borck/lens-analysers)
member that reads a workbook's *reasoning* (formulas, dependencies, error cells, named ranges,
hygiene smells), not its data.

> `records-analyser` reads `.xlsx` via `pd.read_excel` → **computed values only**, profiled as a
> data table. This reads the same bytes through `openpyxl` with `data_only=False` → **formula
> logic**: the SUM/IF/VLOOKUP/INDIRECT calls, the cell-dependency graph, the `#REF!` cells, the
> `=A1*1.075` magic numbers. Because both members ingest `.xlsx`, this one is **explicit-only**
> (`auto_routable: false`) — `.xlsx` continues to auto-route to records-analyser by default;
> invoke `spreadsheet-analyser` deliberately when you want formula-logic signals.

## Install

```bash
pip install spreadsheet-analyser
```

## Use

**Python:**

```python
from spreadsheet_analyser import SpreadsheetAnalyser, SpreadsheetAnalysis

result: SpreadsheetAnalysis = SpreadsheetAnalyser().analyse("budget.xlsx")
print(result.overall_formulas.unique_functions)        # ['IF', 'SUM', 'VLOOKUP']
print(result.overall_formulas.max_nesting_depth)       # 4
print(result.dependencies.circular_references)         # [['Sheet1!A1', 'Sheet1!B1', 'Sheet1!A1']]
print(result.overall_errors.error_cells)               # {'#REF!': 2, '#DIV/0!': 1}
```

**CLI:**

```bash
spreadsheet-analyser budget.xlsx          # human summary
spreadsheet-analyser budget.xlsx --json   # raw JSON
spreadsheet-analyser serve                # HTTP API on port 8011
spreadsheet-analyser manifest             # capability manifest
```

**HTTP** (`spreadsheet-analyser serve` on port 8011):

```bash
curl -F file=@budget.xlsx http://localhost:8011/analyse
curl http://localhost:8011/health
curl http://localhost:8011/manifest
```

## Signals

Per sheet and rolled up across the workbook:

- **Formulas** — count, formula-vs-constant ratio, unique functions used, max nesting depth,
  longest formula, **volatile functions** (`NOW`/`RAND`/`OFFSET`/`INDIRECT`), **hardcoded magic
  numbers in formulas** (a hygiene smell — `=A1*0.15` instead of `=A1*TaxRate`).
- **Dependencies** — cross-sheet references, dependency-chain depth, **circular references**.
- **Errors** — `#REF!` / `#DIV/0!` / `#VALUE!` / `#N/A` / `#NAME?` / `#NULL!` / `#NUM!` cells.
- **Structure** — sheet count, used range per sheet, named ranges, tables, charts present.

## The family

Part of the [lens analyser family](https://github.com/michael-borck/lens-analysers) — small
single-purpose engines that compose via a shared contract ([lens-contract](https://github.com/michael-borck/lens-contract)).

| What you want | Use |
|---|---|
| Spreadsheet *data values* / profiling | **records-analyser** (default for `.xlsx`) |
| Spreadsheet *formula logic* | **spreadsheet-analyser** (this) |
| Any file → right engine | **auto-analyser** |

## Limits

- `.xlsx` and `.xlsm` only for v1 (`.ods` via odfpy is a possible follow-on).
- Cycle detection is direct cell→cell only (not via named-range indirection).
- "Hardcoded magic numbers" is a heuristic (numbers other than 0/1 inside formulas after
  stripping cell refs) — useful as a hygiene signal, not a definitive smell.

## License

MIT
