From e0b261558d88519688d75f56da949a303b881404 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Struewer <j.n.struewer@gmail.com> Date: Thu, 2 Nov 2023 13:33:56 +0100 Subject: [PATCH] Updated KPI calculation to output planned and actual weights for each edge for better transparency in the frontend --- config/baseline.xml | 4 +-- .../kpi/dto/KPIHierarchyEdgeDto.kt | 7 +++- .../kpi/dto/KPITreeChildResponseDto.kt | 2 +- .../kpi/dto/KPITreeResponseDto.kt | 14 +++++++- .../dataprovider/kpi/dto/KpiCalculationDto.kt | 8 +++-- .../dataprovider/kpi/service/KPIService.kt | 5 +-- .../AggregationKPICalculationStrategy.kt | 34 ++++++++++++++++--- .../kpi/strategy/KPICalculationStrategy.kt | 2 +- .../strategy/MaximumKPICalculationStrategy.kt | 4 +-- .../strategy/RatioKPICalculationStrategy.kt | 6 ++-- .../RawValueKPICalculationStrategy.kt | 4 +-- 11 files changed, 68 insertions(+), 22 deletions(-) diff --git a/config/baseline.xml b/config/baseline.xml index b471c071..ba711b53 100644 --- a/config/baseline.xml +++ b/config/baseline.xml @@ -13,9 +13,7 @@ <ID>FunctionNaming:ToolRunRepository.kt$ToolRunRepository$fun findFirstByRepository_IdOrderByCreatedAtDesc(id: UUID): ToolRunEntity </ID> - <ID>FunctionNaming:ToolRunRepository.kt$ToolRunRepository$fun findFirstByRepository_IdOrderByCreatedAtDesc(id: - UUID): ToolRunEntity - </ID> + <ID>MagicNumber:KPITreeResponseDto.kt$KPITreeResponseDto$0.5</ID> </ManuallySuppressedIssues> <CurrentIssues/> </SmellBaseline> diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPIHierarchyEdgeDto.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPIHierarchyEdgeDto.kt index 7ed7f2de..cc042cbf 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPIHierarchyEdgeDto.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPIHierarchyEdgeDto.kt @@ -1,3 +1,8 @@ package de.fraunhofer.iem.dataprovider.kpi.dto -data class KPIHierarchyEdgeDto(val from: KpiCalculationDto, val to: KpiCalculationDto, val weight: Double) +data class KPIHierarchyEdgeDto( + val from: KpiCalculationDto, + val to: KpiCalculationDto, + val plannedWeight: Double, + val actualWeight: Double = plannedWeight +) diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPITreeChildResponseDto.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPITreeChildResponseDto.kt index 8ffb91e6..6113f733 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPITreeChildResponseDto.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPITreeChildResponseDto.kt @@ -1,3 +1,3 @@ package de.fraunhofer.iem.dataprovider.kpi.dto -data class KPITreeChildResponseDto(val kpi: KPITreeResponseDto, val weight: Double) +data class KPITreeChildResponseDto(val kpi: KPITreeResponseDto, val plannedWeight: Double, val actualWeight: Double) diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPITreeResponseDto.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPITreeResponseDto.kt index 4369ab0b..fe62fda0 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPITreeResponseDto.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KPITreeResponseDto.kt @@ -9,4 +9,16 @@ data class KPITreeResponseDto( val children: List<KPITreeChildResponseDto>, val isEmpty: Boolean, val order: Int = 1 -) +) { + val amountOfMissingDataChildren: Double = children.sumOf { + if (it.actualWeight == 0.0) { + it.plannedWeight + } else { + 0.0 + } + } + + val amountOfMissingDataTree: Double = children.sumOf { + it.kpi.amountOfMissingDataChildren + } +} diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KpiCalculationDto.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KpiCalculationDto.kt index e8add332..03f234f2 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KpiCalculationDto.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/dto/KpiCalculationDto.kt @@ -28,7 +28,11 @@ class KpiCalculationDto( fun calculateKPI() { if (calculationStrategy != null) { try { - this.value = calculationStrategy.calculateKPI(this.hierarchyEdges) + val valueAndEdges = calculationStrategy.calculateKPI(this.hierarchyEdges) + this.value = valueAndEdges.first + this.hierarchyEdges.clear() + this.hierarchyEdges.addAll(valueAndEdges.second) + this.isEmpty = false } catch (exception: Exception) { logger.error("Exception during KPI calculation for $kind with $calculationStrategy") @@ -56,7 +60,7 @@ class KpiCalculationDto( fun toViewModel(): KPITreeResponseDto { val children = this.hierarchyEdges.map { val child = it.to.toViewModel() - KPITreeChildResponseDto(child, it.weight) + KPITreeChildResponseDto(kpi = child, plannedWeight = it.plannedWeight, actualWeight = it.actualWeight) } return this.kind.toViewModel(value = value ?: -1, children = children, isEmpty = isEmpty) diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/service/KPIService.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/service/KPIService.kt index 793a9bae..cfe145a5 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/service/KPIService.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/service/KPIService.kt @@ -28,7 +28,7 @@ class KPIService( fun removeKPIChildrenLowerThanSecondLevel(kpiTreeResponseDto: KPITreeResponseDto): KPITreeResponseDto { val children: List<KPITreeChildResponseDto> = kpiTreeResponseDto.children.map { KPITreeChildResponseDto( - KPITreeResponseDto( + kpi = KPITreeResponseDto( value = it.kpi.value, name = it.kpi.name, description = it.kpi.description, @@ -38,7 +38,8 @@ class KPIService( it.kpi.isEmpty, order = it.kpi.order ), - it.weight + actualWeight = it.actualWeight, + plannedWeight = it.plannedWeight ) } diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/AggregationKPICalculationStrategy.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/AggregationKPICalculationStrategy.kt index ca143284..157e8d97 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/AggregationKPICalculationStrategy.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/AggregationKPICalculationStrategy.kt @@ -9,25 +9,51 @@ class AggregationKPICalculationStrategy : KPICalculationStrategy { * If a child is empty it is removed from the calculation and its * corresponding edge weight is distributed evenly between the * remaining children. + * The method returns the KPIs value as well as the updated + * KPIHierarchyEdgeDtos with the actual used weight. */ - override fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Int { + override fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Pair<Int, List<KPIHierarchyEdgeDto>> { if (children.none { !it.to.isEmpty() } || children.isEmpty()) { throw Exception("KPI aggregation of empty children can't be calculated.") } val emptyChildren: List<KPIHierarchyEdgeDto> = children.filter { it.to.isEmpty() } + val notEmptyChildren = children.toMutableList() notEmptyChildren.removeAll(emptyChildren) val additionalWeightPerElement: Double = if (notEmptyChildren.isNotEmpty()) { - val unusedWeight = emptyChildren.sumOf { it.weight } + val unusedWeight = emptyChildren.sumOf { it.plannedWeight } unusedWeight / notEmptyChildren.size } else { 0.0 } - return notEmptyChildren.sumOf { child -> - (child.to.getValue().toFloat() * (child.weight + additionalWeightPerElement)).toInt() + val weightedEdges: MutableList<KPIHierarchyEdgeDto> = emptyChildren + .map { + KPIHierarchyEdgeDto( + from = it.from, + to = it.to, + plannedWeight = it.plannedWeight, + actualWeight = 0.0 + ) + }.toMutableList() + + var value = 0 + + notEmptyChildren.forEach { child -> + val actualWeight = (child.plannedWeight + additionalWeightPerElement) + weightedEdges.add( + KPIHierarchyEdgeDto( + from = child.from, + to = child.to, + plannedWeight = child.plannedWeight, + actualWeight = actualWeight + ) + ) + value += (child.to.getValue().toFloat() * actualWeight).toInt() } + + return Pair(value, weightedEdges) } } diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/KPICalculationStrategy.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/KPICalculationStrategy.kt index 7f715b85..d80d4c55 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/KPICalculationStrategy.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/KPICalculationStrategy.kt @@ -3,5 +3,5 @@ package de.fraunhofer.iem.dataprovider.kpi.strategy import de.fraunhofer.iem.dataprovider.kpi.dto.KPIHierarchyEdgeDto fun interface KPICalculationStrategy { - fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Int + fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Pair<Int, List<KPIHierarchyEdgeDto>> } diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/MaximumKPICalculationStrategy.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/MaximumKPICalculationStrategy.kt index a65c51b7..b22771ae 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/MaximumKPICalculationStrategy.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/MaximumKPICalculationStrategy.kt @@ -5,7 +5,7 @@ import de.fraunhofer.iem.dataprovider.kpi.dto.KPIHierarchyEdgeDto class MaximumKPICalculationStrategy : KPICalculationStrategy { // TODO: Currently it's tailored to the maximum dependency vulnerability score, this should change in the future @Suppress("MagicNumber") - override fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Int { + override fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Pair<Int, List<KPIHierarchyEdgeDto>> { if (children.none { !it.to.isEmpty() } || children.isEmpty()) { throw Exception("KPI maximum of empty children can't be calculated") } @@ -18,6 +18,6 @@ class MaximumKPICalculationStrategy : KPICalculationStrategy { } } } - return (100 - (maximum)) + return Pair((100 - (maximum)), children) } } diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/RatioKPICalculationStrategy.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/RatioKPICalculationStrategy.kt index cfdbbd60..1f258d8a 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/RatioKPICalculationStrategy.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/RatioKPICalculationStrategy.kt @@ -4,15 +4,15 @@ import de.fraunhofer.iem.dataprovider.kpi.dto.KPIHierarchyEdgeDto class RatioKPICalculationStrategy : KPICalculationStrategy { @Suppress("MagicNumber") - override fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Int { + override fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Pair<Int, List<KPIHierarchyEdgeDto>> { if (children.size != 2) { throw Exception("Requires exactly two children") } val firstValue = children[0].to.getValue() val secondValue = children[1].to.getValue() if (firstValue >= secondValue) { - return ((secondValue.toDouble() / firstValue.toDouble()) * 100).toInt() + return Pair(((secondValue.toDouble() / firstValue.toDouble()) * 100).toInt(), children) } - return ((firstValue.toDouble() / secondValue.toDouble()) * 100).toInt() + return Pair(((firstValue.toDouble() / secondValue.toDouble()) * 100).toInt(), children) } } diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/RawValueKPICalculationStrategy.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/RawValueKPICalculationStrategy.kt index 5f477b02..7a8e6629 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/RawValueKPICalculationStrategy.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/strategy/RawValueKPICalculationStrategy.kt @@ -3,7 +3,7 @@ package de.fraunhofer.iem.dataprovider.kpi.strategy import de.fraunhofer.iem.dataprovider.kpi.dto.KPIHierarchyEdgeDto class RawValueKPICalculationStrategy(private val value: Int) : KPICalculationStrategy { - override fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Int { - return this.value + override fun calculateKPI(children: List<KPIHierarchyEdgeDto>): Pair<Int, List<KPIHierarchyEdgeDto>> { + return Pair(this.value, children) } } -- GitLab