diff --git a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/entity/KPIEntity.kt b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/entity/KPIEntity.kt
index 7f9af6821337c7128faa47ca9757ac5be7535769..423e37641b6dfadb9895f75316bef99b16c3db9a 100644
--- a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/entity/KPIEntity.kt
+++ b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/entity/KPIEntity.kt
@@ -1,9 +1,8 @@
 package de.fraunhofer.iem.app.kpi.entity
 
 import de.fraunhofer.iem.app.toolRun.entity.ToolRunEntity
-import de.fraunhofer.iem.kpiCalculator.core.hierarchy.KpiCalculationNode
-import de.fraunhofer.iem.kpiCalculator.core.hierarchy.RawKpiCalculationNode
 import de.fraunhofer.iem.kpiCalculator.model.kpi.KpiId
+import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi
 import jakarta.persistence.*
 import org.hibernate.annotations.CurrentTimestamp
 import org.hibernate.generator.EventType
@@ -34,9 +33,9 @@ class KPIEntity(
     @Column(name = "timestamp")
     var createdAt: Timestamp? = null
 ) {
-    fun toCalculationDto(): KpiCalculationNode {
+    fun toCalculationDto(): RawValueKpi {
         // TODO: fixme
-        return RawKpiCalculationNode(
+        return RawValueKpi(
             kind = this.kind,
             score = this.score,
         )
diff --git a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/service/KPIService.kt b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/service/KPIService.kt
index 2611f5b49ab52db91e8c69a27373af776629765e..3c39cc7afb57f858221e1872a3d117476b6ba291 100644
--- a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/service/KPIService.kt
+++ b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/service/KPIService.kt
@@ -8,7 +8,7 @@ import de.fraunhofer.iem.app.tools.occmd.enumeration.Checks
 import de.fraunhofer.iem.app.tools.occmd.json.RawResultJson
 import de.fraunhofer.iem.kpiCalculator.core.strategy.AggregationKPICalculationStrategy
 import de.fraunhofer.iem.kpiCalculator.model.kpi.KpiId
-import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpiDto
+import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi
 import org.springframework.stereotype.Service
 
 @Service
@@ -49,14 +49,14 @@ class KPIService {
     /**
      * This method calculates the OccmdCreateKpiDtos based upon the given tool results.
      */
-    fun calculateOccmdKpis(rawOccmdResults: List<RawResultJson>): List<RawValueKpiDto> {
-        val kpis = mutableListOf<RawValueKpiDto>()
+    fun calculateOccmdKpis(rawOccmdResults: List<RawResultJson>): List<RawValueKpi> {
+        val kpis = mutableListOf<RawValueKpi>()
 
         rawOccmdResults.forEach {
             when (val check = Checks.fromString(it.check)) {
                 Checks.CheckedInBinaries ->
                     kpis.add(
-                        RawValueKpiDto(
+                        RawValueKpi(
                             kind = KpiId.CHECKED_IN_BINARIES,
                             score = (it.score * 100).toInt()
                         )
@@ -64,7 +64,7 @@ class KPIService {
 
                 Checks.SastUsageBasic ->
                     kpis.add(
-                        RawValueKpiDto(
+                        RawValueKpi(
                             kind = KpiId.SAST_USAGE,
                             score = (it.score * 100).toInt()
                         )
@@ -72,7 +72,7 @@ class KPIService {
 
                 Checks.Secrets ->
                     kpis.add(
-                        RawValueKpiDto(
+                        RawValueKpi(
                             kind = KpiId.SECRETS,
                             score = (it.score * 100).toInt()
                         )
@@ -80,7 +80,7 @@ class KPIService {
 
                 Checks.CommentsInCode ->
                     kpis.add(
-                        RawValueKpiDto(
+                        RawValueKpi(
                             kind = KpiId.COMMENTS_IN_CODE,
                             score = (it.score * 100).toInt()
                         )
@@ -88,7 +88,7 @@ class KPIService {
 
                 Checks.DocumentationInfrastructure ->
                     kpis.add(
-                        RawValueKpiDto(
+                        RawValueKpi(
                             kind = KpiId.DOCUMENTATION_INFRASTRUCTURE,
                             score = (it.score * 100).toInt()
                         )
@@ -106,17 +106,17 @@ class KPIService {
      * Creates a named map of RepositoryCreateDtos, based upon the provided repository details.
      * This method only returns raw KPIs.
      */
-    fun calculateRepositoryDetailsKpis(repoDetailsDto: RepositoryDetailsDto): List<RawValueKpiDto> {
+    fun calculateRepositoryDetailsKpis(repoDetailsDto: RepositoryDetailsDto): List<RawValueKpi> {
         return listOf(
-            RawValueKpiDto(
+            RawValueKpi(
                 kind = KpiId.NUMBER_OF_COMMITS,
                 score = repoDetailsDto.numberOfCommits
             ),
-            RawValueKpiDto(
+            RawValueKpi(
                 kind = KpiId.NUMBER_OF_SIGNED_COMMITS,
                 score = repoDetailsDto.numberOfSignedCommits
             ),
-            RawValueKpiDto(
+            RawValueKpi(
                 kind = KpiId.IS_DEFAULT_BRANCH_PROTECTED,
                 score = if (repoDetailsDto.isDefaultBranchProtected) 100 else 0
             )
diff --git a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/repository/dto/RawValueKpiCreateDtoExtension.kt b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/repository/dto/RawValueKpiCreateDtoExtension.kt
index c9c41ab897f7b034ee4987ed2bf6c820ef7c2685..cb061351c53f47054aa46e80d2cae103dc018aeb 100644
--- a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/repository/dto/RawValueKpiCreateDtoExtension.kt
+++ b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/repository/dto/RawValueKpiCreateDtoExtension.kt
@@ -1,11 +1,11 @@
 package de.fraunhofer.iem.app.repository.dto
 
 import de.fraunhofer.iem.app.toolRun.entity.ToolRunEntity
-import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpiDto
+import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi
 import java.sql.Timestamp
 import java.time.Instant
 
-fun RawValueKpiDto.toDbObject(toolRun: ToolRunEntity): de.fraunhofer.iem.app.kpi.entity.KPIEntity {
+fun RawValueKpi.toDbObject(toolRun: ToolRunEntity): de.fraunhofer.iem.app.kpi.entity.KPIEntity {
     return de.fraunhofer.iem.app.kpi.entity.KPIEntity(
         kind = this.kind,
         score = this.score,
diff --git a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/repository/service/RepositoryService.kt b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/repository/service/RepositoryService.kt
index 177a7a2babb5e7892bf567d083f7e7ceaf3ff6a1..359ccc9924f4534c200724102e42dd70d4496227 100644
--- a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/repository/service/RepositoryService.kt
+++ b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/repository/service/RepositoryService.kt
@@ -13,7 +13,7 @@ import de.fraunhofer.iem.app.toolRun.dto.ToolRunDto
 import de.fraunhofer.iem.app.toolRun.entity.LanguageEntity
 import de.fraunhofer.iem.app.toolRun.entity.ToolRunEntity
 import de.fraunhofer.iem.app.toolRun.repository.ToolRunRepository
-import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpiDto
+import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi
 import org.springframework.stereotype.Service
 import org.springframework.transaction.annotation.Propagation
 import org.springframework.transaction.annotation.Transactional
@@ -30,7 +30,7 @@ class RepositoryService(
     @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
     fun createToolRun(
         repoDto: RepositoryCreateDto,
-        kpiToolList: List<Pair<CreateToolDto, List<RawValueKpiDto>>>,
+        kpiToolList: List<Pair<CreateToolDto, List<RawValueKpi>>>,
         languageMap: Map<String, Float>
     ) {
         val repo = getOrCreate(repoDto)
diff --git a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/toolRun/service/ToolRunService.kt b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/toolRun/service/ToolRunService.kt
index 8b9c3b7b990378ee4aea44f8dbde4c66c02729c5..5c003c1c1a43a9cd1f7f7e9c94d1c027ccb3ae21 100644
--- a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/toolRun/service/ToolRunService.kt
+++ b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/toolRun/service/ToolRunService.kt
@@ -73,7 +73,7 @@ class ToolRunService(
                 // goal is to match the behavior of the old implementation.
                 val rawValueKpiCreateDtos =
                     CveAdapter.transformDataToKpi(vulnerabilityDtos).filterIsInstance<AdapterResult.Success>()
-                        .map { it.rawValueKpiDto }
+                        .map { it.rawValueKpi }
 
                 Pair(ortService.getToolDto(), rawValueKpiCreateDtos)
             },
diff --git a/kpi-calculator/adapter/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/adapter/cve/CveAdapter.kt b/kpi-calculator/adapter/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/adapter/cve/CveAdapter.kt
index e71fdb38c5813820e216a5916938dd782bc84256..931c3a3db6bf4ddf9dc716dbd34dd694361b2fa4 100644
--- a/kpi-calculator/adapter/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/adapter/cve/CveAdapter.kt
+++ b/kpi-calculator/adapter/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/adapter/cve/CveAdapter.kt
@@ -5,7 +5,7 @@ import de.fraunhofer.iem.kpiCalculator.model.adapter.AdapterResult
 import de.fraunhofer.iem.kpiCalculator.model.adapter.ErrorType
 import de.fraunhofer.iem.kpiCalculator.model.adapter.VulnerabilityDto
 import de.fraunhofer.iem.kpiCalculator.model.kpi.KpiId
-import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpiDto
+import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi
 
 object CveAdapter : KpiAdapter<VulnerabilityDto> {
     override val kpiId: KpiId
@@ -16,7 +16,7 @@ object CveAdapter : KpiAdapter<VulnerabilityDto> {
             .map {
                 return@map if (isValid(it)) {
                     AdapterResult.Success(
-                        RawValueKpiDto(
+                        RawValueKpi(
                             kind = kpiId,
                             score = (it.severity * 10).toInt()
                         )
diff --git a/kpi-calculator/adapter/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/adapter/cve/CveAdapterTest.kt b/kpi-calculator/adapter/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/adapter/cve/CveAdapterTest.kt
index 333ea6a0dac01539e25b78544add821637412840..7e8b07aba998fbc1a8b87ece0addd0757e010b60 100644
--- a/kpi-calculator/adapter/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/adapter/cve/CveAdapterTest.kt
+++ b/kpi-calculator/adapter/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/adapter/cve/CveAdapterTest.kt
@@ -21,7 +21,7 @@ class CveAdapterTest {
         )
         when (validKpi) {
             is AdapterResult.Success -> {
-                assert(validKpi.rawValueKpiDto.score in (0..100))
+                assert(validKpi.rawValueKpi.score in (0..100))
             }
 
             is AdapterResult.Error -> {
diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculator.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculator.kt
index bf9fa0c885607452e542739b245cb3ee700629e8..9e9dc5fb6595ed7a51370a373a9a33c71deb7bfb 100644
--- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculator.kt
+++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculator.kt
@@ -3,7 +3,7 @@ package de.fraunhofer.iem.kpiCalculator.core
 import de.fraunhofer.iem.kpiCalculator.core.hierarchy.KpiCalculationNode
 import de.fraunhofer.iem.kpiCalculator.model.kpi.KpiId
 import de.fraunhofer.iem.kpiCalculator.model.kpi.KpiStrategyId
-import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpiDto
+import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi
 import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.KpiHierarchy
 import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.KpiNode
 import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.KpiResultNode
@@ -13,9 +13,9 @@ sealed class KpiResult {}
 object KpiCalculator {
     //XXX: Setup Logger
 
-    fun calculateKpis(hierarchy: KpiHierarchy, rawValueKpiDtos: List<RawValueKpiDto>): KpiResultNode {
+    fun calculateKpis(hierarchy: KpiHierarchy, rawValueKpis: List<RawValueKpi>): KpiResultNode {
         val root = hierarchy.rootNode
-        val connectedHierarchyRoot = connectKpiHierarchyToRawValues(root, rawValueKpiDtos)
+        val connectedHierarchyRoot = connectKpiHierarchyToRawValues(root, rawValueKpis)
         depthFirstTraversal(connectedHierarchyRoot) { it.calculateKpi() }
         return KpiCalculationNode.to(connectedHierarchyRoot)
     }
@@ -31,7 +31,7 @@ object KpiCalculator {
 
         node.hierarchyEdges
             .forEach { child ->
-                depthFirstTraversal(node = child.target, seen = seen, action)
+                depthFirstTraversal(node = child.to, seen = seen, action)
             }
 
         action(node)
@@ -40,57 +40,59 @@ object KpiCalculator {
 
     private fun connectKpiHierarchyToRawValues(
         node: KpiNode,
-        rawValueKpiDtos: List<RawValueKpiDto>
+        rawValueKpis: List<RawValueKpi>
     ): KpiCalculationNode {
 
-        val kindToValues = mutableMapOf<KpiId, MutableList<RawValueKpiDto>>()
+        val kindToValues = mutableMapOf<KpiId, MutableList<RawValueKpi>>()
         KpiId.entries.forEach { kindToValues[it] = mutableListOf() }
 
-        rawValueKpiDtos.forEach {
+        rawValueKpis.forEach {
             kindToValues[it.kind]!!.add(it)
         }
 
         val calculationRoot = KpiCalculationNode.from(node)
 
+        val updates: MutableList<Triple<KpiCalculationNode, KpiCalculationNode, List<RawValueKpi>>> = mutableListOf()
+
         depthFirstTraversal(
             node = calculationRoot
         ) { currentNode ->
-            // TODO: this is complicated as we don't know how many children we will have.
-            // this scenario might reappear again so we should come up with a better solution
-            if (
-                currentNode.kind == KpiId.MAXIMAL_VULNERABILITY &&
-                currentNode.hierarchyEdges.size == 1 &&
-                currentNode.hierarchyEdges.first().target.kind == KpiId.VULNERABILITY_SCORE &&
-                !kindToValues[KpiId.VULNERABILITY_SCORE].isNullOrEmpty()
-            ) {
-                val vulnerabilityCalcNodes = kindToValues[KpiId.VULNERABILITY_SCORE]?.map {
-                    val newNode = KpiCalculationNode(
-                        kind = KpiId.VULNERABILITY_SCORE,
-                        calculationStrategy = KpiStrategyId.RAW_VALUE_STRATEGY
-                    )
-                    newNode.setScore(it.score)
-                    newNode
-                }
+            val correspondingRawValue = kindToValues[currentNode.kind]
+            val parent = currentNode.parent
 
-                if (!vulnerabilityCalcNodes.isNullOrEmpty()) {
-                    currentNode.clearChildren()
-                    val weights = 1.0 / vulnerabilityCalcNodes.size
-                    vulnerabilityCalcNodes.forEach {
-                        currentNode.addChild(it, weights)
-                    }
+            if (!correspondingRawValue.isNullOrEmpty() && parent != null) {
+                updates.add(Triple(parent, currentNode, correspondingRawValue))
+            }
+        }
 
-                }
-            } else {
-                val correspondingRawValue = kindToValues[currentNode.kind]
-                if (!correspondingRawValue.isNullOrEmpty()) {
-                    if (correspondingRawValue.size > 1) {
-                        println("We only expect one RAW value for every KPI")
-                    }
-                    currentNode.setScore(correspondingRawValue.first().score)
-                }
+        updates.forEach {
+            val parent = it.first
+            val currentNode = it.second
+            val correspondingRawValue = it.third
+
+            parent.removeChild(currentNode)
+
+            val rawValueNodes = correspondingRawValue.map { rawValue ->
+                val newNode = KpiCalculationNode(
+                    kind = rawValue.kind,
+                    calculationStrategy = KpiStrategyId.RAW_VALUE_STRATEGY,
+                    parent = parent
+                )
+                newNode.setScore(rawValue.score)
+                newNode
             }
 
+            if (rawValueNodes.isNotEmpty()) {
+                // TODO: this is currently incorrect and also breaks the remaining nodes weights
+                val weights = 1.0 / rawValueNodes.size + parent.hierarchyEdges.size
+                rawValueNodes.forEach { rawValueNode ->
+                    parent.addChild(rawValueNode, weights)
+                }
+
+            }
         }
+
+
         return calculationRoot
     }
 }
diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiCalculationNode.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiCalculationNode.kt
index 0d76f23043c4de2a5bfe5264e3eba335445e8327..afdd7c66e582ba5dd6268f64001ab82146ad9c98 100644
--- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiCalculationNode.kt
+++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiCalculationNode.kt
@@ -11,28 +11,33 @@ import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.KpiResultEdge
 import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.KpiResultNode
 
 
-internal class KpiCalculationNode(val kind: KpiId, val calculationStrategy: KpiStrategyId) {
+internal class KpiCalculationNode(
+    val kind: KpiId,
+    val calculationStrategy: KpiStrategyId,
+    val parent: KpiCalculationNode?
+) {
 
     private var score: KpiCalculationResult = KpiCalculationResult.Empty
     fun setScore(score: Int) {
         this.score = KpiCalculationResult.Success(score)
     }
 
+
     private val _hierarchyEdges: MutableList<KpiHierarchyEdge> = mutableListOf()
     val hierarchyEdges: List<KpiHierarchyEdge>
         get() = _hierarchyEdges
 
     fun addChild(node: KpiCalculationNode, weight: Double) {
-        _hierarchyEdges.add(KpiHierarchyEdge(node, weight))
+        _hierarchyEdges.add(KpiHierarchyEdge(to = node, from = this, weight = weight))
     }
 
-    fun clearChildren() {
-        _hierarchyEdges.clear()
+    fun removeChild(node: KpiCalculationNode) {
+        _hierarchyEdges.removeIf { it.to == node }
     }
 
     fun calculateKpi(): KpiCalculationResult {
         val strategyData = hierarchyEdges.map {
-            Pair(it.target.score, it.weight)
+            Pair(it.to.score, it.weight)
         }
         score = when (calculationStrategy) {
             KpiStrategyId.RAW_VALUE_STRATEGY ->
@@ -59,7 +64,7 @@ internal class KpiCalculationNode(val kind: KpiId, val calculationStrategy: KpiS
                 kpiResult = node.score,
                 children = node.hierarchyEdges.map {
                     KpiResultEdge(
-                        target = to(it.target),
+                        target = to(it.to),
                         weight = it.weight
                     )
                 }
@@ -67,10 +72,17 @@ internal class KpiCalculationNode(val kind: KpiId, val calculationStrategy: KpiS
         }
 
         fun from(node: KpiNode): KpiCalculationNode {
-            val calcNode = KpiCalculationNode(node.kpiId, calculationStrategy = node.strategyType)
+            return from(node, parent = null)
+        }
+
+        private fun from(node: KpiNode, parent: KpiCalculationNode? = null): KpiCalculationNode {
+
+            val calcNode =
+                KpiCalculationNode(kind = node.kpiId, parent = parent, calculationStrategy = node.strategyType)
             val children = node.children.map { child ->
                 KpiHierarchyEdge(
-                    target = from(child.target),
+                    to = from(child.target, calcNode),
+                    from = calcNode,
                     weight = child.weight
                 )
             }
diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiHierarchyEdge.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiHierarchyEdge.kt
index ddb5b74b8acf3c957762d16df1b480ee70edd2f5..03c1a0df664aa42b3c5c32edd90ea2a1205a33d1 100644
--- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiHierarchyEdge.kt
+++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiHierarchyEdge.kt
@@ -1,6 +1,7 @@
 package de.fraunhofer.iem.kpiCalculator.core.hierarchy
 
 internal data class KpiHierarchyEdge(
-    val target: KpiCalculationNode,
+    val from: KpiCalculationNode,
+    val to: KpiCalculationNode,
     val weight: Double,
 )
diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/AggregationKPICalculationStrategy.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/AggregationKPICalculationStrategy.kt
index c4fefe356f6f346cafa964d61c5c6571ceb0007e..fd7538a8106906c6b5ee9a35e1f9afd3d553af65 100644
--- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/AggregationKPICalculationStrategy.kt
+++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/AggregationKPICalculationStrategy.kt
@@ -12,15 +12,36 @@ object AggregationKPICalculationStrategy {
      * The method returns the KPIs value as well as the updated
      * KPIHierarchyEdgeDtos with the actual used weight.
      */
-    fun calculateKPI(childScores: List<Pair<KpiCalculationResult, Double>>): KpiCalculationResult {
+    fun calculateKPI(
+        childScores: List<Pair<KpiCalculationResult, Double>>,
+        considerIncomplete: Boolean = true
+    ): KpiCalculationResult {
 
         if (childScores.isEmpty()) {
             return KpiCalculationResult.Empty
         }
 
-        val successScores = childScores.filter { it.first is KpiCalculationResult.Success }
+        val incompleteResults = if (considerIncomplete) {
+            childScores.mapNotNull {
+                val res = it.first
+                if (res is KpiCalculationResult.Incomplete) {
+                    Pair(KpiCalculationResult.Success(score = res.score), it.second)
+                } else {
+                    null
+                }
+            }
+        } else {
+            emptyList()
+        }
+
+        val successScores = listOf(
+            childScores.filter { it.first is KpiCalculationResult.Success },
+            incompleteResults
+        ).flatten()
+
         val failed = childScores
-            .filter { it.first !is KpiCalculationResult.Success }
+            .filter { it.first is KpiCalculationResult.Error || (it.first is KpiCalculationResult.Empty) }
+
         val missingEdgeWeights = failed.sumOf { it.second }
 
         val additionalWeight =
@@ -38,7 +59,7 @@ object AggregationKPICalculationStrategy {
                 }.toInt()
 
 
-        if (failed.isNotEmpty()) {
+        if (incompleteResults.isNotEmpty()) {
             return KpiCalculationResult.Incomplete(
                 score = aggregation,
                 reason = "There were ${failed.size} elements missing during aggregation.",
diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/RatioKPICalculationStrategy.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/RatioKPICalculationStrategy.kt
index b08c3b1b108a338c43d70073d4cfa27a04a0cd4d..21ef54887989dfce393019fe365668debf6da2e4 100644
--- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/RatioKPICalculationStrategy.kt
+++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/RatioKPICalculationStrategy.kt
@@ -6,10 +6,28 @@ object RatioKPICalculationStrategy {
     /**
      * Returns smallerValue / biggerValue, regardless in which order the values are given.
      */
-    fun calculateKPI(childScores: List<Pair<KpiCalculationResult, Double>>): KpiCalculationResult {
+    fun calculateKPI(
+        childScores: List<Pair<KpiCalculationResult, Double>>,
+        considerIncomplete: Boolean = true
+    ): KpiCalculationResult {
 
-        val successScores =
-            childScores.map { it.first }.filterIsInstance<KpiCalculationResult.Success>()
+        val incompleteResults = if (considerIncomplete) {
+            childScores.mapNotNull {
+                val res = it.first
+                if (res is KpiCalculationResult.Incomplete) {
+                    KpiCalculationResult.Success(res.score)
+                } else {
+                    null
+                }
+            }
+        } else {
+            emptyList()
+        }
+
+        val successScores = listOf(
+            childScores.map { it.first }.filterIsInstance<KpiCalculationResult.Success>(),
+            incompleteResults
+        ).flatten()
 
         if (successScores.size != 2) {
             return KpiCalculationResult.Error(
@@ -18,23 +36,36 @@ object RatioKPICalculationStrategy {
             )
         }
 
-        val firstValue = successScores.first().score
-        val secondValue = successScores[1].score
+        val biggerValue = if (successScores.first().score > successScores[1].score) {
+            successScores.first().score
+        } else {
+            successScores.last().score
+        }
 
-        if (firstValue == secondValue && secondValue == 0) {
+        val smallerValue = if (successScores.first().score < successScores[1].score) {
+            successScores.first().score
+        } else {
+            successScores[1].score
+        }
+
+        if (biggerValue == smallerValue && smallerValue == 0) {
             return KpiCalculationResult.Success(0)
         }
-        try {
-            if (firstValue >= secondValue) {
-                return KpiCalculationResult.Success(
-                    score = ((secondValue.toDouble() / firstValue.toDouble()) * 100).toInt()
+
+        return try {
+            if (incompleteResults.isEmpty()) {
+                KpiCalculationResult.Success(
+                    score = ((smallerValue.toDouble() / biggerValue.toDouble()) * 100).toInt()
+                )
+            } else {
+                KpiCalculationResult.Incomplete(
+                    score = ((smallerValue.toDouble() / biggerValue.toDouble()) * 100).toInt(),
+                    reason = "Incomplete results.",
+                    missingWeights = 0.0
                 )
             }
-            return KpiCalculationResult.Success(
-                score = ((firstValue.toDouble() / secondValue.toDouble()) * 100).toInt()
-            )
         } catch (e: Exception) {
-            return KpiCalculationResult.Error(e.message ?: e.toString())
+            KpiCalculationResult.Error(e.message ?: e.toString())
         }
     }
 }
diff --git a/kpi-calculator/core/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculatorTest.kt b/kpi-calculator/core/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculatorTest.kt
index dfffba34ccd4e5491dc73d43dc9743027852a397..a5f27f67bb46c39ea778113ffaf9396e76922929 100644
--- a/kpi-calculator/core/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculatorTest.kt
+++ b/kpi-calculator/core/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculatorTest.kt
@@ -2,7 +2,7 @@ package de.fraunhofer.iem.kpiCalculator.core
 
 import de.fraunhofer.iem.kpiCalculator.model.kpi.KpiId
 import de.fraunhofer.iem.kpiCalculator.model.kpi.KpiStrategyId
-import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpiDto
+import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi
 import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.*
 import org.junit.jupiter.api.Test
 import kotlin.test.assertEquals
@@ -12,28 +12,28 @@ class KpiCalculatorTest {
 
     @Test
     fun calculateDefaultHierarchyKpis() {
-        val rawValueKpiDtos = listOf(
-            RawValueKpiDto(kind = KpiId.VULNERABILITY_SCORE, score = 8),
-            RawValueKpiDto(kind = KpiId.VULNERABILITY_SCORE, score = 9),
-            RawValueKpiDto(kind = KpiId.CHECKED_IN_BINARIES, score = 100),
-            RawValueKpiDto(kind = KpiId.COMMENTS_IN_CODE, score = 80),
-            RawValueKpiDto(kind = KpiId.NUMBER_OF_COMMITS, score = 90),
-            RawValueKpiDto(kind = KpiId.IS_DEFAULT_BRANCH_PROTECTED, score = 100),
-            RawValueKpiDto(kind = KpiId.NUMBER_OF_SIGNED_COMMITS, score = 80),
-            RawValueKpiDto(kind = KpiId.SECRETS, score = 80),
-            RawValueKpiDto(kind = KpiId.SAST_USAGE, score = 80),
-            RawValueKpiDto(kind = KpiId.DOCUMENTATION_INFRASTRUCTURE, score = 80),
+        val rawValueKpis = listOf(
+            RawValueKpi(kind = KpiId.VULNERABILITY_SCORE, score = 8),
+            RawValueKpi(kind = KpiId.VULNERABILITY_SCORE, score = 9),
+            RawValueKpi(kind = KpiId.CHECKED_IN_BINARIES, score = 100),
+            RawValueKpi(kind = KpiId.COMMENTS_IN_CODE, score = 80),
+            RawValueKpi(kind = KpiId.NUMBER_OF_COMMITS, score = 90),
+            RawValueKpi(kind = KpiId.IS_DEFAULT_BRANCH_PROTECTED, score = 100),
+            RawValueKpi(kind = KpiId.NUMBER_OF_SIGNED_COMMITS, score = 80),
+            RawValueKpi(kind = KpiId.SECRETS, score = 80),
+            RawValueKpi(kind = KpiId.SAST_USAGE, score = 80),
+            RawValueKpi(kind = KpiId.DOCUMENTATION_INFRASTRUCTURE, score = 80),
         )
-        val res = KpiCalculator.calculateKpis(DefaultHierarchy.get(), rawValueKpiDtos)
+        val res = KpiCalculator.calculateKpis(DefaultHierarchy.get(), rawValueKpis)
         println(res)
     }
 
     @Test
     fun calculateMaxKpis() {
-        val rawValueKpiDtos = listOf(
-            RawValueKpiDto(kind = KpiId.VULNERABILITY_SCORE, score = 82),
-            RawValueKpiDto(kind = KpiId.VULNERABILITY_SCORE, score = 90),
-            RawValueKpiDto(kind = KpiId.VULNERABILITY_SCORE, score = 65),
+        val rawValueKpis = listOf(
+            RawValueKpi(kind = KpiId.VULNERABILITY_SCORE, score = 82),
+            RawValueKpi(kind = KpiId.VULNERABILITY_SCORE, score = 90),
+            RawValueKpi(kind = KpiId.VULNERABILITY_SCORE, score = 65),
         )
 
         val root = KpiNode(
@@ -58,7 +58,7 @@ class KpiCalculatorTest {
         )
         val hierarchy = KpiHierarchy.create(root)
 
-        val res = KpiCalculator.calculateKpis(hierarchy, rawValueKpiDtos)
+        val res = KpiCalculator.calculateKpis(hierarchy, rawValueKpis)
         val result = res.kpiResult
 
         if (result is KpiCalculationResult.Success) {
@@ -70,10 +70,10 @@ class KpiCalculatorTest {
 
     @Test
     fun calculateMaxKpisIncomplete() {
-        val rawValueKpiDtos = listOf(
-            RawValueKpiDto(kind = KpiId.VULNERABILITY_SCORE, score = 82),
-            RawValueKpiDto(kind = KpiId.VULNERABILITY_SCORE, score = 90),
-            RawValueKpiDto(kind = KpiId.VULNERABILITY_SCORE, score = 65),
+        val rawValueKpis = listOf(
+            RawValueKpi(kind = KpiId.VULNERABILITY_SCORE, score = 82),
+            RawValueKpi(kind = KpiId.VULNERABILITY_SCORE, score = 90),
+            RawValueKpi(kind = KpiId.VULNERABILITY_SCORE, score = 65),
         )
 
         val root = KpiNode(
@@ -106,9 +106,10 @@ class KpiCalculatorTest {
         )
         val hierarchy = KpiHierarchy.create(root)
 
-        val res = KpiCalculator.calculateKpis(hierarchy, rawValueKpiDtos)
+        val res = KpiCalculator.calculateKpis(hierarchy, rawValueKpis)
         val result = res.kpiResult
 
+        println(res)
         if (result is KpiCalculationResult.Incomplete) {
             assertEquals(90, result.score)
         } else {
diff --git a/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/adapter/AdapterResult.kt b/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/adapter/AdapterResult.kt
index bdb5acf4be959ea80ecaaa0466098707a0ab3020..02d35a75ccc9f84ebe9c370a0fca80d538a32562 100644
--- a/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/adapter/AdapterResult.kt
+++ b/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/adapter/AdapterResult.kt
@@ -1,10 +1,10 @@
 package de.fraunhofer.iem.kpiCalculator.model.adapter
 
-import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpiDto
+import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi
 
 enum class ErrorType { DATA_VALIDATION_ERROR }
 
 sealed class AdapterResult {
-    data class Success(val rawValueKpiDto: RawValueKpiDto) : AdapterResult()
+    data class Success(val rawValueKpi: RawValueKpi) : AdapterResult()
     data class Error(val type: ErrorType) : AdapterResult()
 }
diff --git a/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/RawValueKpi.kt b/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/RawValueKpi.kt
new file mode 100644
index 0000000000000000000000000000000000000000..916cf1538fe402c4d309ca7de26a0a7560815f17
--- /dev/null
+++ b/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/RawValueKpi.kt
@@ -0,0 +1,3 @@
+package de.fraunhofer.iem.kpiCalculator.model.kpi
+
+data class RawValueKpi(val kind: KpiId, val score: Int)
diff --git a/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/RawValueKpiDto.kt b/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/RawValueKpiDto.kt
deleted file mode 100644
index ef3e322c56eecc88e57ad5b447c297753ae3c5a3..0000000000000000000000000000000000000000
--- a/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/RawValueKpiDto.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package de.fraunhofer.iem.kpiCalculator.model.kpi
-
-data class RawValueKpiDto(val kind: KpiId, val score: Int)