This commit is contained in:
Tomas Dvorak
2026-02-22 15:41:27 +01:00
parent 0b88627e54
commit 409acd2e08
84 changed files with 65382 additions and 27475 deletions
+299 -15
View File
@@ -2,6 +2,7 @@ package quality
import (
"fmt"
"strings"
"time"
)
@@ -30,9 +31,12 @@ func (s *Scorer) CalculateScore(findings []Finding) (int, int) {
score := finding.Score * weight
totalScore += score
// Strict score includes open and wontfix findings
if finding.Status == StatusOpen || finding.Status == StatusWontfix {
strictScore += score
// Strict score: ONLY includes truly unresolved issues
// Excludes: fixed, false_positive, ignored, wontfix (if justified)
if s.isStrictlyRelevant(finding) {
// Apply strict multiplier for severity
strictMultiplier := s.getStrictMultiplier(finding)
strictScore += score * strictMultiplier
}
}
@@ -65,6 +69,69 @@ func (s *Scorer) GenerateScorecard(findings []Finding, lastScan time.Time) *Scor
}
}
// isStrictlyRelevant determines if a finding should count in strict scoring
func (s *Scorer) isStrictlyRelevant(finding Finding) bool {
switch finding.Status {
case StatusOpen:
return true
case StatusFixed:
return false // Already resolved
case StatusFalsePositive:
return false // Not a real issue
case StatusIgnored:
return false // Explicitly ignored
case StatusWontfix:
// Only count wontfix if it's not justified with valid reasons
return !s.isJustifiedWontfix(finding)
default:
return true
}
}
// isJustifiedWontfix checks if wontfix has valid justification
func (s *Scorer) isJustifiedWontfix(finding Finding) bool {
if finding.Metadata == nil {
return false
}
note, exists := finding.Metadata["resolution_note"]
if !exists {
return false
}
// Valid wontfix justifications
validJustifications := []string{
"legacy", "deprecated", "external", "third-party",
"temporary", "placeholder", "documentation",
"test-only", "example", "sample",
}
note = strings.ToLower(note)
for _, justification := range validJustifications {
if strings.Contains(note, justification) {
return true
}
}
return false
}
// getStrictMultiplier returns severity multiplier for strict scoring
func (s *Scorer) getStrictMultiplier(finding Finding) int {
switch finding.Severity {
case SeverityT1:
return 1 // T1 issues are less critical
case SeverityT2:
return 2 // T2 issues are moderately important
case SeverityT3:
return 3 // T3 issues need attention
case SeverityT4:
return 5 // T4 issues are critical
default:
return 1
}
}
// GetHealthGrade returns a health grade based on score
func (s *Scorer) GetHealthGrade(score int) string {
percentage := s.getScorePercentage(score)
@@ -85,36 +152,165 @@ func (s *Scorer) GetHealthGrade(score int) string {
// getScorePercentage converts score to percentage (inverted - lower is better)
func (s *Scorer) getScorePercentage(score int) int {
// Invert score so lower debt = higher percentage
maxPossibleScore := 1000 // Arbitrary high value for normalization
percentage := 100 - (score * 100 / maxPossibleScore)
// Strict percentage calculation with multiple factors
if score <= 0 {
return 100
}
// Base calculation with stricter normalization
var percentage int
if score > 10000 {
// Logarithmic scaling for very high scores
percentage = 100 - int(float64(score-10000)/float64(score)*90)
} else if score > 5000 {
// Linear scaling for high scores
percentage = 100 - (score * 100 / 20000)
} else if score > 1000 {
// Linear scaling for medium scores
percentage = 100 - (score * 100 / 10000)
} else {
// Linear scaling for low scores
percentage = 100 - (score * 100 / 2000)
}
if percentage < 0 {
percentage = 0
}
return percentage
}
// GetStrictHealthMetrics returns comprehensive strict health metrics
func (s *Scorer) GetStrictHealthMetrics(findings []Finding) map[string]interface{} {
total := len(findings)
open := 0
critical := 0
high := 0
medium := 0
low := 0
resolved := 0
ignored := 0
strictScore := 0
totalScore := 0
for _, finding := range findings {
totalScore += finding.Score * int(finding.Severity)
switch finding.Status {
case StatusOpen:
open++
if s.isStrictlyRelevant(finding) {
strictScore += finding.Score * int(finding.Severity) * s.getStrictMultiplier(finding)
}
case StatusFixed:
resolved++
case StatusIgnored, StatusFalsePositive:
ignored++
}
switch finding.Severity {
case SeverityT4:
critical++
case SeverityT3:
high++
case SeverityT2:
medium++
case SeverityT1:
low++
}
}
// Calculate strict percentages
openPercentage := float64(open) / float64(total) * 100
criticalPercentage := float64(critical) / float64(total) * 100
resolutionRate := float64(resolved) / float64(total) * 100
// Strict health score (0-100)
healthScore := 100.0
healthScore -= float64(openPercentage) * 0.5 // Penalty for open issues
healthScore -= float64(criticalPercentage) * 2.0 // Higher penalty for critical
healthScore -= float64(high) * 0.1 // Penalty for high severity
healthScore += float64(resolutionRate) * 0.3 // Bonus for resolution rate
if healthScore < 0 {
healthScore = 0
}
if healthScore > 100 {
healthScore = 100
}
return map[string]interface{}{
"total_issues": total,
"open_issues": open,
"critical_issues": critical,
"high_issues": high,
"medium_issues": medium,
"low_issues": low,
"resolved_issues": resolved,
"ignored_issues": ignored,
"open_percentage": openPercentage,
"critical_percentage": criticalPercentage,
"resolution_rate": resolutionRate,
"strict_score": strictScore,
"total_score": totalScore,
"health_score": healthScore,
"grade": s.GetStrictGrade(healthScore),
}
}
// GetStrictGrade returns grade based on strict health score
func (s *Scorer) GetStrictGrade(healthScore float64) string {
switch {
case healthScore >= 95:
return "A+"
case healthScore >= 90:
return "A"
case healthScore >= 85:
return "A-"
case healthScore >= 80:
return "B+"
case healthScore >= 75:
return "B"
case healthScore >= 70:
return "B-"
case healthScore >= 65:
return "C+"
case healthScore >= 60:
return "C"
case healthScore >= 55:
return "C-"
case healthScore >= 50:
return "D+"
case healthScore >= 45:
return "D"
case healthScore >= 40:
return "D-"
default:
return "F"
}
}
// FormatScorecard formats the scorecard for display
func (s *Scorer) FormatScorecard(card *Scorecard) string {
grade := s.GetHealthGrade(card.StrictScore)
percentage := s.getScorePercentage(card.StrictScore)
output := fmt.Sprintf(`
Code Quality Scorecard
🔍 STRICT Code Quality Scorecard
=======================================
Overall Health: %s (%d%%)
Target Score: %d
Current Score: %d (strict: %d)
📊 Overall Health: %s (%d%%)
🎯 Target Score: %d
Current Score: %d (strict: %d)
Findings by Type:
📈 Findings by Type:
`, grade, percentage, card.TargetScore, card.TotalScore, card.StrictScore)
for ftype, count := range card.FindingsByType {
output += fmt.Sprintf(" - %s: %d\n", ftype, count)
}
output += "\nFindings by Severity:\n"
output += "\n🚨 Findings by Severity:\n"
tierNames := map[Severity]string{
SeverityT1: "T1 (Auto-fixable)",
SeverityT2: "T2 (Quick manual)",
@@ -124,16 +320,104 @@ Findings by Type:
for severity, count := range card.FindingsByTier {
if name, ok := tierNames[severity]; ok {
output += fmt.Sprintf(" - %s: %d\n", name, count)
emoji := s.getSeverityEmoji(severity)
output += fmt.Sprintf(" %s %s: %d\n", emoji, name, count)
}
}
output += "\nStatus Breakdown:\n"
output += "\n📋 Status Breakdown:\n"
for status, count := range card.StatusByType {
output += fmt.Sprintf(" - %s: %d\n", status, count)
}
output += fmt.Sprintf("\nLast Scan: %s\n", card.LastScan.Format("2006-01-02 15:04:05"))
output += fmt.Sprintf("\nLast Scan: %s\n", card.LastScan.Format("2006-01-02 15:04:05"))
return output
}
// getSeverityEmoji returns emoji for severity level
func (s *Scorer) getSeverityEmoji(severity Severity) string {
switch severity {
case SeverityT1:
return "🟢"
case SeverityT2:
return "🟡"
case SeverityT3:
return "🟠"
case SeverityT4:
return "🔴"
default:
return "⚪"
}
}
// FormatStrictScorecard formats comprehensive strict scorecard
func (s *Scorer) FormatStrictScorecard(findings []Finding, lastScan time.Time) string {
metrics := s.GetStrictHealthMetrics(findings)
output := fmt.Sprintf(`
🔬 COMPREHENSIVE STRICT ANALYSIS
=======================================
🎯 STRICT HEALTH SCORE: %.1f/100 (%s)
=======================================
📊 ISSUE BREAKDOWN:
Total Issues: %v
🔴 Critical (T4): %v (%.1f%%)
🟠 High (T3): %v
🟡 Medium (T2): %v
🟢 Low (T1): %v
📈 STATUS ANALYSIS:
✅ Resolved: %v (%.1f%%)
🔓 Open: %v (%.1f%%)
⏸️ Ignored: %v
⚖️ SCORING:
Strict Score: %v
Total Score: %v
Health Multiplier: %.2fx
🎯 STRICT CRITERIA:
✓ Only unresolved issues counted
✓ Severity-weighted scoring (T1×1, T2×2, T3×3, T4×5)
✓ Justified wontfix excluded
✓ False positives ignored
✓ Resolution rate bonus applied
📅 Last Analysis: %s
🏆 RECOMMENDATIONS:
`,
metrics["health_score"], metrics["grade"],
metrics["total_issues"],
metrics["critical_issues"], metrics["critical_percentage"],
metrics["high_issues"],
metrics["medium_issues"],
metrics["low_issues"],
metrics["resolved_issues"], metrics["resolution_rate"],
metrics["open_issues"], metrics["open_percentage"],
metrics["ignored_issues"],
metrics["strict_score"], metrics["total_score"],
float64(metrics["strict_score"].(int))/float64(metrics["total_score"].(int)),
lastScan.Format("2006-01-02 15:04:05"))
// Add recommendations based on metrics
if metrics["critical_percentage"].(float64) > 5 {
output += " 🚨 CRITICAL: Address T4 issues immediately\n"
}
if metrics["open_percentage"].(float64) > 70 {
output += " 📈 HIGH DEBT: Focus on resolving open issues\n"
}
if metrics["resolution_rate"].(float64) < 20 {
output += " ⚡ LOW RESOLUTION: Increase fix rate\n"
}
if healthScore, ok := metrics["health_score"].(float64); ok && healthScore < 50 {
output += " ❌ POOR HEALTH: Major refactoring needed\n"
} else if healthScore >= 80 {
output += " ✅ GOOD HEALTH: Maintain current practices\n"
}
return output
}