1use super::analysis::{DetailedScoreBreakdown, RecommendationAnalysisReport};
7use std::fs::File;
8use std::io::Write;
9
10pub fn export_report_json(
14 report: &RecommendationAnalysisReport,
15 filename: &str,
16) -> std::io::Result<()> {
17 let json = serde_json::to_string_pretty(report)?;
18 let mut file = File::create(filename)?;
19 file.write_all(json.as_bytes())?;
20 Ok(())
21}
22
23pub fn export_report_txt(
25 report: &RecommendationAnalysisReport,
26 filename: &str,
27) -> std::io::Result<()> {
28 let mut file = File::create(filename)?;
29
30 write_header(&mut file)?;
31 write_metadata(&mut file, report)?;
32 write_configuration(&mut file, report)?;
33 write_statistics(&mut file, report)?;
34 write_user_profile(&mut file, report)?;
35 write_tag_influence(&mut file, report)?;
36 write_genre_influence(&mut file, report)?;
37 write_reason_distribution(&mut file, report)?;
38 write_top_games(&mut file, report)?;
39
40 Ok(())
41}
42
43pub fn export_games_csv(games: &[DetailedScoreBreakdown], filename: &str) -> std::io::Result<()> {
45 let mut file = File::create(filename)?;
46
47 writeln!(
49 file,
50 "Rank,Title,Final Score,Affinity,Context,Diversity,Genre,Tag,Series,CB Total,CF,Age Penalty,Reason"
51 )?;
52
53 for game in games {
55 writeln!(
56 file,
57 "{},{},{:.4},{:.2},{:.2},{:.2},{:.2},{:.2},{:.2},{:.2},{:.2},{:.4},{}",
58 game.final_rank,
59 game.game_title.replace(",", ";"),
60 game.final_score,
61 game.affinity_score,
62 game.context_score,
63 game.diversity_score,
64 game.genre_score,
65 game.tag_score,
66 game.series_score,
67 game.total_cb,
68 game.total_cf,
69 game.age_penalty,
70 game.reason_label.replace(",", ";")
71 )?;
72 }
73
74 Ok(())
75}
76
77fn write_header(file: &mut File) -> std::io::Result<()> {
80 writeln!(
81 file,
82 "================================================================================"
83 )?;
84 writeln!(
85 file,
86 "RELATÓRIO DE ANÁLISE DE RECOMENDAÇÕES - PLAYLITE v4.0"
87 )?;
88 writeln!(
89 file,
90 "================================================================================"
91 )?;
92 writeln!(file)?;
93 Ok(())
94}
95
96fn write_metadata(file: &mut File, report: &RecommendationAnalysisReport) -> std::io::Result<()> {
97 writeln!(file, "Timestamp: {}", report.timestamp)?;
98 writeln!(file, "Total de jogos analisados: {}", report.total_games)?;
99 writeln!(file)?;
100 Ok(())
101}
102
103fn write_configuration(
104 file: &mut File,
105 report: &RecommendationAnalysisReport,
106) -> std::io::Result<()> {
107 writeln!(file, "📊 CONFIGURAÇÕES")?;
108 writeln!(
109 file,
110 "--------------------------------------------------------------------------------"
111 )?;
112 writeln!(file, "Content Weight: {:.2}", report.config.content_weight)?;
113 writeln!(
114 file,
115 "Collaborative Weight: {:.2}",
116 report.config.collaborative_weight
117 )?;
118 writeln!(file, "Age Decay: {:.2}", report.config.age_decay)?;
119 writeln!(file, "Favor Series: {}", report.config.favor_series)?;
120 writeln!(
121 file,
122 "Filter Adult Content: {}",
123 report.user_settings.filter_adult_content
124 )?;
125 writeln!(file, "Series Limit: {}", report.user_settings.series_limit)?;
126 writeln!(file)?;
127 Ok(())
128}
129
130fn write_statistics(file: &mut File, report: &RecommendationAnalysisReport) -> std::io::Result<()> {
131 writeln!(file, "📈 ESTATÍSTICAS GERAIS")?;
132 writeln!(
133 file,
134 "--------------------------------------------------------------------------------"
135 )?;
136 writeln!(
137 file,
138 "Score Final Médio: {:.4}",
139 report.stats.avg_final_score
140 )?;
141 writeln!(
142 file,
143 "Score Final Mediana: {:.4}",
144 report.stats.median_final_score
145 )?;
146 writeln!(
147 file,
148 "Score Final Máximo: {:.4}",
149 report.stats.max_final_score
150 )?;
151 writeln!(
152 file,
153 "Score Final Mínimo: {:.4}",
154 report.stats.min_final_score
155 )?;
156 writeln!(file)?;
157 writeln!(
158 file,
159 "CB Médio: {:.2} | CF Médio: {:.2}",
160 report.stats.avg_cb_score, report.stats.avg_cf_score
161 )?;
162 writeln!(file)?;
163 writeln!(
164 file,
165 "Affinity Médio: {:.2} ({:.1}%)",
166 report.stats.avg_affinity_score, report.stats.affinity_proportion
167 )?;
168 writeln!(
169 file,
170 "Context Médio: {:.2} ({:.1}%)",
171 report.stats.avg_context_score, report.stats.context_proportion
172 )?;
173 writeln!(
174 file,
175 "Diversity Médio: {:.2} ({:.1}%)",
176 report.stats.avg_diversity_score, report.stats.diversity_proportion
177 )?;
178 writeln!(file)?;
179 writeln!(
180 file,
181 "Genre Score Médio: {:.2} ({:.1}%)",
182 report.stats.avg_genre_score, report.stats.genre_proportion
183 )?;
184 writeln!(file, "Tag Score Médio: {:.2}", report.stats.avg_tag_score)?;
185 writeln!(
186 file,
187 "Series Score Médio: {:.2}",
188 report.stats.avg_series_score
189 )?;
190 writeln!(
191 file,
192 "Age Penalty Médio: {:.4}",
193 report.stats.avg_age_penalty
194 )?;
195 writeln!(file)?;
196 Ok(())
197}
198
199fn write_user_profile(
200 file: &mut File,
201 report: &RecommendationAnalysisReport,
202) -> std::io::Result<()> {
203 writeln!(file, "👤 PERFIL DO USUÁRIO")?;
204 writeln!(
205 file,
206 "--------------------------------------------------------------------------------"
207 )?;
208 writeln!(
209 file,
210 "Total de gêneros no perfil: {}",
211 report.profile_stats.total_genres
212 )?;
213 writeln!(
214 file,
215 "Total de tags no perfil: {}",
216 report.profile_stats.total_tags
217 )?;
218 writeln!(
219 file,
220 "Total de séries no perfil: {}",
221 report.profile_stats.total_series
222 )?;
223 writeln!(file)?;
224
225 if !report.profile_stats.top_genres.is_empty() {
226 writeln!(file, "Top 10 Gêneros no Perfil:")?;
227 for (idx, (genre, weight)) in report.profile_stats.top_genres.iter().enumerate() {
228 writeln!(file, " {}. {} - peso: {:.2}", idx + 1, genre, weight)?;
229 }
230 writeln!(file)?;
231 }
232
233 if !report.profile_stats.top_tags.is_empty() {
234 writeln!(file, "Top 20 Tags no Perfil:")?;
235 for (idx, (slug, key, weight)) in report.profile_stats.top_tags.iter().enumerate() {
236 writeln!(
237 file,
238 " {}. {} ({}) - peso: {:.2}",
239 idx + 1,
240 slug,
241 key,
242 weight
243 )?;
244 }
245 writeln!(file)?;
246 }
247 Ok(())
248}
249
250fn write_tag_influence(
251 file: &mut File,
252 report: &RecommendationAnalysisReport,
253) -> std::io::Result<()> {
254 writeln!(file, "🏷️ TOP TAGS POR INFLUÊNCIA")?;
255 writeln!(
256 file,
257 "--------------------------------------------------------------------------------"
258 )?;
259 for (idx, (_, tag)) in report.tag_influence.iter().take(20).enumerate() {
260 writeln!(file, "{}. {} ({})", idx + 1, tag.tag_name, tag.category)?;
261 writeln!(
262 file,
263 " Jogos: {} | Avg: {:.2} | Max: {:.2} | Razão principal: {} vezes",
264 tag.games_count, tag.avg_contribution, tag.max_contribution, tag.times_as_reason
265 )?;
266 }
267 writeln!(file)?;
268 Ok(())
269}
270
271fn write_genre_influence(
272 file: &mut File,
273 report: &RecommendationAnalysisReport,
274) -> std::io::Result<()> {
275 writeln!(file, "🎮 TOP GÊNEROS POR INFLUÊNCIA")?;
276 writeln!(
277 file,
278 "--------------------------------------------------------------------------------"
279 )?;
280 for (idx, (name, genre)) in report.genre_influence.iter().take(10).enumerate() {
281 writeln!(file, "{}. {}", idx + 1, name)?;
282 writeln!(
283 file,
284 " Jogos: {} | Avg: {:.2} | Max: {:.2} | Razão principal: {} vezes",
285 genre.games_count,
286 genre.avg_contribution,
287 genre.max_contribution,
288 genre.times_as_reason
289 )?;
290 }
291 writeln!(file)?;
292 Ok(())
293}
294
295fn write_reason_distribution(
296 file: &mut File,
297 report: &RecommendationAnalysisReport,
298) -> std::io::Result<()> {
299 writeln!(file, "📊 DISTRIBUIÇÃO DE RAZÕES")?;
300 writeln!(
301 file,
302 "--------------------------------------------------------------------------------"
303 )?;
304 let mut reasons: Vec<_> = report.reason_distribution.iter().collect();
305 reasons.sort_by(|a, b| b.1.cmp(a.1));
306 for (reason, count) in reasons {
307 let percentage = (*count as f32 / report.total_games as f32) * 100.0;
308 writeln!(file, "{}: {} ({:.1}%)", reason, count, percentage)?;
309 }
310 writeln!(file)?;
311 Ok(())
312}
313
314fn write_top_games(file: &mut File, report: &RecommendationAnalysisReport) -> std::io::Result<()> {
315 writeln!(file, "🎯 TOP 50 JOGOS RECOMENDADOS")?;
316 writeln!(
317 file,
318 "================================================================================"
319 )?;
320 for game in report.games.iter().take(50) {
321 writeln!(file)?;
322 writeln!(file, "#{} - {}", game.final_rank, game.game_title)?;
323 writeln!(
324 file,
325 " Score Final: {:.4} | CB: {:.2} | CF: {:.2}",
326 game.final_score, game.total_cb, game.total_cf
327 )?;
328 writeln!(
329 file,
330 " Affinity: {:.2} | Context: {:.2} | Diversity: {:.2}",
331 game.affinity_score, game.context_score, game.diversity_score
332 )?;
333 writeln!(
334 file,
335 " Genre: {:.2} | Tag: {:.2} | Series: {:.2} | Age Penalty: {:.4}",
336 game.genre_score, game.tag_score, game.series_score, game.age_penalty
337 )?;
338 writeln!(file, " Razão: {}", game.reason_label)?;
339
340 if !game.top_genres.is_empty() {
341 write!(file, " Top Gêneros: ")?;
342 for (i, (genre, score)) in game.top_genres.iter().enumerate() {
343 if i > 0 {
344 write!(file, ", ")?;
345 }
346 write!(file, "{} ({:.1})", genre, score)?;
347 }
348 writeln!(file)?;
349 }
350
351 if !game.top_affinity_tags.is_empty() {
352 write!(file, " Top Tags (Affinity): ")?;
353 for (i, (tag, score)) in game.top_affinity_tags.iter().take(5).enumerate() {
354 if i > 0 {
355 write!(file, ", ")?;
356 }
357 write!(file, "{} ({:.1})", tag, score)?;
358 }
359 writeln!(file)?;
360 }
361 writeln!(file)?;
362 }
363
364 Ok(())
365}