Skip to content

Commit

Permalink
Fixes #24829: Replace compliance chart with Score chart and and new d…
Browse files Browse the repository at this point in the history
…etails score charts
  • Loading branch information
VinceMacBuche committed May 2, 2024
1 parent b20096e commit 6e2f39e
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ function homePage (
, nodeCompliance
, nodeComplianceColors
, nodeCount
, scoreDetails
) {
var opts = {
lines: 12, // The number of lines to draw
Expand Down Expand Up @@ -185,6 +186,19 @@ function homePage (
var complianceHColors = nodeCompliance.colors.map(x => complianceHoverColors[x]);
doughnutChart('nodeCompliance', nodeCompliance, allNodes, nodeCompliance.colors, complianceHColors);
}


scoreDetails.forEach(function(score) {
$("#scoreBreakdown .node-charts").append(
`<div class="node-chart">
<h4 class="text-center">${score.name}</h4>
<canvas id="score${score.scoreId}" > </canvas>
<div id="score${score.scoreId}-legend"></div>
</div>`)
var complianceHColors = score.data.colors.map(x => complianceHoverColors[x]);
doughnutChart('score'+ score.scoreId, score.data, score.count, score.data.colors, complianceHColors);
})

initBsTooltips();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import com.normation.rudder.domain.logger.TimingDebugLogger
import com.normation.rudder.domain.reports.ComplianceLevel
import com.normation.rudder.facts.nodes.CoreNodeFact
import com.normation.rudder.facts.nodes.QueryContext
import com.normation.rudder.score.ScoreValue
import com.normation.rudder.users.CurrentUser
import com.normation.zio.*
import net.liftweb.common.*
Expand All @@ -69,10 +70,21 @@ import net.liftweb.http.js.JsCmds.*
import scala.collection.MapView
import scala.xml.*

sealed trait ComplianceLevelPieChart {
def color: String
def label: String
def value: Int
case class ScoreChart(scoreValue: ScoreValue, value: Int) {
def color: String = {

import com.normation.rudder.score.ScoreValue.*
scoreValue match {
case A => "#13beb7"
case B => "#68c96a"
case C => "#b3d337"
case D => "#fedc04"
case E => "#f0940e"
case F => "#da291c"
case NoScore => ""
}
}
def label: String = scoreValue.value

def jsValue: JsArray = {
JsArray(label, value)
Expand All @@ -83,36 +95,6 @@ sealed trait ComplianceLevelPieChart {
}
}

final case class DisabledChart(value: Int) extends ComplianceLevelPieChart {
val label = "Reports Disabled"
val color = "#B1BBCB"
}

final case class GreenChart(value: Int) extends ComplianceLevelPieChart {
val label = "Perfect (100%)"
val color = "#13BEB7"
}

final case class BlueChart(value: Int) extends ComplianceLevelPieChart {
val label = "Good (> 75%)"
val color = "#B1EDA4"
}

final case class OrangeChart(value: Int) extends ComplianceLevelPieChart {
val label = "Average (> 50%)"
val color = "#EF9600"
}

final case class RedChart(value: Int) extends ComplianceLevelPieChart {
val label = "Poor (< 50%)"
val color = "#DA291C"
}

final case class PendingChart(value: Int) extends ComplianceLevelPieChart {
val label = "Applying"
val color = "#5bc0de"
}

object HomePageUtils {
// Format different version naming type into one
def formatAgentVersion(version: String): String = {
Expand Down Expand Up @@ -161,6 +143,7 @@ class HomePage extends StatefulSnippet {
private[this] val rudderDit = RudderConfig.rudderDit
private[this] val reportingService = RudderConfig.reportingService
private[this] val roRuleRepo = RudderConfig.roRuleRepository
private[this] val scoreService = RudderConfig.rci.scoreService

override val dispatch: DispatchIt = {
case "pendingNodes" => pendingNodes
Expand Down Expand Up @@ -200,11 +183,6 @@ class HomePage extends StatefulSnippet {

def getAllCompliance(): NodeSeq = {

sealed trait ChartType
case object PendingChartType extends ChartType
case object DisabledChartType extends ChartType
final case class ColoredChartType(value: Double) extends ChartType

(for {
n2 <- currentTimeMillis
userRules <- roRuleRepo.getIds()
Expand Down Expand Up @@ -235,6 +213,8 @@ class HomePage extends StatefulSnippet {
n5 <- currentTimeMillis
_ <- TimingDebugLoggerPure.trace(s"Compute global compliance in: ${n5 - n4}ms")
_ <- TimingDebugLoggerPure.debug(s"Compute compliance: ${n5 - n2}ms")
scores <- scoreService.getAll()
existingScore <- scoreService.getAvailableScore()
} yield {

// log global compliance info (useful for metrics on number of components and log data analysis)
Expand All @@ -252,52 +232,25 @@ class HomePage extends StatefulSnippet {
* Note: node without reports are also put in "pending".
*/

val complianceByNode: List[ChartType] = compliancePerNodes.values.map { r =>
if (r.pending == r.total) { PendingChartType }
else if (r.reportsDisabled == r.total) { DisabledChartType }
else { ColoredChartType(r.withoutPending.computePercent().compliance) }
}.toList

val complianceDiagram: List[ComplianceLevelPieChart] = (complianceByNode.groupBy { compliance =>
compliance match {
case PendingChartType => PendingChart
case DisabledChartType => DisabledChart
case ColoredChartType(100) => GreenChart
case ColoredChartType(x) if x >= 75 => BlueChart
case ColoredChartType(x) if x >= 50 => OrangeChart
case ColoredChartType(_) => RedChart
}
}.map {
case (PendingChart, compliance) => PendingChart(compliance.size)
case (DisabledChart, compliance) => DisabledChart(compliance.size)
case (GreenChart, compliance) => GreenChart(compliance.size)
case (BlueChart, compliance) => BlueChart(compliance.size)
case (OrangeChart, compliance) => OrangeChart(compliance.size)
case (RedChart, compliance) => RedChart(compliance.size)
case (_, compliance) => RedChart(compliance.size)
}).toList

val sorted = complianceDiagram.sortWith {
case (_: PendingChart, _) => false
case (_: DisabledChart, _) => false
case (_: GreenChart, _) => false
case (_: BlueChart, _: GreenChart) => true
case (_: BlueChart, _) => false
case (_: OrangeChart, (_: GreenChart | _: BlueChart)) => true
case (_: OrangeChart, _) => false
case (_: RedChart, _) => true
val numberOfPendingNodes = compliancePerNodes.values.filter(r => r.pending == r.total).size

val complianceDiagram: List[ScoreChart] =
scores.values.groupBy(_.value).map(v => ScoreChart(v._1, v._2.size)).toList.sortBy(_.scoreValue.value).reverse

val detailsScore = scores.values.flatMap(_.details).toList.groupBy(_.scoreId).map { c =>
(c._1, c._2.groupBy(_.value).map(v => ScoreChart(v._1, v._2.size)).toList.sortBy(_.scoreValue.value).reverse)
}

val numberOfNodes = complianceByNode.size
val pendingNodes = complianceDiagram.collectFirst { case p: PendingChart => p.value } match {
val numberOfNodes = compliancePerNodes.values.size
val pendingNodes = numberOfPendingNodes match {

case None =>
case 0 =>
JsObj(
"pending" -> JsNull,
"active" -> numberOfNodes
)

case Some(pending) =>
case pending =>
JsObj(
"pending" ->
JsObj(
Expand All @@ -308,14 +261,39 @@ class HomePage extends StatefulSnippet {
)
}

val diagramData = sorted.foldLeft((Nil: List[JsExp], Nil: List[JsExp], Nil: List[JsExp])) {
val diagramData = complianceDiagram.foldLeft((Nil: List[JsExp], Nil: List[JsExp], Nil: List[JsExp])) {
case ((labels, values, colors), diag) => (diag.label :: labels, diag.value :: values, diag.color :: colors)
}

val data =
JsObj("labels" -> JsArray(diagramData._1), "values" -> JsArray(diagramData._2), "colors" -> JsArray(diagramData._3))

val diagramColor = JsObj(sorted.map(_.jsColor)*)
val diagramColor = JsObj(complianceDiagram.map(_.jsColor)*)
val scoreDetailsData = JsArray(for {
(scoreId, detailChart) <- detailsScore.toList.sortBy(_._1)
} yield {

val diagramDetailData = detailChart.foldLeft((Nil: List[JsExp], Nil: List[JsExp], Nil: List[JsExp])) {
case ((labels, values, colors), diag) => (diag.label :: labels, diag.value :: values, diag.color :: colors)
}
val detailData = {
JsObj(
"labels" -> JsArray(diagramDetailData._1),
"values" -> JsArray(diagramDetailData._2),
"colors" -> JsArray(diagramDetailData._3)
)
}

val detailDiagramColor = JsObj(complianceDiagram.map(_.jsColor)*)
val name: String = existingScore.find(_._1 == scoreId).map(_._2).getOrElse(scoreId)
JsObj(
"scoreId" -> scoreId,
"data" -> detailData,
"colors" -> detailDiagramColor,
"count" -> detailChart.map(_.value).sum,
"name" -> name
)
})

// Data used for compliance bar, compliance without pending
val (complianceBar, globalCompliance) = global match {
Expand All @@ -336,6 +314,7 @@ class HomePage extends StatefulSnippet {
, ${data.toJsCmd}
, ${diagramColor.toJsCmd}
, ${pendingNodes.toJsCmd}
, ${scoreDetailsData.toJsCmd}
)""")))
}).either.runNow match {
case Right(homePageCompliance) => homePageCompliance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ <h5 class="text-center" id="globalComplianceStats"></h5>
<div class="row">
<div class="col-xl-4 col-lg-4">
<div id="nodeOverview" class="card">
<h5 class="card-title py-2 px-3 mb-0">Nodes by overall compliance</h5>
<h5 class="card-title py-2 px-3 mb-0">Global score breakdown</h5>
<div class="card-body text-center">
<div class="node-compliance-chart">
<canvas id="nodeCompliance"></canvas>
Expand Down Expand Up @@ -117,6 +117,16 @@ <h4 class="text-center">By agent version</h4>
</div>
</div>
</div>

<div class="col-xl-12 col-lg-12" id="scoreBreakdown">
<div class="card">
<h5 class="card-title py-2 px-3 mb-0">Score breakdown</h5>
<div class="card-body text-center">
<div class="node-charts">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Expand Down

0 comments on commit 6e2f39e

Please sign in to comment.