Skip to content

Commit

Permalink
Merge pull request #3442 from cisagov/feature/CSET-2320
Browse files Browse the repository at this point in the history
Refactored icon and answer configuration to support IMR
  • Loading branch information
jekuipers authored Jul 31, 2023
2 parents d02e5f0 + ddbeec9 commit 41f4dab
Show file tree
Hide file tree
Showing 50 changed files with 1,958 additions and 289 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
////////////////////////////////
//
// Copyright 2023 Battelle Energy Alliance, LLC
//
//
////////////////////////////////
using CSETWebCore.DataLayer.Model;
using CSETWebCore.Helpers.ReportWidgets;
using CSETWebCore.Interfaces.AdminTab;
using CSETWebCore.Interfaces.Assessment;
using CSETWebCore.Interfaces.Crr;
using CSETWebCore.Interfaces.Demographic;
using CSETWebCore.Interfaces.Helpers;
using CSETWebCore.Interfaces.Reports;
using Microsoft.AspNetCore.Mvc;
using System.Xml.Linq;
using System.Linq;
using System.Collections.Generic;

namespace CSETWebCore.Api.Controllers
{
/// <summary>
/// Common API intended to supply report content for EDM, CRR and IMR.
/// </summary>
public class ReportsCmuController : Controller
{
private readonly ITokenManager _token;
private readonly ICrrScoringHelper _crr;
private readonly IAssessmentBusiness _assessment;
private readonly IDemographicBusiness _demographic;
private readonly IAssessmentUtil _assessmentUtil;
private readonly IAdminTabBusiness _adminTabBusiness;
private readonly IReportsDataBusiness _report;
private readonly CSETContext _context;

public ReportsCmuController(ITokenManager token, ICrrScoringHelper crr, IAssessmentBusiness assessment,
IDemographicBusiness demographic, IReportsDataBusiness report,
IAssessmentUtil assessmentUtil, IAdminTabBusiness admin, CSETContext context)
{
_token = token;
_crr = crr;
_assessment = assessment;
_demographic = demographic;
_report = report;
_assessmentUtil = assessmentUtil;
_adminTabBusiness = admin;
_context = context;
}


/// <summary>
/// Gets the charts for Mil1 Performance and returns them in a list of raw HTML strings.
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("api/report/cmu/goalperformance")]
public IActionResult GetGoalPerformance()
{
var assessmentId = _token.AssessmentForUser();
_crr.InstantiateScoringHelper(assessmentId);
var XDocument = _crr.XDoc;

List<string> scoreBarCharts = new List<string>();
List<object> stackedBarCharts = new List<object>();

foreach (XElement domain in XDocument.Root.Elements())
{
var domainScores = _crr.MIL1DomainAnswerDistrib(domain.Attribute("abbreviation").Value);
var barChartInput = new BarChartInput() { Height = 50, Width = 75 };
barChartInput.IncludePercentFirstBar = true;
barChartInput.AnswerCounts = new List<int> { domainScores.Green, domainScores.Yellow, domainScores.Red };
scoreBarCharts.Add(new ScoreBarChart(barChartInput).ToString());

var goals = domain.Descendants("Mil").FirstOrDefault().Descendants("Goal");

foreach (XElement goal in goals)
{
var goalScores = _crr.GoalAnswerDistrib(domain.Attribute("abbreviation").Value,
goal.Attribute("abbreviation").Value);
var stackedBarChartInput = new BarChartInput() { Height = 10, Width = 265 };
stackedBarChartInput.AnswerCounts = new List<int> { goalScores.Green, goalScores.Yellow, goalScores.Red };

stackedBarCharts.Add(new { Title = goal.Attribute("title").Value, Chart = new ScoreStackedBarChart(stackedBarChartInput).ToString() });
}
}

return Ok(new { ScoreBarCharts = scoreBarCharts, StackedBarCharts = stackedBarCharts });
}


public IActionResult Index()
{
return View();
}
}
}
2 changes: 2 additions & 0 deletions CSETWebNg/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ import { HydroActionItemsReportComponent } from './reports/hydro/hydro-action-it
import { SdAnswerSummaryComponent } from './assessment/results/sd/sd-answer-summary/sd-answer-summary.component';
import { SdAnswerSummaryReportComponent } from './reports/sd/sd-answer-summary-report/sd-answer-summary-report.component';
import { KeyReportComponent } from './assessment/results/reports/key-report/key-report.component';
import { ImrReportComponent } from './reports/imr/imr-report/imr-report.component';

const appRoutes: Routes = [

Expand Down Expand Up @@ -477,6 +478,7 @@ const appRoutes: Routes = [
{ path: 'crrCommentsMarked', component: CrrCommentsMarkedComponent },
{ path: 'rrareport', component: RraReportComponent },
{ path: 'rraDeficiencyReport', component: RraDeficiencyComponent },
{ path: 'imrreport', component: ImrReportComponent },
{ path: 'vadrDeficiencyReport', component: VadrDeficiencyComponent },
{ path: 'vadrOpenEndedReport', component: OpenEndedQuestionsComponent },
{ path: 'cisSurveyReport', component: CisSurveyComponent },
Expand Down
8 changes: 8 additions & 0 deletions CSETWebNg/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,10 @@ import { HydroActionItemsReportComponent } from './reports/hydro/hydro-action-it
import { SdAnswerSummaryComponent } from './assessment/results/sd/sd-answer-summary/sd-answer-summary.component';
import { SdAnswerSummaryReportComponent } from './reports/sd/sd-answer-summary-report/sd-answer-summary-report.component';
import { KeyReportComponent } from './assessment/results/reports/key-report/key-report.component';
import { ImrReportComponent } from './reports/imr/imr-report/imr-report.component';
import { CmuPerformanceSummaryComponent } from './reports/cmu/cmu-performance-summary/cmu-performance-summary.component';
import { CmuGoalPerfStackedBarComponent } from './reports/cmu/cmu-goal-perf-stacked-bar/cmu-goal-perf-stacked-bar.component';
import { CmuResultsDetailComponent } from './reports/cmu/cmu-results-detail/cmu-results-detail.component';



Expand Down Expand Up @@ -1017,6 +1021,8 @@ import { KeyReportComponent } from './assessment/results/reports/key-report/key-
CrrNistCsfCatSummaryComponent,
CrrNistCsfCatPerformanceComponent,
CrrSideTocComponent,
CmuPerformanceSummaryComponent,
ImrReportComponent,
ReferencesBlockComponent,
NewAssessmentDialogComponent,
CrrMainTocComponent,
Expand Down Expand Up @@ -1113,6 +1119,8 @@ import { KeyReportComponent } from './assessment/results/reports/key-report/key-
SdAnswerSummaryReportComponent,
SdAnswerSummaryComponent,
KeyReportComponent,
CmuGoalPerfStackedBarComponent,
CmuResultsDetailComponent,
],
providers: [
ConfigService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ <h3>{{ groupingTitle }}</h3>


<p class="mb-4" *ngIf="!!groupings">
Unanswered questions are calculated as a '{{questionsSvc.answerButtonLabel(modelId, this.assessSvc.assessment?.maturityModel?.modelId == 12 ? 'NI' : 'N')}}' response
Unanswered questions are calculated as a '{{questionsSvc.answerButtonLabel(modelName, this.assessSvc.assessment?.maturityModel?.modelId == 12 ? 'NI' : 'N')}}' response
</p>

<div class="mb-4" *ngIf="this.assessSvc.assessment?.maturityModel?.modelId == 11">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export class MaturityQuestionsComponent implements OnInit, AfterViewInit {
this.assessSvc.assessment.maturityModel.answerOptions = response.answerOptions;
this.filterSvc.answerOptions = response.answerOptions;
this.filterSvc.maturityModelId = response.modelId;
this.filterSvc.maturityModelName = response.modelName;

this.pageTitle = this.questionsAlias + ' - ' + this.modelName;
this.glossarySvc.glossaryEntries = response.glossary;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@
<div class="btn-group btn-group-toggle answer-group" data-toggle="buttons">
<label *ngFor="let option of answerOptions">
<label *ngIf="(option !== 'NA' && option !== 'A')" class="btn form-check-label"
[class]="questionsSvc.answerOptionCss(maturityModelId, option)" [class.answer-selected]="q.answer === option"
[class]="questionsSvc.answerOptionCss(maturityModelName, option)" [class.answer-selected]="q.answer === option"
[matTooltip]="this.displayTooltip(maturityModelId, option)" [disabled]="option === 'N' && autoGenerateInProgress">
<input name="q_{{q.questionId}}" id="radio-button" class="form-check-input" type="radio" autocomplete="off"
(click)="storeAnswer(q, option)" [checked]="q.answer === option">
{{ questionsSvc.answerButtonLabel(maturityModelId, option) }}
{{ questionsSvc.answerButtonLabel(maturityModelName, option) }}
</label>
</label>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export class QuestionBlockIseComponent implements OnInit {

autoGenerateInProgress: boolean = false;
maturityModelId: number;
maturityModelName: string;


/**
Expand Down Expand Up @@ -119,6 +120,7 @@ export class QuestionBlockIseComponent implements OnInit {
if (this.assessSvc.assessment.maturityModel.modelName != null) {
this.answerOptions = this.assessSvc.assessment.maturityModel.answerOptions;
this.maturityModelId = this.assessSvc.assessment.maturityModel.modelId;
this.maturityModelName = this.assessSvc.assessment.maturityModel.modelName;

this.iseExamLevel = this.ncuaSvc.getExamLevel();

Expand Down Expand Up @@ -197,8 +199,8 @@ export class QuestionBlockIseComponent implements OnInit {
return "break-all";
}

displayTooltip(maturityModelId: number, option: string) {
let toolTip = this.questionsSvc.answerDisplayLabel(maturityModelId, option);
displayTooltip(maturityModelName: string, option: string) {
let toolTip = this.questionsSvc.answerDisplayLabel(maturityModelName, option);
if (toolTip === 'Yes' || toolTip === 'No') {
toolTip = "";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@
<!-- build the list of answer choice buttons -->
<div class="btn-group btn-group-toggle answer-group" data-toggle="buttons">
<label *ngFor="let option of answerOptions" class="btn form-check-label d-flex align-items-center"
[class]="questionsSvc.answerOptionCss(maturityModelId, option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerButtonTooltip(maturityModelId, option)">
[class]="questionsSvc.answerOptionCss(maturityModelName, option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerButtonTooltip(maturityModelName, option)">
<input name="q_{{q.questionId}}" class="form-check-input" type="radio" autocomplete="off"
(click)="storeAnswer(q, option)" [checked]="q.answer === option">
{{questionsSvc.answerButtonLabel(maturityModelId, option)}}
{{questionsSvc.answerButtonLabel(maturityModelName, option)}}
</label>

<!-- Mark For Review Flag -->
Expand Down Expand Up @@ -148,11 +148,11 @@
<!-- build the list of answer options -->
<div class="btn-group btn-group-toggle answer-group ml-0" data-toggle="buttons">
<label *ngFor="let option of answerOptions" class="btn form-check-label"
[class]="questionsSvc.answerOptionCss(maturityModelId, option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerButtonTooltip(maturityModelId, option)">
[class]="questionsSvc.answerOptionCss(maturityModelName, option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerButtonTooltip(maturityModelName, option)">
<input name="q_{{q.questionId}}" class="form-check-input" type="radio" autocomplete="off"
(click)="storeAnswer(q, option)" [checked]="q.answer === option">
{{questionsSvc.answerButtonLabel(maturityModelId, option)}}
{{questionsSvc.answerButtonLabel(maturityModelName, option)}}
</label>

<!-- Mark For Review Flag -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export class QuestionBlockMaturityComponent implements OnInit {
showQuestionIds = false;

maturityModelId: number;
maturityModelName: string;


/**
Expand All @@ -86,6 +87,7 @@ export class QuestionBlockMaturityComponent implements OnInit {
if (this.assessSvc.assessment.maturityModel.modelName != null) {
this.answerOptions = this.assessSvc.assessment.maturityModel.answerOptions;
this.maturityModelId = this.assessSvc.assessment.maturityModel.modelId;
this.maturityModelName = this.assessSvc.assessment.maturityModel.modelName;
}

this.refreshReviewIndicator();
Expand Down Expand Up @@ -131,13 +133,12 @@ export class QuestionBlockMaturityComponent implements OnInit {

/**
* Determines if the level indicator should show or be
* hidden. Someday this behavior might be stored
* in the database as some model-specific behavior.
* hidden. Use config moduleBehavior to define this.
*/
showLevelIndicator(q): boolean {
// CPG (11) does not have levels - don't show the indicator
if ([11].indexOf(q.maturityModelId) >= 0) {
return false;
const behavior = this.configSvc.config.moduleBehaviors.find(m => m.moduleName == this.assessSvc.assessment.maturityModel.modelName)
if (!!behavior) {
return behavior.showMaturityLevelBadge ?? true;
}

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@
<div class="btn-group btn-group-toggle answer-group" style="margin-top:0.5rem;"
data-toggle="buttons">
<label *ngFor="let option of answerOptions" class="btn form-check-label"
[class]="questionsSvc.answerOptionCss(maturityModelId, option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerDisplayLabel(maturityModelId, option)">
[class]="questionsSvc.answerOptionCss(maturityModelName, option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerDisplayLabel(maturityModelName, option)">
<input name="q_{{q.questionId}}" class="form-check-input" type="radio"
autocomplete="off" (click)="storeAnswer(q, option)" [checked]="q.answer === option">
{{questionsSvc.answerButtonLabel(maturityModelId, option)}}
{{questionsSvc.answerButtonLabel(maturityModelName, option)}}
</label>


Expand Down Expand Up @@ -180,11 +180,11 @@
<!-- build the list of answer options -->
<div class="btn-group btn-group-toggle answer-group ml-0 mt-2" data-toggle="buttons">
<label *ngFor="let option of answerOptions" class="btn form-check-label"
[class]="questionsSvc.answerOptionCss(maturityModelId, option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerDisplayLabel(maturityModelId, option)">
[class]="questionsSvc.answerOptionCss(maturityModelName, option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerDisplayLabel(maturityModelName, option)">
<input name="q_{{q.questionId}}" class="form-check-input" type="radio"
autocomplete="off" (click)="storeAnswer(q, option)" [checked]="q.answer === option">
{{questionsSvc.answerButtonLabel(maturityModelId, option)}}
{{questionsSvc.answerButtonLabel(maturityModelName, option)}}
</label>


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,19 @@
[class.answer-selected]="mySubCategory.subCategoryAnswer === 'Y'">
<input name="subcat_{{ mySubCategory.subCategoryId }}" class="form-check-input" type="radio"
autocomplete="off" (click)="setBlockAnswer('Y')" tabindex="0"
[checked]="mySubCategory.subCategoryAnswer === 'Y'">{{questionsSvc.answerButtonLabel(0, 'Y')}}
[checked]="mySubCategory.subCategoryAnswer === 'Y'">{{questionsSvc.answerButtonLabel('', 'Y')}}
</label>
<label *ngIf="showThisOption('N')" class="btn btn-no form-check-label"
[class.answer-selected]="mySubCategory.subCategoryAnswer === 'N'">
<input name="subcat_{{ mySubCategory.subCategoryId }}" class="form-check-input" type="radio"
autocomplete="off" (click)="setBlockAnswer('N')" tabindex="0"
[checked]="mySubCategory.subCategoryAnswer === 'N'">{{questionsSvc.answerButtonLabel(0, 'N')}}
[checked]="mySubCategory.subCategoryAnswer === 'N'">{{questionsSvc.answerButtonLabel('', 'N')}}
</label>
<label *ngIf="showThisOption('NA')" class="btn btn-na form-check-label"
[class.answer-selected]="mySubCategory.subCategoryAnswer === 'NA'">
<input name="subcat_{{ mySubCategory.subCategoryId }}" class="form-check-input" type="radio"
autocomplete="off" (click)="setBlockAnswer('NA')" tabindex="0"
[checked]="mySubCategory.subCategoryAnswer === 'NA'">{{questionsSvc.answerButtonLabel(0, 'NA')}}
[checked]="mySubCategory.subCategoryAnswer === 'NA'">{{questionsSvc.answerButtonLabel('', 'NA')}}
</label>
<label *ngIf="showThisOption('A')" class="btn btn-na form-check-label" style="visibility: hidden;">
<input name="subcat_{{ mySubCategory.subCategoryId }}" class="form-check-input" type="radio" tabindex="0"
Expand Down Expand Up @@ -111,11 +111,11 @@
<div>
<div class="btn-group btn-group-toggle answer-group" data-toggle="buttons">
<label *ngFor="let option of answerOptions" class="btn form-check-label"
[class]="questionsSvc.answerOptionCss(0, option)" [class.answer-selected]="q.answer === option"
[class]="questionsSvc.answerOptionCss('', option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerDisplayLabel(maturityModelId, option)">
<input name="q_{{q.questionId}}" class="form-check-input" type="radio" autocomplete="off"
(click)="storeAnswer(q, option)" [checked]="q.answer === option">
{{questionsSvc.answerButtonLabel(maturityModelId, option)}}
{{questionsSvc.answerButtonLabel(maturityModelName, option)}}
</label>

<!-- Mark For Review Flag -->
Expand Down Expand Up @@ -152,11 +152,11 @@
<div>
<div class="btn-group btn-group-toggle answer-group ml-0" data-toggle="buttons">
<label *ngFor="let option of answerOptions" class="btn form-check-label align-items-center"
[class]="questionsSvc.answerOptionCss(0, option)" [class.answer-selected]="q.answer === option"
[class]="questionsSvc.answerOptionCss('', option)" [class.answer-selected]="q.answer === option"
[matTooltip]="questionsSvc.answerDisplayLabel(maturityModelId, option)">
<input name="q_{{q.questionId}}" class="form-check-input" type="radio" autocomplete="off"
(click)="storeAnswer(q, option)" [checked]="q.answer === option">
{{questionsSvc.answerButtonLabel(maturityModelId, option)}}
{{questionsSvc.answerButtonLabel(maturityModelName, option)}}
</label>

<!-- Mark For Review Flag -->
Expand Down
Loading

0 comments on commit 41f4dab

Please sign in to comment.