Skip to content

Commit 2a5c06d

Browse files
committed
Merge branch 'DLS-Release-v1.2.3' into UAT
2 parents df48153 + b97887c commit 2a5c06d

File tree

7 files changed

+68
-132
lines changed

7 files changed

+68
-132
lines changed

DigitalLearningSolutions.Web.Tests/Helpers/ProfessionalRegistrationNumberHelperTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ public void ValidateProfessionalRegistrationNumber_sets_error_when_hasPrn_is_not
107107

108108
[TestCase(null, ErrorMessagesTestHelper.MissingNumberError)]
109109
[TestCase("", ErrorMessagesTestHelper.MissingNumberError)]
110-
[TestCase("1234", ErrorMessagesTestHelper.LengthError)]
111-
[TestCase("1234", ErrorMessagesTestHelper.LengthError)]
112110
[TestCase("01234_", ErrorMessagesTestHelper.InvalidFormatError)]
113111
[TestCase("01234 ", ErrorMessagesTestHelper.InvalidFormatError)]
114112
[TestCase("01234$", ErrorMessagesTestHelper.InvalidFormatError)]

DigitalLearningSolutions.Web.Tests/TestHelpers/ErrorMessagesTestHelper.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@ namespace DigitalLearningSolutions.Web.Tests.TestHelpers
44
public static class ErrorMessagesTestHelper
55
{
66
public const string InvalidFormatError =
7-
"Invalid professional registration number format. " +
8-
"Valid formats include: 7 digits (e.g., 1234567), 1–2 letters followed by 6 digits (e.g., AB123456), " +
9-
"4–8 digits, an optional 'P' plus 5–6 digits, 'C' or 'P' plus 6 digits, " +
10-
"an optional letter plus 5–6 digits, 'L' plus 4–6 digits, " +
11-
"or 2 digits followed by a hyphen and 4–5 alphanumeric characters (e.g., 12-AB123).";
7+
@"The format you entered isn’t recognised. Please check and try again.
8+
<br>Valid formats include:
9+
<ul>
10+
<li>7 digits - example, 1234567</li>
11+
<li>1–2 letters followed by 6 digits - example, AB123456</li>
12+
<li>‘P’ followed by 5–6 digits - example, P12345, P123456</li>
13+
<li>‘C’ or ‘P’ followed by 6 digits - example, C123456, P123456</li>
14+
<li>Optional letter followed by 5–6 digits - example, A12345, B123456</li>
15+
<li>‘L’ followed by 4–6 digits - example, L1234, L123456</li>
16+
<li>2 digits, hyphen, then 4–5 alphanumeric characters - example, 12-AB123</li>
17+
</ul>";
1218

1319
public const string MissingNumberError = "Enter a professional registration number";
14-
public const string LengthError = "Professional registration number must be between 5 and 20 characters";
20+
public const string LengthError = "Professional registration number must be between 4 and 8 characters";
1521

1622
}
1723
}

DigitalLearningSolutions.Web/Helpers/CompetencyFilterHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public static IEnumerable<Competency> FilterCompetencies(IEnumerable<Competency>
3030
return filteredCompetencies;
3131
}
3232

33-
private static void ApplyResponseStatusFilters(ref IEnumerable<Competency> competencies, IEnumerable<int> filters, string searchText = "")
33+
public static void ApplyResponseStatusFilters(ref IEnumerable<Competency> competencies, IEnumerable<int> filters, string searchText = "")
3434
{
3535
var appliedResponseStatusFilters = filters.Where(IsResponseStatusFilter).ToList();
3636

DigitalLearningSolutions.Web/Helpers/ProfessionalRegistrationNumberHelper.cs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
namespace DigitalLearningSolutions.Web.Helpers
22
{
3+
34
using Microsoft.AspNetCore.Mvc.ModelBinding;
5+
using System.Text;
46
using System.Text.RegularExpressions;
57

68
public class ProfessionalRegistrationNumberHelper
@@ -39,28 +41,29 @@ public static void ValidateProfessionalRegistrationNumber(
3941
modelState.AddModelError("ProfessionalRegistrationNumber", "Enter a professional registration number");
4042
return;
4143
}
42-
43-
if (prn.Length < 5 || prn.Length > 20)
44-
{
45-
modelState.AddModelError(
46-
"ProfessionalRegistrationNumber",
47-
"Professional registration number must be between 5 and 20 characters"
48-
);
49-
}
50-
51-
const string pattern = @"^(\d{7}|[A-Za-z]{1,2}\d{6}|\d{4,8}|P?\d{5,6}|[C|P]\d{6}|[A-Za-z]?\d{5,6}|L\d{4,6}|\d{2}-[A-Za-z\d]{4,5})$";
44+
const string pattern = @"^(\d{7}|[A-Za-z]{1,2}\d{6}|\d{4,8}|P?\d{5,6}|[C|P]\d{6}|[A-Za-z]?\d{5,6}|L\d{4,6}|\d{2}-[A-Za-z\d]{4,5})$";
5245
var rg = new Regex(pattern, RegexOptions.IgnoreCase);
5346
if (!rg.Match(prn).Success)
5447
{
5548
modelState.AddModelError(
5649
"ProfessionalRegistrationNumber",
57-
"Invalid professional registration number format. " +
58-
"Valid formats include: 7 digits (e.g., 1234567), 1–2 letters followed by 6 digits (e.g., AB123456), " +
59-
"4–8 digits, an optional 'P' plus 5–6 digits, 'C' or 'P' plus 6 digits, " +
60-
"an optional letter plus 5–6 digits, 'L' plus 4–6 digits, " +
61-
"or 2 digits followed by a hyphen and 4–5 alphanumeric characters (e.g., 12-AB123)."
50+
GetProfessionalRegistrationNumberErrorMessage()
6251
);
6352
}
6453
}
54+
public static string GetProfessionalRegistrationNumberErrorMessage()
55+
{
56+
return @"The format you entered isn’t recognised. Please check and try again.
57+
<br>Valid formats include:
58+
<ul>
59+
<li>7 digits - example, 1234567</li>
60+
<li>1–2 letters followed by 6 digits - example, AB123456</li>
61+
<li>‘P’ followed by 5–6 digits - example, P12345, P123456</li>
62+
<li>‘C’ or ‘P’ followed by 6 digits - example, C123456, P123456</li>
63+
<li>Optional letter followed by 5–6 digits - example, A12345, B123456</li>
64+
<li>‘L’ followed by 4–6 digits - example, L1234, L123456</li>
65+
<li>2 digits, hyphen, then 4–5 alphanumeric characters - example, 12-AB123</li>
66+
</ul>";
67+
}
6568
}
6669
}

DigitalLearningSolutions.Web/Helpers/SupervisorCompetencyFilterHelper.cs

Lines changed: 1 addition & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public static IEnumerable<Competency> FilterCompetencies(IEnumerable<Competency>
1717
var searchText = search.SearchText?.Trim() ?? string.Empty;
1818
var filters = search.AppliedFilters?.Select(f => int.Parse(f.FilterValue)) ?? Enumerable.Empty<int>();
1919
search.CompetencyFlags = competencyFlags.ToList();
20-
ApplyResponseStatusFilters(ref filteredCompetencies, filters, searchText);
20+
CompetencyFilterHelper.ApplyResponseStatusFilters(ref filteredCompetencies, filters, searchText);
2121
UpdateRequirementsFilterDropdownOptionsVisibility(search, filteredCompetencies);
2222
ApplyRequirementsFilters(ref filteredCompetencies, filters);
2323

@@ -28,109 +28,6 @@ public static IEnumerable<Competency> FilterCompetencies(IEnumerable<Competency>
2828
}
2929
return filteredCompetencies;
3030
}
31-
32-
private static void ApplyResponseStatusFilters(ref IEnumerable<Competency> competencies, IEnumerable<int> filters, string searchText = "")
33-
{
34-
var filteredCompetencies = competencies;
35-
var appliedResponseStatusFilters = filters.Where(f => IsResponseStatusFilter(f));
36-
if (appliedResponseStatusFilters.Any() || searchText.Length > 0)
37-
{
38-
var wordsInSearchText = searchText.Split().Where(w => w != string.Empty);
39-
filters = appliedResponseStatusFilters;
40-
if (filters.Contains((int)SelfAssessmentCompetencyFilter.Verified) && filters.Contains((int)SelfAssessmentCompetencyFilter.Optional))
41-
{
42-
filteredCompetencies = from c in competencies
43-
let searchTextMatchesGroup = wordsInSearchText.All(w => c.CompetencyGroup?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
44-
let searchTextMatchesCompetencyDescription = wordsInSearchText.All(w => c.Description?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
45-
let searchTextMatchesCompetencyName = wordsInSearchText.All(w => c.Name?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
46-
let responseStatusFilterMatchesAnyQuestion =
47-
(filters.Contains((int)SelfAssessmentCompetencyFilter.Verified) && c.AssessmentQuestions.Any(q => q.Verified.HasValue && q.SignedOff == true) && c.Optional)
48-
where (wordsInSearchText.Count() == 0 || searchTextMatchesGroup || searchTextMatchesCompetencyDescription || searchTextMatchesCompetencyName)
49-
&& (!appliedResponseStatusFilters.Any() || responseStatusFilterMatchesAnyQuestion)
50-
select c;
51-
}
52-
else if (filters.Contains((int)SelfAssessmentCompetencyFilter.ConfirmationRejected) && filters.Contains((int)SelfAssessmentCompetencyFilter.Optional))
53-
{
54-
filteredCompetencies = from c in competencies
55-
let searchTextMatchesGroup = wordsInSearchText.All(w => c.CompetencyGroup?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
56-
let searchTextMatchesCompetencyDescription = wordsInSearchText.All(w => c.Description?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
57-
let searchTextMatchesCompetencyName = wordsInSearchText.All(w => c.Name?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
58-
let responseStatusFilterMatchesAnyQuestion =
59-
(filters.Contains((int)SelfAssessmentCompetencyFilter.ConfirmationRejected) && c.AssessmentQuestions.Any(q => q.Verified.HasValue && q.SignedOff != true) && c.Optional)
60-
where (wordsInSearchText.Count() == 0 || searchTextMatchesGroup || searchTextMatchesCompetencyDescription || searchTextMatchesCompetencyName)
61-
&& (!appliedResponseStatusFilters.Any() || responseStatusFilterMatchesAnyQuestion)
62-
select c;
63-
}
64-
else if (filters.Contains((int)SelfAssessmentCompetencyFilter.PendingConfirmation) && filters.Contains((int)SelfAssessmentCompetencyFilter.Optional))
65-
{
66-
filteredCompetencies = from c in competencies
67-
let searchTextMatchesGroup = wordsInSearchText.All(w => c.CompetencyGroup?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
68-
let searchTextMatchesCompetencyDescription = wordsInSearchText.All(w => c.Description?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
69-
let searchTextMatchesCompetencyName = wordsInSearchText.All(w => c.Name?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
70-
let responseStatusFilterMatchesAnyQuestion =
71-
(filters.Contains((int)SelfAssessmentCompetencyFilter.PendingConfirmation) && c.AssessmentQuestions.Any(q => q.ResultId != null && q.Verified == null && q.Requested != null && q.UserIsVerifier == false) && c.Optional)
72-
where (wordsInSearchText.Count() == 0 || searchTextMatchesGroup || searchTextMatchesCompetencyDescription || searchTextMatchesCompetencyName)
73-
&& (!appliedResponseStatusFilters.Any() || responseStatusFilterMatchesAnyQuestion)
74-
select c;
75-
}
76-
else if (filters.Contains((int)SelfAssessmentCompetencyFilter.AwaitingConfirmation) && filters.Contains((int)SelfAssessmentCompetencyFilter.Optional))
77-
{
78-
filteredCompetencies = from c in competencies
79-
let searchTextMatchesGroup = wordsInSearchText.All(w => c.CompetencyGroup?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
80-
let searchTextMatchesCompetencyDescription = wordsInSearchText.All(w => c.Description?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
81-
let searchTextMatchesCompetencyName = wordsInSearchText.All(w => c.Name?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
82-
let responseStatusFilterMatchesAnyQuestion =
83-
(filters.Contains((int)SelfAssessmentCompetencyFilter.AwaitingConfirmation) && c.AssessmentQuestions.Any(q => q.Verified == null && q.Requested != null && q.UserIsVerifier == true) && c.Optional)
84-
where (wordsInSearchText.Count() == 0 || searchTextMatchesGroup || searchTextMatchesCompetencyDescription || searchTextMatchesCompetencyName)
85-
&& (!appliedResponseStatusFilters.Any() || responseStatusFilterMatchesAnyQuestion)
86-
select c;
87-
}
88-
else if (filters.Contains((int)SelfAssessmentCompetencyFilter.RequiresSelfAssessment) && filters.Contains((int)SelfAssessmentCompetencyFilter.Optional))
89-
{
90-
filteredCompetencies = from c in competencies
91-
let searchTextMatchesGroup = wordsInSearchText.All(w => c.CompetencyGroup?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
92-
let searchTextMatchesCompetencyDescription = wordsInSearchText.All(w => c.Description?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
93-
let searchTextMatchesCompetencyName = wordsInSearchText.All(w => c.Name?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
94-
let responseStatusFilterMatchesAnyQuestion =
95-
(filters.Contains((int)SelfAssessmentCompetencyFilter.RequiresSelfAssessment) && c.AssessmentQuestions.Any(q => q.ResultId == null) && c.Optional)
96-
where (wordsInSearchText.Count() == 0 || searchTextMatchesGroup || searchTextMatchesCompetencyDescription || searchTextMatchesCompetencyName)
97-
&& (!appliedResponseStatusFilters.Any() || responseStatusFilterMatchesAnyQuestion)
98-
select c;
99-
}
100-
else if (filters.Contains((int)SelfAssessmentCompetencyFilter.SelfAssessed) && filters.Contains((int)SelfAssessmentCompetencyFilter.Optional))
101-
{
102-
filteredCompetencies = from c in competencies
103-
let searchTextMatchesGroup = wordsInSearchText.All(w => c.CompetencyGroup?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
104-
let searchTextMatchesCompetencyDescription = wordsInSearchText.All(w => c.Description?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
105-
let searchTextMatchesCompetencyName = wordsInSearchText.All(w => c.Name?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
106-
let responseStatusFilterMatchesAnyQuestion =
107-
(filters.Contains((int)SelfAssessmentCompetencyFilter.SelfAssessed) && c.AssessmentQuestions.Any(q => q.ResultId != null && q.Requested == null && q.SignedOff == null) && c.Optional)
108-
where (wordsInSearchText.Count() == 0 || searchTextMatchesGroup || searchTextMatchesCompetencyDescription || searchTextMatchesCompetencyName)
109-
&& (!appliedResponseStatusFilters.Any() || responseStatusFilterMatchesAnyQuestion)
110-
select c;
111-
}
112-
else
113-
{
114-
filteredCompetencies = from c in competencies
115-
let searchTextMatchesGroup = wordsInSearchText.All(w => c.CompetencyGroup?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
116-
let searchTextMatchesCompetencyDescription = wordsInSearchText.All(w => c.Description?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
117-
let searchTextMatchesCompetencyName = wordsInSearchText.All(w => c.Name?.Contains(w, StringComparison.CurrentCultureIgnoreCase) ?? false)
118-
let responseStatusFilterMatchesAnyQuestion =
119-
(filters.Contains((int)SelfAssessmentCompetencyFilter.RequiresSelfAssessment) && c.AssessmentQuestions.Any(q => q.ResultId == null))
120-
|| (filters.Contains((int)SelfAssessmentCompetencyFilter.SelfAssessed) && c.AssessmentQuestions.Any(q => q.ResultId != null && q.Requested == null && q.SignedOff == null))
121-
|| (filters.Contains((int)SelfAssessmentCompetencyFilter.PendingConfirmation) && c.AssessmentQuestions.Any(q => q.ResultId != null && q.Verified == null && q.Requested != null && q.UserIsVerifier == false))
122-
|| (filters.Contains((int)SelfAssessmentCompetencyFilter.ConfirmationRejected) && c.AssessmentQuestions.Any(q => q.Verified.HasValue && q.SignedOff != true))
123-
|| (filters.Contains((int)SelfAssessmentCompetencyFilter.AwaitingConfirmation) && c.AssessmentQuestions.Any(q => q.Verified == null && q.Requested != null && q.UserIsVerifier == true))
124-
|| (filters.Contains((int)SelfAssessmentCompetencyFilter.Verified) && c.AssessmentQuestions.Any(q => q.Verified.HasValue && q.SignedOff == true))
125-
|| (filters.Contains((int)SelfAssessmentCompetencyFilter.Optional) && c.Optional)
126-
where (wordsInSearchText.Count() == 0 || searchTextMatchesGroup || searchTextMatchesCompetencyDescription || searchTextMatchesCompetencyName)
127-
&& (!appliedResponseStatusFilters.Any() || responseStatusFilterMatchesAnyQuestion)
128-
select c;
129-
}
130-
}
131-
competencies = filteredCompetencies;
132-
}
133-
13431
private static void ApplyRequirementsFilters(ref IEnumerable<Competency> competencies, IEnumerable<int> filters)
13532
{
13633
var filteredCompetencies = competencies;

0 commit comments

Comments
 (0)