Commit graph

18 commits

Author SHA1 Message Date
Vadym Samoilenko
d8461076b8 fix: axis labels, bar charts, legend, PDF fonts per Nina's feedback
- layout: _PAD_BOTTOM 100→160, _PAD_LEFT 120→160 to prevent label clipping
- axes: fix _format_tick float-comparison bug (1.1→"1.1" not "1.10");
  add auto-rotation for categorical x-axis labels when they overflow slot width
- engine: tick guard (>20 ticks falls back to nice_ticks); stacked_bar falls
  back to bar instead of crashing; extend _find_date_column keywords
- series: bar width uses 25th-percentile gap instead of min to avoid invisible bars
- legend: line series use line swatch with dash pattern; proportional square size
- export: @font-face injected in HTML <head> for PDF to prevent T3Font in InDesign
- prompts: add categorical bar chart few-shot example; ban stacked_bar; improve
  tick_interval guidance; add bar chart refine examples

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 18:48:17 +01:00
Vadym Samoilenko
d52f088243 Fix bar charts, fonts, axis controls, donut support, and Playwright export
Issue 1 – Bar charts blank/lines only:
- Silent fall-through on unsupported chart_types (donut, stacked_bar, area)
  now raises ValueError instead of producing axes-only output
- Zero-width bars on duplicate/single dates fixed via sorted-gap calculation
- Donut chart type added (ring with percentage labels)
- Pie/donut routing now triggers on any() instead of all()

Issue 2 – Axis controls not applying:
- AxisSpec gains date_min/date_max (x-axis clamping via prompts)
- y-bounds no longer silently widened when user sets min_val/max_val
- Tick clamping: ticks outside user range are dropped not widened
- New dual_y_axis layout with independent left/right Y-axes and y_axis_side per series
- Endpoint Y-axis labels (min/max) always render even when spacing is tight

Issue 3+4 – Font fallback & InDesign compatibility:
- Replace CairoSVG with Playwright/headless Chromium for PNG and PDF export
- Chromium honours @font-face base64 data URIs → Roboto Condensed in all exports
- PDF output contains embedded TTF subsets and real text operators (selectable
  in InDesign/Illustrator, no path-outlining, consistent across regions)
- FastAPI lifespan manages persistent Playwright browser instance

Issue 5 – Stroke weight drift:
- All stroke_width values now carry explicit "px" unit suffix
- SVG root gets width="…px" height="…px" so 1 SVG px = 0.75 PDF pt exactly

AI improvements:
- Prompts document date_min/date_max, scale_kind, dual_y_axis, donut
- Rule 9 softened: user-specified ranges are honoured even if they crop data
- Refinement uses deep-merge so tick_interval/min_val/date_min are never
  accidentally reset to None when Claude modifies unrelated fields
- New donut few-shot example added

Library upgrades: anthropic 0.84→0.97, fastapi 0.135→0.136,
pandas 3.0.1→3.0.2, pydantic 2.12→2.13, uvicorn 0.41→0.46;
cairosvg removed, playwright 1.58.0 added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:15:26 +01:00
Vadym Samoilenko
db853cea9e Fix font exports, chart overflow, legend wrapping, and data interpretation
- Add RobotoCondensed-Bold.ttf and RobotoCondensed-Light.ttf; embed all three
  weights (300/normal/bold) in SVG @font-face for correct browser preview
- Dockerfile: install fontconfig and register fonts as system fonts so
  CairoSVG/Pango resolves Roboto Condensed in PNG/PDF exports (not a fallback)
- Export PNG/PDF at dpi=150 for higher quality output
- Add SVG <clipPath> per panel so data series cannot overflow axis boundaries
- Replace np.arange tick generation with safe while-loop (no float artifacts)
- Legend: multi-row wrapping when items exceed chart width; improved per-char
  text width estimation; legend_row_count() helper for dynamic layout
- Dynamic pad_top in compute_layout() based on legend rows + subtitle presence,
  preventing title/subtitle/legend overlap regardless of series count
- Y-axis: skip overlapping labels (gridlines always drawn); adaptive tick
  precision so -0.0042% shows correctly instead of -0.00%
- transformer.py: strip % signs from string-formatted percentages before
  pd.to_numeric; handle pandas 2.x StringDtype; expand date column detection
  to cover Year, Quarter, report_date, substring matches (start_datetime etc.)
- Sync _find_date_column() in engine.py with transformer.py detection logic
- prompts.py: enforce Roboto Condensed for all text; add axis range, label
  length, and decimal-percentage guidance rules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:17:25 +01:00
Vadym Samoilenko
ef6b457075 Add X-axis rotation, legend layout, 15px margins, and PNG/PDF export
- X-axis labels now rotate -90° when spacing < 96px, with overlap skipping
- Legend is horizontal (top-right) for line/bar charts, vertical (right) for pie
- Fix pie chart legend which previously showed nothing
- Set 15px outer image margins (blank whitespace around entire image)
- Add PNG and PDF export via cairosvg alongside SVG
- Add Download PNG and Download PDF buttons to preview template

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:15:57 +00:00
Vadym Samoilenko
1b36609e5d Fix datetime parsing error and int column name crash
- Wrap pd.to_datetime() in try-except in transformer.py to handle
  non-standard strings like '*M 199901 Interpolate' that pandas 3.x
  raises on even with errors="coerce"
- Fix 'int object has no attribute lower' in analyzer.py by using
  str(col).lower() for numeric column names from Excel files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 21:57:26 +00:00
Vadym Samoilenko
3e7fe2624b Update chart styling to new PIMCO brand guidelines
- 10-color palette replacing old 5-color set
- Title 46pt bold, axis labels 42pt Roboto Condensed light (weight 300)
- Gridlines 2pt (#53565A), zero-axis 4pt, line weight 15pt
- Legend: vertical right-side layout with colored squares
- Right margin widened to 300px to accommodate legend
- Y-axis label auto-rotation when ticks are close together
- New pie chart renderer (app/renderer/pie.py) with SVG arc paths
- Font embedding updated with bold weight variant + synthesis note
- AI prompts updated with full 10-color palette (indices 0-9)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 19:45:10 +00:00
Vadym Samoilenko
096a41db47 Fix SSO login button, HTMX root_path prefix, and static file auth exemption
- msal_shell.html: show "Sign in with Microsoft" button on fresh load instead of auto-redirecting
- upload.html, preview.html: prefix HTMX POST paths with root_path to fix 404s under /Pimco-charts
- middleware.py: exempt /static/ paths from auth check so CSS/fonts load unauthenticated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 20:38:35 +00:00
Vadym Samoilenko
8866f57ee9 Fix hardcoded paths in templates to include root_path prefix
Affects: logout link, download SVG link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:32:48 +00:00
Vadym Samoilenko
a98a297261 Fix CSS URL: use root_path scope instead of url_for
url_for returns absolute URL with localhost:8569 which browser can't reach.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:31:00 +00:00
Vadym Samoilenko
b64154b633 Fix static CSS path using url_for to include root_path prefix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:27:23 +00:00
Vadym Samoilenko
111d9e342d Fix MSAL.js CDN URL (switch to jsdelivr)
alcdn.msauth.net was returning ERR_BLOCKED_BY_ORB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:25:58 +00:00
Vadym Samoilenko
eff02c145e Switch to browser-side auth: MSAL.js + JWT validation
- MSAL.js handles full OAuth flow in browser (SPA-compatible)
- Server validates ID token signature via Azure AD JWKS endpoint (PyJWT)
- Root / serves MSAL shell for unauthenticated users, handles redirect callback
- Remove Python MSAL/PKCE server-side logic
- Replace msal dependency with PyJWT[crypto]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:24:22 +00:00
Vadym Samoilenko
5c8f849151 Fix MSAL scopes: replace reserved OIDC scopes with User.Read
openid/profile/email are reserved and added automatically by MSAL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:14:59 +00:00
Vadym Samoilenko
961640e16c Fix redirects to include root_path prefix (/Pimco-charts)
Without root_path, redirects went to /auth/login instead of /Pimco-charts/auth/login.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:13:54 +00:00
Vadym Samoilenko
21d469bd82 Fix OAuth callback to use root path (match Azure AD registration)
Azure AD redirect URI is registered as /Pimco-charts (no /auth/callback),
so handle the code exchange in the index route and exempt root with ?code= in middleware.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:07:49 +00:00
Vadym Samoilenko
af2a020696 Add Azure AD SSO (MSAL/PKCE) and Docker deployment
- Azure AD authentication via MSAL PublicClientApplication with PKCE flow
- Session middleware, auth middleware, login/callback/logout routes
- Dockerfile, docker-compose.yml for port 8569, output volume
- Idempotent deploy.sh with static file copy and health wait
- User nav bar in base template with Sign Out link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 14:56:07 +00:00
DJP
a3a38e85d2 Initial commit: PIMCO chart generator with iterative refinement
AI-powered tool that generates publication-quality SVG charts matching
PIMCO's InDesign style. Upload Excel/CSV data, write a plain-English
brief, then iterate with natural language edits until the chart is
exactly right.

- Claude Opus 4.6 interprets briefs into structured ChartSpec JSON
- Deterministic SVG renderer via drawsvg (no visual hallucinations)
- Roboto/Roboto Condensed fonts base64-embedded in SVG
- FastAPI + HTMX web frontend with live preview
- Conversational refinement: "make lines thicker", "change title", etc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:29:47 -05:00
Dave Porter
f57c844216 Initial commit 2026-03-05 21:25:24 +00:00