File size: 4,070 Bytes
38bc0dc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | import sys
try:
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')
except Exception:
pass
from feature_pipeline import FEATURE_SCHEMA
FEATURE_DESCRIPTIONS = {
"num_functions": "Many small functions defined (count: {value})",
"num_loops": "High loop count detected (count: {value})",
"num_if": "Excessive branching / if-statements (count: {value})",
"num_try_except": "Heavy error handling blocks (count: {value})",
"num_return": "High number of return statements (count: {value})",
"line_count": "Large codebase size (lines: {value})",
"max_nesting_depth": "Deep nesting detected (depth: {value})",
"cyclomatic_complexity":"High cyclomatic complexity (score: {value})",
"avg_function_length": "Large function size (avg: {value} lines)",
"recursion_flag": "Recursion detected in code",
"global_variable_count":"Global variables used (count: {value})",
}
RISK_THRESHOLDS = {
"num_functions": 5,
"num_loops": 3,
"num_if": 5,
"num_try_except": 2,
"num_return": 4,
"line_count": 40,
"max_nesting_depth": 3,
"cyclomatic_complexity": 5,
"avg_function_length": 15,
"recursion_flag": 0,
"global_variable_count": 1,
}
def explain(model, feature_dict, top_n=3):
importances = model.feature_importances_
importance_map = dict(zip(FEATURE_SCHEMA, importances))
risky_features = []
for feature_name in FEATURE_SCHEMA:
raw_value = float(feature_dict.get(feature_name, 0))
threshold = RISK_THRESHOLDS.get(feature_name, 0)
importance = importance_map.get(feature_name, 0)
if raw_value > threshold:
risky_features.append((feature_name, importance))
risky_features.sort(key=lambda x: x[1], reverse=True)
top_features = risky_features[:top_n]
explanations = []
for feature_name, _ in top_features:
raw_value = feature_dict.get(feature_name, 0)
template = FEATURE_DESCRIPTIONS[feature_name]
if "{value}" in template:
sentence = template.format(value=raw_value)
else:
sentence = template
explanations.append(sentence)
if not explanations:
explanations.append("No significant structural risk factors detected.")
return explanations
if __name__ == "__main__":
from model_trainer import load_model
model = load_model()
risky_features = {
"num_functions": 2,
"num_loops": 4,
"num_if": 9,
"num_try_except": 1,
"num_return": 1,
"line_count": 35,
"max_nesting_depth": 7,
"cyclomatic_complexity": 15,
"avg_function_length": 34.0,
"recursion_flag": 0,
"global_variable_count": 0,
}
clean_features = {
"num_functions": 2,
"num_loops": 1,
"num_if": 2,
"num_try_except": 0,
"num_return": 2,
"line_count": 10,
"max_nesting_depth": 2,
"cyclomatic_complexity": 3,
"avg_function_length": 4.0,
"recursion_flag": 0,
"global_variable_count": 0,
}
print("=" * 55)
print(" EXPLANATION ENGINE — TEST")
print("=" * 55)
print("\n🔴 Risky Code — Top Risk Factors:")
risky_explanations = explain(model, risky_features, top_n=3)
for i, reason in enumerate(risky_explanations, start=1):
print(f" {i}. {reason}")
print("\n🟢 Clean Code — Top Risk Factors:")
clean_explanations = explain(model, clean_features, top_n=3)
for i, reason in enumerate(clean_explanations, start=1):
print(f" {i}. {reason}")
print("=" * 55)
|