lawkit_python/subcommands/
diagnose.rs

1use crate::common_options;
2use crate::subcommands::integration_common::{
3    get_dataset_name, get_numbers_from_input, output_integration_result, parse_analysis_purpose,
4};
5use clap::{ArgMatches, Command};
6use lawkit_core::common::output::{create_output_writer, OutputConfig};
7use lawkit_core::error::Result;
8use lawkit_core::laws::integration::{
9    analyze_all_laws, detect_conflicts_detailed, generate_detailed_recommendations, AnalysisPurpose,
10};
11use std::io::Write;
12
13pub fn command() -> Command {
14    common_options::add_integration_options(common_options::add_common_options(
15        common_options::add_input_arg(Command::new("diagnose").about("矛盾検出と詳細分析レポート")),
16    ))
17}
18
19pub fn run(matches: &ArgMatches) -> Result<()> {
20    if matches.get_flag("recommend") {
21        return run_recommendation_mode(matches);
22    }
23
24    let report_type = matches.get_one::<String>("report").unwrap();
25    match report_type.as_str() {
26        "conflicting" => run_conflict_analysis_mode(matches),
27        "detailed" => run_detailed_analysis_mode(matches),
28        _ => run_detailed_analysis_mode(matches), // Default to detailed
29    }
30}
31
32fn run_detailed_analysis_mode(matches: &ArgMatches) -> Result<()> {
33    let numbers = get_numbers_from_input(matches)?;
34    let dataset_name = get_dataset_name(matches);
35
36    let result = analyze_all_laws(&numbers, &dataset_name)?;
37
38    let mut writer = create_output_writer(matches)?;
39    let output_config = OutputConfig::from_matches(matches);
40
41    output_detailed_integration_result(&mut writer, &result, &output_config)?;
42
43    std::process::exit(result.risk_level.exit_code());
44}
45
46fn run_conflict_analysis_mode(matches: &ArgMatches) -> Result<()> {
47    let numbers = get_numbers_from_input(matches)?;
48    let dataset_name = get_dataset_name(matches);
49    let threshold = *matches.get_one::<f64>("threshold").unwrap();
50
51    let conflict_result = detect_conflicts_detailed(&numbers, &dataset_name, threshold)?;
52
53    let mut writer = create_output_writer(matches)?;
54    let output_config = OutputConfig::from_matches(matches);
55
56    output_conflict_analysis_result(&mut writer, &conflict_result, &output_config)?;
57
58    std::process::exit(conflict_result.integration_result.risk_level.exit_code());
59}
60
61fn run_recommendation_mode(matches: &ArgMatches) -> Result<()> {
62    let numbers = get_numbers_from_input(matches)?;
63    let dataset_name = get_dataset_name(matches);
64
65    let analysis_purpose = matches
66        .get_one::<String>("purpose")
67        .map(|p| parse_analysis_purpose(p))
68        .unwrap_or(AnalysisPurpose::GeneralAnalysis);
69
70    let recommendation_result =
71        generate_detailed_recommendations(&numbers, &dataset_name, analysis_purpose)?;
72
73    let mut writer = create_output_writer(matches)?;
74    let output_config = OutputConfig::from_matches(matches);
75
76    output_recommendation_result(&mut writer, &recommendation_result, &output_config)?;
77
78    std::process::exit(
79        recommendation_result
80            .integration_result
81            .risk_level
82            .exit_code(),
83    );
84}
85
86fn output_detailed_integration_result(
87    writer: &mut Box<dyn Write>,
88    result: &lawkit_core::laws::integration::IntegrationResult,
89    config: &OutputConfig,
90) -> Result<()> {
91    output_integration_result(writer, result, config)?;
92
93    if config.format == "text" {
94        writeln!(writer)?;
95        writeln!(writer, "=== {} ===", get_text("detailed_analysis", "en"))?;
96
97        output_detailed_law_results(writer, result, "en")?;
98        output_data_characteristics(writer, result, "en")?;
99        output_alternative_combinations(writer, result, "en")?;
100    }
101
102    Ok(())
103}
104
105fn output_conflict_analysis_result(
106    writer: &mut Box<dyn Write>,
107    result: &lawkit_core::laws::integration::ConflictAnalysisResult,
108    _config: &OutputConfig,
109) -> Result<()> {
110    writeln!(writer, "{}", get_text("conflict_analysis_title", "en"))?;
111    writeln!(writer)?;
112    writeln!(
113        writer,
114        "{}: {}",
115        get_text("dataset", "en"),
116        result.dataset_name
117    )?;
118    writeln!(
119        writer,
120        "{}: {:.3}",
121        get_text("threshold", "en"),
122        result.threshold
123    )?;
124    writeln!(
125        writer,
126        "{}: {:?}",
127        get_text("conflict_severity", "en"),
128        result.conflict_severity
129    )?;
130    writeln!(writer)?;
131
132    if !result.detailed_conflicts.is_empty() {
133        writeln!(writer, "{}:", get_text("detailed_conflicts", "en"))?;
134        for (i, conflict) in result.detailed_conflicts.iter().enumerate() {
135            writeln!(writer, "{}. {}", i + 1, conflict.base_conflict.description)?;
136            writeln!(
137                writer,
138                "   {}: {:.3}",
139                get_text("significance", "en"),
140                conflict.statistical_significance
141            )?;
142            writeln!(
143                writer,
144                "   {}: {:?}",
145                get_text("impact", "en"),
146                conflict.impact_assessment
147            )?;
148            writeln!(
149                writer,
150                "   {}: {}",
151                get_text("root_cause", "en"),
152                conflict.root_cause_analysis
153            )?;
154            writeln!(writer)?;
155        }
156    }
157
158    if !result.resolution_strategies.is_empty() {
159        writeln!(writer, "{}:", get_text("resolution_strategies", "en"))?;
160        for strategy in &result.resolution_strategies {
161            writeln!(
162                writer,
163                "• {} ({:?})",
164                strategy.strategy_name, strategy.priority
165            )?;
166            writeln!(
167                writer,
168                "  {}: {}",
169                get_text("expected_outcome", "en"),
170                strategy.expected_outcome
171            )?;
172            writeln!(
173                writer,
174                "  {}: {:.3}",
175                get_text("confidence", "en"),
176                strategy.confidence
177            )?;
178            writeln!(writer)?;
179        }
180    }
181
182    Ok(())
183}
184
185fn output_recommendation_result(
186    writer: &mut Box<dyn Write>,
187    result: &lawkit_core::laws::integration::DetailedRecommendationResult,
188    _config: &OutputConfig,
189) -> Result<()> {
190    writeln!(writer, "{}", get_text("recommendation_title", "en"))?;
191    writeln!(writer)?;
192    writeln!(
193        writer,
194        "{}: {}",
195        get_text("dataset", "en"),
196        result.dataset_name
197    )?;
198    writeln!(
199        writer,
200        "{}: {:?}",
201        get_text("analysis_purpose", "en"),
202        result.analysis_purpose
203    )?;
204    writeln!(writer)?;
205
206    writeln!(writer, "{}:", get_text("purpose_recommendations", "en"))?;
207    for rec in &result.purpose_specific_recommendations {
208        writeln!(
209            writer,
210            "• {:?}: {}",
211            rec.purpose,
212            rec.recommended_laws.join(", ")
213        )?;
214        writeln!(
215            writer,
216            "  {}: {}",
217            get_text("rationale", "en"),
218            rec.rationale
219        )?;
220        writeln!(
221            writer,
222            "  {}: {:.3}",
223            get_text("effectiveness", "en"),
224            rec.effectiveness
225        )?;
226        writeln!(writer)?;
227    }
228
229    if !result.combination_analysis.is_empty() {
230        writeln!(writer, "{}:", get_text("combination_analysis", "en"))?;
231        for combo in result.combination_analysis.iter().take(3) {
232            writeln!(
233                writer,
234                "• {}: {:.3}",
235                combo.laws.join(" + "),
236                combo.synergy_score
237            )?;
238        }
239        writeln!(writer)?;
240    }
241
242    Ok(())
243}
244
245fn output_detailed_law_results(
246    writer: &mut Box<dyn Write>,
247    result: &lawkit_core::laws::integration::IntegrationResult,
248    _lang: &str,
249) -> Result<()> {
250    writeln!(writer, "{}:", get_text("individual_law_results", "en"))?;
251
252    if let Some(ref benf_result) = result.benford_result {
253        writeln!(
254            writer,
255            "• {}: {:.3} ({:?})",
256            get_law_name("benf", "en"),
257            1.0 - (benf_result.mean_absolute_deviation / 100.0),
258            benf_result.risk_level
259        )?;
260    }
261
262    if let Some(ref pareto_result) = result.pareto_result {
263        writeln!(
264            writer,
265            "• {}: {:.3} ({:?})",
266            get_law_name("pareto", "en"),
267            pareto_result.concentration_index,
268            pareto_result.risk_level
269        )?;
270    }
271
272    if let Some(ref zipf_result) = result.zipf_result {
273        writeln!(
274            writer,
275            "• {}: {:.3} ({:?})",
276            get_law_name("zipf", "en"),
277            zipf_result.distribution_quality,
278            zipf_result.risk_level
279        )?;
280    }
281
282    if let Some(ref normal_result) = result.normal_result {
283        writeln!(
284            writer,
285            "• {}: {:.3} ({:?})",
286            get_law_name("normal", "en"),
287            normal_result.normality_score,
288            normal_result.risk_level
289        )?;
290    }
291
292    if let Some(ref poisson_result) = result.poisson_result {
293        writeln!(
294            writer,
295            "• {}: {:.3} ({:?})",
296            get_law_name("poisson", "en"),
297            poisson_result.goodness_of_fit_score,
298            poisson_result.risk_level
299        )?;
300    }
301
302    writeln!(writer)?;
303    Ok(())
304}
305
306fn output_data_characteristics(
307    writer: &mut Box<dyn Write>,
308    result: &lawkit_core::laws::integration::IntegrationResult,
309    _lang: &str,
310) -> Result<()> {
311    let chars = &result.data_characteristics;
312
313    writeln!(writer, "{}:", get_text("data_characteristics", "en"))?;
314    writeln!(
315        writer,
316        "  {}: {:?}",
317        get_text("data_type", "en"),
318        chars.data_type
319    )?;
320    writeln!(
321        writer,
322        "  {}: {:?}",
323        get_text("distribution_shape", "en"),
324        chars.distribution_shape
325    )?;
326    writeln!(
327        writer,
328        "  {}: {:?}",
329        get_text("outlier_presence", "en"),
330        chars.outlier_presence
331    )?;
332    writeln!(
333        writer,
334        "  {}: {:?}",
335        get_text("scale_range", "en"),
336        chars.scale_range
337    )?;
338    writeln!(
339        writer,
340        "  {}: {:?}",
341        get_text("sample_size_category", "en"),
342        chars.sample_size_category
343    )?;
344    writeln!(writer)?;
345
346    Ok(())
347}
348
349fn output_alternative_combinations(
350    writer: &mut Box<dyn Write>,
351    result: &lawkit_core::laws::integration::IntegrationResult,
352    _lang: &str,
353) -> Result<()> {
354    writeln!(writer, "{}:", get_text("alternative_combinations", "en"))?;
355
356    for combo in &result.recommendations.alternative_combinations {
357        writeln!(writer, "• {} ({})", combo.purpose, combo.laws.join(" + "))?;
358        writeln!(
359            writer,
360            "  {}: {:.3}",
361            get_text("effectiveness", "en"),
362            combo.effectiveness_score
363        )?;
364        writeln!(
365            writer,
366            "  {}: {}",
367            get_text("description", "en"),
368            combo.description
369        )?;
370        writeln!(writer)?;
371    }
372
373    Ok(())
374}
375
376fn get_text(key: &str, _lang: &str) -> String {
377    match key {
378        "detailed_analysis" => "Detailed Analysis",
379        "conflict_analysis_title" => "Conflict Analysis",
380        "threshold" => "Threshold",
381        "conflict_severity" => "Conflict Severity",
382        "detailed_conflicts" => "Detailed Conflicts",
383        "significance" => "Significance",
384        "impact" => "Impact",
385        "root_cause" => "Root Cause",
386        "resolution_strategies" => "Resolution Strategies",
387        "expected_outcome" => "Expected Outcome",
388        "confidence" => "Confidence",
389        "recommendation_title" => "Recommendations",
390        "analysis_purpose" => "Analysis Purpose",
391        "purpose_recommendations" => "Purpose-Based Recommendations",
392        "combination_analysis" => "Combination Analysis",
393        "individual_law_results" => "Individual Law Results",
394        "data_characteristics" => "Data Characteristics",
395        "data_type" => "Data Type",
396        "distribution_shape" => "Distribution Shape",
397        "outlier_presence" => "Outlier Presence",
398        "scale_range" => "Scale Range",
399        "sample_size_category" => "Sample Size Category",
400        "alternative_combinations" => "Alternative Combinations",
401        "effectiveness" => "Effectiveness",
402        "rationale" => "Rationale",
403        "dataset" => "Dataset",
404        _ => key,
405    }
406    .to_string()
407}
408
409fn get_law_name(law: &str, _lang: &str) -> String {
410    match law {
411        "benf" => "Benford's Law",
412        "pareto" => "Pareto Principle",
413        "zipf" => "Zipf's Law",
414        "normal" => "Normal Distribution",
415        "poisson" => "Poisson Distribution",
416        _ => law,
417    }
418    .to_string()
419}