Skip to main content

lawkit_python/subcommands/
integration_common.rs

1use crate::colors;
2use crate::common_options::{get_optimized_reader, setup_automatic_optimization_config};
3use clap::ArgMatches;
4use lawkit_core::common::input::parse_text_input;
5use lawkit_core::common::output::OutputConfig;
6use lawkit_core::error::{BenfError, Result};
7use lawkit_core::laws::integration::IntegrationResult;
8use lawkit_core::{IntegrationData, LawkitResult};
9use std::io::Write;
10
11#[allow(dead_code)]
12pub fn print_integration_header(matches: &ArgMatches) {
13    let no_color = matches.get_flag("no-color");
14
15    if no_color {
16        println!("LAWKIT INTEGRATED ANALYSIS");
17        println!("==========================");
18    } else {
19        println!("{}", colors::bold("LAWKIT INTEGRATED ANALYSIS"));
20        println!("{}", colors::bold("=========================="));
21    }
22    println!();
23}
24
25#[allow(dead_code)]
26pub fn print_integration_summary(data: &IntegrationData, no_color: bool) {
27    // Analysis summary
28    println!("{}", data.analysis_summary);
29    println!();
30
31    // Laws analyzed
32    if no_color {
33        println!("LAWS ANALYZED");
34        println!("-------------");
35    } else {
36        println!("{}", colors::bold("LAWS ANALYZED"));
37        println!("{}", colors::bold("-------------"));
38    }
39
40    for law in &data.laws_analyzed {
41        println!("✓ {law}");
42    }
43    println!();
44
45    // Overall risk
46    let risk_display = if no_color {
47        format!("Overall Risk Level: {}", data.overall_risk)
48    } else {
49        match data.overall_risk.as_str() {
50            "HIGH" => format!("Overall Risk Level: {}", colors::red(&data.overall_risk)),
51            "MEDIUM" => format!("Overall Risk Level: {}", colors::yellow(&data.overall_risk)),
52            _ => format!("Overall Risk Level: {}", colors::green(&data.overall_risk)),
53        }
54    };
55    println!("{risk_display}");
56    println!();
57
58    // Conflicting results if any
59    if !data.conflicting_results.is_empty() {
60        if no_color {
61            println!("CONFLICTING RESULTS");
62            println!("-------------------");
63        } else {
64            println!("{}", colors::bold("CONFLICTING RESULTS"));
65            println!("{}", colors::bold("-------------------"));
66        }
67
68        for conflict in &data.conflicting_results {
69            let msg = format!("⚠ {conflict}");
70            println!("{}", if no_color { msg } else { colors::yellow(&msg) });
71        }
72        println!();
73    }
74
75    // Recommendations
76    if !data.recommendations.is_empty() {
77        if no_color {
78            println!("RECOMMENDATIONS");
79            println!("---------------");
80        } else {
81            println!("{}", colors::bold("RECOMMENDATIONS"));
82            println!("{}", colors::bold("---------------"));
83        }
84
85        for recommendation in &data.recommendations {
86            println!("• {recommendation}");
87        }
88        println!();
89    }
90}
91
92#[allow(dead_code)]
93pub fn print_individual_results(results: &[LawkitResult], matches: &ArgMatches) {
94    let no_color = matches.get_flag("no-color");
95    let verbose = matches.get_flag("verbose");
96
97    for result in results {
98        match result {
99            LawkitResult::BenfordAnalysis(name, data) => {
100                println!();
101                if no_color {
102                    println!("--- {} ---", name.to_uppercase());
103                } else {
104                    println!("--- {} ---", colors::cyan(&name.to_uppercase()));
105                }
106                println!("Risk Level: {}", data.risk_level);
107                if verbose {
108                    println!(
109                        "Chi-square: {:.4}, P-value: {:.4}, MAD: {:.4}",
110                        data.chi_square, data.p_value, data.mad
111                    );
112                }
113            }
114            LawkitResult::ParetoAnalysis(name, data) => {
115                println!();
116                if no_color {
117                    println!("--- {} ---", name.to_uppercase());
118                } else {
119                    println!("--- {} ---", colors::cyan(&name.to_uppercase()));
120                }
121                println!("Risk Level: {}", data.risk_level);
122                if verbose {
123                    println!(
124                        "Top 20% contribution: {:.1}%, Pareto ratio: {:.2}",
125                        data.top_20_percent_contribution, data.pareto_ratio
126                    );
127                }
128            }
129            LawkitResult::ZipfAnalysis(name, data) => {
130                println!();
131                if no_color {
132                    println!("--- {} ---", name.to_uppercase());
133                } else {
134                    println!("--- {} ---", colors::cyan(&name.to_uppercase()));
135                }
136                println!("Risk Level: {}", data.risk_level);
137                if verbose {
138                    println!(
139                        "Zipf coefficient: {:.3}, Correlation: {:.3}",
140                        data.zipf_coefficient, data.correlation_coefficient
141                    );
142                }
143            }
144            LawkitResult::NormalAnalysis(name, data) => {
145                println!();
146                if no_color {
147                    println!("--- {} ---", name.to_uppercase());
148                } else {
149                    println!("--- {} ---", colors::cyan(&name.to_uppercase()));
150                }
151                println!("Risk Level: {}", data.risk_level);
152                if verbose {
153                    println!(
154                        "Mean: {:.3}, Std Dev: {:.3}, Skewness: {:.3}, Kurtosis: {:.3}",
155                        data.mean, data.std_dev, data.skewness, data.kurtosis
156                    );
157                }
158            }
159            LawkitResult::PoissonAnalysis(name, data) => {
160                println!();
161                if no_color {
162                    println!("--- {} ---", name.to_uppercase());
163                } else {
164                    println!("--- {} ---", colors::cyan(&name.to_uppercase()));
165                }
166                println!("Risk Level: {}", data.risk_level);
167                if verbose {
168                    println!(
169                        "Lambda: {:.3}, Variance ratio: {:.3}, P-value: {:.4}",
170                        data.lambda, data.variance_ratio, data.poisson_test_p
171                    );
172                }
173            }
174            _ => {} // Skip other result types for now
175        }
176    }
177}
178
179pub fn get_dataset_name(matches: &ArgMatches) -> String {
180    matches
181        .get_one::<String>("input")
182        .map(|s| s.to_string())
183        .unwrap_or_else(|| "stdin".to_string())
184}
185
186pub fn get_numbers_from_input(matches: &ArgMatches) -> Result<Vec<f64>> {
187    let (_parallel_config, _memory_config) = setup_automatic_optimization_config();
188
189    let buffer = if let Some(input) = matches.get_one::<String>("input") {
190        if input == "-" {
191            get_optimized_reader(None)
192        } else {
193            get_optimized_reader(Some(input))
194        }
195    } else {
196        get_optimized_reader(None)
197    };
198
199    let data = buffer.map_err(|e| BenfError::ParseError(e.to_string()))?;
200    parse_text_input(&data)
201}
202
203pub fn output_integration_result(
204    writer: &mut Box<dyn Write>,
205    result: &IntegrationResult,
206    config: &OutputConfig,
207) -> Result<()> {
208    match config.format.as_str() {
209        "text" => output_text_integration_result(writer, result, config),
210        "json" => output_json_integration_result(writer, result),
211        "csv" => output_csv_integration_result(writer, result),
212        "yaml" => output_yaml_integration_result(writer, result),
213        "toml" => output_toml_integration_result(writer, result),
214        "xml" => output_xml_integration_result(writer, result),
215        _ => output_text_integration_result(writer, result, config),
216    }
217}
218
219fn output_text_integration_result(
220    writer: &mut Box<dyn Write>,
221    result: &IntegrationResult,
222    config: &OutputConfig,
223) -> Result<()> {
224    writeln!(writer, "Integration Analysis Results")?;
225    writeln!(writer, "=============================")?;
226    writeln!(writer)?;
227
228    writeln!(writer, "Dataset: {}", result.dataset_name)?;
229    writeln!(writer, "Overall Risk Level: {:?}", result.risk_level)?;
230    writeln!(writer)?;
231
232    if !config.quiet {
233        writeln!(writer, "Laws Executed: {}", result.laws_executed.join(", "))?;
234        writeln!(
235            writer,
236            "Overall Quality Score: {:.3}",
237            result.overall_quality_score
238        )?;
239        writeln!(writer, "Consistency Score: {:.3}", result.consistency_score)?;
240        writeln!(writer, "Conflicts Detected: {}", result.conflicts_detected)?;
241        writeln!(writer)?;
242
243        if let Some(ref benford) = result.benford_result {
244            writeln!(
245                writer,
246                "- Benford Law: {} (Chi-square: {:.4})",
247                benford.risk_level, benford.chi_square
248            )?;
249        }
250        if let Some(ref pareto) = result.pareto_result {
251            writeln!(
252                writer,
253                "- Pareto Principle: {} (Top 20%: {:.1}%)",
254                pareto.risk_level, pareto.top_20_percent_share
255            )?;
256        }
257        if let Some(ref zipf) = result.zipf_result {
258            writeln!(
259                writer,
260                "- Zipf Law: {} (Exponent: {:.3})",
261                zipf.risk_level, zipf.zipf_exponent
262            )?;
263        }
264        if let Some(ref normal) = result.normal_result {
265            writeln!(
266                writer,
267                "- Normal Distribution: {} (Mean: {:.3})",
268                normal.risk_level, normal.mean
269            )?;
270        }
271        if let Some(ref poisson) = result.poisson_result {
272            writeln!(
273                writer,
274                "- Poisson Distribution: {} (Lambda: {:.3})",
275                poisson.risk_level, poisson.lambda
276            )?;
277        }
278        writeln!(writer)?;
279    }
280
281    if !result.conflicts.is_empty() {
282        writeln!(writer, "Conflicts:")?;
283        for conflict in &result.conflicts {
284            writeln!(writer, "• {}", conflict.description)?;
285        }
286        writeln!(writer)?;
287    }
288
289    Ok(())
290}
291
292fn output_json_integration_result(
293    writer: &mut Box<dyn Write>,
294    result: &IntegrationResult,
295) -> Result<()> {
296    use serde_json::json;
297
298    let output = json!({
299        "dataset_name": result.dataset_name,
300        "risk_level": format!("{:?}", result.risk_level),
301        "numbers_analyzed": result.numbers_analyzed,
302        "laws_executed": result.laws_executed,
303        "overall_quality_score": result.overall_quality_score,
304        "consistency_score": result.consistency_score,
305        "conflicts_detected": result.conflicts_detected
306    });
307
308    writeln!(writer, "{}", serde_json::to_string_pretty(&output).unwrap())?;
309    Ok(())
310}
311
312fn output_csv_integration_result(
313    writer: &mut Box<dyn Write>,
314    result: &IntegrationResult,
315) -> Result<()> {
316    writeln!(
317        writer,
318        "dataset,risk_level,quality_score,consistency_score,conflicts"
319    )?;
320    writeln!(
321        writer,
322        "{},{:?},{:.3},{:.3},{}",
323        result.dataset_name,
324        result.risk_level,
325        result.overall_quality_score,
326        result.consistency_score,
327        result.conflicts_detected
328    )?;
329    Ok(())
330}
331
332fn output_yaml_integration_result(
333    writer: &mut Box<dyn Write>,
334    result: &IntegrationResult,
335) -> Result<()> {
336    writeln!(writer, "dataset_name: \"{}\"", result.dataset_name)?;
337    writeln!(writer, "risk_level: \"{:?}\"", result.risk_level)?;
338    writeln!(
339        writer,
340        "primary_recommendation: {}",
341        result.recommendations.primary_law
342    )?;
343    writeln!(
344        writer,
345        "recommendation_confidence: {:.3}",
346        result.recommendations.confidence
347    )?;
348    Ok(())
349}
350
351fn output_toml_integration_result(
352    writer: &mut Box<dyn Write>,
353    result: &IntegrationResult,
354) -> Result<()> {
355    writeln!(writer, "dataset_name = \"{}\"", result.dataset_name)?;
356    writeln!(writer, "risk_level = \"{:?}\"", result.risk_level)?;
357    writeln!(
358        writer,
359        "primary_recommendation = \"{}\"",
360        result.recommendations.primary_law
361    )?;
362    writeln!(
363        writer,
364        "recommendation_confidence = {:.3}",
365        result.recommendations.confidence
366    )?;
367    Ok(())
368}
369
370fn output_xml_integration_result(
371    writer: &mut Box<dyn Write>,
372    result: &IntegrationResult,
373) -> Result<()> {
374    writeln!(writer, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")?;
375    writeln!(writer, "<integration_analysis>")?;
376    writeln!(
377        writer,
378        "  <dataset_name>{}</dataset_name>",
379        result.dataset_name
380    )?;
381    writeln!(writer, "  <risk_level>{:?}</risk_level>", result.risk_level)?;
382    writeln!(
383        writer,
384        "  <primary_recommendation>{}</primary_recommendation>",
385        result.recommendations.primary_law
386    )?;
387    writeln!(
388        writer,
389        "  <recommendation_confidence>{:.3}</recommendation_confidence>",
390        result.recommendations.confidence
391    )?;
392    writeln!(writer, "</integration_analysis>")?;
393    Ok(())
394}
395
396pub fn parse_analysis_purpose(purpose: &str) -> lawkit_core::laws::integration::AnalysisPurpose {
397    use lawkit_core::laws::integration::AnalysisPurpose;
398    match purpose {
399        "quality" => AnalysisPurpose::QualityAudit,
400        "fraud" => AnalysisPurpose::FraudDetection,
401        "concentration" => AnalysisPurpose::ConcentrationAnalysis,
402        "anomaly" => AnalysisPurpose::AnomalyDetection,
403        "distribution" => AnalysisPurpose::DistributionFitting,
404        _ => AnalysisPurpose::GeneralAnalysis,
405    }
406}