Coverage for src / tracekit / reporting / content / minimal.py: 95%

76 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-11 23:04 +0000

1"""Minimal boilerplate content generation. 

2 

3This module eliminates unnecessary static text and focuses on data-driven 

4narrative with compact formatting and automated captions. 

5 

6 

7Example: 

8 >>> from tracekit.reporting.content import generate_compact_text 

9 >>> compact = generate_compact_text(value=2.3e-9, spec=5e-9, unit="s") 

10 >>> print(compact) # "Rise time: 2.3ns (spec <5ns, ✓ 54% margin)" 

11 

12References: 

13""" 

14 

15from __future__ import annotations 

16 

17from dataclasses import dataclass 

18from typing import Any, Literal 

19 

20 

21@dataclass 

22class MinimalContent: 

23 """Minimal boilerplate content generator. 

24 

25 Focuses on data-driven content with minimal filler text. 

26 

27 Attributes: 

28 auto_units: Auto-scale units (2300ns → 2.3μs). 

29 data_first: Show results before methodology. 

30 show_passing: Include passing tests or violations only. 

31 auto_captions: Generate captions from data, not static templates. 

32 

33 References: 

34 REPORT-003: Minimal Boilerplate Content 

35 """ 

36 

37 auto_units: bool = True 

38 data_first: bool = True 

39 show_passing: bool = True 

40 auto_captions: bool = True 

41 

42 

43def generate_compact_text( 

44 value: float, 

45 spec: float | None = None, 

46 unit: str = "", 

47 *, 

48 spec_type: Literal["max", "min"] = "max", 

49 name: str = "", 

50) -> str: 

51 """Generate compact, data-driven text. 

52 

53 Avoids filler text like "The measurement was performed and the result was...". 

54 Instead produces compact format: "Rise time: 2.3ns (spec <5ns, ✓ 54% margin)". 

55 

56 Args: 

57 value: Measured value. 

58 spec: Specification limit (optional). 

59 unit: Unit string. 

60 spec_type: Type of specification (max or min). 

61 name: Measurement name (optional). 

62 

63 Returns: 

64 Compact formatted string. 

65 

66 Example: 

67 >>> generate_compact_text(2.3e-9, 5e-9, "s", name="Rise time") 

68 'Rise time: 2.3ns (spec <5ns, ✓ 54% margin)' 

69 

70 References: 

71 REPORT-003: Minimal Boilerplate Content 

72 """ 

73 from tracekit.reporting.formatting.numbers import format_with_units 

74 

75 # Format value with auto-scaled units 

76 value_str = format_with_units(value, unit) 

77 

78 # Build compact text 

79 parts = [] 

80 if name: 

81 parts.append(f"{name}:") 

82 

83 parts.append(value_str) 

84 

85 # Add spec context if provided 

86 if spec is not None: 

87 spec_str = format_with_units(spec, unit) 

88 spec_symbol = "<" if spec_type == "max" else ">" 

89 

90 # Calculate pass/fail and margin 

91 if spec_type == "max": 

92 passed = value <= spec 

93 margin = (spec - value) / spec * 100 if spec != 0 else 0 

94 else: 

95 passed = value >= spec 

96 margin = (value - spec) / spec * 100 if spec != 0 else 0 

97 

98 status_symbol = "\u2713" if passed else "\u2717" # ✓ or ✗ 

99 spec_part = f"(spec {spec_symbol}{spec_str}, {status_symbol} {margin:.0f}% margin)" 

100 parts.append(spec_part) 

101 

102 return " ".join(parts) 

103 

104 

105def auto_caption( 

106 data_type: str, 

107 data: dict[str, Any], 

108 *, 

109 include_stats: bool = True, 

110) -> str: 

111 """Generate automated captions from data. 

112 

113 Instead of static templates, generates captions based on actual data content. 

114 

115 Args: 

116 data_type: Type of data (measurement, plot, table). 

117 data: Data dictionary. 

118 include_stats: Include statistics in caption. 

119 

120 Returns: 

121 Generated caption string. 

122 

123 Example: 

124 >>> data = {"name": "Rise time", "count": 100, "mean": 2.3e-9} 

125 >>> auto_caption("measurement", data) 

126 'Rise time measurement (n=100, mean=2.3ns)' 

127 

128 References: 

129 REPORT-003: Minimal Boilerplate Content 

130 """ 

131 parts = [] 

132 

133 # Extract key information 

134 name = data.get("name", data_type.title()) 

135 parts.append(name) 

136 

137 # Add data-specific information 

138 if data_type == "measurement" and include_stats: 

139 count = data.get("count") 

140 if count: 

141 parts.append(f"(n={count}") 

142 

143 mean = data.get("mean") 

144 if mean is not None: 

145 from tracekit.reporting.formatting.numbers import format_with_units 

146 

147 unit = data.get("unit", "") 

148 mean_str = format_with_units(mean, unit) 

149 parts[-1] += f", mean={mean_str}" 

150 

151 parts[-1] += ")" 

152 

153 elif data_type == "plot": 

154 plot_type = data.get("type", "plot") 

155 if plot_type != "plot": 

156 parts.append(f"- {plot_type}") 

157 

158 elif data_type == "table": 

159 rows = data.get("rows") 

160 cols = data.get("cols") 

161 if rows and cols: 

162 parts.append(f"({rows}x{cols})") 

163 

164 return " ".join(parts) 

165 

166 

167def remove_filler_text(text: str) -> str: 

168 """Remove common filler phrases from text. 

169 

170 Args: 

171 text: Input text. 

172 

173 Returns: 

174 Text with filler removed. 

175 

176 Example: 

177 >>> text = "The measurement was performed and the result was 2.3ns." 

178 >>> remove_filler_text(text) 

179 'Result: 2.3ns.' 

180 

181 References: 

182 REPORT-003: Minimal Boilerplate Content 

183 """ 

184 # Common filler phrases to remove 

185 filler_phrases = [ 

186 "The measurement was performed and", 

187 "The result was", 

188 "It was found that", 

189 "The analysis shows that", 

190 "It can be seen that", 

191 "As can be observed", 

192 "The data indicates", 

193 "It should be noted that", 

194 ] 

195 

196 result = text 

197 for phrase in filler_phrases: 

198 result = result.replace(phrase, "").strip() 

199 

200 # Clean up extra spaces 

201 while " " in result: 

202 result = result.replace(" ", " ") 

203 

204 # Capitalize first letter after removal 

205 if result and not result[0].isupper(): 

206 result = result[0].upper() + result[1:] 

207 

208 return result 

209 

210 

211def conditional_section( 

212 data: list[Any] | dict[str, Any], 

213 section_title: str, 

214) -> tuple[bool, str]: 

215 """Determine if section should be shown. 

216 

217 Only show sections if data exists (no empty sections). 

218 

219 Args: 

220 data: Section data. 

221 section_title: Section title. 

222 

223 Returns: 

224 Tuple of (should_show, reason). 

225 

226 Example: 

227 >>> should_show, reason = conditional_section([], "Violations") 

228 >>> print(should_show) # False 

229 >>> print(reason) # "No violations found" 

230 

231 References: 

232 REPORT-003: Minimal Boilerplate Content 

233 """ 

234 if isinstance(data, list): 

235 if not data: 

236 return False, f"No {section_title.lower()} found" 

237 return True, f"{len(data)} {section_title.lower()}" 

238 

239 elif isinstance(data, dict): 239 ↛ 246line 239 didn't jump to line 246 because the condition on line 239 was always true

240 if not data or all(not v for v in data.values()): 

241 return False, f"No {section_title.lower()} found" 

242 return True, "" 

243 

244 else: 

245 # For other types, check if truthy 

246 if not data: # type: ignore[unreachable] 

247 return False, f"No {section_title.lower()} found" 

248 return True, "" 

249 

250 

251__all__ = [ 

252 "MinimalContent", 

253 "auto_caption", 

254 "conditional_section", 

255 "generate_compact_text", 

256 "remove_filler_text", 

257]