All files / app/codeCharta/ui/ribbonBar/artificialIntelligence/selectors/util suspiciousMetricsHelper.ts

97.82% Statements 45/46
96.55% Branches 28/29
100% Functions 5/5
97.82% Lines 45/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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  4x 4x                                                 4x 5x   5x 12x         12x 8x 8x       5x     4x 21x 56x       56x 56x 56x         11x 5x   6x 1x   5x       8x 8x 7x   1x     4x       9x             9x   9x 99x 99x 80x     19x 19x   19x 8x 11x 11x         11x 8x         9x 20x 20x 1x       9x    
import { CodeMapNode, ColorRange } from "../../../../../codeCharta.model"
import { metricTitles } from "../../../../../util/metric/metricTitles"
import { getAssociatedMetricThresholds } from "./getMetricThresholds"
 
interface MetricValues {
    [metric: string]: number[]
}
 
export interface MetricValuesByLanguage {
    [language: string]: MetricValues
}
 
export interface MetricAssessmentResults {
    suspiciousMetrics: Map<string, ColorRange>
    unsuspiciousMetrics: string[]
    outliersThresholds: Map<string, number>
    untrackedMetrics: string[]
}
 
export interface MetricSuggestionParameters {
    metric: string
    from: number
    to: number
    isOutlier?: boolean
    outlierThreshold?: number
}
 
export function calculateSuspiciousMetrics(metricAssessmentResults: MetricAssessmentResults): MetricSuggestionParameters[] {
    const noticeableMetricSuggestionLinks = new Map<string, MetricSuggestionParameters>()
 
    for (const [metricName, colorRange] of metricAssessmentResults.suspiciousMetrics) {
        noticeableMetricSuggestionLinks.set(metricName, {
            metric: metricName,
            ...colorRange
        })
 
        if (metricAssessmentResults.outliersThresholds.has(metricName)) {
            noticeableMetricSuggestionLinks.get(metricName).isOutlier = true
            noticeableMetricSuggestionLinks.get(metricName).outlierThreshold = metricAssessmentResults.outliersThresholds.get(metricName)
        }
    }
 
    return [...noticeableMetricSuggestionLinks.values()].sort(compareSuspiciousMetricSuggestionLinks)
}
 
export function setMetricValuesByLanguage(node: CodeMapNode, metricValuesByLanguage: MetricValuesByLanguage, fileExtension: string) {
    for (const [metricName, value] of Object.entries(node.attributes)) {
        Iif (value === 0) {
            continue
        }
 
        metricValuesByLanguage[fileExtension] ??= {}
        metricValuesByLanguage[fileExtension][metricName] ??= []
        metricValuesByLanguage[fileExtension][metricName].push(value)
    }
}
 
function compareSuspiciousMetricSuggestionLinks(a: MetricSuggestionParameters, b: MetricSuggestionParameters): number {
    if (a.isOutlier && !b.isOutlier) {
        return -1
    }
    if (!a.isOutlier && b.isOutlier) {
        return 1
    }
    return 0
}
 
function getNameAndDescriptionOfMetric(metricName: string): string {
    const metricDescription = metricTitles.get(metricName)
    if (metricDescription) {
        return `${metricName} (${metricDescription})`
    }
    return `${metricName}`
}
 
export function findGoodAndBadMetrics(
    metricValuesByLanguages: MetricValuesByLanguage,
    mainProgrammingLanguage: string
): MetricAssessmentResults {
    const metricAssessmentResults: MetricAssessmentResults = {
        suspiciousMetrics: new Map<string, ColorRange>(),
        unsuspiciousMetrics: [],
        outliersThresholds: new Map<string, number>(),
        untrackedMetrics: []
    }
 
    const languageSpecificMetricThresholds = getAssociatedMetricThresholds(mainProgrammingLanguage)
 
    for (const metricName of Object.keys(languageSpecificMetricThresholds)) {
        const valuesOfMetric = metricValuesByLanguages[mainProgrammingLanguage]?.[metricName]
        if (valuesOfMetric === undefined) {
            continue
        }
 
        const thresholdConfig = languageSpecificMetricThresholds[metricName]
        const maxMetricValue = Math.max(...valuesOfMetric)
 
        if (maxMetricValue <= thresholdConfig.percentile70) {
            metricAssessmentResults.unsuspiciousMetrics.push(getNameAndDescriptionOfMetric(metricName))
        } else if (maxMetricValue > thresholdConfig.percentile70) {
            metricAssessmentResults.suspiciousMetrics.set(metricName, {
                from: thresholdConfig.percentile70,
                to: thresholdConfig.percentile80
            })
 
            if (maxMetricValue > thresholdConfig.percentile90) {
                metricAssessmentResults.outliersThresholds.set(metricName, thresholdConfig.percentile90)
            }
        }
    }
 
    for (const key in metricValuesByLanguages[mainProgrammingLanguage]) {
        const keys = Object.keys(languageSpecificMetricThresholds)
        if (!keys.includes(key) && !metricAssessmentResults.untrackedMetrics.includes(key)) {
            metricAssessmentResults.untrackedMetrics.push(key)
        }
    }
 
    return metricAssessmentResults
}