Skip to content

Commit 03485b8

Browse files
committed
{feat} Documentation optimisations
1 parent 243f42a commit 03485b8

9 files changed

Lines changed: 361 additions & 1 deletion

File tree

CLAUDE.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance for Claude Code (and human developers) working with the py-svg-chart codebase.
4+
5+
## Project Overview
6+
7+
**py-svg-chart** is a Python library for generating SVG charts entirely in Python. It produces clean, standalone SVG output that can be embedded directly in web applications without JavaScript dependencies or post-processing.
8+
9+
**Key differentiator:** Unlike image-based charting libraries or JS-dependent solutions, this generates resolution-independent, customizable SVG markup server-side.
10+
11+
## Quick Start
12+
13+
```bash
14+
pip install pysvgchart
15+
```
16+
17+
```python
18+
import pysvgchart as psc
19+
20+
# Simple donut chart
21+
chart = psc.DonutChart([25, 30, 20, 25])
22+
svg = chart.render()
23+
24+
# Line chart
25+
chart = psc.SimpleLineChart(
26+
x_values=[1, 2, 3, 4, 5],
27+
y_values=[[10, 20, 15, 25, 30]],
28+
y_names=['Sales']
29+
)
30+
chart.add_legend()
31+
svg = chart.render()
32+
```
33+
34+
## Architecture Overview
35+
36+
```
37+
pysvgchart/
38+
├── charts.py # Chart classes (entry points for users)
39+
├── axes.py # Axis management (XAxis, YAxis, CategoryYAxis)
40+
├── series.py # Data series (LineSeries, BarSeries, ScatterSeries, DonutSegment)
41+
├── scales.py # Scale calculations (Linear, Logarithmic, Categorical)
42+
├── legends.py # Legend rendering
43+
├── shapes.py # SVG primitives (Point, Line, Circle, Rect, Text, Group)
44+
├── helpers.py # Utility functions (tick generation, formatting)
45+
├── styles.py # CSS style rendering for hover effects
46+
└── shared.py # Type definitions
47+
```
48+
49+
### Class Hierarchy
50+
51+
```
52+
Chart (ABC)
53+
├── CartesianChart
54+
│ ├── VerticalChart # Y-axis vertical, X-axis horizontal
55+
│ │ ├── LineChart # Line/area charts
56+
│ │ │ ├── SimpleLineChart
57+
│ │ │ ├── BarChart
58+
│ │ │ ├── NormalisedBarChart
59+
│ │ │ └── ScatterChart
60+
│ │ └── (inherits axis setup)
61+
│ └── HorizontalChart # X-axis has values, Y-axis has categories
62+
│ └── HorizontalBarChart
63+
└── DonutChart # Pie/donut (non-Cartesian)
64+
```
65+
66+
### Data Flow
67+
68+
1. **User creates chart** with x_values, y_values, configuration
69+
2. **Axes created** via `x_axis_type`/`y_axis_type` using `scale_maker` functions
70+
3. **Series constructed** via `series_constructor` (converts data to positioned shapes)
71+
4. **Optional additions:** legends, grids, hover modifiers, custom elements
72+
5. **Render:** `chart.render()` or `chart.render_with_all_styles()` produces SVG string
73+
74+
### Key Design Patterns
75+
76+
- **Factory Pattern:** `series_constructor` functions create appropriate Series types
77+
- **Template Method:** Base `Chart` defines `render()`, subclasses implement `get_element_list()`
78+
- **Strategy Pattern:** Pluggable `scale_maker` functions, `label_format` callbacks
79+
- **Composition:** Charts compose axes, series, legends, shapes
80+
81+
## Chart Types
82+
83+
| Chart | Class | Use Case |
84+
|-------|-------|----------|
85+
| Line | `LineChart`, `SimpleLineChart` | Time series, trends |
86+
| Bar (vertical) | `BarChart` | Category comparisons |
87+
| Bar (horizontal) | `HorizontalBarChart` | Long category names |
88+
| Stacked (100%) | `NormalisedBarChart` | Part-of-whole comparisons |
89+
| Scatter | `ScatterChart` | Correlation, distribution |
90+
| Donut/Pie | `DonutChart` | Proportions |
91+
92+
## Common Patterns
93+
94+
### Adding Interactivity (Hover Effects)
95+
96+
```python
97+
def hover_fn(position, x_value, y_value, series_name, styles):
98+
return [psc.Text(x=position.x, y=position.y-10, content=str(y_value),
99+
classes=['psc-hover-data'])]
100+
101+
chart.add_hover_modifier(hover_fn, radius=5)
102+
svg = chart.render_with_all_styles() # Must use this for hovers
103+
```
104+
105+
### Custom Styling
106+
107+
```python
108+
# Direct series style modification
109+
chart.series['Series Name'].styles = {'stroke': 'red', 'stroke-width': '3'}
110+
111+
# Access axis elements
112+
chart.x_axis.tick_lines = [] # Remove tick marks
113+
chart.y_axis.axis_line.styles['stroke'] = '#ccc'
114+
```
115+
116+
### Adding Custom Elements
117+
118+
```python
119+
chart.add_custom_element(psc.Circle(x=100, y=100, radius=5, styles={'fill': 'red'}))
120+
chart.add_custom_element(psc.Text(x=200, y=50, content='Annotation'))
121+
```
122+
123+
## Testing
124+
125+
```bash
126+
# Run all tests
127+
pytest
128+
129+
# Run specific test file
130+
pytest tests/test_pysvgchart.py
131+
132+
# Tests generate SVG files in showcase/ directory
133+
```
134+
135+
The `showcase/` directory contains generated SVG examples from the test suite.
136+
137+
## File Purposes
138+
139+
| File | Purpose |
140+
|------|---------|
141+
| `charts.py` | Main entry points. All public chart classes. Series constructors. |
142+
| `axes.py` | Axis rendering, tick generation, scale positioning |
143+
| `series.py` | Data series types that render as SVG paths/shapes |
144+
| `scales.py` | Numeric scale calculations (min/max, tick intervals) |
145+
| `shapes.py` | Low-level SVG elements (Point, Line, Circle, Text, Group) |
146+
| `legends.py` | Legend components for each chart type |
147+
| `helpers.py` | Utility functions (formatting, element list flattening) |
148+
| `styles.py` | CSS generation for hover effects |
149+
| `shared.py` | Type aliases used across modules |
150+
151+
## Type System
152+
153+
Key type aliases from `shared.py`:
154+
- `number = float | int`
155+
- `numbers_sequence = list[number] | tuple[number, ...]`
156+
- `style_def = dict[str, str]` (SVG style attributes)
157+
158+
## Common Tasks
159+
160+
### Adding a new chart type
161+
162+
1. Choose base class (`VerticalChart`, `HorizontalChart`, or `Chart`)
163+
2. Set class attributes: `x_axis_type`, `y_axis_type`, `series_constructor`, `scale_maker`s
164+
3. Override `add_legend()` if needed
165+
4. Add to `__init__.py` exports
166+
167+
### Modifying axis behavior
168+
169+
Axes are in `axes.py`. Key methods:
170+
- `get_positions()` - Convert data values to pixel positions
171+
- Scale creation via `scale_maker` parameter
172+
173+
### Adding new shapes
174+
175+
Add to `shapes.py`. Inherit from `Element` or `Shape`, implement `get_element_list()` to return SVG strings.
176+
177+
## Dependencies
178+
179+
- Python 3.10+
180+
- No runtime dependencies (standard library only)
181+
- Dev: pytest for testing

pysvgchart/__init__.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,36 @@
1-
"""Top-level package for py-svg-chart"""
1+
"""
2+
pysvgchart - Python SVG Chart Generator
3+
4+
Generate clean, standalone SVG charts in Python for embedding in web applications.
5+
6+
Chart Types:
7+
LineChart, SimpleLineChart - Line plots for time series and trends
8+
BarChart - Vertical bar charts for category comparisons
9+
HorizontalBarChart - Horizontal bars (useful for long category names)
10+
NormalisedBarChart - 100% stacked bars for part-of-whole comparisons
11+
ScatterChart - Scatter plots for correlation/distribution
12+
DonutChart - Pie/donut charts for proportions
13+
14+
Shape Primitives (for custom elements):
15+
Circle, Line, Text
16+
17+
Styling:
18+
hover_style_name - CSS class name for hover elements
19+
render_all_styles() - Generate CSS for hover effects
20+
21+
Quick Start:
22+
>>> import pysvgchart as psc
23+
>>> chart = psc.DonutChart([25, 30, 20, 25], labels=['Q1', 'Q2', 'Q3', 'Q4'])
24+
>>> svg = chart.render()
25+
26+
>>> chart = psc.SimpleLineChart(x_values=[1,2,3], y_values=[[10,20,15]], y_names=['Sales'])
27+
>>> chart.add_legend()
28+
>>> svg = chart.render()
29+
30+
Documentation:
31+
README.rst - Full documentation with examples and API reference
32+
CLAUDE.md - Architecture overview for AI assistants and quick reference
33+
"""
234

335
__author__ = "Alex Rowley"
436
__email__ = ""

pysvgchart/axes.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
"""
2+
Axis classes for chart rendering.
3+
4+
This module handles axis creation, tick generation, and value-to-pixel positioning.
5+
6+
Classes:
7+
Axis: Abstract base class for all axes
8+
XAxis: Horizontal axis (values increase left-to-right)
9+
YAxis: Vertical axis (values increase bottom-to-top, so pixel Y is inverted)
10+
CategoryYAxis: Y-axis for categorical data (preserves order top-to-bottom)
11+
12+
Key responsibilities:
13+
- Scale creation via pluggable scale_maker functions
14+
- Tick line and label positioning
15+
- Grid line storage (populated by chart.add_grids())
16+
- Axis title rendering
17+
18+
The get_positions() method converts data values to pixel coordinates using the axis scale.
19+
"""
120
from __future__ import annotations
221
from abc import abstractmethod
322
from typing import Any, Callable

pysvgchart/charts.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,39 @@
1+
"""
2+
Chart classes - the main entry points for creating SVG charts.
3+
4+
This module contains all chart types available in pysvgchart:
5+
- LineChart / SimpleLineChart: Line plots for time series and trends
6+
- BarChart: Vertical bar charts for category comparisons
7+
- HorizontalBarChart: Horizontal bars (useful for long category names)
8+
- NormalisedBarChart: 100% stacked bars for part-of-whole comparisons
9+
- ScatterChart: Scatter plots for correlation/distribution
10+
- DonutChart: Pie/donut charts for proportions
11+
12+
Class Hierarchy:
13+
Chart (ABC)
14+
├── CartesianChart
15+
│ ├── VerticalChart -> LineChart, BarChart, ScatterChart, NormalisedBarChart
16+
│ └── HorizontalChart -> HorizontalBarChart
17+
└── DonutChart
18+
19+
Typical usage:
20+
import pysvgchart as psc
21+
22+
chart = psc.SimpleLineChart(
23+
x_values=[1, 2, 3],
24+
y_values=[[10, 20, 15]],
25+
y_names=['Sales']
26+
)
27+
chart.add_legend()
28+
svg = chart.render()
29+
30+
Series constructors (internal):
31+
- line_series_constructor: Creates LineSeries from data
32+
- bar_series_constructor: Creates vertical BarSeries
33+
- horizontal_bar_series_constructor: Creates horizontal BarSeries
34+
- normalised_bar_series_constructor: Creates 100% stacked bars
35+
- scatter_series_constructor: Creates ScatterSeries
36+
"""
137
from abc import ABC, abstractmethod
238
from collections.abc import Callable
339
from itertools import zip_longest, cycle

pysvgchart/helpers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
"""
2+
Utility functions for chart rendering.
3+
4+
Functions:
5+
default_format(value): Format numbers with thousand separators
6+
collapse_element_list(*lists): Flatten nested element lists to SVG strings
7+
get_numeric_ticks(values, max_ticks, ...): Calculate nice tick values for numeric axes
8+
get_logarithmic_ticks(values, max_ticks, ...): Calculate tick values for log scales
9+
get_date_or_time_ticks(dates, max_ticks, ...): Calculate ticks for date/datetime ranges
10+
11+
The tick functions implement "nice number" algorithms to find visually appealing
12+
tick intervals (e.g., 1, 2, 5, 10 rather than 3, 7, 11).
13+
"""
114
import math
215
import datetime as dt
316

pysvgchart/legends.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
"""
2+
Legend components for different chart types.
3+
4+
Classes:
5+
Legend: Abstract base class
6+
LineLegend: For line charts (short line segment + text label)
7+
BarLegend: For bar charts (colored rectangle + text label)
8+
ScatterLegend: For scatter charts (point shape + text label)
9+
DonutLegend: For donut/pie charts (colored circle + text label)
10+
11+
Legends are added via chart.add_legend(x, y) and rendered as part of get_element_list().
12+
"""
113
from abc import abstractmethod
214

315
from .helpers import collapse_element_list

pysvgchart/scales.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
"""
2+
Scale classes for mapping data values to chart positions.
3+
4+
Scales determine:
5+
- The min/max range of an axis
6+
- Tick mark positions (nice round numbers)
7+
- How to convert a data value to a 0-1 fraction of the axis
8+
9+
Classes:
10+
Scale: Abstract base class
11+
MappedLinearScale: Linear scale with optional value mapping (base for Linear/Log)
12+
LinearScale: Standard linear scale for numbers, dates, datetimes
13+
LogarithmicScale: Logarithmic scale (log10 mapping)
14+
MappingScale: Categorical scale for non-numeric values
15+
16+
Factory functions:
17+
make_linear_scale(): Creates LinearScale or MappingScale based on value types
18+
make_logarithmic_scale(): Creates LogarithmicScale for numeric data
19+
make_categories_scale(): Creates MappingScale for categorical data
20+
21+
The value_to_fraction() method is the key interface - converts a data value to
22+
a proportion (0.0 to 1.0) along the axis, which the Axis then converts to pixels.
23+
"""
124
from __future__ import annotations
225

326
from abc import ABC, abstractmethod

pysvgchart/series.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
"""
2+
Data series classes that render chart data as SVG elements.
3+
4+
Each series type converts positioned data points into SVG markup:
5+
6+
Classes:
7+
Series: Abstract base class for all data series
8+
LineSeries: Renders as SVG path (connected line through points)
9+
BarSeries: Renders as vertical rectangles
10+
HorizontalBarSeries: Renders as horizontal rectangles
11+
ScatterSeries: Renders as point shapes (circles by default)
12+
DonutSegment: Renders as arc path for pie/donut charts
13+
14+
Key pattern:
15+
- Series store both pixel positions (points) and original data values
16+
- pv_generator property yields (point, x_value, y_value) tuples for hover modifiers
17+
- get_element_list() returns SVG string fragments
18+
- custom_elements list allows adding hover markers and annotations
19+
"""
120
from typing import Callable
221
import math
322

pysvgchart/shapes.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
"""
2+
Low-level SVG shape primitives.
3+
4+
These are the building blocks for all chart elements. Each shape knows how to
5+
render itself as SVG markup via get_element_list().
6+
7+
Classes:
8+
Point: 2D coordinate dataclass (x, y)
9+
Element: Abstract base class with styles and CSS classes
10+
Shape: Abstract positioned element (has position: Point)
11+
Line: SVG <line> element
12+
Circle: SVG <circle> element
13+
Rect: SVG <rect> element
14+
Text: SVG <text> element
15+
Group: SVG <g> container for multiple elements
16+
17+
Common patterns:
18+
- All shapes have styles (dict of SVG attributes) and classes (CSS class names)
19+
- attributes property formats styles/classes for SVG attribute string
20+
- Shapes can be added to charts via chart.add_custom_element(shape)
21+
22+
Example:
23+
text = Text(x=100, y=50, content='Label', styles={'fill': 'red'})
24+
circle = Circle(x=200, y=100, radius=5, classes=['highlight'])
25+
"""
126
from abc import ABC, abstractmethod
227
from dataclasses import dataclass
328

0 commit comments

Comments
 (0)