Coverage for /builds/svesch/cikit/src/cikit/timeline/presets.py: 100%
23 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-09 14:17 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-09 14:17 +0000
1# SPDX-License-Identifier: GPL-3.0-or-later
2"""Timeline presets and styles for cikit charts.
4Presets define complete visual configurations including the pygal chart class,
5style, guides, legend placement, dimensions, and chart-specific behavior.
7Author:
8 Sven Schlender
9Date:
10 2026-09-13
11"""
13from enum import StrEnum
15import pygal
17from .charts import FilledLine
20class TimelinePreset(StrEnum):
21 """Define complete visual presets for timeline charts.
23 Each preset selects both the pygal chart class and matching chart options.
24 Callers may apply additional overrides after selecting a preset.
25 """
27 LINE = "line"
28 LINE_COMPACT = "line_compact"
29 LINE_REPORT = "line_report"
30 LINE_REPORT_FILLED = "line_report_filled"
31 BAR = "bar"
32 BAR_COMPACT = "bar_compact"
33 BAR_REPORT = "bar_report"
34 STACKED_BAR = "stacked_bar"
36 @property
37 def description(self) -> str:
38 """Return the human-readable description of the preset.
40 Returns:
41 Description explaining the intended chart presentation.
42 """
43 return PRESET_CONFIGS[self]["description"]
45 @property
46 def display_name(self) -> str:
47 """Return a display name derived from the preset identifier.
49 Returns:
50 Title-cased preset name suitable for documentation headings.
51 """
52 return self.value.replace("_", " ").title()
55CIKIT_STYLE = pygal.style.Style(
56 colors=(
57 "#1565C0",
58 "#C62828",
59 "#2E7D32",
60 "#6A1B9A",
61 "#00838F",
62 "#AD1457",
63 "#EF6C00",
64 "#37474F",
65 ),
66 stroke_width=2,
67 stroke_width_hover=4,
68 linecap="round",
69 linejoin="round",
70 guide_stroke_color="rgba(0, 0, 0, 0.08)",
71 major_guide_stroke_color="rgba(0, 0, 0, 0.14)",
72 guide_stroke_dasharray="0,10",
73 major_guide_stroke_dasharray="4,10",
74 opacity=".85",
75 opacity_hover=".95",
76)
78CIKIT_FILL_STYLE = pygal.style.Style(
79 colors=(
80 "#1976D2",
81 "#D32F2F",
82 "#388E3C",
83 "#7B1FA2",
84 "#0097A7",
85 "#C2185B",
86 "#F57C00",
87 "#546E7A",
88 ),
89 opacity=".28",
90 opacity_hover=".40",
91 stroke_opacity=".95",
92 stroke_opacity_hover="1",
93 stroke_width="2.5",
94 linecap="round",
95 linejoin="round",
96 guide_stroke_color="rgba(0, 0, 0, 0.08)",
97 major_guide_stroke_color="rgba(0, 0, 0, 0.14)",
98 guide_stroke_dasharray="0,10",
99 major_guide_stroke_dasharray="4,10",
100)
102CIKIT_FILL_CSS = [
103 "file://style.css",
104 "file://graph.css",
105 f"""inline:
106.series:hover .timeline-fill {{
107 fill-opacity: {CIKIT_FILL_STYLE.opacity_hover} !important;
108}}
109""",
110]
112CIKIT_BAR_STYLE = pygal.style.Style(
113 colors=(
114 "#1565C0",
115 "#C62828",
116 "#2E7D32",
117 "#6A1B9A",
118 "#00838F",
119 "#AD1457",
120 "#EF6C00",
121 "#37474F",
122 ),
123 opacity=".85",
124 opacity_hover="1",
125 guide_stroke_color="rgba(0, 0, 0, 0.08)",
126 major_guide_stroke_color="rgba(0, 0, 0, 0.14)",
127 guide_stroke_dasharray="1,10",
128 major_guide_stroke_dasharray="4,10",
129)
132PRESET_CONFIGS = {
133 TimelinePreset.LINE: {
134 "description": (
135 "Default line chart for general timeline data. It emphasizes value changes over "
136 "successive references while keeping the presentation compact and uncluttered."
137 ),
138 "chart_cls": pygal.Line,
139 "options": {
140 "style": CIKIT_STYLE,
141 "show_dots": False,
142 "show_y_guides": True,
143 "show_x_guides": False,
144 "legend_at_bottom": True,
145 "truncate_legend": 40,
146 },
147 },
148 TimelinePreset.LINE_COMPACT: {
149 "description": (
150 "Compact line chart for space-constrained reports or dashboards. The reduced height "
151 "and hidden legend prioritize the trend itself over supporting chart furniture."
152 ),
153 "chart_cls": pygal.Line,
154 "options": {
155 "style": CIKIT_STYLE,
156 "show_dots": False,
157 "show_y_guides": True,
158 "show_x_guides": False,
159 "show_legend": False,
160 "height": 280,
161 "margin": 8,
162 },
163 },
164 TimelinePreset.LINE_REPORT: {
165 "description": (
166 "Report-oriented line chart with horizontal and vertical guides. It provides more "
167 "visual structure for charts embedded in detailed documentation and reports."
168 ),
169 "chart_cls": pygal.Line,
170 "options": {
171 "style": CIKIT_STYLE,
172 "show_dots": False,
173 "show_y_guides": True,
174 "show_x_guides": True,
175 "legend_at_bottom": True,
176 "x_labels_major_every": 5,
177 "truncate_legend": 50,
178 "height": 420,
179 "margin": 16,
180 },
181 },
182 TimelinePreset.LINE_REPORT_FILLED: {
183 "description": (
184 "Filled report line chart that combines clear trend lines with translucent areas. "
185 "It gives multiple series more visual weight while preserving readable boundaries."
186 ),
187 "chart_cls": FilledLine,
188 "options": {
189 "style": CIKIT_FILL_STYLE,
190 "css": CIKIT_FILL_CSS,
191 "fill": True,
192 "show_dots": False,
193 "show_y_guides": True,
194 "show_x_guides": True,
195 "x_labels_major_count": 5,
196 "legend_at_bottom": True,
197 "truncate_legend": 50,
198 "height": 420,
199 "margin": 16,
200 },
201 },
202 TimelinePreset.BAR: {
203 "description": (
204 "Default grouped bar chart for discrete timeline values. It is useful when individual "
205 "measurements should be compared more strongly than the continuous trend between them."
206 ),
207 "chart_cls": pygal.Bar,
208 "options": {
209 "style": CIKIT_BAR_STYLE,
210 "show_y_guides": True,
211 "show_x_guides": False,
212 "legend_at_bottom": True,
213 "truncate_legend": 40,
214 "rounded_bars": 1,
215 "dynamic_print_values": True,
216 "tooltip_border_radius": 5,
217 },
218 },
219 TimelinePreset.BAR_COMPACT: {
220 "description": (
221 "Compact grouped bar chart for small documentation areas. The reduced height and "
222 "hidden legend keep the focus on the relative size of the displayed values."
223 ),
224 "chart_cls": pygal.Bar,
225 "options": {
226 "style": CIKIT_BAR_STYLE,
227 "show_y_guides": True,
228 "show_x_guides": False,
229 "show_legend": False,
230 "height": 280,
231 "margin": 8,
232 "rounded_bars": 1,
233 "dynamic_print_values": True,
234 "tooltip_border_radius": 5,
235 },
236 },
237 TimelinePreset.BAR_REPORT: {
238 "description": (
239 "Report-oriented grouped bar chart with both axis guides and a bottom legend. It is "
240 "intended for detailed comparisons of discrete values across timeline references."
241 ),
242 "chart_cls": pygal.Bar,
243 "options": {
244 "style": CIKIT_BAR_STYLE,
245 "show_y_guides": True,
246 "show_x_guides": True,
247 "legend_at_bottom": True,
248 "truncate_legend": 50,
249 "height": 420,
250 "margin": 16,
251 "rounded_bars": 1,
252 "dynamic_print_values": True,
253 "tooltip_border_radius": 5,
254 },
255 },
256 TimelinePreset.STACKED_BAR: {
257 "description": (
258 "Stacked bar chart for additive series that form a meaningful total. It is especially "
259 "useful for distributions such as percentages or other values with a common reference."
260 ),
261 "chart_cls": pygal.StackedBar,
262 "options": {
263 "style": CIKIT_BAR_STYLE,
264 "show_y_guides": True,
265 "show_x_guides": False,
266 "legend_at_bottom": True,
267 "truncate_legend": 40,
268 "rounded_bars": 1,
269 "dynamic_print_values": True,
270 },
271 },
272}