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

1# SPDX-License-Identifier: GPL-3.0-or-later 

2"""Timeline presets and styles for cikit charts. 

3 

4Presets define complete visual configurations including the pygal chart class, 

5style, guides, legend placement, dimensions, and chart-specific behavior. 

6 

7Author: 

8 Sven Schlender 

9Date: 

10 2026-09-13 

11""" 

12 

13from enum import StrEnum 

14 

15import pygal 

16 

17from .charts import FilledLine 

18 

19 

20class TimelinePreset(StrEnum): 

21 """Define complete visual presets for timeline charts. 

22 

23 Each preset selects both the pygal chart class and matching chart options. 

24 Callers may apply additional overrides after selecting a preset. 

25 """ 

26 

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" 

35 

36 @property 

37 def description(self) -> str: 

38 """Return the human-readable description of the preset. 

39 

40 Returns: 

41 Description explaining the intended chart presentation. 

42 """ 

43 return PRESET_CONFIGS[self]["description"] 

44 

45 @property 

46 def display_name(self) -> str: 

47 """Return a display name derived from the preset identifier. 

48 

49 Returns: 

50 Title-cased preset name suitable for documentation headings. 

51 """ 

52 return self.value.replace("_", " ").title() 

53 

54 

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) 

77 

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) 

101 

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] 

111 

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) 

130 

131 

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}