Skip to content

Commit 25fa3e0

Browse files
authored
Merge pull request #3357 from TechnologyEnhancedLearning/Develop/Features/TD-483-AddComptenciesToAssessment
TD-483 add comptencies to assessment
2 parents d4b6252 + e181cfe commit 25fa3e0

31 files changed

+1202
-79
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace DigitalLearningSolutions.Data.Migrations
2+
{
3+
using FluentMigrator;
4+
[Migration(202508181154)]
5+
public class CreateOrAlterMoveCompetenciesAndGroups : Migration
6+
{
7+
public override void Up()
8+
{
9+
Execute.Sql(Properties.Resources.TD_483_uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP);
10+
Execute.Sql(Properties.Resources.TD_483_uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP);
11+
}
12+
public override void Down()
13+
{
14+
Execute.Sql("DROP PROCEDURE IF EXISTS [dbo].[usp_MoveCompetencyGroupInSelfAssessment]");
15+
Execute.Sql("DROP PROCEDURE IF EXISTS [dbo].[usp_MoveCompetencyInSelfAssessment]");
16+
Execute.Sql("DROP PROCEDURE IF EXISTS [dbo].[usp_RenumberSelfAssessmentStructure]");
17+
}
18+
}
19+
}

DigitalLearningSolutions.Data.Migrations/Properties/Resources.Designer.cs

Lines changed: 52 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,12 @@
499499
<data name="TD-5759_CreateOrAlterSelfAssessmentReportSPandTVF-Fix_UP" type="System.Resources.ResXFileRef, System.Windows.Forms">
500500
<value>..\Scripts\TD-5759_CreateOrAlterSelfAssessmentReportSPandTVF-Fix_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
501501
</data>
502+
<data name="TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP" type="System.Resources.ResXFileRef, System.Windows.Forms">
503+
<value>..\Scripts\TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
504+
</data>
505+
<data name="TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP" type="System.Resources.ResXFileRef, System.Windows.Forms">
506+
<value>..\Scripts\TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
507+
</data>
502508
<data name="TD_5535_Alter_GetActivitiesForDelegateEnrolment_Down" type="System.Resources.ResXFileRef, System.Windows.Forms">
503509
<value>..\Scripts\TD-5535-Alter_GetActivitiesForDelegateEnrolment_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
504510
</data>
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
2+
CREATE OR ALTER PROCEDURE usp_RenumberSelfAssessmentStructure
3+
@SelfAssessmentID INT
4+
AS
5+
BEGIN
6+
SET NOCOUNT ON;
7+
8+
/*
9+
Step 1: Build an ordered list of groups
10+
- Each group is ranked by its current Min(Ordering)
11+
- Ungrouped competencies (NULL CompetencyGroupID) are treated as their own "pseudo group"
12+
*/
13+
;WITH GroupRanks AS (
14+
SELECT
15+
CompetencyGroupID,
16+
ROW_NUMBER() OVER (ORDER BY MIN(Ordering)) AS GroupRank
17+
FROM SelfAssessmentStructure
18+
WHERE SelfAssessmentID = @SelfAssessmentID
19+
GROUP BY CompetencyGroupID
20+
)
21+
/*
22+
Step 2: Renumber groups and competencies
23+
- Groups get ranked (1,2,3…)
24+
- Within each group, competencies are ordered by their current Ordering and renumbered (1,2,3…)
25+
*/
26+
UPDATE sas
27+
SET sas.Ordering = rn.NewOrdering
28+
FROM SelfAssessmentStructure sas
29+
INNER JOIN (
30+
SELECT
31+
s.ID,
32+
-- new group position * 1000 + row within group
33+
-- gives room between groups and avoids clashes
34+
(g.GroupRank * 1000) +
35+
ROW_NUMBER() OVER (PARTITION BY s.CompetencyGroupID, g.GroupRank ORDER BY s.Ordering, s.ID) AS NewOrdering
36+
FROM SelfAssessmentStructure s
37+
INNER JOIN GroupRanks g
38+
ON g.CompetencyGroupID = s.CompetencyGroupID
39+
WHERE s.SelfAssessmentID = @SelfAssessmentID
40+
) rn ON sas.ID = rn.ID;
41+
42+
END
43+
44+
GO
45+
46+
CREATE OR ALTER PROCEDURE usp_MoveCompetencyGroupInSelfAssessment
47+
@SelfAssessmentID INT,
48+
@GroupID INT,
49+
@Direction NVARCHAR(10) -- 'up' or 'down'
50+
AS
51+
BEGIN
52+
SET NOCOUNT ON;
53+
SET XACT_ABORT ON;
54+
55+
BEGIN TRY
56+
BEGIN TRAN;
57+
58+
/* 1) Rank groups by current Min(Ordering) (NULL groups excluded here; include if desired). */
59+
;WITH GroupRanks AS (
60+
SELECT
61+
CompetencyGroupID,
62+
MIN(Ordering) AS MinOrder,
63+
ROW_NUMBER() OVER (
64+
ORDER BY MIN(Ordering), MIN(CompetencyGroupID)
65+
) AS RankPos
66+
FROM SelfAssessmentStructure
67+
WHERE SelfAssessmentID = @SelfAssessmentID
68+
AND CompetencyGroupID IS NOT NULL
69+
GROUP BY CompetencyGroupID
70+
)
71+
SELECT *
72+
INTO #Groups
73+
FROM GroupRanks;
74+
75+
DECLARE @CurRank INT, @SwapRank INT, @SwapGroupID INT;
76+
77+
SELECT @CurRank = RankPos
78+
FROM #Groups
79+
WHERE CompetencyGroupID = @GroupID;
80+
81+
IF @CurRank IS NULL
82+
BEGIN
83+
DROP TABLE #Groups;
84+
COMMIT TRAN; RETURN; -- nothing to do
85+
END
86+
87+
IF LOWER(@Direction) = 'up'
88+
SET @SwapRank = @CurRank - 1;
89+
ELSE IF LOWER(@Direction) = 'down'
90+
SET @SwapRank = @CurRank + 1;
91+
ELSE
92+
BEGIN
93+
DROP TABLE #Groups;
94+
ROLLBACK TRAN; THROW 50000, 'Direction must be ''up'' or ''down''.', 1;
95+
END
96+
97+
SELECT @SwapGroupID = CompetencyGroupID
98+
FROM #Groups
99+
WHERE RankPos = @SwapRank;
100+
101+
IF @SwapGroupID IS NULL
102+
BEGIN
103+
DROP TABLE #Groups;
104+
COMMIT TRAN; RETURN; -- already at top/bottom
105+
END
106+
107+
/* 2) Build a mapping where ONLY the two groups swap ranks; others keep theirs. */
108+
SELECT
109+
g.CompetencyGroupID,
110+
CASE
111+
WHEN g.CompetencyGroupID = @GroupID THEN @SwapRank
112+
WHEN g.CompetencyGroupID = @SwapGroupID THEN @CurRank
113+
ELSE g.RankPos
114+
END AS NewRank
115+
INTO #RankMap
116+
FROM #Groups g;
117+
118+
/* 3) Choose a block size big enough to keep groups separated when we recompute.
119+
Using count(rows in this self assessment) + 10 is a safe dynamic choice. */
120+
DECLARE @Block INT =
121+
(SELECT COUNT(*) FROM SelfAssessmentStructure WHERE SelfAssessmentID = @SelfAssessmentID) + 10;
122+
123+
/* 4) Recompute EVERY row’s Ordering from the rank map (this sets the new global order).
124+
We preserve within-group relative order using the existing Ordering (and ID as tiebreak). */
125+
;WITH NewOrders AS (
126+
SELECT
127+
s.ID,
128+
(m.NewRank * @Block)
129+
+ ROW_NUMBER() OVER (
130+
PARTITION BY s.CompetencyGroupID
131+
ORDER BY s.Ordering, s.ID
132+
) AS NewOrdering
133+
FROM SelfAssessmentStructure s
134+
JOIN #RankMap m
135+
ON m.CompetencyGroupID = s.CompetencyGroupID
136+
WHERE s.SelfAssessmentID = @SelfAssessmentID
137+
)
138+
UPDATE s
139+
SET s.Ordering = n.NewOrdering
140+
FROM SelfAssessmentStructure s
141+
JOIN NewOrders n ON n.ID = s.ID;
142+
143+
/* 5) (Optional) Compress to 1..N while keeping the just-established relative order. */
144+
;WITH Ordered AS (
145+
SELECT ID,
146+
ROW_NUMBER() OVER (ORDER BY Ordering, ID) AS Seq
147+
FROM SelfAssessmentStructure
148+
WHERE SelfAssessmentID = @SelfAssessmentID
149+
)
150+
UPDATE s
151+
SET s.Ordering = o.Seq
152+
FROM SelfAssessmentStructure s
153+
JOIN Ordered o ON o.ID = s.ID;
154+
155+
DROP TABLE #Groups;
156+
DROP TABLE #RankMap;
157+
158+
COMMIT TRAN;
159+
END TRY
160+
BEGIN CATCH
161+
IF @@TRANCOUNT > 0 ROLLBACK TRAN;
162+
THROW;
163+
END CATCH
164+
END;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
CREATE OR ALTER PROCEDURE usp_MoveCompetencyInSelfAssessment
2+
@SelfAssessmentID INT,
3+
@CompetencyID INT,
4+
@Direction NVARCHAR(10)
5+
AS
6+
BEGIN
7+
SET NOCOUNT ON;
8+
9+
DECLARE @GroupID INT, @CurrentOrder INT;
10+
11+
SELECT
12+
@GroupID = CompetencyGroupID,
13+
@CurrentOrder = Ordering
14+
FROM SelfAssessmentStructure
15+
WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @CompetencyID;
16+
17+
IF @GroupID IS NULL
18+
BEGIN
19+
-- Can't reorder ungrouped competencies via group-based logic
20+
RETURN;
21+
END
22+
23+
DECLARE @TargetCompetencyID INT, @TargetOrder INT;
24+
25+
IF @Direction = 'up'
26+
BEGIN
27+
SELECT TOP 1
28+
@TargetCompetencyID = CompetencyID,
29+
@TargetOrder = Ordering
30+
FROM SelfAssessmentStructure
31+
WHERE SelfAssessmentID = @SelfAssessmentID
32+
AND CompetencyGroupID = @GroupID
33+
AND Ordering < @CurrentOrder
34+
ORDER BY Ordering DESC;
35+
END
36+
ELSE IF @Direction = 'down'
37+
BEGIN
38+
SELECT TOP 1
39+
@TargetCompetencyID = CompetencyID,
40+
@TargetOrder = Ordering
41+
FROM SelfAssessmentStructure
42+
WHERE SelfAssessmentID = @SelfAssessmentID
43+
AND CompetencyGroupID = @GroupID
44+
AND Ordering > @CurrentOrder
45+
ORDER BY Ordering ASC;
46+
END
47+
48+
IF @TargetCompetencyID IS NOT NULL
49+
BEGIN
50+
-- Swap the orderings
51+
UPDATE SelfAssessmentStructure
52+
SET Ordering = @TargetOrder
53+
WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @CompetencyID;
54+
55+
UPDATE SelfAssessmentStructure
56+
SET Ordering = @CurrentOrder
57+
WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @TargetCompetencyID;
58+
END
59+
END

0 commit comments

Comments
 (0)