Coverage for src / tracekit / reporting / content / filtering.py: 100%

60 statements  

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

1"""Smart content filtering for reports. 

2 

3Shows only relevant information based on context, severity, and audience 

4with conditional sections and violation-only modes. 

5 

6 

7References: 

8""" 

9 

10from __future__ import annotations 

11 

12from dataclasses import dataclass 

13from enum import Enum 

14from typing import Any, Literal 

15 

16from tracekit.reporting.formatting.standards import Severity 

17 

18 

19class AudienceType(Enum): 

20 """Audience types for content filtering.""" 

21 

22 EXECUTIVE = "executive" 

23 ENGINEERING = "engineering" 

24 DEBUG = "debug" 

25 REGULATORY = "regulatory" 

26 PRODUCTION = "production" 

27 

28 

29@dataclass 

30class ContentFilter: 

31 """Smart content filtering configuration. 

32 

33 Attributes: 

34 severity_threshold: Minimum severity to show (critical/warning/info). 

35 audience: Target audience type. 

36 show_only: Show only specific content (violations, changes, all). 

37 hide_empty_sections: Hide sections with no data. 

38 relevance_threshold: Minimum relevance score (0.0-1.0). 

39 

40 References: 

41 REPORT-005: Smart Content Filtering 

42 """ 

43 

44 severity_threshold: Severity = Severity.INFO 

45 audience: AudienceType = AudienceType.ENGINEERING 

46 show_only: Literal["all", "violations", "changes"] = "all" 

47 hide_empty_sections: bool = True 

48 relevance_threshold: float = 0.5 

49 

50 

51def filter_by_severity( 

52 items: list[dict[str, Any]], 

53 min_severity: Severity | str, 

54) -> list[dict[str, Any]]: 

55 """Filter items by severity level. 

56 

57 Args: 

58 items: List of items with 'severity' field. 

59 min_severity: Minimum severity to include. 

60 

61 Returns: 

62 Filtered list of items. 

63 

64 Example: 

65 >>> items = [{"name": "test1", "severity": "critical"}, 

66 ... {"name": "test2", "severity": "info"}] 

67 >>> filtered = filter_by_severity(items, "warning") 

68 >>> len(filtered) # Only critical items 

69 1 

70 

71 References: 

72 REPORT-005: Smart Content Filtering 

73 """ 

74 if isinstance(min_severity, str): 

75 min_severity = Severity(min_severity.lower()) 

76 

77 severity_order = { 

78 Severity.INFO: 0, 

79 Severity.WARNING: 1, 

80 Severity.ERROR: 2, 

81 Severity.CRITICAL: 3, 

82 } 

83 

84 min_level = severity_order.get(min_severity, 0) 

85 

86 filtered = [] 

87 for item in items: 

88 item_severity_str = item.get("severity", "info").lower() 

89 try: 

90 item_severity = Severity(item_severity_str) 

91 item_level = severity_order.get(item_severity, 0) 

92 if item_level >= min_level: 

93 filtered.append(item) 

94 except (ValueError, KeyError): 

95 continue 

96 

97 return filtered 

98 

99 

100def filter_by_audience( 

101 content: dict[str, Any], 

102 audience: AudienceType | str, 

103) -> dict[str, Any]: 

104 """Filter content for specific audience. 

105 

106 Args: 

107 content: Content dictionary with various sections. 

108 audience: Target audience type. 

109 

110 Returns: 

111 Filtered content dictionary. 

112 

113 Example: 

114 >>> content = {"methodology": "...", "results": "...", "raw_data": "..."} 

115 >>> filtered = filter_by_audience(content, "executive") 

116 >>> "raw_data" in filtered # False - executives don't need raw data 

117 False 

118 

119 References: 

120 REPORT-005: Smart Content Filtering 

121 """ 

122 if isinstance(audience, str): 

123 audience = AudienceType(audience.lower()) 

124 

125 # Define what each audience sees 

126 audience_sections = { 

127 AudienceType.EXECUTIVE: ["executive_summary", "key_findings", "recommendations"], 

128 AudienceType.ENGINEERING: ["summary", "results", "methodology", "plots"], 

129 AudienceType.DEBUG: ["summary", "results", "methodology", "plots", "raw_data", "logs"], 

130 AudienceType.REGULATORY: ["summary", "compliance", "test_procedures", "standards"], 

131 AudienceType.PRODUCTION: ["summary", "pass_fail", "margin", "yield"], 

132 } 

133 

134 allowed_sections = audience_sections.get(audience, list(content.keys())) 

135 

136 filtered = {} 

137 for key, value in content.items(): 

138 if key in allowed_sections: 

139 filtered[key] = value 

140 

141 return filtered 

142 

143 

144def calculate_relevance_score( 

145 item: dict[str, Any], 

146 context: dict[str, Any] | None = None, 

147) -> float: 

148 """Calculate relevance score for content item. 

149 

150 Args: 

151 item: Content item. 

152 context: Optional context for relevance scoring. 

153 

154 Returns: 

155 Relevance score 0.0-1.0. 

156 

157 References: 

158 REPORT-005: Smart Content Filtering 

159 """ 

160 score = 0.5 # Base score 

161 

162 # Increase score for violations 

163 if item.get("status") == "fail": 

164 score += 0.3 

165 

166 # Increase score for critical severity 

167 severity = item.get("severity", "").lower() 

168 if severity == "critical": 

169 score += 0.3 

170 elif severity == "warning": 

171 score += 0.1 

172 

173 # Increase score for outliers 

174 if item.get("is_outlier"): 

175 score += 0.2 

176 

177 # Increase score for low margins 

178 margin = item.get("margin_pct") 

179 if margin is not None and margin < 20: 

180 score += 0.2 

181 

182 return min(1.0, score) 

183 

184 

185__all__ = [ 

186 "AudienceType", 

187 "ContentFilter", 

188 "calculate_relevance_score", 

189 "filter_by_audience", 

190 "filter_by_severity", 

191]