mirror of
https://github.com/ludo-technologies/pyscn.git
synced 2025-10-06 00:59:45 +03:00
feat: include medium-risk classes in CBO penalty calculation
**Problem:** CBO analysis was only counting High Risk classes (CBO > 7) for penalty calculation, completely ignoring Medium Risk classes (3 < CBO ≤ 7). Example: Project with 364 classes, multiple classes at CBO=6, but score = 100/100 because all were "Medium Risk" (not counted). **Solution:** 1. Added `MediumCouplingClasses` field to track Medium Risk classes 2. Updated penalty calculation to use weighted ratio: - High Risk classes: weight = 1.0 - Medium Risk classes: weight = 0.5 Formula: (HighRisk × 1.0 + MediumRisk × 0.5) / TotalClasses **Impact:** Projects with many Medium Risk classes will now receive appropriate penalties: - 10% weighted ratio → -6 points (Low penalty) - 30% weighted ratio → -12 points (Medium penalty) - 60% weighted ratio → -20 points (High penalty) This makes CBO scoring more realistic and catches coupling issues that were previously ignored. **Files Changed:** - domain/analyze.go: Added MediumCouplingClasses field, updated penalty logic - app/analyze_usecase.go: Set MediumCouplingClasses from CBO analysis - domain/analyze.go: Added validation for new field 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -497,6 +497,7 @@ func (uc *AnalyzeUseCase) calculateSummary(summary *domain.AnalyzeSummary, respo
|
||||
if response.CBO != nil {
|
||||
summary.CBOClasses = response.CBO.Summary.TotalClasses
|
||||
summary.HighCouplingClasses = response.CBO.Summary.HighRiskClasses
|
||||
summary.MediumCouplingClasses = response.CBO.Summary.MediumRiskClasses
|
||||
summary.AverageCoupling = response.CBO.Summary.AverageCBO
|
||||
}
|
||||
|
||||
|
||||
@@ -112,9 +112,10 @@ type AnalyzeSummary struct {
|
||||
CloneGroups int `json:"clone_groups" yaml:"clone_groups"`
|
||||
CodeDuplication float64 `json:"code_duplication_percentage" yaml:"code_duplication_percentage"`
|
||||
|
||||
CBOClasses int `json:"cbo_classes" yaml:"cbo_classes"`
|
||||
HighCouplingClasses int `json:"high_coupling_classes" yaml:"high_coupling_classes"`
|
||||
AverageCoupling float64 `json:"average_coupling" yaml:"average_coupling"`
|
||||
CBOClasses int `json:"cbo_classes" yaml:"cbo_classes"`
|
||||
HighCouplingClasses int `json:"high_coupling_classes" yaml:"high_coupling_classes"` // CBO > 7 (High Risk)
|
||||
MediumCouplingClasses int `json:"medium_coupling_classes" yaml:"medium_coupling_classes"` // 3 < CBO ≤ 7 (Medium Risk)
|
||||
AverageCoupling float64 `json:"average_coupling" yaml:"average_coupling"`
|
||||
|
||||
// Overall health score (0-100)
|
||||
HealthScore int `json:"health_score" yaml:"health_score"`
|
||||
@@ -160,9 +161,19 @@ func (s *AnalyzeSummary) Validate() error {
|
||||
}
|
||||
|
||||
// CBO checks
|
||||
if s.CBOClasses > 0 && s.HighCouplingClasses > s.CBOClasses {
|
||||
return fmt.Errorf("HighCouplingClasses (%d) cannot exceed CBOClasses (%d)",
|
||||
s.HighCouplingClasses, s.CBOClasses)
|
||||
if s.CBOClasses > 0 {
|
||||
if s.HighCouplingClasses > s.CBOClasses {
|
||||
return fmt.Errorf("HighCouplingClasses (%d) cannot exceed CBOClasses (%d)",
|
||||
s.HighCouplingClasses, s.CBOClasses)
|
||||
}
|
||||
if s.MediumCouplingClasses > s.CBOClasses {
|
||||
return fmt.Errorf("MediumCouplingClasses (%d) cannot exceed CBOClasses (%d)",
|
||||
s.MediumCouplingClasses, s.CBOClasses)
|
||||
}
|
||||
if (s.HighCouplingClasses + s.MediumCouplingClasses) > s.CBOClasses {
|
||||
return fmt.Errorf("HighCouplingClasses + MediumCouplingClasses (%d) cannot exceed CBOClasses (%d)",
|
||||
s.HighCouplingClasses+s.MediumCouplingClasses, s.CBOClasses)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -206,13 +217,18 @@ func (s *AnalyzeSummary) calculateDuplicationPenalty() int {
|
||||
}
|
||||
}
|
||||
|
||||
// calculateCouplingPenalty calculates the penalty for class coupling (max 16)
|
||||
// calculateCouplingPenalty calculates the penalty for class coupling (max 20)
|
||||
// Considers both High and Medium risk classes with different weights
|
||||
func (s *AnalyzeSummary) calculateCouplingPenalty() int {
|
||||
if s.CBOClasses == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
ratio := float64(s.HighCouplingClasses) / float64(s.CBOClasses)
|
||||
// Calculate combined problematic classes ratio
|
||||
// Weight: High Risk = 1.0, Medium Risk = 0.5
|
||||
weightedProblematicClasses := float64(s.HighCouplingClasses) + (0.5 * float64(s.MediumCouplingClasses))
|
||||
ratio := weightedProblematicClasses / float64(s.CBOClasses)
|
||||
|
||||
switch {
|
||||
case ratio > CouplingRatioHigh:
|
||||
return CouplingPenaltyHigh
|
||||
|
||||
Reference in New Issue
Block a user