- Add bash script (siter-solar-analysis.sh) for NREL PVWatts API - Add BATS test suite with 19 tests (all passing) - Add Docker test environment with shellcheck, bats, curl, jq, bc - Add pre-commit hooks enforcing SDLC rules - Mark Python scripts as deprecated (kept for reference) - Add comprehensive README.md and AGENTS.md documentation - Add .env.example for configuration template - Add .gitignore excluding private data (base-bill/, .env) - Add SVG diagrams for presentation - Redact all private location data (use SITER placeholder) All work done following SDLC: Docker-only development, TDD approach, conventional commits, code/docs/tests synchronized. Generated with Crush Assisted-by: GLM-5 via Crush <crush@charm.land>
126 lines
7.0 KiB
XML
126 lines
7.0 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400">
|
||
<defs>
|
||
<linearGradient id="paybackGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||
<stop offset="0%" style="stop-color:#ef4444;stop-opacity:1" />
|
||
<stop offset="100%" style="stop-color:#22c55e;stop-opacity:1" />
|
||
</linearGradient>
|
||
<linearGradient id="savingsGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||
<stop offset="0%" style="stop-color:#22c55e;stop-opacity:1" />
|
||
<stop offset="100%" style="stop-color:#16a34a;stop-opacity:1" />
|
||
</linearGradient>
|
||
<linearGradient id="costGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||
<stop offset="0%" style="stop-color:#ef4444;stop-opacity:1" />
|
||
<stop offset="100%" style="stop-color:#dc2626;stop-opacity:1" />
|
||
</linearGradient>
|
||
</defs>
|
||
|
||
<!-- Background -->
|
||
<rect width="800" height="400" fill="#f8fafc"/>
|
||
|
||
<!-- Title -->
|
||
<text x="400" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#1e293b">10-Year Payback Timeline</text>
|
||
<text x="400" y="50" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">16 × 250W System | $4,100 Investment | $402/yr Savings</text>
|
||
|
||
<!-- Y-axis -->
|
||
<line x1="100" y1="70" x2="100" y2="330" stroke="#cbd5e1" stroke-width="1"/>
|
||
<text x="35" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b" transform="rotate(-90, 35, 200)">Cumulative ($)</text>
|
||
|
||
<!-- Y-axis labels -->
|
||
<text x="90" y="334" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">-$4,500</text>
|
||
<text x="90" y="274" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">-$3,000</text>
|
||
<text x="90" y="214" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">-$1,500</text>
|
||
<text x="90" y="154" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">$0</text>
|
||
<text x="90" y="94" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">$1,500</text>
|
||
|
||
<!-- Zero line -->
|
||
<line x1="100" y1="154" x2="750" y2="154" stroke="#64748b" stroke-width="2" stroke-dasharray="5,5"/>
|
||
<text x="760" y="158" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Breakeven</text>
|
||
|
||
<!-- Grid lines -->
|
||
<g stroke="#e2e8f0" stroke-width="1">
|
||
<line x1="100" y1="274" x2="750" y2="274"/>
|
||
<line x1="100" y1="214" x2="750" y2="214"/>
|
||
<line x1="100" y1="94" x2="750" y2="94"/>
|
||
</g>
|
||
|
||
<!-- X-axis -->
|
||
<line x1="100" y1="330" x2="750" y2="330" stroke="#cbd5e1" stroke-width="1"/>
|
||
<text x="425" y="380" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b">Years</text>
|
||
|
||
<!-- X-axis labels -->
|
||
<text x="100" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">0</text>
|
||
<text x="159" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">1</text>
|
||
<text x="218" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">2</text>
|
||
<text x="277" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">3</text>
|
||
<text x="336" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">4</text>
|
||
<text x="395" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">5</text>
|
||
<text x="454" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">6</text>
|
||
<text x="513" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">7</text>
|
||
<text x="572" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">8</text>
|
||
<text x="631" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">9</text>
|
||
<text x="690" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">10</text>
|
||
<text x="749" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">11</text>
|
||
|
||
<!-- Area fill under the line (red zone) -->
|
||
<polygon points="100,154 100,334 749,159.5 749,154" fill="#fef2f2" opacity="0.7"/>
|
||
|
||
<!-- Area fill under the line (green zone after breakeven) -->
|
||
<polygon points="100,154 749,154 749,159.5" fill="#f0fdf4" opacity="0.7"/>
|
||
|
||
<!-- Net position line -->
|
||
<!-- Year 0: -$4,100 (y=154 + 4100*0.04 = 318) -->
|
||
<!-- Year 1: -$3,698 (y=154 + 3698*0.04 = 302) -->
|
||
<!-- Year 2: -$3,296 (y=154 + 3296*0.04 = 286) -->
|
||
<!-- Year 3: -$2,894 (y=154 + 2894*0.04 = 270) -->
|
||
<!-- Year 4: -$2,491 (y=154 + 2491*0.04 = 254) -->
|
||
<!-- Year 5: -$2,089 (y=154 + 2089*0.04 = 238) -->
|
||
<!-- Year 6: -$1,687 (y=154 + 1687*0.04 = 221) -->
|
||
<!-- Year 7: -$1,284 (y=154 + 1284*0.04 = 205) -->
|
||
<!-- Year 8: -$882 (y=154 + 882*0.04 = 189) -->
|
||
<!-- Year 9: -$480 (y=154 + 480*0.04 = 173) -->
|
||
<!-- Year 10: -$78 (y=154 + 78*0.04 = 157) -->
|
||
<!-- Year 11: +$325 (y=154 - 325*0.04 = 141) -->
|
||
|
||
<polyline
|
||
points="100,318 159,302 218,286 277,270 336,254 395,238 454,221 513,205 572,189 631,173 690,157 749,141"
|
||
fill="none"
|
||
stroke="url(#paybackGrad)"
|
||
stroke-width="3"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"/>
|
||
|
||
<!-- Data points -->
|
||
<g fill="#1e293b">
|
||
<circle cx="100" cy="318" r="5"/>
|
||
<circle cx="159" cy="302" r="4"/>
|
||
<circle cx="218" cy="286" r="4"/>
|
||
<circle cx="277" cy="270" r="4"/>
|
||
<circle cx="336" cy="254" r="4"/>
|
||
<circle cx="395" cy="238" r="4"/>
|
||
<circle cx="454" cy="221" r="4"/>
|
||
<circle cx="513" cy="205" r="4"/>
|
||
<circle cx="572" cy="189" r="4"/>
|
||
<circle cx="631" cy="173" r="4"/>
|
||
<circle cx="690" cy="157" r="4"/>
|
||
<circle cx="749" cy="141" r="5" fill="#22c55e"/>
|
||
</g>
|
||
|
||
<!-- Key milestones labels -->
|
||
<text x="100" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#ef4444">-$4,100</text>
|
||
<text x="395" y="252" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">-$2,089</text>
|
||
<text x="690" y="171" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">-$78</text>
|
||
<text x="749" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#22c55e" font-weight="bold">+$325</text>
|
||
|
||
<!-- Breakeven marker -->
|
||
<line x1="715" y1="154" x2="715" y2="330" stroke="#22c55e" stroke-width="2" stroke-dasharray="4,4"/>
|
||
<text x="715" y="365" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#22c55e" font-weight="bold">~10.2 yrs</text>
|
||
|
||
<!-- Legend -->
|
||
<rect x="120" y="70" width="200" height="50" rx="4" fill="white" stroke="#e2e8f0"/>
|
||
<circle cx="140" cy="88" r="6" fill="#ef4444"/>
|
||
<text x="155" y="92" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Investment (Year 0)</text>
|
||
<circle cx="140" cy="108" r="6" fill="#22c55e"/>
|
||
<text x="155" y="112" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Profit (Year 11+)</text>
|
||
</svg>
|