-
Notifications
You must be signed in to change notification settings - Fork 1
Heedls 563 course delegates basic #564
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
768c6d4
2f2e527
1f611a3
062732c
8d7b601
38e9481
1903903
3ae1897
82d686f
6a3f7af
1c0ef80
d757924
c54befa
2da8b2b
26df4f9
471fd1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| namespace DigitalLearningSolutions.Data.Tests.DataServices | ||
| { | ||
| using System; | ||
| using System.Linq; | ||
| using DigitalLearningSolutions.Data.DataServices; | ||
| using DigitalLearningSolutions.Data.Models.CourseDelegates; | ||
| using DigitalLearningSolutions.Data.Tests.TestHelpers; | ||
| using FluentAssertions; | ||
| using FluentAssertions.Execution; | ||
| using NUnit.Framework; | ||
|
|
||
| public class CourseDelegatesDataServiceTests | ||
| { | ||
| private ICourseDelegatesDataService courseDelegatesDataService = null!; | ||
|
|
||
| [SetUp] | ||
| public void Setup() | ||
| { | ||
| var connection = ServiceTestHelper.GetDatabaseConnection(); | ||
| courseDelegatesDataService = new CourseDelegatesDataService(connection); | ||
| } | ||
|
|
||
| [Test] | ||
| public void GetDelegatesOnCourse_returns_expected_values() | ||
| { | ||
| // Given | ||
| var expectedFirstRecord = new CourseDelegate | ||
| { | ||
| Active = true, | ||
| CandidateNumber = "PC97", | ||
| CompleteBy = null, | ||
| DelegateId = 32926, | ||
| EmailAddress = "erpock.hs@5bntu", | ||
| Enrolled = new DateTime(2012, 07, 02, 13, 30, 37, 807), | ||
| FirstName = "xxxxx", | ||
| LastName = "xxxx", | ||
| LastUpdated = new DateTime(2012, 07, 31, 10, 18, 39, 993), | ||
| Locked = false, | ||
| ProgressId = 18395, | ||
| RemovedDate = null, | ||
| Completed = null, | ||
| AllAttempts = 0, | ||
| AttemptsPassed = 0 | ||
| }; | ||
|
|
||
| // When | ||
| var result = courseDelegatesDataService.GetDelegatesOnCourse(1, 2).ToList(); | ||
|
|
||
| // Then | ||
| using (new AssertionScope()) | ||
| { | ||
| result.Should().HaveCount(3); | ||
| result.First().Should().BeEquivalentTo(expectedFirstRecord); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| namespace DigitalLearningSolutions.Data.Tests.Models.User | ||
| { | ||
| using DigitalLearningSolutions.Data.Models.Courses; | ||
| using FluentAssertions; | ||
| using NUnit.Framework; | ||
|
|
||
| public class CourseTests | ||
| { | ||
| [Test] | ||
| public void Course_name_should_be_application_name_if_customisation_name_is_null() | ||
| { | ||
| // When | ||
| var courseStatistics = new Course | ||
| { | ||
| ApplicationName = "Test application", | ||
| CustomisationName = string.Empty | ||
| }; | ||
|
|
||
| // Then | ||
| courseStatistics.CourseName.Should().BeEquivalentTo("Test application"); | ||
| } | ||
|
|
||
| [Test] | ||
| public void Course_name_should_include_customisation_name_if_it_is_not_null() | ||
| { | ||
| // When | ||
| var courseStatistics = new Course | ||
| { | ||
| ApplicationName = "Test application", | ||
| CustomisationName = "customisation" | ||
| }; | ||
|
|
||
| // Then | ||
| courseStatistics.CourseName.Should().BeEquivalentTo("Test application - customisation"); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| namespace DigitalLearningSolutions.Data.Tests.Services | ||
| { | ||
| using System.Collections.Generic; | ||
| using DigitalLearningSolutions.Data.DataServices; | ||
| using DigitalLearningSolutions.Data.Models.CourseDelegates; | ||
| using DigitalLearningSolutions.Data.Models.Courses; | ||
| using DigitalLearningSolutions.Data.Services; | ||
| using DigitalLearningSolutions.Data.Tests.TestHelpers; | ||
| using FakeItEasy; | ||
| using FluentAssertions; | ||
| using FluentAssertions.Execution; | ||
| using NUnit.Framework; | ||
|
|
||
| public class CourseDelegatesServiceTests | ||
| { | ||
| private ICourseDataService courseDataService = null!; | ||
| private ICourseDelegatesDataService courseDelegatesDataService = null!; | ||
| private ICourseDelegatesService courseDelegatesService = null!; | ||
|
|
||
| [SetUp] | ||
| public void Setup() | ||
| { | ||
| courseDataService = A.Fake<ICourseDataService>(); | ||
| courseDelegatesDataService = A.Fake<ICourseDelegatesDataService>(); | ||
|
|
||
| courseDelegatesService = new CourseDelegatesService( | ||
| courseDataService, | ||
| courseDelegatesDataService | ||
| ); | ||
| } | ||
|
|
||
| [Test] | ||
| public void GetCoursesAndCourseDelegatesForCentre_populates_course_delegates_data() | ||
| { | ||
| // Given | ||
| const int centreId = 2; | ||
| const int categoryId = 1; | ||
| A.CallTo(() => courseDataService.GetCoursesAtCentreForAdminCategoryId(centreId, categoryId)) | ||
| .Returns(new List<Course> { new Course { CustomisationId = 1 } }); | ||
| A.CallTo(() => courseDelegatesDataService.GetDelegatesOnCourse(A<int>._, A<int>._)) | ||
| .Returns(new List<CourseDelegate> { new CourseDelegate() }); | ||
|
|
||
| // When | ||
| var result = courseDelegatesService.GetCoursesAndCourseDelegatesForCentre( | ||
| centreId, | ||
| categoryId, | ||
| null | ||
| ); | ||
|
|
||
| // Then | ||
| using (new AssertionScope()) | ||
| { | ||
| result.Courses.Should().HaveCount(1); | ||
| result.Delegates.Should().HaveCount(1); | ||
| result.CustomisationId.Should().Be(1); | ||
| } | ||
| } | ||
|
|
||
| [Test] | ||
| public void GetCoursesAndCourseDelegatesForCentre_contains_empty_lists_with_no_courses_in_category() | ||
| { | ||
| // Given | ||
| A.CallTo(() => courseDataService.GetCoursesAtCentreForAdminCategoryId(2, 7)).Returns(new List<Course>()); | ||
|
|
||
| // When | ||
| var result = courseDelegatesService.GetCoursesAndCourseDelegatesForCentre(2, 7, null); | ||
|
|
||
| // Then | ||
| using (new AssertionScope()) | ||
| { | ||
| A.CallTo(() => courseDelegatesDataService.GetDelegatesOnCourse(A<int>._, A<int>._)) | ||
| .MustNotHaveHappened(); | ||
| result.Courses.Should().BeEmpty(); | ||
| result.Delegates.Should().BeEmpty(); | ||
| result.CustomisationId.Should().BeNull(); | ||
| } | ||
| } | ||
|
|
||
| [Test] | ||
| public void GetCoursesAndCourseDelegatesForCentre_uses_passed_in_customisation_id() | ||
| { | ||
| // Given | ||
| const int customisationId = 2; | ||
| const int centreId = 2; | ||
| const int categoryId = 1; | ||
| A.CallTo(() => courseDataService.GetCoursesAtCentreForAdminCategoryId(centreId, categoryId)) | ||
| .Returns(new List<Course> { new Course { CustomisationId = 1 } }); | ||
| A.CallTo(() => courseDelegatesDataService.GetDelegatesOnCourse(A<int>._, A<int>._)) | ||
| .Returns(new List<CourseDelegate> { new CourseDelegate() }); | ||
|
|
||
| // When | ||
| var result = courseDelegatesService.GetCoursesAndCourseDelegatesForCentre( | ||
| centreId, | ||
| categoryId, | ||
| customisationId | ||
| ); | ||
|
|
||
| // Then | ||
| A.CallTo(() => courseDelegatesDataService.GetDelegatesOnCourse(customisationId, centreId)) | ||
| .MustHaveHappened(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,10 +17,11 @@ public interface ICourseDataService | |
| void RemoveCurrentCourse(int progressId, int candidateId); | ||
| void EnrolOnSelfAssessment(int selfAssessmentId, int candidateId); | ||
| int GetNumberOfActiveCoursesAtCentreForCategory(int centreId, int categoryId); | ||
| IEnumerable<CourseStatistics> GetCourseStatisticsAtCentreForCategoryId(int centreId, int categoryId); | ||
| IEnumerable<CourseStatistics> GetCourseStatisticsAtCentreForAdminCategoryId(int centreId, int categoryId); | ||
| IEnumerable<DelegateCourseInfo> GetDelegateCoursesInfo(int delegateId); | ||
| (int totalAttempts, int attemptsPassed) GetDelegateCourseAttemptStats(int delegateId, int customisationId); | ||
| CourseDetails? GetCourseDetails(int customisationId, int centreId, int categoryId); | ||
| CourseDetails? GetCourseDetailsForAdminCategoryId(int customisationId, int centreId, int categoryId); | ||
| IEnumerable<Course> GetCoursesAtCentreForAdminCategoryId(int centreId, int categoryId); | ||
| } | ||
|
|
||
| public class CourseDataService : ICourseDataService | ||
|
|
@@ -171,14 +172,17 @@ FROM Customisations AS c | |
| ); | ||
| } | ||
|
|
||
| public IEnumerable<CourseStatistics> GetCourseStatisticsAtCentreForCategoryId(int centreId, int categoryId) | ||
| // Admins have a non-nullable category ID where 0 = all. This is why we have the | ||
| // @categoryId = 0 in the WHERE clause, to prevent filtering on category ID when it is 0 | ||
| public IEnumerable<CourseStatistics> GetCourseStatisticsAtCentreForAdminCategoryId(int centreId, int categoryId) | ||
| { | ||
| return connection.Query<CourseStatistics>( | ||
| @$"SELECT | ||
| cu.CustomisationID, | ||
| cu.CentreID, | ||
| cu.Active, | ||
| cu.AllCentres, | ||
| ap.ApplicationId, | ||
| ap.ApplicationName, | ||
| cu.CustomisationName, | ||
| {DelegateCountQuery}, | ||
|
|
@@ -252,7 +256,9 @@ FROM AssessAttempts aa | |
| ); | ||
| } | ||
|
|
||
| public CourseDetails? GetCourseDetails(int customisationId, int centreId, int categoryId) | ||
| // Admins have a non-nullable category ID where 0 = all. This is why we have the | ||
| // @categoryId = 0 in the WHERE clause, to prevent filtering on category ID when it is 0 | ||
| public CourseDetails? GetCourseDetailsForAdminCategoryId(int customisationId, int centreId, int categoryId) | ||
| { | ||
| return connection.Query<CourseDetails>( | ||
| @$"SELECT | ||
|
|
@@ -298,5 +304,25 @@ AND ap.ArchivedDate IS NULL | |
| new { customisationId, centreId, categoryId } | ||
| ).FirstOrDefault(); | ||
| } | ||
|
|
||
| // Admins have a non-nullable category ID where 0 = all. This is why we have the | ||
| // @categoryId = 0 in the WHERE clause, to prevent filtering on category ID when it is 0 | ||
| public IEnumerable<Course> GetCoursesAtCentreForAdminCategoryId(int centreId, int categoryId) | ||
| { | ||
| return connection.Query<Course>( | ||
| @"SELECT | ||
| c.CustomisationID, | ||
| c.CentreID, | ||
| c.ApplicationID, | ||
stellake marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| a.ApplicationName, | ||
| c.CustomisationName, | ||
| c.Active | ||
| FROM Customisations AS c | ||
| JOIN Applications AS a on a.ApplicationID = c.ApplicationID | ||
| WHERE (CentreID = @centreId OR CentreID = 0) | ||
| AND (a.CourseCategoryID = @categoryId OR @categoryId = 0)", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'd still prefer the categoryId to be null with no filter rather than "0" which can be ambiguous. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Admins don't have a nullable CategoryId. I think we should keep this behaviour consistent with the AdminUser records so it doesn't cause confusion unless we want to go through and change everything. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But I think in this query the "categoryId" is effectively a filter on category id, which intuitively would be:
|
||
| new { centreId, categoryId } | ||
| ); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.