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 {
|
if response.CBO != nil {
|
||||||
summary.CBOClasses = response.CBO.Summary.TotalClasses
|
summary.CBOClasses = response.CBO.Summary.TotalClasses
|
||||||
summary.HighCouplingClasses = response.CBO.Summary.HighRiskClasses
|
summary.HighCouplingClasses = response.CBO.Summary.HighRiskClasses
|
||||||
|
summary.MediumCouplingClasses = response.CBO.Summary.MediumRiskClasses
|
||||||
summary.AverageCoupling = response.CBO.Summary.AverageCBO
|
summary.AverageCoupling = response.CBO.Summary.AverageCBO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,9 +112,10 @@ type AnalyzeSummary struct {
|
|||||||
CloneGroups int `json:"clone_groups" yaml:"clone_groups"`
|
CloneGroups int `json:"clone_groups" yaml:"clone_groups"`
|
||||||
CodeDuplication float64 `json:"code_duplication_percentage" yaml:"code_duplication_percentage"`
|
CodeDuplication float64 `json:"code_duplication_percentage" yaml:"code_duplication_percentage"`
|
||||||
|
|
||||||
CBOClasses int `json:"cbo_classes" yaml:"cbo_classes"`
|
CBOClasses int `json:"cbo_classes" yaml:"cbo_classes"`
|
||||||
HighCouplingClasses int `json:"high_coupling_classes" yaml:"high_coupling_classes"`
|
HighCouplingClasses int `json:"high_coupling_classes" yaml:"high_coupling_classes"` // CBO > 7 (High Risk)
|
||||||
AverageCoupling float64 `json:"average_coupling" yaml:"average_coupling"`
|
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)
|
// Overall health score (0-100)
|
||||||
HealthScore int `json:"health_score" yaml:"health_score"`
|
HealthScore int `json:"health_score" yaml:"health_score"`
|
||||||
@@ -160,9 +161,19 @@ func (s *AnalyzeSummary) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CBO checks
|
// CBO checks
|
||||||
if s.CBOClasses > 0 && s.HighCouplingClasses > s.CBOClasses {
|
if s.CBOClasses > 0 {
|
||||||
return fmt.Errorf("HighCouplingClasses (%d) cannot exceed CBOClasses (%d)",
|
if s.HighCouplingClasses > s.CBOClasses {
|
||||||
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
|
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 {
|
func (s *AnalyzeSummary) calculateCouplingPenalty() int {
|
||||||
if s.CBOClasses == 0 {
|
if s.CBOClasses == 0 {
|
||||||
return 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 {
|
switch {
|
||||||
case ratio > CouplingRatioHigh:
|
case ratio > CouplingRatioHigh:
|
||||||
return CouplingPenaltyHigh
|
return CouplingPenaltyHigh
|
||||||
|
|||||||
Reference in New Issue
Block a user