diff --git a/CSETWebNg/src/app/app.module.ts b/CSETWebNg/src/app/app.module.ts
index 868f1426af..7362407bab 100644
--- a/CSETWebNg/src/app/app.module.ts
+++ b/CSETWebNg/src/app/app.module.ts
@@ -302,6 +302,8 @@ import { CommonModule } from '@angular/common';
import { NavBackNextComponent } from './assessment/navigation/nav-back-next/nav-back-next.component';
import { CsetOriginComponent } from './initial/cset-origin/cset-origin.component';
import { ComplianceScoreComponent } from './assessment/results/mat-cmmc/chart-components/compliance-score/compliance-score.component';
+import { ScoreRangeComponent } from './assessment/results/score-range/score-range.component';
+import { ScoreRangesComponent } from './assessment/results/score-ranges/score-ranges.component';
import { CmmcStyleService } from './services/cmmc-style.service';
import { InherentRiskProfileComponent } from './acet/inherent-risk-profile/inherent-risk-profile.component';
import { IrpSectionComponent } from './reports/irp/irp.component';
@@ -840,6 +842,8 @@ import { AnalyticsResultsComponent } from './assessment/results/analytics-result
Cmmc2DomainResultsComponent,
SprsScoreComponent,
ComplianceScoreComponent,
+ ScoreRangeComponent,
+ ScoreRangesComponent,
ModelSelectComponent,
CategoryBlockComponent,
AskQuestionsComponent,
diff --git a/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.html b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.html
index 02065c9f87..a06d49ce18 100644
--- a/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.html
+++ b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.html
@@ -22,10 +22,20 @@
Assessment Analytics
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.ts b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.ts
index 8701b96f6d..599b319245 100644
--- a/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.ts
+++ b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.ts
@@ -48,6 +48,9 @@ export class AnalyticsResultsComponent implements OnInit {
@ViewChild('barCanvas') private barCanvas!: ElementRef;
private barChart!: Chart;
+ // result from API call
+ scoreBarData: any;
+
// Toggle state
dataType: "mySector" | "allSectors" = "mySector";
@@ -113,7 +116,14 @@ export class AnalyticsResultsComponent implements OnInit {
} else {
result = await this.analyticsSvc.getAnalyticResults(this.assessmentId, this.modelId, this.sectorId).toPromise();
}
- this.setData(result);
+ //this.setData(result);
+
+
+ this.scoreBarData = result;
+ this.sampleSize = result.sampleSize;
+
+
+
} catch (error) {
console.error('Error fetching analytics results', error);
}
diff --git a/CSETWebNg/src/app/assessment/results/score-range/score-range.component.html b/CSETWebNg/src/app/assessment/results/score-range/score-range.component.html
new file mode 100644
index 0000000000..26cbe4b0c4
--- /dev/null
+++ b/CSETWebNg/src/app/assessment/results/score-range/score-range.component.html
@@ -0,0 +1,33 @@
+
+
\ No newline at end of file
diff --git a/CSETWebNg/src/app/assessment/results/score-range/score-range.component.scss b/CSETWebNg/src/app/assessment/results/score-range/score-range.component.scss
new file mode 100644
index 0000000000..55ef941a5c
--- /dev/null
+++ b/CSETWebNg/src/app/assessment/results/score-range/score-range.component.scss
@@ -0,0 +1,3 @@
+rect, circle, line {
+ transition: all 0.5s ease;
+ }
\ No newline at end of file
diff --git a/CSETWebNg/src/app/assessment/results/score-range/score-range.component.ts b/CSETWebNg/src/app/assessment/results/score-range/score-range.component.ts
new file mode 100644
index 0000000000..dd573b16e6
--- /dev/null
+++ b/CSETWebNg/src/app/assessment/results/score-range/score-range.component.ts
@@ -0,0 +1,78 @@
+////////////////////////////////
+//
+// Copyright 2024 Battelle Energy Alliance, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+////////////////////////////////
+import { Component, Input, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-score-range',
+ standalone: false,
+ templateUrl: './score-range.component.html',
+ styleUrl: './score-range.component.scss'
+})
+export class ScoreRangeComponent implements OnInit {
+
+ @Input()
+ chartWidth: number;
+
+ containerWidth: number;
+
+
+
+ /**
+ * height this svg is rendered at
+ */
+ h = 50;
+
+ barH: number;
+
+ @Input()
+ label: string;
+
+ @Input()
+ min: number;
+
+ @Input()
+ max: number;
+
+ @Input()
+ median: number;
+
+ @Input()
+ myScore: number;
+
+ @Input()
+ myColor = "#0000aa";
+
+ rangeColor = "#87909e";
+
+ /**
+ * padding value to get things away from the left and right edge
+ */
+ p = 10;
+
+
+ ngOnInit(): void {
+ this.containerWidth = this.chartWidth * 1.05;
+ this.barH = this.h * .1;
+ }
+}
diff --git a/CSETWebNg/src/app/assessment/results/score-ranges/score-ranges.component.html b/CSETWebNg/src/app/assessment/results/score-ranges/score-ranges.component.html
new file mode 100644
index 0000000000..5d788d551a
--- /dev/null
+++ b/CSETWebNg/src/app/assessment/results/score-ranges/score-ranges.component.html
@@ -0,0 +1,63 @@
+
+
+
+
+
+ Current assessment score
+
+
+ |
+ Vertical bar is median score for sample
+
+
+
+
+
+
+ {{cat.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSETWebNg/src/app/assessment/results/score-ranges/score-ranges.component.scss b/CSETWebNg/src/app/assessment/results/score-ranges/score-ranges.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/CSETWebNg/src/app/assessment/results/score-ranges/score-ranges.component.ts b/CSETWebNg/src/app/assessment/results/score-ranges/score-ranges.component.ts
new file mode 100644
index 0000000000..0997a90b78
--- /dev/null
+++ b/CSETWebNg/src/app/assessment/results/score-ranges/score-ranges.component.ts
@@ -0,0 +1,83 @@
+////////////////////////////////
+//
+// Copyright 2024 Battelle Energy Alliance, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+////////////////////////////////
+import { Component, ElementRef, HostListener, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
+
+@Component({
+ selector: 'app-score-ranges',
+ standalone: false,
+ templateUrl: './score-ranges.component.html',
+ styleUrl: './score-ranges.component.scss'
+})
+export class ScoreRangesComponent implements OnInit, OnChanges {
+
+ @Input()
+ data: any;
+
+ categories: any[];
+
+ // categories: any[] = [
+ // { label: 'Invent', min: 10, max: 77, median: 42, myScore: 33},
+ // { label: 'Prevent', min: 40, max: 95, median: 61, myScore: 83},
+ // { label: 'Circumvent', min: 25, max: 54, median: 33, myScore: 50},
+ // { label: 'Dryer Vent', min: 0, max: 94, median: 67, myScore: 23},
+ // { label: 'Lament', min: 47, max: 62, median: 52, myScore: 47},
+ // { label: 'Intent', min: 8, max: 80, median: 63, myScore: 33},
+ // { label: 'Get Bent', min: 14, max: 58, median: 36, myScore: 29}
+ // ];
+
+ @Input()
+ chartWidth: number;
+
+ containerWidth: number;
+
+
+ @Input()
+ myColor: string;
+
+ ticks: any;
+
+ @ViewChild('myDiv') myDiv!: ElementRef;
+ divWidth: number | null = null;
+
+
+ /**
+ *
+ */
+ ngOnInit(): void {
+ this.containerWidth = this.chartWidth * 1.05;
+
+ // build scale
+ this.ticks = Array.from({ length: 11 }, (_, i) => ({
+ value: i * 10,
+ x: (i * this.chartWidth * .1)
+ }));
+ }
+
+ /**
+ *
+ */
+ ngOnChanges(changes: SimpleChanges): void {
+ this.categories = this.data?.categories;
+ }
+}
diff --git a/CSETWebNg/src/app/services/analytics.service.ts b/CSETWebNg/src/app/services/analytics.service.ts
index 09183dcae2..ba9c136045 100644
--- a/CSETWebNg/src/app/services/analytics.service.ts
+++ b/CSETWebNg/src/app/services/analytics.service.ts
@@ -45,23 +45,23 @@ export class AnalyticsService {
url += `§orId=${sectorId}`;
}
return this.http.get(url);
-}
+ }
getAnalyticsToken(username, password): any {
return this.http.post(
- this.analyticsUrl + 'auth/login', { "email":username, password }, this.headers
+ this.analyticsUrl + 'auth/login', { "email": username, password }, this.headers
);
}
postAnalyticsWithLogin(token): any {
-
+
return this.http.get(
- this.baseUrl + 'assessment/exportandsend?token='+token
+ this.baseUrl + 'assessment/exportandsend?token=' + token
);
}
// pingAnalyticsService(): any {
- // return this.http.get(this.analyticsUrl + 'ping/GetPing');
+ // return this.http.get(this.analyticsUrl + 'ping/GetPing');
// }
}
\ No newline at end of file
diff --git a/CSETWebNg/src/assets/settings/config.json b/CSETWebNg/src/assets/settings/config.json
index 3978bcfe7d..d2f48d2bbc 100644
--- a/CSETWebNg/src/assets/settings/config.json
+++ b/CSETWebNg/src/assets/settings/config.json
@@ -22,7 +22,7 @@
"port": "5000",
"apiIdentifier": "api"
},
- "csetAnalyticsUrl": "http://csetac:5210/api/analytics/maturity?",
+ "csetAnalyticsUrl": "http://localhost:5002/api/analytics/maturity/bars?",
"csetGithubApiUrl": "https://api.github.com/repos/cisagov/cset/releases/latest",
"helpContactEmail": "CSET_PMO@cisa.dhs.gov",
"helpContactPhone": "",
diff --git a/Database Scripts/Functions/func_MQ.func.sql b/Database Scripts/Functions/func_MQ.func.sql
index 6891159cf2..c4a2f8ccc8 100644
--- a/Database Scripts/Functions/func_MQ.func.sql
+++ b/Database Scripts/Functions/func_MQ.func.sql
@@ -1,3 +1,4 @@
+
-- =============================================
-- Author: Randy Woods
-- Create date: 10-OCT-2023
@@ -34,7 +35,10 @@ RETURNS
[Scope] [nvarchar](250) NULL,
[Recommend_Action] [nvarchar](max) NULL,
[Risk_Addressed] [nvarchar](max) NULL,
- [Services] [nvarchar](max) NULL
+ [Services] [nvarchar](max) NULL,
+ [Outcome] nvarchar(max) null,
+ [Security_Practice] nvarchar(max) null,
+ [Implementation_Guides] nvarchar(max) null
)
AS
BEGIN
diff --git a/Database Scripts/Stored Procedures/FillAll.proc.sql b/Database Scripts/Stored Procedures/FillAll.proc.sql
new file mode 100644
index 0000000000..22a52687c6
--- /dev/null
+++ b/Database Scripts/Stored Procedures/FillAll.proc.sql
@@ -0,0 +1,19 @@
+
+-- =============================================
+-- Author:
+-- Create date:
+-- Description:
+-- =============================================
+CREATE PROCEDURE [dbo].[FillAll]
+ -- Add the parameters for the stored procedure here
+ @Assessment_Id int
+AS
+BEGIN
+ -- SET NOCOUNT ON added to prevent extra result sets from
+ -- interfering with SELECT statements.
+ SET NOCOUNT ON;
+
+ -- Insert statements for procedure here
+ exec FillEmptyMaturityQuestionsForAnalysis @assessment_id
+ exec FillEmptyQuestionsForAnalysis @assessment_id
+END
diff --git a/Database Scripts/Stored Procedures/FillEmptyMaturityQuestionsForModel.proc.sql b/Database Scripts/Stored Procedures/FillEmptyMaturityQuestionsForModel.proc.sql
new file mode 100644
index 0000000000..e54a673f41
--- /dev/null
+++ b/Database Scripts/Stored Procedures/FillEmptyMaturityQuestionsForModel.proc.sql
@@ -0,0 +1,38 @@
+
+-- =============================================
+-- Author: Randy Woods
+-- Create date: 09/10/2024
+-- Description: Create empty data for questions that have not been filled out.
+-- This version of the proc is designed for deliberately fleshing out SSG questions
+-- because their relevance is not determined by AVAILABLE_MATURITY_MODELS.
+-- =============================================
+CREATE PROCEDURE [dbo].[FillEmptyMaturityQuestionsForModel]
+ @Assessment_Id int,
+ @Model_Id int
+AS
+BEGIN
+ DECLARE @result int;
+ begin
+ BEGIN TRANSACTION;
+ EXEC @result = sp_getapplock @Resource = '[Answer]', @LockMode = 'Exclusive';
+ INSERT INTO [dbo].[ANSWER] ([Question_Or_Requirement_Id],[Answer_Text],[Question_Type],[Assessment_Id])
+ select mq.Mat_Question_Id,Answer_Text = 'U', Question_Type='Maturity', Assessment_Id = @Assessment_Id
+ from [dbo].[MATURITY_QUESTIONS] mq
+ where Maturity_Model_Id = @Model_Id
+ and Mat_Question_Id not in
+ (select Question_Or_Requirement_Id from [dbo].[ANSWER]
+ where Assessment_Id = @Assessment_Id and Maturity_Model_Id = @Model_Id)
+ IF @result = -3
+ BEGIN
+ ROLLBACK TRANSACTION;
+ END
+ ELSE
+ BEGIN
+ EXEC sp_releaseapplock @Resource = '[Answer]';
+ COMMIT TRANSACTION;
+ END
+ end
+
+END
+/****** Object: StoredProcedure [dbo].[FillEmptyQuestionsForAnalysis] Script Date: 12/16/2020 11:01:33 AM ******/
+SET ANSI_NULLS ON
diff --git a/Database Scripts/Stored Procedures/GetAnswerCountsForGroupings.proc.sql b/Database Scripts/Stored Procedures/GetAnswerCountsForGroupings.proc.sql
new file mode 100644
index 0000000000..26b07c7f84
--- /dev/null
+++ b/Database Scripts/Stored Procedures/GetAnswerCountsForGroupings.proc.sql
@@ -0,0 +1,28 @@
+
+-- =============================================
+-- Author: WOODRK
+-- Create date: 8/29/2024
+-- Description: Generically return answer counts for all groupings in
+-- an assessment's maturity model
+-- =============================================
+CREATE PROCEDURE [dbo].[GetAnswerCountsForGroupings]
+ @assessmentId int
+AS
+BEGIN
+ -- SET NOCOUNT ON added to prevent extra result sets from
+ -- interfering with SELECT statements.
+ SET NOCOUNT ON;
+
+ exec [FillEmptyMaturityQuestionsForAnalysis] @assessmentId
+
+ select Title, Sequence, Grouping_Id, Parent_Id, Answer_Text, count(*) as AnsCount
+ from (
+ select mg.Title, mg.Sequence, mg.grouping_id, mg.Parent_Id, a.Answer_Text
+ from answer a
+ left join maturity_questions mq on a.Question_Or_Requirement_Id = mq.Mat_Question_Id and a.Question_Type = 'maturity'
+ left join maturity_groupings mg on mq.Grouping_Id = mg.Grouping_Id
+ where assessment_id = @assessmentId
+ ) b
+ group by title, sequence, grouping_id, parent_id, answer_text
+ order by sequence, answer_text
+END
diff --git a/Database Scripts/Stored Procedures/GetAnswerDistribGroupings.proc.sql b/Database Scripts/Stored Procedures/GetAnswerDistribGroupings.proc.sql
index 00692004db..4134e868ed 100644
--- a/Database Scripts/Stored Procedures/GetAnswerDistribGroupings.proc.sql
+++ b/Database Scripts/Stored Procedures/GetAnswerDistribGroupings.proc.sql
@@ -1,3 +1,4 @@
+
-- =============================================
-- Author: Randy Woods
-- Create date: 15 November 2022
@@ -7,14 +8,22 @@
-- specific grouping? g.Parent_Id = X
-- =============================================
CREATE PROCEDURE [dbo].[GetAnswerDistribGroupings]
- @assessmentId int
+ @assessmentId int,
+ @modelId int = null
AS
BEGIN
SET NOCOUNT ON;
exec FillEmptyMaturityQuestionsForAnalysis @assessmentId
+ -- get the main model ID for the assessment
declare @maturityModelId int = (select model_id from AVAILABLE_MATURITY_MODELS where Assessment_Id = @assessmentId)
+ -- if the caller specified a model ID, use that instead
+ if @modelId is not null
+ BEGIN
+ select @maturityModelId = @modelId
+ END
+
select [grouping_id], [title], [answer_text], count(answer_text) as [answer_count]
from (
select g.grouping_id, g.title, g.sequence, a.Answer_Text
diff --git a/Database Scripts/Stored Procedures/GetMaturityComparisonBestToWorst.proc.sql b/Database Scripts/Stored Procedures/GetMaturityComparisonBestToWorst.proc.sql
new file mode 100644
index 0000000000..e70e2e6317
--- /dev/null
+++ b/Database Scripts/Stored Procedures/GetMaturityComparisonBestToWorst.proc.sql
@@ -0,0 +1,58 @@
+
+-- =============================================
+-- Author: WOODRK
+-- Create date: 8/29/2024
+-- Description:
+-- =============================================
+CREATE PROCEDURE [dbo].[GetMaturityComparisonBestToWorst]
+@assessment_id int
+AS
+BEGIN
+ -- SET NOCOUNT ON added to prevent extra result sets from
+ -- interfering with SELECT statements.
+ SET NOCOUNT ON;
+
+
+ SELECT Assessment_Id,
+ AssessmentName = Alias,
+ Name = Title,
+ *
+ --AlternateCount = [I],
+ --AlternateValue = Round(((cast(([I]) as float)/isnull(nullif(Total,0),1)))*100,2),
+ --NaCount = [S],
+ --NaValue = Round(((cast(([S]) as float)/isnull(nullif(Total,0),1)))*100,2),
+ --NoCount = [N],
+ --NoValue = Round(((cast(([N]) as float)/isnull(nullif(Total,0),1)))*100,2),
+ --TotalCount = Total,
+ --TotalValue = Total,
+ --UnansweredCount = [U],
+ --UnansweredValue = Round(cast([U] as float)/Total*100,2),
+ --YesCount = [Y],
+ --YesValue = Round((cast(([Y]) as float)/isnull(nullif(Total,0),1))*100,2),
+ --Value = Round(((cast(([Y]+ isnull([I],0)) as float)/isnull(nullif((Total-[S]),0),1)))*100,2)
+ FROM
+ (
+ select b.Assessment_Id, f.Alias, b.Title, b.Answer_Text, isnull(c.Value,0) as Value,
+ Total = sum(c.Value) over(partition by b.Assessment_Id, b.Title)
+ from
+ (select distinct a.[Assessment_Id], g.Title, l.answer_Text
+ from answer_lookup l, (select * from Answer_Maturity where assessment_id = @assessment_id) a
+ join [MATURITY_QUESTIONS] q on a.Question_Or_Requirement_Id = q.Mat_Question_Id
+ join MATURITY_GROUPINGS g on q.Grouping_Id = g.Grouping_Id
+ ) b left join
+ (select a.Assessment_Id, g.Title, a.Answer_Text, count(a.answer_text) as Value
+ from (select * from Answer_Maturity where assessment_id = @assessment_id) a
+ join [MATURITY_QUESTIONS] q on a.Question_Or_Requirement_Id = q.Mat_Question_Id
+ join MATURITY_GROUPINGS g on q.Grouping_Id = g.Grouping_Id
+ group by Assessment_Id, g.Title, a.Answer_Text) c
+ on b.Assessment_Id = c.Assessment_Id and b.Title = c.Title and b.Answer_Text = c.Answer_Text
+ join ASSESSMENTS f on b.Assessment_Id = f.Assessment_Id
+ ) p
+
+
+ PIVOT
+ (
+ sum(value)
+ FOR answer_text IN ( [Y],[I],[S],[N],[U] )
+ ) AS pvt
+END
diff --git a/Database Scripts/Stored Procedures/Get_Cie_Merge_Conflicts.proc.sql b/Database Scripts/Stored Procedures/Get_Cie_Merge_Conflicts.proc.sql
new file mode 100644
index 0000000000..0d4d1b533a
--- /dev/null
+++ b/Database Scripts/Stored Procedures/Get_Cie_Merge_Conflicts.proc.sql
@@ -0,0 +1,231 @@
+-- =============================================
+-- Author: Matt Winston
+-- Create date: 4/12/24
+-- Description: Gets the merge conflicts for CIE assessments
+-- =============================================
+CREATE PROCEDURE [dbo].[Get_Cie_Merge_Conflicts]
+ -- At least 2 assessments are required to merge
+ @id1 int, @id2 int,
+
+ -- Up to 10 total assessments are allowed
+ @id3 int = NULL, @id4 int = NULL, @id5 int = NULL, @id6 int = NULL,
+ @id7 int = NULL, @id8 int = NULL, @id9 int = NULL, @id10 int = NULL
+
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+EXEC FillEmptyMaturityQuestionsForAnalysis @id1
+EXEC FillEmptyMaturityQuestionsForAnalysis @id2
+EXEC FillEmptyMaturityQuestionsForAnalysis @id3
+EXEC FillEmptyMaturityQuestionsForAnalysis @id4
+EXEC FillEmptyMaturityQuestionsForAnalysis @id5
+EXEC FillEmptyMaturityQuestionsForAnalysis @id6
+EXEC FillEmptyMaturityQuestionsForAnalysis @id7
+EXEC FillEmptyMaturityQuestionsForAnalysis @id8
+EXEC FillEmptyMaturityQuestionsForAnalysis @id9
+EXEC FillEmptyMaturityQuestionsForAnalysis @id10
+
+SELECT
+ (SELECT Question_Text FROM MATURITY_QUESTIONS WHERE Mat_Question_Id = a.Question_Or_Requirement_Id) as Question_Text,
+
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id1) as Assessment_Name1,
+ (SELECT Facility_Name FROM INFORMATION WHERE Id = @id1) as Facility,
+ a.Assessment_Id as Assessment_id1,
+ a.Question_Or_Requirement_Id as Question_Or_Requirement_Id1,
+ a.Answer_Text as Answer_Text1,
+ a.FeedBack as Feedback1,
+ a.Comment as Comment1,
+ a.Free_Response_Answer as Free_Response_Answer1,
+
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id2) as Assessment_Name2,
+ b.Assessment_Id as Assessment_id2,
+ b.Question_Or_Requirement_Id as Question_Or_Requirement_Id2,
+ b.Answer_Text as Answer_Text2,
+ b.FeedBack as Feedback2,
+ b.Comment as Comment2,
+ b.Free_Response_Answer as Free_Response_Answer2,
+
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id3) as Assessment_Name3,
+ c.Assessment_Id as Assessment_id3,
+ c.Question_Or_Requirement_Id as Question_Or_Requirement_Id3,
+ c.Answer_Text as Answer_Text3,
+ c.FeedBack as Feedback3,
+ c.Comment as Comment3,
+ c.Free_Response_Answer as Free_Response_Answer3,
+
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id4) as Assessment_Name4,
+ d.Assessment_Id as Assessment_id4,
+ d.Question_Or_Requirement_Id as Question_Or_Requirement_Id4,
+ d.Answer_Text as Answer_Text4,
+ d.FeedBack as Feedback4,
+ d.Comment as Comment4,
+ d.Free_Response_Answer as Free_Response_Answer4,
+
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id5) as Assessment_Name5,
+ e.Assessment_Id as Assessment_id5,
+ e.Question_Or_Requirement_Id as Question_Or_Requirement_Id5,
+ e.Answer_Text as Answer_Text5,
+ e.FeedBack as Feedback5,
+ e.Comment as Comment5,
+ e.Free_Response_Answer as Free_Response_Answer5,
+
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id6) as Assessment_Name6,
+ f.Assessment_Id as Assessment_id6,
+ f.Question_Or_Requirement_Id as Question_Or_Requirement_Id6,
+ f.Answer_Text as Answer_Text6,
+ f.FeedBack as Feedback6,
+ f.Comment as Comment6,
+ f.Free_Response_Answer as Free_Response_Answer6,
+
+ g.Assessment_Id as Assessment_id7,
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id7) as Assessment_Name7,
+ g.Question_Or_Requirement_Id as Question_Or_Requirement_Id7,
+ g.Answer_Text as Answer_Text7,
+ g.FeedBack as Feedback7,
+ g.Comment as Comment7,
+ g.Free_Response_Answer as Free_Response_Answer7,
+
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id8) as Assessment_Name8,
+ h.Assessment_Id as Assessment_id8,
+ h.Question_Or_Requirement_Id as Question_Or_Requirement_Id8,
+ h.Answer_Text as Answer_Text8,
+ h.FeedBack as Feedback8,
+ h.Comment as Comment8,
+ h.Free_Response_Answer as Free_Response_Answer8,
+
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id9) as Assessment_Name9,
+ i.Assessment_Id as Assessment_id9,
+ i.Question_Or_Requirement_Id as Question_Or_Requirement_Id9,
+ i.Answer_Text as Answer_Text9,
+ i.FeedBack as Feedback9,
+ i.Comment as Comment9,
+ i.Free_Response_Answer as Free_Response_Answer9,
+
+ (SELECT Assessment_Name FROM INFORMATION WHERE Id = @id10) as Assessment_Name10,
+ j.Assessment_Id as Assessment_id10,
+ j.Question_Or_Requirement_Id as Question_Or_Requirement_Id10,
+ j.Answer_Text as Answer_Text10,
+ j.FeedBack as Feedback10,
+ j.Comment as Comment10,
+ j.Free_Response_Answer as Free_Response_Answer10
+
+
+FROM (SELECT * FROM ANSWER WHERE Assessment_Id = @id1) a
+
+FULL OUTER JOIN (SELECT * FROM ANSWER WHERE Assessment_Id = @id2) b
+ON (a.Question_Or_Requirement_Id = b.Question_Or_Requirement_Id) AND (a.Question_Type = b.Question_Type)
+
+FULL OUTER JOIN (SELECT * FROM ANSWER WHERE Assessment_Id = @id3) c
+ON (a.Question_Or_Requirement_Id = c.Question_Or_Requirement_Id) AND (a.Question_Type = c.Question_Type)
+
+FULL OUTER JOIN (SELECT * FROM ANSWER WHERE Assessment_Id = @id4) d
+ON (a.Question_Or_Requirement_Id = d.Question_Or_Requirement_Id) AND (a.Question_Type = d.Question_Type)
+
+FULL OUTER JOIN (SELECT * FROM ANSWER WHERE Assessment_Id = @id5) e
+ON (a.Question_Or_Requirement_Id = e.Question_Or_Requirement_Id) AND (a.Question_Type = e.Question_Type)
+
+FULL OUTER JOIN (SELECT * FROM ANSWER WHERE Assessment_Id = @id6) f
+ON (a.Question_Or_Requirement_Id = f.Question_Or_Requirement_Id) AND (a.Question_Type = f.Question_Type)
+
+FULL OUTER JOIN (SELECT * FROM ANSWER WHERE Assessment_Id = @id7) g
+ON (a.Question_Or_Requirement_Id = g.Question_Or_Requirement_Id) AND (a.Question_Type = g.Question_Type)
+
+FULL OUTER JOIN (SELECT * FROM ANSWER WHERE Assessment_Id = @id8) h
+ON (a.Question_Or_Requirement_Id = h.Question_Or_Requirement_Id) AND (a.Question_Type = h.Question_Type)
+
+FULL OUTER JOIN (SELECT * FROM ANSWER WHERE Assessment_Id = @id9) i
+ON (a.Question_Or_Requirement_Id = i.Question_Or_Requirement_Id) AND (a.Question_Type = i.Question_Type)
+
+FULL OUTER JOIN (SELECT * FROM ANSWER WHERE Assessment_Id = @id10) j
+ON (a.Question_Or_Requirement_Id = j.Question_Or_Requirement_Id) AND (a.Question_Type = j.Question_Type)
+
+WHERE
+ -- Compare Exam 1 (a) to all other exams being merged
+ (
+ (a.Answer_Text != b.Answer_Text)
+ AND ((a.Answer_Text = 'NA') OR (a.Answer_Text = 'U' AND a.Free_Response_Answer is not null))
+ AND ((b.Answer_Text = 'NA') OR (b.Answer_Text = 'U' AND b.Free_Response_Answer is not null))
+ ) OR
+ ((a.Answer_Text != c.Answer_Text) AND (a.Answer_Text = 'NA' OR (a.Answer_Text = 'U' AND a.Free_Response_Answer is null)) AND (c.Answer_Text = 'NA' OR (c.Answer_Text = 'U' AND c.Free_Response_Answer is null))) OR
+ ((a.Answer_Text != d.Answer_Text) AND (a.Answer_Text = 'NA' OR (a.Answer_Text = 'U' AND a.Free_Response_Answer is null)) AND (d.Answer_Text = 'NA' OR (d.Answer_Text = 'U' AND d.Free_Response_Answer is null))) OR
+ ((a.Answer_Text != e.Answer_Text) AND (a.Answer_Text = 'NA' OR (a.Answer_Text = 'U' AND a.Free_Response_Answer is null)) AND (e.Answer_Text = 'NA' OR (e.Answer_Text = 'U' AND e.Free_Response_Answer is null))) OR
+ ((a.Answer_Text != f.Answer_Text) AND (a.Answer_Text = 'NA' OR (a.Answer_Text = 'U' AND a.Free_Response_Answer is null)) AND (f.Answer_Text = 'NA' OR (f.Answer_Text = 'U' AND f.Free_Response_Answer is null))) OR
+ ((a.Answer_Text != g.Answer_Text) AND (a.Answer_Text = 'NA' OR (a.Answer_Text = 'U' AND a.Free_Response_Answer is null)) AND (g.Answer_Text = 'NA' OR (g.Answer_Text = 'U' AND g.Free_Response_Answer is null))) OR
+ ((a.Answer_Text != h.Answer_Text) AND (a.Answer_Text = 'NA' OR (a.Answer_Text = 'U' AND a.Free_Response_Answer is null)) AND (h.Answer_Text = 'NA' OR (h.Answer_Text = 'U' AND h.Free_Response_Answer is null))) OR
+ ((a.Answer_Text != i.Answer_Text) AND (a.Answer_Text = 'NA' OR (a.Answer_Text = 'U' AND a.Free_Response_Answer is null)) AND (i.Answer_Text = 'NA' OR (i.Answer_Text = 'U' AND i.Free_Response_Answer is null))) OR
+ ((a.Answer_Text != j.Answer_Text) AND (a.Answer_Text = 'NA' OR (a.Answer_Text = 'U' AND a.Free_Response_Answer is null)) AND (j.Answer_Text = 'NA' OR (j.Answer_Text = 'U' AND j.Free_Response_Answer is null))) OR
+
+ --((a.Answer_Text != b.Answer_Text) AND ((a.Answer_Text = 'U' AND a.Free_Response_Answer is NULL) OR (c.Answer_Text = 'U' AND c.Free_Response_Answer is NULL))) OR
+ --((a.Answer_Text != b.Answer_Text) AND ((a.Answer_Text = 'U' AND a.Free_Response_Answer is NULL) OR (d.Answer_Text = 'U' AND d.Free_Response_Answer is NULL))) OR
+ --((a.Answer_Text != b.Answer_Text) AND ((a.Answer_Text = 'U' AND a.Free_Response_Answer is NULL) OR (e.Answer_Text = 'U' AND e.Free_Response_Answer is NULL))) OR
+ --((a.Answer_Text != b.Answer_Text) AND ((a.Answer_Text = 'U' AND a.Free_Response_Answer is NULL) OR (f.Answer_Text = 'U' AND f.Free_Response_Answer is NULL))) OR
+ --((a.Answer_Text != b.Answer_Text) AND ((a.Answer_Text = 'U' AND a.Free_Response_Answer is NULL) OR (g.Answer_Text = 'U' AND g.Free_Response_Answer is NULL))) OR
+ --((a.Answer_Text != b.Answer_Text) AND ((a.Answer_Text = 'U' AND a.Free_Response_Answer is NULL) OR (h.Answer_Text = 'U' AND h.Free_Response_Answer is NULL))) OR
+ --((a.Answer_Text != b.Answer_Text) AND ((a.Answer_Text = 'U' AND a.Free_Response_Answer is NULL) OR (i.Answer_Text = 'U' AND i.Free_Response_Answer is NULL))) OR
+ --((a.Answer_Text != b.Answer_Text) AND ((a.Answer_Text = 'U' AND a.Free_Response_Answer is NULL) OR (j.Answer_Text = 'U' AND j.Free_Response_Answer is NULL))) OR
+
+ --((a.Answer_Text != b.Answer_Text) OR (a.Answer_Text != b.Answer_Text)) OR
+ --((a.Answer_Text != c.Answer_Text) OR (a.Answer_Text != c.Answer_Text)) OR
+ --((a.Answer_Text != d.Answer_Text) OR (a.Answer_Text != d.Answer_Text)) OR
+ --((a.Answer_Text != e.Answer_Text) OR (a.Answer_Text != e.Answer_Text)) OR
+ --((a.Answer_Text != f.Answer_Text) OR (a.Answer_Text != f.Answer_Text)) OR
+ --((a.Answer_Text != g.Answer_Text) OR (a.Answer_Text != g.Answer_Text)) OR
+ --((a.Answer_Text != h.Answer_Text) OR (a.Answer_Text != h.Answer_Text)) OR
+ --((a.Answer_Text != i.Answer_Text) OR (a.Answer_Text != i.Answer_Text)) OR
+ --((a.Answer_Text != j.Answer_Text) OR (a.Answer_Text != j.Answer_Text)) OR
+
+ -- Compare Exam 2 (b) to all other exams being merged
+ ((b.Answer_Text != c.Answer_Text) OR (b.Answer_Text != c.Answer_Text)) OR
+ ((b.Answer_Text != d.Answer_Text) OR (b.Answer_Text != d.Answer_Text)) OR
+ ((b.Answer_Text != e.Answer_Text) OR (b.Answer_Text != e.Answer_Text)) OR
+ ((b.Answer_Text != f.Answer_Text) OR (b.Answer_Text != f.Answer_Text)) OR
+ ((b.Answer_Text != g.Answer_Text) OR (b.Answer_Text != g.Answer_Text)) OR
+ ((b.Answer_Text != h.Answer_Text) OR (b.Answer_Text != h.Answer_Text)) OR
+ ((b.Answer_Text != i.Answer_Text) OR (b.Answer_Text != i.Answer_Text)) OR
+ ((b.Answer_Text != j.Answer_Text) OR (b.Answer_Text != j.Answer_Text)) OR
+
+ -- Compare Exam 3 (c)
+ ((c.Answer_Text != d.Answer_Text) OR (c.Answer_Text != d.Answer_Text)) OR
+ ((c.Answer_Text != e.Answer_Text) OR (c.Answer_Text != e.Answer_Text)) OR
+ ((c.Answer_Text != f.Answer_Text) OR (c.Answer_Text != f.Answer_Text)) OR
+ ((c.Answer_Text != g.Answer_Text) OR (c.Answer_Text != g.Answer_Text)) OR
+ ((c.Answer_Text != h.Answer_Text) OR (c.Answer_Text != h.Answer_Text)) OR
+ ((c.Answer_Text != i.Answer_Text) OR (c.Answer_Text != i.Answer_Text)) OR
+ ((c.Answer_Text != j.Answer_Text) OR (c.Answer_Text != j.Answer_Text)) OR
+
+ -- Compare Exam 4 (d)
+ ((d.Answer_Text != e.Answer_Text) OR (d.Answer_Text != e.Answer_Text)) OR
+ ((d.Answer_Text != f.Answer_Text) OR (d.Answer_Text != f.Answer_Text)) OR
+ ((d.Answer_Text != g.Answer_Text) OR (d.Answer_Text != g.Answer_Text)) OR
+ ((d.Answer_Text != h.Answer_Text) OR (d.Answer_Text != h.Answer_Text)) OR
+ ((d.Answer_Text != i.Answer_Text) OR (d.Answer_Text != i.Answer_Text)) OR
+ ((d.Answer_Text != j.Answer_Text) OR (d.Answer_Text != j.Answer_Text)) OR
+
+ -- Compare Exam 5 (e)
+ ((e.Answer_Text != f.Answer_Text) OR (e.Answer_Text != f.Answer_Text)) OR
+ ((e.Answer_Text != g.Answer_Text) OR (e.Answer_Text != g.Answer_Text)) OR
+ ((e.Answer_Text != h.Answer_Text) OR (e.Answer_Text != h.Answer_Text)) OR
+ ((e.Answer_Text != i.Answer_Text) OR (e.Answer_Text != i.Answer_Text)) OR
+ ((e.Answer_Text != j.Answer_Text) OR (e.Answer_Text != j.Answer_Text)) OR
+
+ -- Compare Exam 6 (f)
+ ((f.Answer_Text != g.Answer_Text) OR (f.Answer_Text != g.Answer_Text)) OR
+ ((f.Answer_Text != h.Answer_Text) OR (f.Answer_Text != h.Answer_Text)) OR
+ ((f.Answer_Text != i.Answer_Text) OR (f.Answer_Text != i.Answer_Text)) OR
+ ((f.Answer_Text != j.Answer_Text) OR (f.Answer_Text != j.Answer_Text)) OR
+
+ -- Compare Exam 7 (g)
+ ((g.Answer_Text != h.Answer_Text) OR (g.Answer_Text != h.Answer_Text)) OR
+ ((g.Answer_Text != i.Answer_Text) OR (g.Answer_Text != i.Answer_Text)) OR
+ ((g.Answer_Text != j.Answer_Text) OR (g.Answer_Text != j.Answer_Text)) OR
+
+ -- Compare Exam 8 (h)
+ ((h.Answer_Text != i.Answer_Text) OR (h.Answer_Text != i.Answer_Text)) OR
+ ((h.Answer_Text != j.Answer_Text) OR (h.Answer_Text != j.Answer_Text)) OR
+
+ -- Compare Exam 9 (i)
+ ((i.Answer_Text != j.Answer_Text) OR (i.Answer_Text != j.Answer_Text))
+
+
+END
diff --git a/Database Scripts/Stored Procedures/analytics_Compute_MaturityAll.proc.sql b/Database Scripts/Stored Procedures/analytics_Compute_MaturityAll.proc.sql
index 800e0ee323..0d3c473d27 100644
--- a/Database Scripts/Stored Procedures/analytics_Compute_MaturityAll.proc.sql
+++ b/Database Scripts/Stored Procedures/analytics_Compute_MaturityAll.proc.sql
@@ -1,13 +1,20 @@
+
-- =============================================
-- Author: Luke G, Lilly, Barry
-- Create date: 4-6-2022
-- Description: 18 stored procedures for analytics.
-- This procedure returns the AVG, MIN, MAX, MEDIAN Question Group Heading for the Question_Type 'Maturity' for all sectory and industry.
+--
+-- Modification date: 12-NOV-2024
+-- Author: Randy
+-- Description: Made sector and industry parameters optional, to cast a wider net.
+-- Also added consideration for a sector and industry stored in DETAILS_DEMOGRAPHICS.
+-- Also the groupings are sorted in their sequence order, rather than alphabetically.
-- =============================================
CREATE PROCEDURE [dbo].[analytics_Compute_MaturityAll]
@maturity_model_id int,
-@sector_id int,
-@industry_id int
+@sector_id int = NULL,
+@industry_id int = NULL
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
@@ -22,6 +29,7 @@ BEGIN
IF OBJECT_ID('tempdb..#Temp2') IS NOT NULL DROP TABLE #Temp2
IF OBJECT_ID('tempdb..#Temp3') IS NOT NULL DROP TABLE #Temp3
+
--step 1 get the base data
select a.Assessment_Id,Question_Group, Answer_Text, isnull(COUNT(a.answer_text),0) Answer_Count,
sum(isnull(count(answer_text),0)) OVER(PARTITION BY a.assessment_id,question_group) AS Total
@@ -31,30 +39,38 @@ select a.Assessment_Id,Question_Group, Answer_Text, isnull(COUNT(a.answer_text),
join MATURITY_QUESTIONS q on a.Question_Or_Requirement_Id = q.Mat_Question_Id
join ANALYTICS_MATURITY_GROUPINGS g on q.Mat_Question_Id=g.Maturity_Question_Id
left join demographics d on a.assessment_id = d.assessment_id
+ left join details_demographics ddsector on a.Assessment_Id = ddsector.Assessment_Id and ddsector.DataItemName = 'SECTOR'
+ left join details_demographics ddsubsector on a.Assessment_Id = ddsubsector.Assessment_Id and ddsubsector.DataItemName = 'SUBSECTOR'
where a.question_type = 'Maturity' and q.Maturity_Model_Id=@maturity_model_id and g.Maturity_Model_Id=@maturity_model_id
- and nullif(@sector_id,sectorid) is null
- and nullif(@industry_id,industryid) is null
+ and (nullif(@sector_id, sectorid) is null or nullif(@sector_id, ddsector.IntValue) is null)
+ and (nullif(@industry_id,industryid) is null or nullif(@industry_id, ddsubsector.IntValue) is null)
group by a.assessment_id, Question_Group, Answer_Text
+
+
--step 2 handle the cases where we have all yes, all no, and mixed
--get the yes and mixed case
select * into #temp2 from #temp where answer_text='Y'
--get the all no case
insert #temp2
select assessment_id,QUESTION_GROUP,Answer_Text='Y',Answer_Count,total, [percentage]=0 from #temp where Answer_Text = 'N' and Answer_Count=total
+
+
--step 3 calculate the min,max,avg
- select G1.Question_Group as Question_Group_Heading, min(isnull([percentage],0)) [minimum],max(isnull([percentage],0)) [maximum],avg(isnull([percentage],0)) [average]
+ select G1.Question_Group as Question_Group_Heading, g1.Global_Sequence, min(isnull([percentage],0)) [minimum],max(isnull([percentage],0)) [maximum],avg(isnull([percentage],0)) [average]
into #temp3
from
(
- select distinct Question_Group from ANALYTICS_MATURITY_GROUPINGS where Maturity_Model_Id = @maturity_model_id
+ select distinct Question_Group, global_sequence from ANALYTICS_MATURITY_GROUPINGS where Maturity_Model_Id = @maturity_model_id
) G1 LEFT OUTER JOIN #temp2 G2 ON G1.Question_Group = G2.Question_Group
- group by G1.Question_Group
+ group by G1.Question_Group, global_sequence
+
+
--step 4 add median
- select a.Question_Group_Heading,cast(a.minimum as float) as minimum,cast(a.maximum as float) as maximum,cast(a.average as float) as average,isnull(b.median,0) as median from
+ select a.Question_Group_Heading, cast(a.minimum as float) as minimum,cast(a.maximum as float) as maximum,cast(a.average as float) as average,isnull(b.median,0) as median from
#temp3 a left join
(
select distinct Question_Group as Question_Group_Heading
,isnull(PERCENTILE_disc(0.5) WITHIN GROUP (ORDER BY [Percentage]) OVER (PARTITION BY question_group),0) AS median
from #temp2) b on a.Question_Group_Heading=b.Question_Group_Heading
- order by a.Question_Group_Heading
+ order by a.Global_Sequence
end
diff --git a/Database Scripts/Stored Procedures/analytics_Compute_MaturitySampleSize.proc.sql b/Database Scripts/Stored Procedures/analytics_Compute_MaturitySampleSize.proc.sql
new file mode 100644
index 0000000000..9cc6d55fc1
--- /dev/null
+++ b/Database Scripts/Stored Procedures/analytics_Compute_MaturitySampleSize.proc.sql
@@ -0,0 +1,33 @@
+
+-- =============================================
+-- Author: Mostafa, Randy
+-- Create date: 11-12-2024
+-- Description: Return Assessment count based on maturity model id and optional sector_id
+-- =============================================
+CREATE PROCEDURE [dbo].[analytics_Compute_MaturitySampleSize]
+@maturity_model_id int,
+@sector_id int = NULL
+AS
+BEGIN
+ -- SET NOCOUNT ON added to prevent extra result sets from
+ -- interfering with SELECT statements.
+ SET NOCOUNT ON;
+ --test base case where there is no data in db at all
+SELECT SectorId, COUNT(Assessment_Id) AS AssessmentCount
+FROM (
+ SELECT dd.Assessment_Id, dd.IntValue as SectorId FROM DETAILS_DEMOGRAPHICS dd
+ JOIN AVAILABLE_MATURITY_MODELS amm
+ ON dd.Assessment_Id = amm.Assessment_Id
+ WHERE amm.model_id = @maturity_model_id
+ AND (@sector_id IS NULL OR (DataItemName = 'SECTOR' AND dd.IntValue = @sector_id))
+
+ UNION
+
+ SELECT d.Assessment_Id, d.SectorId FROM DEMOGRAPHICS d
+ JOIN AVAILABLE_MATURITY_MODELS amm
+ ON d.Assessment_Id = amm.Assessment_Id
+ WHERE amm.model_id = @maturity_model_id
+ AND (@sector_id IS NULL OR d.SectorId = @sector_id)
+) AS CombinedResult
+GROUP BY SectorId;
+END
diff --git a/Database Scripts/Stored Procedures/analytics_SequenceMaturityGroups.proc.sql b/Database Scripts/Stored Procedures/analytics_SequenceMaturityGroups.proc.sql
new file mode 100644
index 0000000000..cdbc0e768f
--- /dev/null
+++ b/Database Scripts/Stored Procedures/analytics_SequenceMaturityGroups.proc.sql
@@ -0,0 +1,54 @@
+
+-- =============================================
+-- Author: Randy
+-- Create date: 19-NOV-2024
+-- Description: Determine a "global" sequence for all maturity groupings in context.
+-- This is needed because a child grouping may start its sequencing at 1, which would
+-- put it ahead of its parent with a simple sort by sequence.
+--
+-- All groupings with their global sequence are stored in [MATURITY_GLOBAL_SEQUENCES].
+--
+-- It can currently handle a grouping structure depth of 4. This can be expanded in the future if needed.
+-- =============================================
+CREATE PROCEDURE [dbo].[analytics_SequenceMaturityGroups]
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ DELETE FROM [MATURITY_GLOBAL_SEQUENCES]
+
+
+ DECLARE seq_cursor CURSOR FOR
+ select g1.Maturity_Model_Id,
+ ROW_NUMBER() over (order by (select null)) as global_sequence,
+ g1.grouping_Id as [g1], g2.grouping_id as [g2], g3.grouping_id as [g3], g4.grouping_id as [g4]
+ from [MATURITY_GROUPINGS] g1
+ left join [MATURITY_GROUPINGS] g2 on g2.parent_id = g1.grouping_id
+ left join [MATURITY_GROUPINGS] g3 on g3.parent_id = g2.grouping_id
+ left join [MATURITY_GROUPINGS] g4 on g4.parent_id = g3.grouping_id
+ where g1.parent_id is null
+ and (g1.Maturity_Model_Id = g2.Maturity_Model_Id or g2.Maturity_Model_Id is null)
+ and (g2.Maturity_Model_Id = g3.Maturity_Model_Id or g3.Maturity_Model_Id is null)
+ and (g3.Maturity_Model_Id = g4.Maturity_Model_Id or g4.Maturity_Model_Id is null)
+ order by g1.maturity_model_id, g1.sequence, g2.sequence, g3.sequence, g4.sequence;
+
+
+ DECLARE @Maturity_Model_Id int, @global_sequence int, @g1 int, @g2 int, @g3 int, @g4 int;
+
+ OPEN seq_cursor;
+
+ FETCH NEXT FROM seq_cursor INTO @Maturity_Model_Id, @global_sequence, @g1, @g2, @g3, @g4
+
+ WHILE @@FETCH_STATUS = 0
+ BEGIN
+ INSERT INTO [MATURITY_GLOBAL_SEQUENCES](
+ [Maturity_Model_Id], [global_sequence], [g1], [g2], [g3], [g4])
+ values (@Maturity_Model_Id, @global_sequence, @g1, @g2, @g3, @g4)
+
+ FETCH NEXT FROM seq_cursor into @Maturity_Model_Id, @global_sequence, @g1, @g2, @g3, @g4
+ END
+
+ CLOSE seq_cursor;
+ DEALLOCATE seq_cursor;
+END
+
diff --git a/Database Scripts/Stored Procedures/analytics_compute_single_averages_maturity.proc.sql b/Database Scripts/Stored Procedures/analytics_compute_single_averages_maturity.proc.sql
index 1278bf611a..b9a0848fd2 100644
--- a/Database Scripts/Stored Procedures/analytics_compute_single_averages_maturity.proc.sql
+++ b/Database Scripts/Stored Procedures/analytics_compute_single_averages_maturity.proc.sql
@@ -1,3 +1,4 @@
+
-- =============================================
-- Author: Barry
-- Create date: 4/6/2022
@@ -16,13 +17,13 @@ BEGIN
from
(
select distinct Assessment_Id= @assessment_id,
- Question_Group as Title, Answer_Text = 'Y'
+ Question_Group as Title, Global_Sequence, Answer_Text = 'Y'
from ANALYTICS_MATURITY_GROUPINGS
where Maturity_Model_Id= @maturity_model_id
) G1 LEFT OUTER JOIN
(
select
- Question_Group as Title,answer_text, count(answer_text) answer_count,
+ Question_Group as Title, answer_text, count(answer_text) answer_count,
sum(count(answer_text)) OVER(PARTITION BY Question_Group) AS Total,
cast(IsNull(Round((cast((COUNT(a.answer_text)) as float)/(isnull(nullif(sum(count(answer_text)) OVER(PARTITION BY Question_Group),0),1)))*100,0),0) as int) as [Percentage]
from Analytics_Answers a
@@ -31,7 +32,5 @@ BEGIN
group by Question_Group, Answer_Text
) G2 ON G1.Title = G2.Title AND G1.Answer_Text = G2.Answer_Text
where g1.answer_text = 'Y'
- order by Title
+ order by Global_Sequence
END
-
---update ANSWER set Answer_Text = 'NA' where Assessment_Id = 9
diff --git a/Database Scripts/Stored Procedures/analytics_setup_maturity_groupings.proc.sql b/Database Scripts/Stored Procedures/analytics_setup_maturity_groupings.proc.sql
index 461a3f6daf..81afac0a98 100644
--- a/Database Scripts/Stored Procedures/analytics_setup_maturity_groupings.proc.sql
+++ b/Database Scripts/Stored Procedures/analytics_setup_maturity_groupings.proc.sql
@@ -1,3 +1,4 @@
+
-- =============================================
-- Author: hansbk
-- Create date: 3/31/2022
@@ -10,6 +11,8 @@ CREATE PROCEDURE [dbo].[analytics_setup_maturity_groupings]
AS
BEGIN
SET NOCOUNT ON;
+
+
/*
clean out the table and rebuild it
go through the maturity models table and for each one select the appropriate level
@@ -18,6 +21,7 @@ BEGIN
*/
delete from analytics_maturity_Groupings
+
declare @maturity_model_id int, @analytics_rollup_level int
@@ -30,12 +34,16 @@ OPEN maturity_cursor
FETCH NEXT FROM maturity_cursor
INTO @maturity_model_id, @analytics_rollup_level
+
WHILE @@FETCH_STATUS = 0
BEGIN
- INSERT INTO [dbo].[ANALYTICS_MATURITY_GROUPINGS] ([Maturity_Model_Id],[Maturity_Question_Id],[Question_Group])
- select distinct g.Maturity_Model_Id,q.Mat_Question_Id, title
- from MATURITY_GROUPINGS g join MATURITY_QUESTIONS q on g.Grouping_Id=q.Grouping_Id
- where q.Maturity_Model_Id = @maturity_model_id and g.Maturity_Model_Id=@maturity_model_id
+ INSERT INTO [dbo].[ANALYTICS_MATURITY_GROUPINGS]
+ ([Maturity_Model_Id], [Maturity_Question_Id], [Question_Group], [Group_Id], [Group_Sequence], [Global_Sequence])
+
+ select distinct q.Maturity_Model_Id, q.Mat_Question_Id, title, g.Grouping_Id as [Group_Id], g.sequence, null
+ from [MATURITY_GROUPINGS] g
+ join [MATURITY_QUESTIONS] q on g.Grouping_Id = q.Grouping_Id
+ where g.Maturity_Model_Id = @maturity_model_id and g.Maturity_Model_Id=@maturity_model_id
and Group_Level = @analytics_rollup_level
FETCH NEXT FROM maturity_cursor
@@ -44,5 +52,15 @@ END
CLOSE maturity_cursor;
DEALLOCATE maturity_cursor;
+
+-- define a 'global' sequence for all groupings in all models
+EXEC [analytics_SequenceMaturityGroups]
+
+-- include the global sequence on the ANALYTICS_MATURITY_GROUPINGS work table
+update ANALYTICS_MATURITY_GROUPINGS
+set Global_Sequence = (
+ select top 1 global_sequence from [MATURITY_GLOBAL_SEQUENCES]
+ where group_id = g1 or group_id = g2 or group_id = g3 or group_id = g4
+)
END
diff --git a/Database Scripts/Stored Procedures/spEXECsp_RECOMPILE.proc.sql b/Database Scripts/Stored Procedures/spEXECsp_RECOMPILE.proc.sql
new file mode 100644
index 0000000000..0c85f23aed
--- /dev/null
+++ b/Database Scripts/Stored Procedures/spEXECsp_RECOMPILE.proc.sql
@@ -0,0 +1,45 @@
+CREATE PROCEDURE [dbo].[spEXECsp_RECOMPILE] AS
+
+SET NOCOUNT ON
+
+-- 1 - Declaration statements for all variables
+DECLARE @TableName varchar(128)
+DECLARE @OwnerName varchar(128)
+DECLARE @CMD1 varchar(8000)
+DECLARE @TableListLoop int
+DECLARE @TableListTable table
+(UIDTableList int IDENTITY (1,1),
+OwnerName varchar(128),
+TableName varchar(128))
+
+-- 2 - Outer loop for populating the database names
+INSERT INTO @TableListTable(OwnerName, TableName)
+SELECT u.[Name], o.[Name]
+FROM sys.objects o
+INNER JOIN sys.schemas u
+ ON o.schema_id = u.schema_id
+WHERE o.Type = 'V'
+ORDER BY o.[Name]
+
+
+
+-- 3 - Determine the highest UIDDatabaseList to loop through the records
+SELECT @TableListLoop = MAX(UIDTableList) FROM @TableListTable
+
+-- 4 - While condition for looping through the database records
+WHILE @TableListLoop > 0
+ BEGIN
+
+ -- 5 - Set the @DatabaseName parameter
+ SELECT @TableName = TableName,
+ @OwnerName = OwnerName
+ FROM @TableListTable
+ WHERE UIDTableList = @TableListLoop
+
+ -- 6 - String together the final backup command
+ SELECT @CMD1 = 'EXEC sp_recompile ' + '[' + @OwnerName + '.' + @TableName + ']' + char(13)
+
+ -- 7 - Execute the final string to complete the backups
+ SELECT @CMD1
+ --EXEC (@CMD1)
+ end
diff --git a/Database Scripts/Stored Procedures/usp_GetMaturityAnswerTotals.proc.sql b/Database Scripts/Stored Procedures/usp_GetMaturityAnswerTotals.proc.sql
new file mode 100644
index 0000000000..c3ca50de9d
--- /dev/null
+++ b/Database Scripts/Stored Procedures/usp_GetMaturityAnswerTotals.proc.sql
@@ -0,0 +1,40 @@
+
+-- =============================================
+-- Author: Randy Woods
+-- Create date: 27 AUG 2024
+-- Description: Flexible answer count/percentages for maturity models that have their own
+-- answer options other than Y, N, NA, etc.
+--
+-- A model ID can be supplied (for querying SSG answers), or if not supplied,
+-- the assigned model ID is used.
+-- =============================================
+CREATE PROCEDURE [dbo].[usp_GetMaturityAnswerTotals]
+ @assessment_id int,
+ @model_id int = null
+AS
+BEGIN
+ -- Get the assigned maturity model ID if a model was not specified in the arguments
+ if @model_id is null begin
+ set @model_id = (select model_id from AVAILABLE_MATURITY_MODELS where assessment_id = @assessment_id)
+ end
+
+ -- Create all missing answer rows
+ exec [FillEmptyMaturityQuestionsForAnalysis] @assessment_id
+
+
+ select *
+ into #answers
+ from answer
+ where question_type = 'maturity' and Question_Or_Requirement_Id in (select mat_question_id from maturity_questions where maturity_model_id = @model_id)
+
+ select [Answer_Text],
+ isnull(qc, 0) as [QC],
+ isnull(m.Total, 0) as [Total],
+ isnull(cast(IsNull(Round((cast((qc) as float)/(isnull(nullif(Total,0),1)))*100,0),0) as int), 0) as [Percent]
+ from (
+ SELECT a.Answer_Text, count(a.question_or_requirement_id) qc, SUM(count(a.question_or_requirement_id)) OVER() AS [Total]
+ FROM #answers a
+ where a.Assessment_Id = @assessment_id
+ group by a.Answer_Text
+ ) m
+END
diff --git a/Database Scripts/Stored Procedures/usp_GetQuestions.proc.sql b/Database Scripts/Stored Procedures/usp_GetQuestions.proc.sql
new file mode 100644
index 0000000000..a75634a782
--- /dev/null
+++ b/Database Scripts/Stored Procedures/usp_GetQuestions.proc.sql
@@ -0,0 +1,74 @@
+-- =============================================
+-- Author: Barry Hansen
+-- Create date: 10/8/2024
+-- Description: Ranked Questions
+-- =============================================
+CREATE PROCEDURE [dbo].[usp_GetQuestions]
+@assessment_id INT
+AS
+BEGIN
+ -- SET NOCOUNT ON added to prevent extra result sets from
+ -- interfering with SELECT statements.
+ SET NOCOUNT ON;
+ EXECUTE [dbo].[FillEmptyQuestionsForAnalysis] @Assessment_Id
+ -- get the application mode
+ declare @applicationMode nvarchar(50)
+ exec dbo.GetApplicationModeDefault @assessment_id, @ApplicationMode output
+ -- get currently selected sets
+ IF OBJECT_ID('tempdb..#mySets') IS NOT NULL DROP TABLE #mySets
+ select set_name into #mySets from AVAILABLE_STANDARDS where Assessment_Id = @assessment_Id and Selected = 1
+
+ if(@ApplicationMode = 'Questions Based')
+ begin
+ SELECT Short_Name as ShortName,
+ Question_Group_Heading as [Category],
+ Simple_Question as [QuestionText],
+ c.Question_Id as [QuestionId],
+ null as [RequirementId],
+ a.Answer_ID as [AnswerID],
+ Answer_Text as [AnswerText],
+ c.Universal_Sal_Level as [Level],
+ CONVERT(varchar(10), a.Question_Number) as [QuestionRef],
+ Question_Group_Heading + ' # ' + CONVERT(varchar(10), a.Question_Number) as CategoryAndNumber,
+ a.Question_Or_Requirement_Id as [QuestionOrRequirementID]
+ FROM Answer_Questions a
+ join NEW_QUESTION c on a.Question_Or_Requirement_Id = c.Question_Id
+ join vQuestion_Headings h on c.Heading_Pair_Id = h.heading_pair_Id
+ join (
+ select distinct s.question_id, ns.Short_Name from NEW_QUESTION_SETS s
+ join AVAILABLE_STANDARDS v on s.Set_Name = v.Set_Name
+ join SETS ns on s.Set_Name = ns.Set_Name
+ join NEW_QUESTION_LEVELS l on s.New_Question_Set_Id = l.New_Question_Set_Id
+ join STANDARD_SELECTION ss on v.Assessment_Id = ss.Assessment_Id
+ join UNIVERSAL_SAL_LEVEL ul on ss.Selected_Sal_Level = ul.Full_Name_Sal
+ where v.Selected = 1 and v.Assessment_Id = @assessment_id and l.Universal_Sal_Level = ul.Universal_Sal_Level
+ ) s on c.Question_Id = s.Question_Id
+ where a.Assessment_Id = @assessment_id
+ order by ShortName,Question_Group_Heading,Question_Number
+ end
+ else
+ begin
+ SELECT Short_Name ShortName,
+ Standard_Category as [Category],
+ Requirement_Text as [QuestionText],
+ null as [QuestionId],
+ req.Requirement_Id as [RequirementId],
+ Answer_Id as [AnswerID],
+ Answer_Text as [AnswerText],
+ u.Universal_Sal_Level as [Level],
+ requirement_title as [QuestionRef],
+ Standard_Category + ' - ' + requirement_title as CategoryAndNumber,
+ rs.Requirement_Id as [QuestionOrRequirementID]
+ from REQUIREMENT_SETS rs
+ left join ANSWER ans on ans.Question_Or_Requirement_Id = rs.Requirement_Id
+ left join [SETS] s on rs.Set_Name = s.Set_Name
+ left join NEW_REQUIREMENT req on rs.Requirement_Id = req.Requirement_Id
+ left join REQUIREMENT_LEVELS rl on rl.Requirement_Id = req.Requirement_Id
+ left join STANDARD_SELECTION ss on ss.Assessment_Id = @assessment_Id
+ left join UNIVERSAL_SAL_LEVEL u on u.Full_Name_Sal = ss.Selected_Sal_Level
+ where rs.Set_Name in (select set_name from #mySets)
+ and ans.Assessment_Id = @assessment_id
+ and rl.Standard_Level = u.Universal_Sal_Level
+ order by rs.Requirement_Sequence
+ end
+END
diff --git a/Database Scripts/Stored Procedures/usp_getMaturitySummaryOverall.proc.sql b/Database Scripts/Stored Procedures/usp_getMaturitySummaryOverall.proc.sql
new file mode 100644
index 0000000000..92aa21c38c
--- /dev/null
+++ b/Database Scripts/Stored Procedures/usp_getMaturitySummaryOverall.proc.sql
@@ -0,0 +1,29 @@
+-- =============================================
+-- Author: hansbk
+-- Create date: 8/30/2018
+-- Description: Stub needs completed
+-- =============================================
+CREATE PROCEDURE [dbo].[usp_getMaturitySummaryOverall]
+ @assessment_id int
+AS
+BEGIN
+ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
+
+ -- SET NOCOUNT ON added to prevent extra result sets from
+ -- interfering with SELECT statements.
+ SET NOCOUNT ON;
+
+select *
+into #answers
+from answer
+where question_type = 'maturity' and Question_Or_Requirement_Id in (select mat_question_id from maturity_questions where maturity_model_id = 5)
+ select a.Answer_Full_Name,a.Answer_Text, isnull(m.qc,0) qc,isnull(m.Total,0) Total, isnull(cast(IsNull(Round((cast((qc) as float)/(isnull(nullif(Total,0),1)))*100,0),0) as int),0) as [Percent]
+ from ANSWER_LOOKUP a left join (
+ SELECT a.Answer_Text, isnull(count(a.question_or_requirement_id),0) qc, SUM(count(a.question_or_requirement_id)) OVER() AS Total
+ FROM #answers a
+ where a.Assessment_Id = @assessment_id
+ group by a.Answer_Text
+ ) m on a.Answer_Text = m.Answer_Text
+
+END
+
diff --git a/Database Scripts/Views/Analytics_Answers.view.sql b/Database Scripts/Views/Analytics_Answers.view.sql
index b839b0a7f5..60ea3b376a 100644
--- a/Database Scripts/Views/Analytics_Answers.view.sql
+++ b/Database Scripts/Views/Analytics_Answers.view.sql
@@ -1,13 +1,7 @@
-
CREATE VIEW [dbo].[Analytics_Answers]
AS
-SELECT
-Assessment_Id,
-Question_Or_Requirement_Id,
-Question_Type,
-CASE WHEN ANSWER.Answer_Text = 'U' OR ANSWER.Answer_Text = 'N' THEN N'N'
-WHEN ANSWER.Answer_Text = 'A' OR ANSWER.Answer_Text = 'Y' THEN N'Y' END
-AS Answer_Text
-FROM [dbo].[ANSWER]
-WHERE ANSWER.Answer_Text != 'NA'
+SELECT Assessment_Id, Question_Or_Requirement_Id, Question_Type, CASE WHEN ANSWER.Answer_Text = 'A' OR
+ ANSWER.Answer_Text = 'Y' THEN N'Y' ELSE 'N' END AS Answer_Text
+FROM dbo.ANSWER
+WHERE (Answer_Text <> 'NA')