Coverage for session_buddy / utils / format_utils.py: 24.81%

97 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-04 00:43 -0800

1#!/usr/bin/env python3 

2"""Formatting and output utilities for session management. 

3 

4This module provides text formatting and display functionality following 

5crackerjack architecture patterns with single responsibility principle. 

6""" 

7 

8from __future__ import annotations 

9 

10from typing import Any 

11 

12 

13def _format_statistics_header(user_id: str) -> list[str]: 

14 """Format the header section for interruption statistics.""" 

15 return [ 

16 f"📊 **Interruption Statistics for {user_id}**", 

17 "", 

18 ] 

19 

20 

21def _format_session_statistics(sessions: dict[str, Any]) -> list[str]: 

22 """Format session-related statistics.""" 

23 if not sessions: 

24 return ["📝 No session data available"] 

25 

26 return [ 

27 "**Session Activity:**", 

28 f"• Total sessions: {sessions.get('total', 0)}", 

29 f"• Average duration: {sessions.get('avg_duration_minutes', 0):.1f} minutes", 

30 f"• Longest session: {sessions.get('max_duration_minutes', 0):.1f} minutes", 

31 "", 

32 ] 

33 

34 

35def _format_interruption_type_details(by_type: list[Any]) -> list[str]: 

36 """Format interruption type breakdown.""" 

37 if not by_type: 

38 return [] 

39 

40 lines = ["**Interruption Types:**"] 

41 for item in by_type[:5]: # Show top 5 

42 lines.append(f"{item['type']}: {item['count']} occurrences") 

43 

44 lines.append("") 

45 return lines 

46 

47 

48def _format_interruption_statistics(interruptions: dict[str, Any]) -> list[str]: 

49 """Format interruption-related statistics.""" 

50 if not interruptions: 

51 return ["🚫 No interruption data available"] 

52 

53 lines = [ 

54 "**Interruption Patterns:**", 

55 f"• Total interruptions: {interruptions.get('total', 0)}", 

56 f"• Average per session: {interruptions.get('avg_per_session', 0):.1f}", 

57 f"• Most active hour: {interruptions.get('peak_hour', 'Unknown')}", 

58 "", 

59 ] 

60 

61 # Add type breakdown if available 

62 if "by_type" in interruptions: 

63 lines.extend(_format_interruption_type_details(interruptions["by_type"])) 

64 

65 return lines 

66 

67 

68def _format_snapshot_statistics(snapshots: dict[str, Any]) -> list[str]: 

69 """Format snapshot-related statistics.""" 

70 if not snapshots: 

71 return ["💾 No snapshot data available"] 

72 

73 return [ 

74 "**Context Snapshots:**", 

75 f"• Total snapshots: {snapshots.get('total', 0)}", 

76 f"• Successful restores: {snapshots.get('successful_restores', 0)}", 

77 f"• Average snapshot size: {snapshots.get('avg_size_kb', 0):.1f} KB", 

78 "", 

79 ] 

80 

81 

82def _calculate_efficiency_rates( 

83 sessions: dict[str, Any], 

84 interruptions: dict[str, Any], 

85 snapshots: dict[str, Any], 

86) -> dict[str, float]: 

87 """Calculate efficiency metrics from statistics.""" 

88 efficiency = { 

89 "interruption_rate": 0.0, 

90 "recovery_rate": 0.0, 

91 "productivity_score": 0.0, 

92 } 

93 

94 if sessions.get("total", 0) > 0: 

95 efficiency["interruption_rate"] = ( 

96 interruptions.get("total", 0) / sessions["total"] 

97 ) 

98 

99 if snapshots.get("total", 0) > 0: 

100 efficiency["recovery_rate"] = ( 

101 snapshots.get("successful_restores", 0) / snapshots["total"] 

102 ) 

103 

104 # Simple productivity score (inverse of interruption rate, scaled) 

105 efficiency["productivity_score"] = max( 

106 0, 

107 100 - (efficiency["interruption_rate"] * 20), 

108 ) 

109 

110 return efficiency 

111 

112 

113def _format_efficiency_metrics( 

114 sessions: dict[str, Any], 

115 interruptions: dict[str, Any], 

116 snapshots: dict[str, Any], 

117) -> list[str]: 

118 """Format efficiency and productivity metrics.""" 

119 efficiency = _calculate_efficiency_rates(sessions, interruptions, snapshots) 

120 

121 return [ 

122 "**Efficiency Metrics:**", 

123 f"• Interruption rate: {efficiency['interruption_rate']:.2f} per session", 

124 f"• Context recovery rate: {efficiency['recovery_rate']:.1%}", 

125 f"• Productivity score: {efficiency['productivity_score']:.1f}/100", 

126 "", 

127 ] 

128 

129 

130def _has_statistics_data( 

131 sessions: dict[str, Any], 

132 interruptions: dict[str, Any], 

133 snapshots: dict[str, Any], 

134) -> bool: 

135 """Check if any meaningful statistics data exists.""" 

136 return bool( 

137 sessions.get("total", 0) > 0 

138 or interruptions.get("total", 0) > 0 

139 or snapshots.get("total", 0) > 0, 

140 ) 

141 

142 

143def _format_no_data_message(user_id: str) -> list[str]: 

144 """Format message when no statistics data is available.""" 

145 return [ 

146 f"📊 **No Statistics Available for {user_id}**", 

147 "", 

148 "No interruption monitoring data found. To start collecting statistics:", 

149 "• Use the start_interruption_monitoring tool", 

150 "• Work on development tasks with file monitoring enabled", 

151 "• Create session contexts to track productivity patterns", 

152 "", 

153 "Statistics will be available after some monitored activity.", 

154 ] 

155 

156 

157def _build_search_header( 

158 query: str, 

159 total_found: int, 

160 chunk_info: dict[str, Any] | None = None, 

161) -> list[str]: 

162 """Build formatted header for search results.""" 

163 header = [f"🔍 **Search Results for: '{query}'**", ""] 

164 

165 if chunk_info: 

166 current = chunk_info.get("current_chunk", 1) 

167 total = chunk_info.get("total_chunks", 1) 

168 header.extend( 

169 [ 

170 f"📊 Found {total_found} results (Page {current}/{total})", 

171 "", 

172 ], 

173 ) 

174 else: 

175 header.extend( 

176 [ 

177 f"📊 Found {total_found} results", 

178 "", 

179 ], 

180 ) 

181 

182 return header 

183 

184 

185def _format_search_results(results: list[Any]) -> list[str]: 

186 """Format search results for display.""" 

187 if not results: 187 ↛ 188line 187 didn't jump to line 188 because the condition on line 187 was never true

188 return ["No results found"] 

189 

190 formatted = [] 

191 

192 for i, result in enumerate(results, 1): 

193 content = result.get("content", "").strip() 

194 project = result.get("project", "Unknown") 

195 timestamp = result.get("timestamp", "") 

196 

197 # Truncate content for display 

198 if len(content) > 300: 

199 content = content[:297] + "..." 

200 

201 formatted.extend( 

202 [ 

203 f"**{i}. [{project}]** {timestamp}", 

204 f" {content}", 

205 "", 

206 ], 

207 ) 

208 

209 return formatted 

210 

211 

212def _format_monitoring_status(quality_data: dict[str, Any]) -> list[str]: 

213 """Format current monitoring status information.""" 

214 lines = [ 

215 "📊 **Current Monitoring Status**", 

216 "", 

217 ] 

218 

219 if quality_data.get("monitoring_active"): 

220 lines.extend( 

221 [ 

222 "✅ Quality monitoring is active", 

223 f"• Last check: {quality_data.get('last_check', 'Unknown')}", 

224 f"• Checks performed: {quality_data.get('total_checks', 0)}", 

225 ], 

226 ) 

227 else: 

228 lines.extend( 

229 [ 

230 "⏸️ Quality monitoring is inactive", 

231 "• Use quality_monitor tool to start monitoring", 

232 ], 

233 ) 

234 

235 lines.append("") 

236 return lines 

237 

238 

239def _format_quality_trend(quality_data: dict[str, Any]) -> list[str]: 

240 """Format quality trend information.""" 

241 trend = quality_data.get("trend", {}) 

242 if not trend: 

243 return ["📈 No trend data available"] 

244 

245 return [ 

246 "📈 **Quality Trend Analysis**", 

247 "", 

248 f"• Current quality score: {trend.get('current_score', 0)}/100", 

249 f"• Trend direction: {trend.get('direction', 'Unknown')}", 

250 f"• Change from last check: {trend.get('change', 0):+.1f} points", 

251 "", 

252 ] 

253 

254 

255def _format_quality_alerts(quality_data: dict[str, Any]) -> list[str]: 

256 """Format quality alerts and warnings.""" 

257 alerts = quality_data.get("alerts", []) 

258 if not alerts: 

259 return ["✅ No quality alerts"] 

260 

261 lines = [ 

262 "🚨 **Quality Alerts**", 

263 "", 

264 ] 

265 

266 for alert in alerts: 

267 severity = alert.get("severity", "info") 

268 message = alert.get("message", "") 

269 icon = {"high": "🔴", "medium": "🟡", "low": "🔵"}.get(severity, "ℹ️") 

270 lines.append(f"{icon} {message}") 

271 

272 lines.append("") 

273 return lines 

274 

275 

276def _format_proactive_recommendations(quality_data: dict[str, Any]) -> list[str]: 

277 """Format proactive quality improvement recommendations.""" 

278 recommendations = quality_data.get("recommendations", []) 

279 if not recommendations: 

280 return ["💡 No recommendations at this time"] 

281 

282 lines = [ 

283 "💡 **Proactive Recommendations**", 

284 "", 

285 ] 

286 

287 for i, rec in enumerate(recommendations, 1): 

288 lines.append(f"{i}. {rec}") 

289 

290 lines.append("") 

291 return lines 

292 

293 

294def _format_monitor_usage_guidance() -> list[str]: 

295 """Format usage guidance for quality monitoring.""" 

296 return [ 

297 "📖 **Usage Guidance**", 

298 "", 

299 "• Run quality_monitor periodically to track project health", 

300 "• Monitor alerts for early warning of quality issues", 

301 "• Follow recommendations to maintain code quality", 

302 "• Use with Crackerjack integration for best results", 

303 "", 

304 "Quality monitoring helps maintain consistent development standards.", 

305 ]