Metadata-Version: 2.4
Name: EasyPour
Version: 0.1.0
Summary: Write markdown report easily! Optionally transform into a PDF, streamlit page, or dash page!
Project-URL: Homepage, https://github.com/ESPR3SS0/EasyPour
Project-URL: Repository, https://github.com/ESPR3SS0/EasyPour
Author-email: ESPR3SS0 <98864173+ESPR3SS0@users.noreply.github.com>
License: MIT License
        
        Copyright (c) 2025 ESPR3SS0
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Requires-Python: >=3.9
Requires-Dist: build<2,>=1.3.0
Requires-Dist: markdown-it-py<5,>=4.0.0
Requires-Dist: reportlab<5,>=4.4.4
Provides-Extra: cli
Requires-Dist: cyclopts<3,>=2.7; extra == 'cli'
Provides-Extra: dash
Requires-Dist: dash<4,>=3.2.0; extra == 'dash'
Provides-Extra: dev
Requires-Dist: pytest<9,>=8.4.2; extra == 'dev'
Requires-Dist: ruff<0.7,>=0.6.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material<10,>=9.6.22; extra == 'docs'
Requires-Dist: mkdocs<2,>=1.6.1; extra == 'docs'
Requires-Dist: pymdown-extensions<11,>=10.12; extra == 'docs'
Provides-Extra: plots
Requires-Dist: matplotlib; extra == 'plots'
Provides-Extra: streamlit
Requires-Dist: streamlit<2,>=1.50.0; extra == 'streamlit'
Provides-Extra: weasy
Requires-Dist: weasyprint<67,>=66.0; extra == 'weasy'
Description-Content-Type: text/markdown

# EasyPour — Markdown Reports to HTML/PDF (with a smile)

Turn tidy Python objects into Markdown, then to beautiful HTML and PDF — all with a tiny, friendly API and a simple CLI.

## What Is It?
- Build a `Report` of nested `Section`s with text, `Table`s, and `Image`s.
- Render Markdown to HTML with tasteful defaults, or to PDF via ReportLab.
- Drive it from Python or from the command line using `python -m easypour.cli`.

## Install
Pick one of the options below.

- From PyPI (if/when published):
  - `pip install EasyPour`
- Include optional WeasyPrint dependency (for Markdown→PDF via CLI):
  - `pip install "EasyPour[weasy]"`
- From source (this repo):
  - `pip install .`  (or editable: `pip install -e .`)

PDF rendering uses ReportLab (pure Python wheels available on PyPI). If you don’t already have it: `pip install reportlab`.

## Quick Start (Library)
Create a small report, write Markdown, and export HTML/PDF.

```python
from easypour import Report, Table, Image, b, i, code

# Build a report
rpt = Report(title="Weekly Model Analysis", author="ESPR3SS0", meta={"draft": True})
sec = rpt.add_section("Summary")
sec.add_text(
    f"This week was {b('great')} — latency down, accuracy steady.",
    f"We also verified {code('predict()')} on fresh data.",
)

# Add a table
metrics = Table(headers=["Metric", "Value"], rows=[["Accuracy", "92.8%"], ["F1", "91.5%"]])
rpt.add_section("Metrics").add_table(metrics)

# Add an image (caption and width use a <figure> wrapper)
rpt.add_section("Artifacts").add_image(Image("./charts/latency.png", alt="Latency", caption="P95 latency", width="60%"))

# Write Markdown
md_path = rpt.write_markdown("report.md")
print("Wrote:", md_path)

# Render to HTML (inline CSS included)
from easypour import markdown_to_html
html = markdown_to_html(rpt.to_markdown(), title=rpt.title)
open("report.html", "w", encoding="utf-8").write(html)

# Render to PDF (ReportLab backend)
rpt.write_pdf("report.pdf")

# Configure PDF defaults in code (page size, margins, fonts, captions)
rpt.configure_pdf(
    page_size=(8.5 * 72, 11 * 72),
    margins=(54, 54, 72, 72),
    layout="two",
    column_gap=22,
    font="Times-Roman",
    figure_caption_style={"font": "Times-Italic"},
)
rpt.write_pdf("report_two_col.pdf")

# Use the IEEE preset for two-column output (optional)
from easypour.ieee import IEEETemplate
rpt.write_pdf("report_ieee.pdf", template=IEEETemplate())
```

## Quick Start (CLI)
You can also use the CLI module.

- From an existing Markdown file to HTML/PDF (PDF requires `EasyPour[weasy]`):
  - `python -m easypour.cli --from-md report.md --html report.html`
  - `python -m easypour.cli --from-md report.md --pdf report.pdf`
- From a Python builder to Markdown/HTML/PDF:
  1) Create `builder.py` with a `build_report()` function that returns either a `Report` or a Markdown `str`:
     ```python
     # builder.py
     from easypour import Report
     def build_report():
         r = Report("CLI Report", author="You")
         r.add_section("Hello").add_text("This was generated via the CLI.")
         return r
     ```
  2) Run it:
     - Markdown: `python -m easypour.cli --builder builder.py --md out.md`
     - HTML: `python -m easypour.cli --builder builder.py --html out.html`
     - PDF (requires `build_report()` to return a `Report`): `python -m easypour.cli --builder builder.py --pdf out.pdf`

Tip: `--builder` and `--from-md` are mutually exclusive.

## Little Things That Delight
- Inline helpers: `b("bold")`, `i("italic")`, `u("underline")`, `code("snippet")`, `link("text", "https://…")`.
- Tables: `Table.from_dicts([{...}, {...}])` or explicit headers/rows.
- Images: pass `caption` and/or `width` (like `"60%"` or `"380px"`) for a `<figure>` wrapper.
- Math snippets: `Section.add_math(r"\int_0^1 ...", caption="Integral")` renders TeX-like formulas via matplotlib.
- Figures/tables with numbering: `Section.add_figure(...)` / `Section.add_table(..., numbered=True)` auto-generate IEEE-style captions.
- Citations: `Report.add_reference(...)` + `report.cite("smith19")` give you `[1]` references and an auto-built References section.
- Layout control: `PDFTemplate(layout="two", column_gap=24)` or even `template.register_layout("cover", builder)` let you define single/two/custom column frames and caption styles without touching ReportLab internals.
- Global PDF tuning without templates: `report.configure_pdf(page_size=..., margins=..., font="Times-Roman", header_fn=...)` sets default page size, margins, fonts, column layouts, headers/footers, and caption styles. If you also pass a custom template, EasyPour will warn when your code-level choices override template values so you always know which settings win.
- Interactive plots: `Section.add_matplotlib(fig, interactive=True)` keeps the PDF static while upgrading the Streamlit/Dash view to Plotly (zoom/pan/hover).
- Cross references: Label figures/tables (`label="fig:latency"`) and drop `report.ref("fig:latency")` anywhere to emit `Figure N`.
- Sensible HTML defaults: readable fonts, clean tables, page numbers for PDF.
- Extra styling: `markdown_to_html(md, extra_css="body { color: #333; }")` or pass a custom `PDFTemplate` to `Report.write_pdf`.

## Why EasyPour?
- Small surface area, batteries included.
- Markdown first; HTML/PDF are just a render away.
- Works great in scripts, notebooks, and CI.

## Troubleshooting
- PDF export fails or looks odd:
  - Ensure ReportLab is installed: `pip install reportlab`.
  - Check that any referenced images exist on disk and are reachable from your working directory.
- CLI says the builder is missing:
  - Your `builder.py` must define `build_report()`.

## Contributing & Tests
- Run all tests: `pytest -q`
- Quick, no-PDF/CLI tests: `pytest -q -m "not pdf and not cli"`
- Lint/format: `ruff check .` and `ruff format .`

Enjoy making many marks! ✨

## Documentation

The docs site lives under `docs/` and is built with MkDocs Material. Install the optional extras and run:

```bash
pip install ".[docs]"
mkdocs serve
```

Commits to `main` automatically build and publish the static site to GitHub Pages.

## More Examples

All examples below are pure Python — you can drop them in a `builder.py` and run them via the CLI too.

### Bullets, Checklists, Code Blocks, Strikethrough

```python
from easypour.core import Report

r = Report("Lists + Code")
s = r.add_section("Goodies")
s.add_bullets(["Item A", "Item B", "Item C"])           # - bullets
s.add_checklist([("Do A", False), ("Done B", True)])      # - [ ] / [x]
s.add_codeblock("print('hello world')", language="python") # ```python
s.add_strikethrough("old text")                            # ~~old text~~

open("lists.md", "w").write(r.to_markdown())
```

### Tables (from dicts) and Images (by path)

```python
from easypour.core import Report, Table

r = Report("Data + Image")
data = [
    {"Metric": "Accuracy", "Value": "92.8%"},
    {"Metric": "F1",       "Value": "91.5%"},
]
r.add_section("Results").add_table(Table.from_dicts(data))
r.add_section("Plot").add_image_path("./charts/acc.png", alt="acc", caption="Accuracy", width="50%")
```

### Matplotlib → Image (inline convenience)

```python
from easypour.core import Report
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(3, 2))
ax.plot([0, 1, 2], [0, 1, 0])
ax.set_title("Curve")

r = Report("Matplotlib Demo")
r.add_section("Figure").add_matplotlib(fig, out_dir="assets", filename="curve.png", caption="A curve", width="60%")

r.write_pdf("mpl.pdf")
```

### Two-Column Layout + References

```python
from easypour import Report
from easypour.core import Table
from easypour.render import PDFTemplate

r = Report("Two-Column Demo", author="EasyPour Labs")
r.add_reference("smith19", "Smith et al., 'Cool Paper', IEEE, 2019.")

sec = r.add_section("Results")
sec.add_text(
    f"This result improves latency by 20% {r.cite('smith19')} as illustrated in {r.ref('fig:latency')}."
)
sec.add_table(
    Table(headers=["Metric", "Value"], rows=[["Latency", "12ms"], ["AUC", "0.962"]]),
    caption="Key performance metrics",
    numbered=True,
    label="tab:metrics",
)
fig_sec = r.add_section("Figures")
fig_sec.add_figure("./charts/latency.png", caption="Latency over input rate", label="fig:latency", width="60%")

r.ensure_references_section()

template = PDFTemplate(layout="two", column_gap=24)
r.write_pdf("two_column.pdf", template=template)
```
Use `r.ref("fig:latency")` (or any label you assign) anywhere in your narrative to produce the resolved text (e.g., `Figure 1`), keeping captions and references consistent across Markdown, HTML, PDF, Streamlit, and Dash.

### Math Formulas → Image

```python
from easypour.core import Report

r = Report("Math Demo")
sec = r.add_section("Equations")
sec.add_math(r"\int_0^1 x^2\,dx = \frac{1}{3}", caption="Simple integral", width="220px")

r.write_pdf("math.pdf")
```

### IEEE-Style Sample

```bash
python examples/pdf/ieee_builder.py
```

Generates Markdown and an IEEE-style PDF (two columns, captions, references) in `examples/out/` using `IEEETemplate`.

### Simple Full Workflow (Markdown + PDF + Streamlit/Dash)

- Generate Markdown/PDF: `python examples/playbook/simple_full.py`
- Preview in Streamlit: `streamlit run examples/playbook/simple_full.py -- --preview streamlit`
- Preview in Dash: `python examples/playbook/simple_full.py --preview dash`

### Advanced Full Workflow (IEEE template + interactive figures)

- Generate Markdown/PDF: `python examples/playbook/advanced_full.py`
- Preview in Streamlit: `streamlit run examples/playbook/advanced_full.py -- --preview streamlit`
- Preview in Dash: `python examples/playbook/advanced_full.py --preview dash`

### Streamlit Example

Run an interactive Streamlit app that builds a report, previews Markdown/HTML, and lets you download a PDF.

- Command: `streamlit run examples/streamlit/app.py`
- Minimal gist:

```python
# examples/streamlit/app.py (excerpt)
import streamlit as st
from easypour.core import Report, Table
from easypour import markdown_to_html

def build_report(include_table: bool) -> Report:
    rpt = Report("Streamlit Demo", author="Examples")
    rpt.add_section("Summary").add_text("Generated inside Streamlit.")
    if include_table:
        rpt.add_section("Metrics").add_table(Table.from_dicts([
            {"Metric": "Accuracy", "Value": "92.8%"},
            {"Metric": "F1", "Value": "91.5%"},
        ]))
    return rpt

st.sidebar.header("Options")
opt_table = st.sidebar.checkbox("Include table", value=True)
rpt = build_report(opt_table)
md = rpt.to_markdown()
st.code(md, language="markdown")
html = markdown_to_html(md, title=rpt.title)
st.components.v1.html(html, height=400, scrolling=True)
```

Tip: `Report.show_streamlit()` now defaults to four tabs—`Report` (native Streamlit rendering), `Markdown`, `HTML`, and `PDF` (download button)—and you can reorder or trim them via `configure_streamlit(tabs=[...])`.

### Streamlit Interactive Plots Example

- Command: `streamlit run examples/streamlit/interactive_plots.py`
- Demonstrates how `Section.add_matplotlib(fig, interactive=True, ...)` preserves the static PNG for Markdown/PDF while automatically upgrading the Streamlit view to Plotly charts (zoom, pan, hover).

### Dash Example

Run a Dash app that renders the report HTML inside the app.

- Command: `python examples/dash/app.py` then open http://127.0.0.1:8050/
- Minimal gist:

```python
# examples/dash/app.py (excerpt)
from dash import Dash, dcc, html, Input, Output
from easypour.core import Report, Table
from easypour import markdown_to_html

def build_report(include_table: bool) -> Report:
    r = Report("Dash Demo", author="Examples")
    r.add_section("Summary").add_text("Generated inside Dash.")
    if include_table:
        r.add_section("Metrics").add_table(Table.from_dicts([
            {"Metric": "Accuracy", "Value": "92.8%"},
            {"Metric": "F1", "Value": "91.5%"},
        ]))
    return r

app = Dash(__name__)
app.layout = html.Div([
    dcc.Checklist(id="opts", options=[{"label": "Include table", "value": "table"}], value=["table"]),
    html.Div(id="content"),
])

@app.callback(Output("content", "children"), [Input("opts", "value")])
def update_content(values):
    rpt = build_report("table" in (values or []))
    html_doc = markdown_to_html(rpt.to_markdown(), title=rpt.title)
    return html.Iframe(srcDoc=html_doc, style={"width": "100%", "height": "70vh"})
```

Tip: Dash apps created via `report.to_dash_app()` default to tabs for `Report`, `Markdown`, `HTML`, and `PDF` (download button powered by ReportLab). Customize the subset/order with `report.configure_dash(tabs=["Report", "PDF"])`, or add your own layout around the returned Dash app.

### Dash Interactive Plots Example

- Command: `python examples/dash/interactive_plots.py`
- Mirrors the Streamlit demo but inside Dash: Matplotlib figures remain PNGs in Markdown/PDF yet show up as fully interactive Plotly graphs in the Dash app.
