Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
printavo-ruby (0.18.0)
printavo-ruby (0.18.1)
faraday (~> 2.0)
faraday-retry (~> 2.0)
thor (~> 1.0)
Expand Down Expand Up @@ -236,7 +236,7 @@ CHECKSUMS
notiffany (0.1.3) sha256=d37669605b7f8dcb04e004e6373e2a780b98c776f8eb503ac9578557d7808738
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54
printavo-ruby (0.18.0)
printavo-ruby (0.18.1)
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
Expand Down
14 changes: 2 additions & 12 deletions docs/FUTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,8 @@ See [docs/CACHING.md](docs/CACHING.md) for caching options.

## Visualization

### Workflow Diagram Generation (SVG/PNG)

Generate a visual map of a shop's Printavo status workflow:

```ruby
client.workflow.diagram(format: :svg)
# => Outputs an SVG flowchart: Quote → Approved → In Production → Completed
```

Implementation options:
- [ruby-graphviz](https://github.com/glejeune/Ruby-Graphviz) — DOT → SVG/PNG
- Pure Ruby → Mermaid output (copy-paste into docs or GitHub markdown)
See **[docs/VISUALIZATION.md](VISUALIZATION.md)** for workflow diagram generation —
Mermaid, DOT, ASCII, and SVG output via the standalone example script.

## Multi-Language SDK Family

Expand Down
9 changes: 9 additions & 0 deletions docs/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,15 @@ return values — they are exposed via model field accessors, not separate resou
- [x] `default_ttl: 300` configurable per-client
- [x] `docs/CACHING.md` updated with built-in adapter usage examples

### v0.18.1 — Examples: Diagramming & Reporting

- [x] `examples/diagramming/workflow_diagram.rb` — Mermaid, DOT, ASCII, SVG (dot CLI + ruby-graphviz gem)
- [x] `examples/reporting/sales_report.rb` — today, week, month, quarter, YTD, last year, custom range
- [x] `examples/reporting/sales_tax_report.rb` — invoice-level estimates + fee-level taxable detail
- [x] `examples/reporting/customers_export.rb` — CSV, XLSX, XLS, vCard (.vcf), HubSpot, Salesforce, Mailchimp, Constant Contact, Beehiiv, Brevo, ActiveCampaign
- [x] `examples/reporting/outstanding_tasks.rb` — overdue, due today, this week, upcoming, no due date; by-assignee summary; HTML calendar export
- [x] `docs/FUTURE.md` — Workflow Diagram section replaced with examples reference and rationale

### v0.99.0 — API Freeze

- [ ] Community feedback integration
Expand Down
81 changes: 81 additions & 0 deletions docs/VISUALIZATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<!-- docs/VISUALIZATION.md -->
# Visualization for [printavo-ruby](https://github.com/scarver2/printavo-ruby)

## Workflow Diagram Generation

See **[examples/diagramming/workflow_diagram.rb](../examples/diagramming/workflow_diagram.rb)**
for a complete, runnable example covering all four output formats.

Adding diagram generation to the gem itself would require either shipping
`ruby-graphviz` as a hard dependency or assuming a system `dot` binary —
both are burdensome for a Ruby API client. The standalone example gives
consumers full control over their output format and toolchain without
bloating the gem.

**Supported formats in the example:**

| Format | Dependency | Best for |
|---|---|---|
| `:ascii` | none | Terminal output, quick sanity-check |
| `:mermaid` | none | GitHub README/issues/PRs, VS Code preview |
| `:dot` | none (text) | Piping to `dot -Tsvg` or any Graphviz renderer |
| `:svg` | `brew install graphviz` or `gem install ruby-graphviz` | Production-grade visual files |

Statuses are fetched via `client.statuses.all` and rendered as a linear
left-to-right flow in the order Printavo returns them. Printavo has no
explicit transition edges in its API — the workflow is an implied sequence.

### Quick Start

```bash
cd examples/diagramming
cp .env.example .env # fill in PRINTAVO_EMAIL and PRINTAVO_TOKEN
gem install printavo-ruby dotenv
ruby workflow_diagram.rb
```

Output files written to the current directory:

| File | Format |
|---|---|
| *(stdout)* | ASCII chain |
| *(stdout)* | Mermaid fenced block |
| `workflow.dot` | Graphviz DOT source |
| `workflow_cli.svg` | SVG via `dot` CLI (if graphviz installed) |
| `workflow_gv.svg` | SVG via `ruby-graphviz` gem (if installed) |

### Rendering DOT to Other Formats

```bash
# SVG
dot -Tsvg workflow.dot > workflow.svg

# PNG
dot -Tpng workflow.dot > workflow.png

# PDF
dot -Tpdf workflow.dot > workflow.pdf
```

### Embedding Mermaid in GitHub Markdown

Paste the Mermaid output between fences in any `.md` file:

````markdown
```mermaid
flowchart LR
s_quote["Quote"] --> s_in_production["In Production"] --> s_completed["Completed"]
```
````

GitHub renders it automatically in READMEs, issues, PRs, and wiki pages.

---

## Colophon

[MIT License](LICENSE)

&copy;2026 [Stan Carver II](https://stancarver.com)

![Made in Texas](https://raw.githubusercontent.com/scarver2/howdy-world/master/_dashboard/www/assets/made-in-texas.png)
9 changes: 9 additions & 0 deletions examples/diagramming/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# examples/diagramming/.env.example
#
# Copy to .env and fill in your Printavo credentials.
# Find your API token at: Printavo → My Account → API Token
#
# Never commit .env to version control.

PRINTAVO_EMAIL=you@example.com
PRINTAVO_TOKEN=your-api-token-from-printavo-my-account
227 changes: 227 additions & 0 deletions examples/diagramming/workflow_diagram.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# examples/diagramming/workflow_diagram.rb
# frozen_string_literal: true

# Printavo Workflow Diagram Example
# ==================================
# Fetches your shop's Printavo statuses and renders the workflow as:
#
# :mermaid — GitHub-renderable flowchart (default, no dependencies)
# :dot — Graphviz DOT source (pipe to `dot -Tsvg` for SVG/PNG)
# :ascii — Terminal-friendly linear chain
# :svg — SVG file via dot CLI or ruby-graphviz gem (both optional)
#
# Why examples instead of gem internals?
# ----------------------------------------
# Generating diagrams would require shipping graphviz as a dependency or
# assuming a system `dot` binary — both are burdensome for a Ruby API client.
# This standalone script gives you full control over output and dependencies
# without bloating the gem.
#
# Setup:
# gem install printavo-ruby dotenv
# cp .env.example .env # fill in PRINTAVO_EMAIL and PRINTAVO_TOKEN
# ruby workflow_diagram.rb
#
# Optional SVG output (either works independently):
# brew install graphviz # for dot CLI approach
# gem install ruby-graphviz # for ruby-graphviz gem approach

require 'dotenv/load'
require 'printavo'

client = Printavo::Client.new(
email: ENV.fetch('PRINTAVO_EMAIL'),
token: ENV.fetch('PRINTAVO_TOKEN')
)

statuses = client.statuses.all
abort 'No statuses found. Check your credentials.' if statuses.empty?

puts "Fetched #{statuses.size} statuses from Printavo.\n\n"

# ── Helpers ───────────────────────────────────────────────────────────────────

# Safe alphanumeric node identifier — prefixed with 's' to avoid
# starting with a digit (invalid in both Mermaid and DOT).
def node_id(status)
slug = status.name.downcase.gsub(/[^a-z0-9]+/, '_').squeeze('_').gsub(/^_|_$/, '')
"s_#{slug}"
end

# Determine readable text color (black or white) against a hex background.
def contrast_color(hex)
hex = hex.to_s.delete('#').then { |h| h.length == 3 ? h.chars.map { |c| c * 2 }.join : h }
r, g, b = hex.scan(/../).map { |h| h.to_i(16) }
((r * 299) + (g * 587) + (b * 114)) / 1000 > 128 ? '#000000' : '#ffffff'
end

def fill_color(status)
status.color.to_s.match?(/\A#?[0-9a-fA-F]{3,6}\z/) ? "##{status.color.delete('#')}" : '#888888'
end

# ── Mermaid ───────────────────────────────────────────────────────────────────
#
# Renders as a flowchart in any GitHub Markdown file, issue, PR, wiki page,
# VS Code preview (with Markdown Preview Mermaid Support extension), or
# any tool that supports Mermaid.js.
#
# Paste the output between ```mermaid and ``` fences.

def to_mermaid(statuses)
lines = ['flowchart LR']
lines << ''

statuses.each do |s|
fill = fill_color(s)
color = contrast_color(fill)
lines << " #{node_id(s)}[\"#{s.name}\"]"
lines << " style #{node_id(s)} fill:#{fill},color:#{color},stroke:#{fill}"
end

lines << ''

statuses.each_cons(2) do |a, b|
lines << " #{node_id(a)} --> #{node_id(b)}"
end

lines.join("\n")
end

# ── DOT (Graphviz) ────────────────────────────────────────────────────────────
#
# Graphviz DOT source. Pipe it to the dot CLI to generate any format:
#
# ruby workflow_diagram.rb 2>/dev/null | sed -n '/^digraph/,/^}/p' | dot -Tsvg > workflow.svg
# ruby workflow_diagram.rb 2>/dev/null | sed -n '/^digraph/,/^}/p' | dot -Tpng > workflow.png
# ruby workflow_diagram.rb 2>/dev/null | sed -n '/^digraph/,/^}/p' | dot -Tpdf > workflow.pdf
#
# Or use the file written by this script: `dot -Tsvg workflow.dot > workflow.svg`

def dot_node_defs(statuses)
statuses.map do |s|
fill = fill_color(s)
" #{node_id(s)} [label=\"#{s.name}\", fillcolor=\"#{fill}\", fontcolor=\"#{contrast_color(fill)}\"];"
end
end

def to_dot(statuses)
edges = statuses.each_cons(2).map { |a, b| " #{node_id(a)} -> #{node_id(b)};" }
[
'digraph PrintavoWorkflow {',
' rankdir=LR;',
' graph [fontname="Helvetica", bgcolor=transparent];',
' node [shape=box, style="filled,rounded", fontname="Helvetica", margin="0.2,0.1"];',
' edge [color="#666666", arrowsize=0.8];',
'', *dot_node_defs(statuses),
'', *edges,
'}'
].join("\n")
end

# ── ASCII ─────────────────────────────────────────────────────────────────────
#
# Zero-dependency terminal output. Useful for CLI tools, log output,
# or anywhere you want a quick sanity-check of the status order.

def to_ascii(statuses)
statuses.map(&:name).join(' → ')
end

# ── SVG via dot CLI ───────────────────────────────────────────────────────────
#
# Requires: brew install graphviz (provides the `dot` binary)
# No gem dependency — shells out to the system dot command.

def svg_via_dot_cli(dot_source)
require 'open3'
stdout, stderr, status = Open3.capture3('dot', '-Tsvg', stdin_data: dot_source)
raise "dot command failed: #{stderr.strip}" unless status.success?

stdout
end

# ── SVG via ruby-graphviz gem ─────────────────────────────────────────────────
#
# Requires: gem install ruby-graphviz AND brew install graphviz
# Gives you a Ruby object model for the graph if you want to manipulate it
# further before rendering.

def configure_graphviz(gviz)
gviz[:rankdir] = 'LR'
gviz.graph[:fontname] = 'Helvetica'
gviz.graph[:bgcolor] = 'transparent'
gviz.node[:shape] = 'box'
gviz.node[:style] = 'filled,rounded'
gviz.node[:fontname] = 'Helvetica'
end

def svg_via_graphviz_gem(statuses)
require 'graphviz'
g = GraphViz.new(:PrintavoWorkflow, type: :digraph)
configure_graphviz(g)
nodes = statuses.map do |s|
fill = fill_color(s)
g.add_nodes(node_id(s), label: s.name, fillcolor: fill, fontcolor: contrast_color(fill))
end
nodes.each_cons(2) { |a, b| g.add_edges(a, b) }
g.output(svg: String)
end

# ── Output ────────────────────────────────────────────────────────────────────

separator = '─' * 62

# — ASCII —
puts separator
puts 'ASCII (linear status chain)'
puts separator
puts to_ascii(statuses)
puts

# — Mermaid —
puts separator
puts 'MERMAID (paste between ```mermaid fences in any .md file)'
puts separator
puts '```mermaid'
puts to_mermaid(statuses)
puts '```'
puts

# — DOT —
dot_source = to_dot(statuses)
File.write('workflow.dot', dot_source)

puts separator
puts 'GRAPHVIZ DOT (written to workflow.dot)'
puts separator
puts dot_source
puts

# — SVG via dot CLI —
puts separator
puts 'SVG via dot CLI (workflow_cli.svg)'
puts separator
begin
svg = svg_via_dot_cli(dot_source)
File.write('workflow_cli.svg', svg)
puts "workflow_cli.svg written — #{svg.bytesize} bytes"
rescue Errno::ENOENT
puts 'Skipped: `dot` not found — run: brew install graphviz'
rescue RuntimeError => e
puts "Skipped: #{e.message}"
end
puts

# — SVG via ruby-graphviz gem —
puts separator
puts 'SVG via ruby-graphviz gem (workflow_gv.svg)'
puts separator
begin
svg = svg_via_graphviz_gem(statuses)
File.write('workflow_gv.svg', svg)
puts "workflow_gv.svg written — #{svg.bytesize} bytes"
rescue LoadError
puts 'Skipped: run `gem install ruby-graphviz` to enable'
rescue RuntimeError => e
puts "Skipped: #{e.message}"
end
14 changes: 14 additions & 0 deletions examples/reporting/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# examples/reporting/.env.example
#
# Copy to .env and fill in your Printavo credentials.
# Find your API token at: Printavo → My Account → API Token
#
# Never commit .env to version control.

PRINTAVO_EMAIL=you@example.com
PRINTAVO_TOKEN=your-api-token-from-printavo-my-account

# Optional: override the date range used by sales_report.rb and sales_tax_report.rb.
# Format: YYYY-MM-DD. Leave blank to use the built-in period helpers.
# REPORT_START=2026-01-01
# REPORT_END=2026-03-31
Loading
Loading