diff --git a/LearningHub.Nhs.WebUI/Views/Search/_ResourceSearchResult.cshtml b/LearningHub.Nhs.WebUI/Views/Search/_ResourceSearchResult.cshtml
index 415e9d550..d4faffd0d 100644
--- a/LearningHub.Nhs.WebUI/Views/Search/_ResourceSearchResult.cshtml
+++ b/LearningHub.Nhs.WebUI/Views/Search/_ResourceSearchResult.cshtml
@@ -7,28 +7,24 @@
@using LearningHub.Nhs.Models.Search.SearchFeedback;
@using LearningHub.Nhs.Models.Enums;
@using LearningHub.Nhs.WebUI.Models.Search;
+@using LearningHub.Nhs.Models.Search.SearchClick;
@{
var resourceResult = Model.ResourceSearchResult;
var pagingModel = Model.ResourceResultPaging;
var index = pagingModel.CurrentPage * pagingModel.PageSize;
var searchString = HttpUtility.UrlEncode(Model.SearchString);
- var searchSignal = resourceResult.Feedback?.FeedbackAction?.Payload?.SearchSignal;
- int qVectorIndex = searchSignal.Query?.IndexOf("q_vector") ?? -1;
- var searchSignalQuery = searchSignal?.Query;
- // Check if "q_vector" is found in the string. if Yes, Remove "q_vector" and everything after it
- if (qVectorIndex != -1)
- {
- searchSignalQuery = searchSignal?.Query.Substring(0, qVectorIndex);
- }
- string GetUrl(int resourceReferenceId, int itemIndex, int nodePathId)
+
+ string GetUrl(int resourceReferenceId, int itemIndex, int nodePathId, SearchClickPayloadModel payload)
{
+ var searchSignal = payload?.SearchSignal;
string groupId = HttpUtility.UrlEncode(Model.GroupId.ToString());
- string searchSignalQueryEncoded = HttpUtility.UrlEncode(HttpUtility.UrlDecode(searchSignalQuery));
+ string searchSignalQueryEncoded = HttpUtility.UrlEncode(HttpUtility.UrlDecode(searchSignal?.Query));
- return $@"/search/record-resource-click?url=/Resource/{resourceReferenceId}&nodePathId={nodePathId}&itemIndex={itemIndex}
-&pageIndex={pagingModel.CurrentPage}&totalNumberOfHits={resourceResult.TotalHits}&searchText={searchString}&resourceReferenceId={resourceReferenceId}
-&groupId={groupId}&searchId={searchSignal?.SearchId}&timeOfSearch={searchSignal?.TimeOfSearch}&userQuery={HttpUtility.UrlEncode(searchSignal.UserQuery)}&query={searchSignalQueryEncoded}";
+ return $@"/search/record-resource-click?url=/Resource/{resourceReferenceId}&nodePathId={nodePathId}&itemIndex={payload?.HitNumber}
+&pageIndex={pagingModel.CurrentPage}&totalNumberOfHits={payload?.SearchSignal?.Stats?.TotalHits}&searchText={searchString}&resourceReferenceId={resourceReferenceId}
+&groupId={groupId}&searchId={searchSignal?.SearchId}&timeOfSearch={searchSignal?.TimeOfSearch}&userQuery={HttpUtility.UrlEncode(searchSignal.UserQuery)}
+&query={searchSignalQueryEncoded}&title={payload?.DocumentFields?.Title}";
}
bool showCatalogueFieldsInResources = ViewBag.ShowCatalogueFieldsInResources == null || ViewBag.ShowCatalogueFieldsInResources == true;
@@ -41,7 +37,7 @@
@if (provider != null)
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj
index 0047f4483..0af6830e4 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs
index 3675959a6..a4c1fbf4c 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs
@@ -1,7 +1,9 @@
namespace LearningHub.Nhs.OpenApi.Models.ViewModels
{
+ using LearningHub.Nhs.Models.Entities.Activity;
using System.Collections.Generic;
+
///
/// Class.
///
@@ -23,20 +25,25 @@ public ResourceMetadataViewModel()
///
.
///
.
///
.
+ ///
.
public ResourceMetadataViewModel(
int resourceId,
string title,
string description,
List
references,
string resourceType,
- decimal rating)
+ int? majorVersion,
+ decimal rating,
+ List userSummaryActivityStatuses)
{
this.ResourceId = resourceId;
this.Title = title;
this.Description = description;
this.References = references;
this.ResourceType = resourceType;
+ this.MajorVersion = majorVersion;
this.Rating = rating;
+ this.UserSummaryActivityStatuses = userSummaryActivityStatuses;
}
///
@@ -64,9 +71,20 @@ public ResourceMetadataViewModel(
///
public string ResourceType { get; set; }
+ ///
+ /// Gets or sets .
+ ///
+ public int? MajorVersion { get; set; }
+
+
///
/// Gets or sets .
///
public decimal Rating { get; set; }
+
+ ///
+ /// Gets or sets .
+ ///
+ public List UserSummaryActivityStatuses { get; set; }
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs
index cf31bdf54..41d1b197b 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs
@@ -1,3 +1,6 @@
+using LearningHub.Nhs.Models.Entities.Activity;
+using System.Collections.Generic;
+
namespace LearningHub.Nhs.OpenApi.Models.ViewModels
{
///
@@ -14,8 +17,10 @@ public class ResourceReferenceWithResourceDetailsViewModel
/// .
/// .
/// .
+ ///
/// .
/// .
+ ///
public ResourceReferenceWithResourceDetailsViewModel(
int resourceId,
int refId,
@@ -23,17 +28,21 @@ public ResourceReferenceWithResourceDetailsViewModel(
string description,
CatalogueViewModel catalogueViewModel,
string resourceType,
+ int? majorVersion,
decimal rating,
- string link)
+ string link,
+ List userSummaryActivityStatuses)
{
this.ResourceId = resourceId;
this.RefId = refId;
this.Title = title;
this.Description = description;
this.Catalogue = catalogueViewModel;
+ this.MajorVersion = majorVersion;
this.ResourceType = resourceType;
this.Rating = rating;
this.Link = link;
+ this.UserSummaryActivityStatuses = userSummaryActivityStatuses;
}
///
@@ -66,14 +75,27 @@ public ResourceReferenceWithResourceDetailsViewModel(
///
public string ResourceType { get; }
+
+ ///
+ /// Gets .
+ ///
+ public int? MajorVersion { get; }
+
///
/// Gets .
///
+ ///
+
public decimal Rating { get; }
///
/// Gets .
///
public string Link { get; }
+
+ ///
+ /// Gets .
+ ///
+ public List UserSummaryActivityStatuses { get; }
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs
index 50eff71f6..52b3dee08 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs
@@ -2,6 +2,7 @@ namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories
{
using System.Collections.Generic;
using System.Threading.Tasks;
+ using LearningHub.Nhs.Models.Entities.Activity;
using LearningHub.Nhs.Models.Entities.Resource;
///
@@ -23,5 +24,19 @@ public interface IResourceRepository
/// Resource references.
public Task> GetResourceReferencesByOriginalResourceReferenceIds(
IEnumerable originalResourceReferenceIds);
+
+ ///
+ /// Gets resource activity for resourceReferenceIds and userIds.
+ ///
+ /// .
+ ///
+ /// ResourceActivityDTO.
+ Task> GetResourceActivityPerResourceMajorVersion(IEnumerable? resourceReferenceIds, IEnumerable? userIds);
+
+ ///
+ /// GetAchievedCertificatedResourceIds
+ ///
+ /// .
+ public Task> GetAchievedCertificatedResourceIds(int currentUserId);
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs
index 020a4f1f5..c1ebf721b 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs
@@ -295,6 +295,11 @@ public LearningHubDbContext(LearningHubDbContextOptions options)
///
public virtual DbSet FileChunkDetail { get; set; }
+ ///
+ /// Gets or sets the ResourceActivityDto. These are not entities. They are returned from the [activity].[GetResourceActivityPerResourceMajorVersion] stored proc..
+ ///
+ public virtual DbSet ResourceActivityDTO { get; set; }
+
///
/// Gets or sets the RecentlyAddedResources. These are not entities. They are returned from the [resources].[GetRecentlyAddedResources] stored proc..
///
@@ -312,14 +317,14 @@ public LearningHubDbContext(LearningHubDbContextOptions options)
public virtual DbSet DashboardResourceDto { get; set; }
///
- /// Gets or sets the ScormContentDetailsViewModel.
+ /// Gets or sets the ExternalContentDetailsViewModel.
///
- public virtual DbSet ScormContentDetailsViewModel { get; set; }
+ public virtual DbSet ExternalContentDetailsViewModel { get; set; }
///
- /// Gets or sets the ScormContentServerViewModel.
+ /// Gets or sets the ContentServerViewModel.
///
- public virtual DbSet ScormContentServerViewModel { get; set; }
+ public virtual DbSet ContentServerViewModel { get; set; }
///
/// Gets or sets the DashboardCatalogueDto
@@ -520,12 +525,12 @@ public LearningHubDbContext(LearningHubDbContextOptions options)
///
/// Gets or sets the whole slide image annotation.
///
- public virtual DbSet WholeSlideImageAnnotation { get; set; }
+ public virtual DbSet ImageAnnotation { get; set; }
///
/// Gets or sets the whole slide image annotation mark.
///
- public virtual DbSet WholeSlideImageAnnotationMark { get; set; }
+ public virtual DbSet ImageAnnotationMark { get; set; }
///
/// Gets or sets the media block.
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs
index 668d318b1..8db1cd876 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs
@@ -110,8 +110,8 @@ public static void AddLearningHubMappings(this IServiceCollection services, ICon
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Content/PageSectionDetailMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Content/PageSectionDetailMap.cs
index 1bdbb69c8..42d2e309f 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Content/PageSectionDetailMap.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Content/PageSectionDetailMap.cs
@@ -17,8 +17,6 @@ protected override void InternalMap(EntityTypeBuilder entity)
{
entity.ToTable("PageSectionDetail", "content");
- entity.Property(e => e.AssetPositionId).HasDefaultValueSql("((2))");
-
entity.Property(e => e.BackgroundColour).HasMaxLength(20);
entity.Property(e => e.Description).HasMaxLength(512);
@@ -31,7 +29,7 @@ protected override void InternalMap(EntityTypeBuilder entity)
entity.Property(e => e.TextColour).HasMaxLength(20);
- entity.Property(e => e.Title).HasMaxLength(128);
+ entity.Property(e => e.SectionTitle).HasMaxLength(128);
entity.Property(e => e.DeletePending).IsRequired(false);
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/LogMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/LogMap.cs
index 62ebffd0c..0b8bb7428 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/LogMap.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/LogMap.cs
@@ -68,10 +68,6 @@ protected void InternalMap(EntityTypeBuilder modelBuilder)
modelBuilder.Property(e => e.UserId)
.HasColumnName("UserId");
- modelBuilder.HasOne(d => d.User)
- .WithMany(p => p.Logs)
- .HasForeignKey(d => d.UserId)
- .OnDelete(DeleteBehavior.ClientSetNull);
}
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMap.cs
index 18a94369a..00f7f46cb 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMap.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMap.cs
@@ -7,7 +7,7 @@
///
/// The whole slide image annotation map.
///
- public class WholeSlideImageAnnotationMap : BaseEntityMap
+ public class ImageAnnotationMap : BaseEntityMap
{
///
/// The internal map.
@@ -15,15 +15,15 @@ public class WholeSlideImageAnnotationMap : BaseEntityMap
/// The model builder.
///
- protected override void InternalMap(EntityTypeBuilder modelBuilder)
+ protected override void InternalMap(EntityTypeBuilder modelBuilder)
{
- modelBuilder.ToTable("WholeSlideImageAnnotation", "resources");
+ modelBuilder.ToTable("ImageAnnotation", "resources");
modelBuilder.HasOne(a => a.WholeSlideImage)
- .WithMany(i => i.WholeSlideImageAnnotations)
+ .WithMany(i => i.ImageAnnotations)
.HasForeignKey(a => a.WholeSlideImageId)
.OnDelete(DeleteBehavior.Cascade)
- .HasConstraintName("FK_WholeSlideImageAnnotation_WholeSlideImageId");
+ .HasConstraintName("FK_ImageAnnotation_WholeSlideImageId");
}
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMarkMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMarkMap.cs
index 2720b23db..9db0a6db6 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMarkMap.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMarkMap.cs
@@ -7,7 +7,7 @@
///
/// The whole slide image annotation map.
///
- public class WholeSlideImageAnnotationMarkMap : BaseEntityMap
+ public class ImageAnnotationMarkMap : BaseEntityMap
{
///
/// The internal map.
@@ -15,15 +15,15 @@ public class WholeSlideImageAnnotationMarkMap : BaseEntityMap
/// The model builder.
///
- protected override void InternalMap(EntityTypeBuilder modelBuilder)
+ protected override void InternalMap(EntityTypeBuilder modelBuilder)
{
- modelBuilder.ToTable("WholeSlideImageAnnotationMark", "resources");
+ modelBuilder.ToTable("ImageAnnotationMark", "resources");
- modelBuilder.HasOne(a => a.WholeSlideImageAnnotation)
- .WithMany(i => i.WholeSlideImageAnnotationMarks)
- .HasForeignKey(a => a.WholeSlideImageAnnotationId)
+ modelBuilder.HasOne(a => a.ImageAnnotation)
+ .WithMany(i => i.ImageAnnotationMarks)
+ .HasForeignKey(a => a.ImageAnnotationId)
.OnDelete(DeleteBehavior.Cascade)
- .HasConstraintName("FK_WholeSlideImageAnnotationMark_WholeSlideImageAnnotationId");
+ .HasConstraintName("FK_ImageAnnotationMark_ImageAnnotationId");
}
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/ResourceVersionMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/ResourceVersionMap.cs
index a87bee808..d96c3b4fe 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/ResourceVersionMap.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/ResourceVersionMap.cs
@@ -39,6 +39,9 @@ protected override void InternalMap(EntityTypeBuilder modelBuil
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_ResourceVersion_Resource");
+ modelBuilder.Property(e => e.ResourceAccessibilityEnum).HasColumnName("ResourceAccessibilityId")
+ .HasConversion();
+
modelBuilder.Property(e => e.VersionStatusEnum).HasColumnName("VersionStatusId")
.HasConversion();
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs
index a826c627e..79b7a239b 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs
@@ -1,11 +1,18 @@
namespace LearningHub.Nhs.OpenApi.Repositories.Repositories
{
+ using System;
using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Data;
using System.Linq;
using System.Threading.Tasks;
+ using LearningHub.Nhs.Models.Dashboard;
+ using LearningHub.Nhs.Models.Entities;
+ using LearningHub.Nhs.Models.Entities.Activity;
using LearningHub.Nhs.Models.Entities.Resource;
using LearningHub.Nhs.OpenApi.Repositories.EntityFramework;
using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories;
+ using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
///
@@ -69,5 +76,48 @@ public async Task> GetResourceReferencesByOrigina
.ThenInclude(r => r.ResourceVersionRatingSummary)
.ToListAsync();
}
+
+ ///
+ public async Task> GetAchievedCertificatedResourceIds(int currentUserId)
+ {
+ // Use dashboard logic to ensure same resources determined has having achieved certificates
+ var param0 = new SqlParameter("@userId", SqlDbType.Int) { Value = currentUserId };
+ var param4 = new SqlParameter("@TotalRecords", SqlDbType.Int) { Direction = ParameterDirection.Output };
+
+ var result = this.dbContext.DashboardResourceDto.FromSqlRaw("resources.GetAchievedCertificatedResourcesWithOptionalPagination @userId = @userId, @TotalRecords = @TotalRecords output", param0, param4).ToList();
+ List achievedCertificatedResourceIds = result.Select(drd => drd.ResourceId).Distinct().ToList();
+
+ return achievedCertificatedResourceIds;
+ }
+
+ ///
+ ///
+ ///
+ /// .
+ /// A representing the result of the asynchronous operation.
+ public async Task> GetResourceActivityPerResourceMajorVersion(
+ IEnumerable? resourceIds, IEnumerable? userIds)
+ {
+ var resourceIdsParam = resourceIds != null
+ ? string.Join(",", resourceIds)
+ : null;
+
+ var userIdsParam = userIds != null
+ ? string.Join(",", userIds)
+ : null;
+
+ var resourceIdsParameter = new SqlParameter("@p0", resourceIdsParam ?? (object)DBNull.Value);
+ var userIdsParameter = new SqlParameter("@p1", userIdsParam ?? (object)DBNull.Value);
+
+ List resourceActivityDTOs = await dbContext.ResourceActivityDTO
+ .FromSqlRaw(
+ "[activity].[GetResourceActivityPerResourceMajorVersion] @p0, @p1",
+ resourceIdsParameter,
+ userIdsParameter)
+ .AsNoTracking()
+ .ToListAsync();
+
+ return resourceActivityDTOs;
+ }
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj
index 6372c89b6..55ce79f06 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IResourceService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IResourceService.cs
index 8b2e46dee..f5f59cb1d 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IResourceService.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IResourceService.cs
@@ -10,18 +10,35 @@ namespace LearningHub.Nhs.OpenApi.Services.Interface.Services
///
public interface IResourceService
{
+ ///
+ /// The get resource by activityStatusIds async.
+ ///
+ /// activityStatusIds.
+ /// c.
+ /// The the resourceMetaDataViewModel corresponding to the resource reference.
+ Task> GetResourceReferenceByActivityStatus(List activityStatusIds, int currentUserId);
+
///
/// The get resource by id async.
///
/// The original resource reference id.
+ /// .
/// The the resourceMetaDataViewModel corresponding to the resource reference.
- Task GetResourceReferenceByOriginalId(int originalResourceReferenceId);
+ Task GetResourceReferenceByOriginalId(int originalResourceReferenceId, int? currentUserId);
+
+ ///
+ /// The get resource references for certificates
+ ///
+ /// currentUserId.
+ /// The ResourceReferenceWithResourceDetailsViewModelthe resourceMetaDataViewModel corresponding to the resource reference.
+ Task> GetResourceReferencesForCertificates(int currentUserId);
///
/// The get resources by Ids endpoint.
///
/// The original resource reference Ids.
+ /// .
/// The resourceReferenceMetaDataViewModel.
- Task GetResourceReferencesByOriginalIds(List originalResourceReferenceIds);
+ Task GetResourceReferencesByOriginalIds(List originalResourceReferenceIds, int? currentUserId);
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/ISearchService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/ISearchService.cs
index 63155c5eb..c9b6e3031 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/ISearchService.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/ISearchService.cs
@@ -14,6 +14,6 @@ public interface ISearchService
///
/// .
/// .
- Task Search(ResourceSearchRequest query);
+ Task Search(ResourceSearchRequest query, int? currentUserId);
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj
index 2207dfdd4..dece11f1c 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj
@@ -1,4 +1,4 @@
-
+
net6.0
@@ -24,6 +24,7 @@
+
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs
index 1e3f4e563..4848cc1e6 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs
@@ -2,10 +2,14 @@ namespace LearningHub.Nhs.OpenApi.Services.Services
{
using System;
using System.Collections.Generic;
+ using System.Data;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
+ using LearningHub.Nhs.Models.Entities.Activity;
using LearningHub.Nhs.Models.Entities.Resource;
+ using LearningHub.Nhs.Models.Enums;
+ using LearningHub.Nhs.Models.ViewModels.Helpers;
using LearningHub.Nhs.OpenApi.Models.Exceptions;
using LearningHub.Nhs.OpenApi.Models.ViewModels;
using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories;
@@ -47,9 +51,11 @@ public ResourceService(ILearningHubService learningHubService, IResourceReposito
/// the get by id async.
///
/// the id.
+ /// .
/// the resource.
- public async Task GetResourceReferenceByOriginalId(int originalResourceReferenceId)
+ public async Task GetResourceReferenceByOriginalId(int originalResourceReferenceId, int? currentUserId)
{
+ List resourceActivities = new List() { };
var list = new List() { originalResourceReferenceId };
var resourceReferences = await this.resourceRepository.GetResourceReferencesByOriginalResourceReferenceIds(list);
@@ -64,7 +70,15 @@ public async Task GetResourceRefe
throw new HttpResponseException("No matching resource reference", HttpStatusCode.NotFound);
}
- return this.GetResourceReferenceWithResourceDetailsViewModel(resourceReference);
+ if (currentUserId.HasValue)
+ {
+ List resourceIds = new List() { resourceReference.ResourceId };
+ List userIds = new List() { currentUserId.Value };
+
+ resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(resourceIds, userIds))?.ToList() ?? new List() { };
+ }
+
+ return this.GetResourceReferenceWithResourceDetailsViewModel(resourceReference, resourceActivities);
}
catch (InvalidOperationException exception)
{
@@ -78,8 +92,11 @@ public async Task GetResourceRefe
///
/// the resource reference ids.
/// the resource.
- public async Task GetResourceReferencesByOriginalIds(List originalResourceReferenceIds)
+ public async Task GetResourceReferencesByOriginalIds(List originalResourceReferenceIds, int? currentUserId)
{
+ List resourceActivities = new List() { };
+ List majorVersionIdActivityStatusDescription = new List() { };
+
var resourceReferences = await this.resourceRepository.GetResourceReferencesByOriginalResourceReferenceIds(originalResourceReferenceIds);
var resourceReferencesList = resourceReferences.ToList();
var matchedIds = resourceReferencesList.Select(r => r.OriginalResourceReferenceId).ToList();
@@ -95,18 +112,85 @@ public async Task GetResourceReferencesByOrigina
this.logger.LogWarning($"Multiple resource references found with OriginalResourceReferenceId {duplicateIds.First()}");
}
- var matchedResources = resourceReferencesList
- .Select(this.GetResourceReferenceWithResourceDetailsViewModel)
- .ToList();
+ if (currentUserId.HasValue)
+ {
+ List resourceIds = resourceReferencesList.Select(rrl => rrl.ResourceId).ToList();
+ List userIds = new List() { currentUserId.Value };
+
+ resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(resourceIds, userIds))?.ToList() ?? new List() { };
+ }
+
+ List matchedResources = resourceReferencesList
+ .Select(rr => this.GetResourceReferenceWithResourceDetailsViewModel(rr, resourceActivities.Where(ra => ra.ResourceId == rr.ResourceId).ToList()))
+ .ToList();
return new BulkResourceReferenceViewModel(matchedResources, unmatchedIds);
}
- private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithResourceDetailsViewModel(ResourceReference resourceReference)
+
+ ///
+ /// the get by id async.
+ ///
+ /// .
+ /// c.
+ /// list resource ViewModel.
+ public async Task> GetResourceReferenceByActivityStatus(List activityStatusIds, int currentUserId)
+ {
+ List resourceActivities = new List() { };
+ List resourceReferenceWithResourceDetailsViewModelLS = new List() { };
+
+ resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(new List(){ }, new List(){ currentUserId }))?.ToList() ?? new List() { };
+
+ // Removing resources that have no major versions with the required activitystatus
+ List resourceIds = resourceActivities
+ .GroupBy(ra => ra.ResourceId)
+ .Where(group => group.Any(g => activityStatusIds.Contains(g.ActivityStatusId)))
+ .Select(group => group.Key)
+ .Distinct()
+ .ToList();
+
+ var resourceReferencesList = (await this.resourceRepository.GetResourcesFromIds(resourceIds)).SelectMany(r => r.ResourceReference).ToList();
+
+ resourceReferenceWithResourceDetailsViewModelLS = resourceReferencesList.Select(rr => this.GetResourceReferenceWithResourceDetailsViewModel(rr, resourceActivities)).ToList();
+
+ return resourceReferenceWithResourceDetailsViewModelLS;
+ }
+
+ ///
+ /// Gets ResourceReferences ForCertificates using the ResourceReferenceWithResourceDetailsViewModel .
+ ///
+ /// user Id.
+ /// list resource reference ViewModel.
+ public async Task> GetResourceReferencesForCertificates(int currentUserId)
+ {
+
+ List resourceActivities = new List() { };
+ List resourceReferenceWithResourceDetailsViewModelLS = new List() { };
+ List achievedCertificatedResourceIds = (await this.resourceRepository.GetAchievedCertificatedResourceIds(currentUserId)).ToList();
+
+ resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(achievedCertificatedResourceIds, new List() { currentUserId }))?.ToList() ?? new List() { };
+
+ var resourceList = (await this.resourceRepository.GetResourcesFromIds(achievedCertificatedResourceIds)).ToList();
+
+ resourceReferenceWithResourceDetailsViewModelLS = resourceList.SelectMany(r => r.ResourceReference)
+ .Distinct()
+ .Select(rr => this.GetResourceReferenceWithResourceDetailsViewModel(rr, resourceActivities)).ToList();
+
+ return resourceReferenceWithResourceDetailsViewModelLS;
+ }
+
+ private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithResourceDetailsViewModel(ResourceReference resourceReference, List resourceActivities)
{
var hasCurrentResourceVersion = resourceReference.Resource.CurrentResourceVersion != null;
var hasRating = resourceReference.Resource.CurrentResourceVersion?.ResourceVersionRatingSummary != null;
+ List majorVersionIdActivityStatusDescription = new List() { };
+
+ if (resourceActivities != null && resourceActivities.Count != 0)
+ {
+ majorVersionIdActivityStatusDescription = ActivityStatusHelper.GetMajorVersionIdActivityStatusDescriptionLSPerResource(resourceReference.Resource, resourceActivities).ToList();
+ }
+
if (resourceReference.Resource == null)
{
throw new Exception("No matching resource");
@@ -135,8 +219,10 @@ private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithRe
resourceReference.Resource.CurrentResourceVersion?.Description ?? string.Empty,
resourceReference.GetCatalogue(),
resourceTypeNameOrEmpty,
+ resourceReference.Resource?.CurrentResourceVersion?.MajorVersion ?? 0,
resourceReference.Resource?.CurrentResourceVersion?.ResourceVersionRatingSummary?.AverageRating ?? 0,
- this.learningHubService.GetResourceLaunchUrl(resourceReference.OriginalResourceReferenceId));
+ this.learningHubService.GetResourceLaunchUrl(resourceReference.OriginalResourceReferenceId),
+ majorVersionIdActivityStatusDescription);
}
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs
index 656a8cb14..c5ede76fb 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs
@@ -3,8 +3,11 @@ namespace LearningHub.Nhs.OpenApi.Services.Services
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+ using LearningHub.Nhs.Models.Entities.Activity;
using LearningHub.Nhs.Models.Entities.Resource;
+ using LearningHub.Nhs.Models.Resource;
using LearningHub.Nhs.Models.Search;
+ using LearningHub.Nhs.Models.ViewModels.Helpers;
using LearningHub.Nhs.OpenApi.Models.ServiceModels.Findwise;
using LearningHub.Nhs.OpenApi.Models.ServiceModels.Resource;
using LearningHub.Nhs.OpenApi.Models.ViewModels;
@@ -57,7 +60,7 @@ public SearchService(
}
///
- public async Task Search(ResourceSearchRequest query)
+ public async Task Search(ResourceSearchRequest query, int? currentUserId)
{
var findwiseResultModel = await this.findwiseClient.Search(query);
@@ -66,7 +69,7 @@ public async Task Search(ResourceSearchRequest query)
return ResourceSearchResultModel.FailedWithStatus(findwiseResultModel.FindwiseRequestStatus);
}
- var resourceMetadataViewModels = await this.GetResourceMetadataViewModels(findwiseResultModel);
+ var resourceMetadataViewModels = await this.GetResourceMetadataViewModels(findwiseResultModel, currentUserId);
var totalHits = findwiseResultModel.SearchResults?.Stats.TotalHits;
@@ -77,8 +80,9 @@ public async Task Search(ResourceSearchRequest query)
}
private async Task> GetResourceMetadataViewModels(
- FindwiseResultModel findwiseResultModel)
+ FindwiseResultModel findwiseResultModel, int? currentUserId)
{
+ List resourceActivities = new List() { };
var documentsFound = findwiseResultModel.SearchResults?.DocumentList.Documents?.ToList() ??
new List();
var findwiseResourceIds = documentsFound.Select(d => int.Parse(d.Id)).ToList();
@@ -90,7 +94,7 @@ private async Task> GetResourceMetadataViewModel
var resourcesFound = await this.resourceRepository.GetResourcesFromIds(findwiseResourceIds);
- var resourceMetadataViewModels = resourcesFound.Select(this.MapToViewModel)
+ List resourceMetadataViewModels = resourcesFound.Select(resource => MapToViewModel(resource, resourceActivities.Where(x => x.ResourceId == resource.Id).ToList()))
.OrderBySequence(findwiseResourceIds)
.ToList();
@@ -105,14 +109,29 @@ private async Task> GetResourceMetadataViewModel
unmatchedResourcesIdsString);
}
+ if (currentUserId.HasValue)
+ {
+ List resourceIds = resourcesFound.Select(x => x.Id).ToList();
+ List userIds = new List() { currentUserId.Value };
+
+ resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(resourceIds, userIds))?.ToList() ?? new List() { };
+ }
return resourceMetadataViewModels;
}
- private ResourceMetadataViewModel MapToViewModel(Resource resource)
+ private ResourceMetadataViewModel MapToViewModel(Resource resource, List resourceActivities)
{
var hasCurrentResourceVersion = resource.CurrentResourceVersion != null;
var hasRating = resource.CurrentResourceVersion?.ResourceVersionRatingSummary != null;
+ List majorVersionIdActivityStatusDescription = new List() { };
+
+ if (resourceActivities != null && resourceActivities.Count != 0)
+ {
+ majorVersionIdActivityStatusDescription = ActivityStatusHelper.GetMajorVersionIdActivityStatusDescriptionLSPerResource(resource, resourceActivities)
+ .ToList();
+ }
+
if (!hasCurrentResourceVersion)
{
this.logger.LogInformation(
@@ -131,13 +150,17 @@ private ResourceMetadataViewModel MapToViewModel(Resource resource)
this.logger.LogError($"Resource has unrecognised type: {resource.ResourceTypeEnum}");
}
+
return new ResourceMetadataViewModel(
resource.Id,
resource.CurrentResourceVersion?.Title ?? ResourceHelpers.NoResourceVersionText,
resource.CurrentResourceVersion?.Description ?? string.Empty,
resource.ResourceReference.Select(this.GetResourceReferenceViewModel).ToList(),
resourceTypeNameOrEmpty,
- resource.CurrentResourceVersion?.ResourceVersionRatingSummary?.AverageRating ?? 0.0m);
+ resource.CurrentResourceVersion?.MajorVersion ?? 0,
+ resource.CurrentResourceVersion?.ResourceVersionRatingSummary?.AverageRating ?? 0.0m,
+ majorVersionIdActivityStatusDescription
+ );
}
private ResourceReferenceViewModel GetResourceReferenceViewModel(
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Controllers/ResourceControllerTests.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Controllers/ResourceControllerTests.cs
index 49f901c72..d0126a840 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Controllers/ResourceControllerTests.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Controllers/ResourceControllerTests.cs
@@ -18,8 +18,12 @@ namespace LearningHub.Nhs.OpenApi.Tests.Controllers
using Moq;
using Newtonsoft.Json;
using Xunit;
+ using Microsoft.AspNetCore.Http;
+ using Microsoft.AspNetCore.Mvc;
+ using System.Security.Claims;
+ using LearningHub.Nhs.Models.Enums;
- public sealed class ResourceControllerTests : IDisposable
+ public sealed class ResourceControllerTests
{
private readonly Mock searchService;
private readonly Mock resourceService;
@@ -87,6 +91,7 @@ await Assert.ThrowsAsync(
public async Task SearchEndpointUsesDefaultLimitGivenInConfig()
{
// Given
+ int? currentUserId = null; //E.g if hitting endpoint with ApiKey auth
this.GivenSearchServiceSucceedsButFindsNoItems();
this.GivenDefaultLimitForFindwiseSearchIs(12);
this.resourceController = new ResourceController(
@@ -99,7 +104,7 @@ public async Task SearchEndpointUsesDefaultLimitGivenInConfig()
// Then
this.searchService.Verify(
- service => service.Search(It.Is(request => request.Limit == 12)));
+ service => service.Search(It.Is(request => request.Limit == 12), currentUserId));
}
[Fact]
@@ -177,6 +182,41 @@ await Assert.ThrowsAsync(
exception.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
+ [Fact]
+ public void CurrentUserIdSetByAuth()
+ {
+ // Arrange
+ ResourceController resourceController = new ResourceController(
+ this.searchService.Object,
+ this.resourceService.Object,
+ this.findwiseConfigOptions.Object
+ );
+
+
+ // This Id is the development accountId
+ int currentUserId = 57541;
+
+ // Create claims identity with the specified user id
+ var claims = new List
+ {
+ new Claim(ClaimTypes.NameIdentifier, currentUserId.ToString()),
+ };
+ var identity = new ClaimsIdentity(claims, "AuthenticationTypes.Federation"); // Set the authentication type to "Federation"
+
+ // Create claims principal with the claims identity
+ var claimsPrincipal = new ClaimsPrincipal(identity);
+
+ // Create a mock HttpContext and set it to the ControllerContext
+ var httpContext = new DefaultHttpContext { User = claimsPrincipal };
+ var controllerContext = new ControllerContext { HttpContext = httpContext };
+ resourceController.ControllerContext = controllerContext;
+
+ // Act
+
+ // Assert that the CurrentUserId property of the resourceController matches the currentUserId
+ Assert.Equal(currentUserId, resourceController.CurrentUserId);
+ }
+
[Theory]
[InlineData(1)]
[InlineData(20)]
@@ -184,6 +224,7 @@ await Assert.ThrowsAsync(
public async Task SearchEndpointUsesPassedInLimitIfGiven(int limit)
{
// Given
+ int? currentUserId = null; //E.g if hitting endpoint with ApiKey auth
this.GivenSearchServiceSucceedsButFindsNoItems();
this.GivenDefaultLimitForFindwiseSearchIs(20);
this.resourceController = new ResourceController(
@@ -196,12 +237,46 @@ public async Task SearchEndpointUsesPassedInLimitIfGiven(int limit)
// Then
this.searchService.Verify(
- service => service.Search(It.Is(request => request.Limit == limit)));
+ service => service.Search(It.Is(request => request.Limit == limit), currentUserId));
+ }
+
+ [Fact]
+ public async Task GetResourceReferencesByCompleteThrowsErrorWhenNoUserId()
+ {
+ // When
+ var exception = await Assert.ThrowsAsync(async () =>
+ {
+ await this.resourceController.GetResourceReferencesByActivityStatus((int)ActivityStatusEnum.Completed);
+ });
+
+ // Then
+ Assert.Equal("User Id required.", exception.Message);
+ }
+
+ [Fact]
+ public async Task GetResourceReferencesByInProgressThrowsErrorWhenNoUserId()
+ {
+ // When
+ var exception = await Assert.ThrowsAsync(async () =>
+ {
+ await this.resourceController.GetResourceReferencesByActivityStatus((int)ActivityStatusEnum.Incomplete);// in complete in db is in progress front endS
+ });
+
+ // Then
+ Assert.Equal("User Id required.", exception.Message);
}
- public void Dispose()
+ [Fact]
+ public async Task GetResourceReferencesBycertificatesThrowsErrorWhenNoUserId()
{
- this.resourceController?.Dispose();
+ // When
+ var exception = await Assert.ThrowsAsync(async () =>
+ {
+ await this.resourceController.GetResourceReferencesByCertificates();
+ });
+
+ // Then
+ Assert.Equal("User Id required.", exception.Message);
}
private void GivenDefaultLimitForFindwiseSearchIs(int limit)
@@ -212,14 +287,17 @@ private void GivenDefaultLimitForFindwiseSearchIs(int limit)
private void GivenSearchServiceFailsWithStatus(FindwiseRequestStatus status)
{
- this.searchService.Setup(ss => ss.Search(It.IsAny())).ReturnsAsync(
+ int? currentUserId = null; //E.g if hitting endpoint with ApiKey auth
+ this.searchService.Setup(ss => ss.Search(It.IsAny(), currentUserId)).ReturnsAsync(
new ResourceSearchResultModel(new List(), status, 0));
}
private void GivenSearchServiceSucceedsButFindsNoItems()
{
- this.searchService.Setup(ss => ss.Search(It.IsAny())).ReturnsAsync(
+ int? currentUserId = null; //E.g if hitting endpoint with ApiKey auth
+ this.searchService.Setup(ss => ss.Search(It.IsAny(), currentUserId)).ReturnsAsync(
new ResourceSearchResultModel(new List(), FindwiseRequestStatus.Success, 0));
}
+
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs
index d6ae8f3db..ddddc6ebc 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs
@@ -2,10 +2,12 @@ namespace LearningHub.Nhs.OpenApi.Tests.Services.Services
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using System.Net;
using System.Threading.Tasks;
using FizzWare.NBuilder;
using FluentAssertions;
+ using LearningHub.Nhs.Models.Entities.Activity;
using LearningHub.Nhs.Models.Entities.Resource;
using LearningHub.Nhs.Models.Enums;
using LearningHub.Nhs.OpenApi.Models.Exceptions;
@@ -22,14 +24,35 @@ public class ResourceServiceTests
private readonly Mock learningHubService;
private readonly ResourceService resourceService;
private readonly Mock resourceRepository;
+ private readonly int currentUserId;
public ResourceServiceTests()
{
+ // This Id is the development accountId
+ this.currentUserId = 57541;
+
this.learningHubService = new Mock();
this.resourceRepository = new Mock();
this.resourceService = new ResourceService(this.learningHubService.Object, this.resourceRepository.Object, new NullLogger());
}
+ private List ResourceActivityDTOList => new List()
+ {
+ new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 5, MajorVersion = 5 },
+ new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 7, MajorVersion = 4 },
+ new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 3, MajorVersion = 3 },
+ new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 7, MajorVersion = 2 },
+ new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 3, MajorVersion = 1 },
+
+ new ResourceActivityDTO{ ResourceId = 2, ActivityStatusId = 5, MajorVersion = 5 }, // Passed
+ new ResourceActivityDTO{ ResourceId = 2, ActivityStatusId = 4, MajorVersion = 4 }, // Failed
+ new ResourceActivityDTO{ ResourceId = 2, ActivityStatusId = 3, MajorVersion = 3 }, // complete
+
+ new ResourceActivityDTO{ ResourceId = 3, ActivityStatusId = 4, MajorVersion = 2 }, // Failed
+ new ResourceActivityDTO{ ResourceId = 3, ActivityStatusId = 4, MajorVersion = 1 }, // Failed
+ new ResourceActivityDTO{ ResourceId = 3, ActivityStatusId = 7, MajorVersion = 4 }, // In complete
+ };
+
private List ResourceList => new List()
{
ResourceTestHelper.CreateResourceWithDetails(id: 1, title: "title1", description: "description1", rating: 3m, resourceType: ResourceTypeEnum.Article),
@@ -63,7 +86,7 @@ public async Task SingleResourceEndpointReturnsTheCorrectInformationIfThereIsAMa
.ReturnsAsync(this.ResourceReferenceList.GetRange(0, 1));
// When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(1);
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(1, null);
// Then
x.Rating.Should().Be(3);
@@ -80,7 +103,7 @@ public async Task SingleResourceReturnsA404IfTheresNoResourceReferenceWithAMatch
.ReturnsAsync(new List());
// When / Then
- var exception = await Assert.ThrowsAsync(async () => await this.resourceService.GetResourceReferenceByOriginalId(999));
+ var exception = await Assert.ThrowsAsync(async () => await this.resourceService.GetResourceReferenceByOriginalId(999, null));
exception.StatusCode.Should().Be(HttpStatusCode.NotFound);
exception.ResponseBody.Should().Be("No matching resource reference");
}
@@ -93,7 +116,7 @@ public async Task SingleResourceEndpointReturnsAResourceMetadataViewModelObjectW
.ReturnsAsync(this.ResourceReferenceList.GetRange(1, 1));
// When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(2);
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(2, null);
// Then
x.Title.Should().Be("No current resource version");
@@ -108,7 +131,7 @@ public async Task SingleResourceEndpointReturnsAMessageSayingNoCatalogueIfThereI
.ReturnsAsync(this.ResourceReferenceList.GetRange(2, 1));
// When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(3);
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(3, null);
// Then
x.Catalogue.Name.Should().Be("No catalogue for resource reference");
@@ -122,7 +145,7 @@ public async Task SingleResourceEndpointReturnsAMessageSayingNoCatalogueIfThereI
.ReturnsAsync(this.ResourceReferenceList.GetRange(3, 1));
// When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(4);
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(4, null);
// Then
x.Catalogue.Name.Should().Be("No catalogue for resource reference");
@@ -136,7 +159,7 @@ public async Task SingleResourceEndpointReturnsAMessageSayingNoCatalogueIfThereI
.ReturnsAsync(this.ResourceReferenceList.GetRange(5, 1));
// When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(6);
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(6, null);
// Then
x.Catalogue.Name.Should().Be("No catalogue for resource reference");
@@ -150,7 +173,7 @@ public async Task SingleResourceEndpointReturnsAZeroForRatingIfTheresNoRatingSum
.ReturnsAsync(this.ResourceReferenceList.GetRange(7, 1));
// When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(8);
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(8, null);
// Then
x.Catalogue.Name.Should().Be("catalogue3");
@@ -165,7 +188,7 @@ public async Task SingleResourceEndpointThrowsAnErrorAndReturnsABlankStringIfThe
.ReturnsAsync(this.ResourceReferenceList.GetRange(8, 1));
// When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(9);
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(9, null);
// Then
x.ResourceType.Should().Be(string.Empty);
@@ -179,7 +202,7 @@ public async Task SingleResourceEndpointThrowsAnErrorIfThereIsMoreThanOneResourc
.ReturnsAsync(this.ResourceReferenceList.GetRange(9, 2));
// When / Then
- await Assert.ThrowsAsync(async () => await this.resourceService.GetResourceReferenceByOriginalId(10));
+ await Assert.ThrowsAsync(async () => await this.resourceService.GetResourceReferenceByOriginalId(10, null));
}
/*[Fact]
@@ -198,7 +221,7 @@ public async Task BulkEndpointReturnsAllMatchingResources()
.ReturnsAsync(this.ResourceReferenceList.GetRange(0, 2));
// When
- var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp);
+ var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp, null);
// Then
x.ResourceReferences.Count.Should().Be(2);
@@ -220,7 +243,7 @@ public async Task BulkEndpointReturnsA404IfThereAreNoMatchingResources()
.ReturnsAsync(new List());
// When
- var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp);
+ var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp, null);
// Then
x.UnmatchedResourceReferenceIds.Count.Should().Be(2);
@@ -237,7 +260,7 @@ public async Task BulkEndpointReturnsResourcesWithIncompleteInformation()
.ReturnsAsync(this.ResourceReferenceList.GetRange(0, 4));
// When
- var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp);
+ var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp, null);
// Then
x.ResourceReferences.Count.Should().Be(4);
@@ -257,7 +280,7 @@ public async Task BulkEndpointReturnsUnmatchedResourcesWithMatchedResources()
.ReturnsAsync(this.ResourceReferenceList.GetRange(0, 1));
// When
- var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp);
+ var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp, null);
// Then
x.ResourceReferences.Count.Should().Be(1);
@@ -277,7 +300,7 @@ public async Task ResourceServiceReturnsTheOriginalResourceReferenceIdAsTheRefId
.ReturnsAsync(this.ResourceReferenceList.GetRange(5, 2));
// When
- var x = await this.resourceService.GetResourceReferencesByOriginalIds(list);
+ var x = await this.resourceService.GetResourceReferencesByOriginalIds(list, null);
// Then
x.ResourceReferences[0].RefId.Should().Be(6);
@@ -292,12 +315,12 @@ public async Task ResourceServiceReturnsTheOriginalResourceReferenceIdAsTheRefId
this.resourceRepository.Setup(rr => rr.GetResourceReferencesByOriginalResourceReferenceIds(list))
.ReturnsAsync(this.ResourceReferenceList.GetRange(5, 1));
- // When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(6);
+ // When
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(6, null);
- // Then
+ // Then
x.RefId.Should().Be(6);
- }
+ }
[Fact]
public async Task ResourceServiceReturnsThatARestrictedCatalogueIsRestricted()
@@ -308,7 +331,7 @@ public async Task ResourceServiceReturnsThatARestrictedCatalogueIsRestricted()
.ReturnsAsync(this.ResourceReferenceList.GetRange(8, 1));
// When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(9);
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(9, null);
// Then
x.Catalogue.IsRestricted.Should().BeTrue();
@@ -323,10 +346,192 @@ public async Task ResourceServiceReturnsThatAnUnrestrictedCatalogueIsUnrestricte
.ReturnsAsync(this.ResourceReferenceList.GetRange(7, 1));
// When
- var x = await this.resourceService.GetResourceReferenceByOriginalId(8);
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(8, null);
// Then
x.Catalogue.IsRestricted.Should().BeFalse();
}
+
+ [Fact]
+ public async Task SingleResourceEndpointReturnsActivitySummaryWhenCurrentUserIdProvided()
+ {
+ // Given
+ this.resourceRepository.Setup(rr => rr.GetResourceReferencesByOriginalResourceReferenceIds(new List() { 1 }))
+ .ReturnsAsync(this.ResourceReferenceList.GetRange(0, 1));
+
+ this.resourceRepository.Setup(rr => rr.GetResourceActivityPerResourceMajorVersion(new List() { 1 }, new List() { currentUserId }))
+ .ReturnsAsync(this.ResourceActivityDTOList.ToList());
+
+ // When
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(1, currentUserId);
+
+ // Then
+ x.UserSummaryActivityStatuses.Should().NotBeNull();
+ x.UserSummaryActivityStatuses[0].MajorVersionId.Should().Be(5);
+ x.UserSummaryActivityStatuses[1].MajorVersionId.Should().Be(4);
+ x.UserSummaryActivityStatuses[2].MajorVersionId.Should().Be(3);
+ x.UserSummaryActivityStatuses[3].MajorVersionId.Should().Be(2);
+ x.UserSummaryActivityStatuses[4].MajorVersionId.Should().Be(1);
+
+ x.UserSummaryActivityStatuses[0].ActivityStatusDescription.Should().Be("Passed");
+ x.UserSummaryActivityStatuses[1].ActivityStatusDescription.Should().Be("In progress");
+ x.UserSummaryActivityStatuses[2].ActivityStatusDescription.Should().Be("Viewed");
+ x.UserSummaryActivityStatuses[3].ActivityStatusDescription.Should().Be("In progress");
+ x.UserSummaryActivityStatuses[4].ActivityStatusDescription.Should().Be("Viewed");
+ }
+
+ [Fact]
+ public async Task SingleResourceEndpointReturnsEmptyActivitySummaryWhenNoCurrentUserIdProvided()
+ {
+ // Given
+ this.resourceRepository.Setup(rr => rr.GetResourceReferencesByOriginalResourceReferenceIds(new List() { 1 }))
+ .ReturnsAsync(this.ResourceReferenceList.GetRange(0, 1));
+
+ // This should not be hit
+ this.resourceRepository.Setup(rr => rr.GetResourceActivityPerResourceMajorVersion(new List() { 1 }, new List() { currentUserId }))
+ .ReturnsAsync(this.ResourceActivityDTOList.ToList());
+
+ // When
+ var x = await this.resourceService.GetResourceReferenceByOriginalId(1, null);
+
+ // Then
+ x.UserSummaryActivityStatuses.Should().BeEmpty();
+ }
+
+ [Fact]
+ public async Task GetResourceReferencesByCompleteReturnsCorrectInformation()
+ {
+ // Given
+ List resourceIds = new List() { 1, 2 };
+ List resources = this.ResourceList.GetRange(0, 2);
+ resources[0].ResourceReference.ToList()[0].Resource = ResourceTestHelper.CreateResourceWithDetails(id: 1, title: "title1", description: "description1", rating: 3m, resourceType: ResourceTypeEnum.Article);
+ resources[1].ResourceReference.ToList()[0].Resource = ResourceTestHelper.CreateResourceWithDetails(id: 2, hasCurrentResourceVersion: false, hasNodePath: false, resourceType: ResourceTypeEnum.Assessment);
+
+ this.resourceRepository.Setup(rr => rr.GetResourceActivityPerResourceMajorVersion(It.IsAny>(), It.IsAny>()))
+ .ReturnsAsync(this.ResourceActivityDTOList);
+
+ this.resourceRepository.Setup(rr => rr.GetResourcesFromIds(resourceIds))
+ .ReturnsAsync(resources);
+
+ // When
+ var x = await this.resourceService.GetResourceReferenceByActivityStatus(new List() { (int)ActivityStatusEnum.Completed }, currentUserId);
+
+ // Then
+
+ // Two groups resourceId 1 and 2 have completed for a major version. ResourceId 3 had resourceActivity data but not completed
+ x.Count().Should().Be(2);
+
+ // We are including all the major versions not just the matching ones if there exists one matching one
+ x[0].ResourceId.Should().Be(1);
+ x[0].UserSummaryActivityStatuses.Count().Should().Be(5);
+
+ // Return all the activitySummaries if one match
+ x[1].ResourceId.Should().Be(2);
+ x[1].UserSummaryActivityStatuses.Count().Should().Be(3);
+
+ // we are not excluding major version that are not completed. We return the resource and all its activitySummaries if one matches
+ x[0].UserSummaryActivityStatuses[1].ActivityStatusDescription.Should().Be("In progress");
+ x[0].UserSummaryActivityStatuses[2].ActivityStatusDescription.Should().Be("Viewed"); // Rename completed and still return it
+
+ }
+
+ [Fact]
+ public async Task GetResourceReferencesByInProgressReturnsCorrectInformation()
+ {
+ // Given
+ List resourceIds = new List() { 1, 3 };
+ List resources = new List() { this.ResourceList[0], this.ResourceList[2] };
+ resources[0].ResourceReference.ToList()[0].Resource = ResourceTestHelper.CreateResourceWithDetails(id: 1, title: "title1", description: "description1", rating: 3m, resourceType: ResourceTypeEnum.Article);
+ resources[1].ResourceReference.ToList()[0].Resource = ResourceTestHelper.CreateResourceWithDetails(id: 3, title: "title2", description: "description2");
+
+ this.resourceRepository.Setup(rr => rr.GetResourceActivityPerResourceMajorVersion(It.IsAny>(), It.IsAny>()))
+ .ReturnsAsync(this.ResourceActivityDTOList);
+
+ this.resourceRepository.Setup(rr => rr.GetResourcesFromIds(resourceIds))
+ .ReturnsAsync(resources);
+
+ // When
+ var x = await this.resourceService.GetResourceReferenceByActivityStatus(new List() { (int)ActivityStatusEnum.Incomplete }, currentUserId); // In complete in the database is in progress im database
+
+ // Then
+
+ // Two groups resourceId 1 and 3 have completed for a major version. ResourceId 2 had resourceActivity data but not "in progress"
+ x.Count().Should().Be(2);
+
+ // We are including all the major versions not just the matching ones if there exists one matching one
+ x[0].ResourceId.Should().Be(1);
+ x[0].UserSummaryActivityStatuses.Count().Should().Be(5);
+
+ // Return all the activitySummaries if one match
+ x[1].ResourceId.Should().Be(3);
+ x[1].UserSummaryActivityStatuses.Count().Should().Be(3);
+
+ // we are not excluding major version that are not completed. We return the resource and all its activitySummaries if one matches
+ x[0].UserSummaryActivityStatuses[1].ActivityStatusDescription.Should().Be("In progress");
+ x[0].UserSummaryActivityStatuses[2].ActivityStatusDescription.Should().Be("Viewed"); // Rename completed and still return it
+
+ }
+
+ [Fact]
+ public async Task GetResourceReferencesByCertificatesReturnsCorrectInformation()
+ {
+
+ // Given
+ List resourceIds = new List() { 1, 3 }; // Ids returned from activity
+
+ List resources = new List() { this.ResourceList[0], this.ResourceList[2] };
+ resources[0].ResourceReference.ToList()[0].Resource = ResourceTestHelper.CreateResourceWithDetails(id: 1, title: "title1", description: "description1", rating: 3m, resourceType: ResourceTypeEnum.Article);
+ resources[1].ResourceReference.ToList()[0].Resource = ResourceTestHelper.CreateResourceWithDetails(id: 3, title: "title2", description: "description2");
+
+
+ // Will be passed resourceIds and currentUserId
+ this.resourceRepository.Setup(rr => rr.GetResourceActivityPerResourceMajorVersion(It.IsAny>(), It.IsAny>()))
+ .ReturnsAsync(this.ResourceActivityDTOList);
+
+ this.resourceRepository.Setup(rr => rr.GetAchievedCertificatedResourceIds(currentUserId))
+ .ReturnsAsync(resourceIds);
+
+ this.resourceRepository.Setup(rr => rr.GetResourcesFromIds(resourceIds))
+ .ReturnsAsync(resources);
+
+ // When
+ var x = await this.resourceService.GetResourceReferencesForCertificates(currentUserId);
+
+ // Then
+
+ x.Count().Should().Be(2);
+
+ // We are including all the major versions not just the matching ones if there exists one matching one
+ x[0].ResourceId.Should().Be(1);
+ x[0].UserSummaryActivityStatuses.Count().Should().Be(5);
+
+ // Return all the activitySummaries if one match
+ x[1].ResourceId.Should().Be(3);
+ x[1].UserSummaryActivityStatuses.Count().Should().Be(3);
+
+ // we are not excluding major version that are not completed (assuming here that its completed and has certificated flag). We return the resource and all its activitySummaries if one matches
+ x[0].UserSummaryActivityStatuses[1].ActivityStatusDescription.Should().Be("In progress");
+ x[0].UserSummaryActivityStatuses[2].ActivityStatusDescription.Should().Be("Viewed"); // Rename completed and still return it
+ }
+
+ [Fact]
+ public async Task GetResourceReferencesByCompleteNoActivitySummaryFound()
+ {
+ // Given
+ List resourceIds = new List() { };
+ List resources = this.ResourceList.GetRange(0, 0);
+
+ this.resourceRepository.Setup(rr => rr.GetResourceActivityPerResourceMajorVersion(It.IsAny>(), It.IsAny>()))
+ .ReturnsAsync(this.ResourceActivityDTOList.GetRange(8, 3));
+
+ this.resourceRepository.Setup(rr => rr.GetResourcesFromIds(resourceIds))
+ .ReturnsAsync(resources);
+
+ // When
+ var x = await this.resourceService.GetResourceReferenceByActivityStatus(new List() { (int)ActivityStatusEnum.Completed }, currentUserId);
+
+ // Then
+ x.Count().Should().Be(0);
+ }
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/SearchServiceTests.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/SearchServiceTests.cs
index d393ce59d..ee84c1c51 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/SearchServiceTests.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/SearchServiceTests.cs
@@ -6,6 +6,7 @@ namespace LearningHub.Nhs.OpenApi.Tests.Services.Services
using FizzWare.NBuilder;
using FluentAssertions;
using FluentAssertions.Execution;
+ using LearningHub.Nhs.Models.Entities.Activity;
using LearningHub.Nhs.Models.Entities.Resource;
using LearningHub.Nhs.Models.Enums;
using LearningHub.Nhs.Models.Search;
@@ -73,7 +74,7 @@ public async Task SearchPassesQueryOnToFindwise()
.ReturnsAsync(FindwiseResultModel.Failure(FindwiseRequestStatus.Timeout));
// When
- await this.searchService.Search(searchRequest);
+ await this.searchService.Search(searchRequest, null);
// Then
this.findwiseClient.Verify(fc => fc.Search(searchRequest));
@@ -90,7 +91,7 @@ public async Task SearchReturnsTotalHitsAndSearchResult()
this.GivenFindwiseReturnsSuccessfulResponse(74, Enumerable.Range(1, 34));
// When
- var searchResult = await this.searchService.Search(searchRequest);
+ var searchResult = await this.searchService.Search(searchRequest, null);
// Then
searchResult.Resources.Count.Should().Be(34);
@@ -137,7 +138,7 @@ public async Task SearchResultsReturnExpectedValues()
this.GivenFindwiseReturnsSuccessfulResponse(2, new[] { 1, 2, 3 });
// When
- var searchResult = await this.searchService.Search(searchRequest);
+ var searchResult = await this.searchService.Search(searchRequest, null);
// Then
searchResult.Resources.Count.Should().Be(2);
@@ -180,7 +181,7 @@ public async Task SearchReturnsResourcesInOrderMatchingFindwise()
.ReturnsAsync(resources);
// When
- var searchResultModel = await this.searchService.Search(new ResourceSearchRequest("text", 0, 10));
+ var searchResultModel = await this.searchService.Search(new ResourceSearchRequest("text", 0, 10), null);
// Then
searchResultModel.Resources.Select(r => r.ResourceId).Should().ContainInOrder(new[] { 1, 3, 2 });
@@ -194,7 +195,6 @@ public async Task SearchReplacesNullPropertiesOfResourceWithDefaultValues()
{
Builder.CreateNew()
.With(r => r.Id = 1)
- .With(r => r.CurrentResourceVersion = null)
.With(
r => r.ResourceReference = new[]
{
@@ -212,7 +212,7 @@ public async Task SearchReplacesNullPropertiesOfResourceWithDefaultValues()
this.GivenFindwiseReturnsSuccessfulResponse(1, new[] { 1 });
// When
- var searchResult = await this.searchService.Search(new ResourceSearchRequest("text", 0, 10));
+ var searchResult = await this.searchService.Search(new ResourceSearchRequest("text", 0, 10), null);
// Then
using var scope = new AssertionScope();
@@ -233,7 +233,9 @@ public async Task SearchReplacesNullPropertiesOfResourceWithDefaultValues()
string.Empty,
expectedResourceReferences,
"Article",
- 0));
+ 0,
+ 0,
+ new List(){ }));
}
private void GivenFindwiseReturnsSuccessfulResponse(int totalHits, IEnumerable resourceIds)
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/BookmarkController.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/BookmarkController.cs
index d5897aa2a..9c7d8f0b9 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/BookmarkController.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/BookmarkController.cs
@@ -15,7 +15,7 @@
[Authorize]
[Route("Bookmark")]
[ApiController]
- public class BookmarkController : Controller
+ public class BookmarkController : OpenApiControllerBase
{
private readonly IBookmarkService bookmarkService;
@@ -28,6 +28,7 @@ public BookmarkController(IBookmarkService bookmarkService)
this.bookmarkService = bookmarkService;
}
+ ///
///
/// Gets all bookmarks by parent.
///
@@ -36,11 +37,7 @@ public BookmarkController(IBookmarkService bookmarkService)
[Route("GetAllByParent")]
public async Task> GetAllByParent()
{
- var accessToken = await this.HttpContext
- .GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
-
- return await this.bookmarkService.GetAllByParent(
- accessToken);
+ return await this.bookmarkService.GetAllByParent(this.TokenWithoutBearer);
}
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs
new file mode 100644
index 000000000..16fd3799c
--- /dev/null
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs
@@ -0,0 +1,60 @@
+namespace LearningHub.NHS.OpenAPI.Controllers
+{
+ using System.Net;
+ using System.Security.Claims;
+ using LearningHub.Nhs.OpenApi.Models.Exceptions;
+ using Microsoft.AspNetCore.Mvc;
+
+ ///
+ /// The base class for API controllers.
+ ///
+ public abstract class OpenApiControllerBase : ControllerBase
+ {
+ ///
+ /// Gets the current user's ID.
+ ///
+ public int? CurrentUserId
+ {
+ get
+ {
+ // This check is to determine between the two ways of authorising, OAuth and APIKey.OAuth provides userId and APIKey does not. For OpenApi we provide the data without specific user info.
+ if ((this.User?.Identity?.AuthenticationType ?? null) == "AuthenticationTypes.Federation")
+ {
+ int userId;
+ if (int.TryParse(User.FindFirst(ClaimTypes.NameIdentifier).Value, out userId))
+ {
+ return userId;
+ }
+ else
+ {
+ // If parsing fails, return null - for apikey this will be the name
+ return null;
+ }
+ }
+ else
+ {
+ // When authorizing by ApiKey we do not have a user for example
+ return null;
+ }
+ }
+ }
+
+ ///
+ /// Gets the bearer token from OAuth and removes "Bearer " prepend.
+ ///
+ public string TokenWithoutBearer
+ {
+ get
+ {
+ string accessToken = this.HttpContext.Request.Headers["Authorization"].ToString();
+
+ if (string.IsNullOrEmpty(accessToken))
+ {
+ throw new HttpResponseException($"No token provided please use OAuth", HttpStatusCode.Unauthorized);
+ }
+
+ return accessToken.StartsWith("Bearer ") ? accessToken.Substring("Bearer ".Length) : accessToken;
+ }
+ }
+ }
+}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ResourceController.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ResourceController.cs
index 95591961f..ec8774fea 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ResourceController.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ResourceController.cs
@@ -1,10 +1,12 @@
namespace LearningHub.NHS.OpenAPI.Controllers
{
using System;
+ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
+ using LearningHub.Nhs.Models.Enums;
using LearningHub.Nhs.OpenApi.Models.Configuration;
using LearningHub.Nhs.OpenApi.Models.Exceptions;
using LearningHub.Nhs.OpenApi.Models.ServiceModels.Findwise;
@@ -21,7 +23,7 @@ namespace LearningHub.NHS.OpenAPI.Controllers
///
[Route("Resource")]
[Authorize]
- public class ResourceController : Controller
+ public class ResourceController : OpenApiControllerBase
{
private const int MaxNumberOfReferenceIds = 1000;
private readonly ISearchService searchService;
@@ -75,7 +77,7 @@ await this.searchService.Search(
offset,
limit ?? this.findwiseConfig.DefaultItemLimitForSearch,
catalogueId,
- resourceTypes));
+ resourceTypes), this.CurrentUserId);
switch (resourceSearchResult.FindwiseRequestStatus)
{
@@ -109,7 +111,7 @@ await this.searchService.Search(
[HttpGet("{originalResourceReferenceId}")]
public async Task GetResourceReferenceByOriginalId(int originalResourceReferenceId)
{
- return await this.resourceService.GetResourceReferenceByOriginalId(originalResourceReferenceId);
+ return await this.resourceService.GetResourceReferenceByOriginalId(originalResourceReferenceId, this.CurrentUserId);
}
///
@@ -118,14 +120,14 @@ public async Task GetResourceRefe
/// ids.
/// ResourceReferenceViewModels for matching resources.
[HttpGet("Bulk")]
- public async Task GetResourceReferencesByOriginalIds([FromQuery]List resourceReferenceIds)
+ public async Task GetResourceReferencesByOriginalIds([FromQuery] List resourceReferenceIds)
{
if (resourceReferenceIds.Count > MaxNumberOfReferenceIds)
{
throw new HttpResponseException($"Too many resources requested. The maximum is {MaxNumberOfReferenceIds}", HttpStatusCode.BadRequest);
}
- return await this.resourceService.GetResourceReferencesByOriginalIds(resourceReferenceIds.ToList());
+ return await this.resourceService.GetResourceReferencesByOriginalIds(resourceReferenceIds.ToList(), this.CurrentUserId);
}
///
@@ -134,7 +136,7 @@ public async Task GetResourceReferencesByOrigina
/// ids.
/// ResourceReferenceViewModels for matching resources.
[HttpGet("BulkJson")]
- public async Task GetResourceReferencesByOriginalIdsFromJson([FromQuery]string resourceReferences)
+ public async Task GetResourceReferencesByOriginalIdsFromJson([FromQuery] string resourceReferences)
{
var bulkResourceReferences = JsonConvert.DeserializeObject(resourceReferences);
@@ -148,7 +150,51 @@ public async Task GetResourceReferencesByOrigina
throw new HttpResponseException($"Too many resources requested. The maximum is {MaxNumberOfReferenceIds}", HttpStatusCode.BadRequest);
}
- return await this.resourceService.GetResourceReferencesByOriginalIds(bulkResourceReferences.ResourceReferenceIds);
+ return await this.resourceService.GetResourceReferencesByOriginalIds(bulkResourceReferences.ResourceReferenceIds, this.CurrentUserId);
+ }
+
+ ///
+ /// Get resourceReferences that have an in progress activity summary
+ ///
+ /// activityStatusId.
+ /// ResourceReferenceViewModels for matching resources.
+ [HttpGet("User/{activityStatusId}")]
+ public async Task> GetResourceReferencesByActivityStatus(int activityStatusId)
+ {
+ // These activity statuses are set with other activity statuses and resource type within the ActivityStatusHelper.GetActivityStatusDescription
+ // Note In progress is in complete in the db
+ List activityStatusIdsNotInUseInDB = new List() { (int)ActivityStatusEnum.Launched, (int)ActivityStatusEnum.InProgress, (int)ActivityStatusEnum.Viewed, (int)ActivityStatusEnum.Downloaded };
+ if (this.CurrentUserId == null)
+ {
+ throw new UnauthorizedAccessException("User Id required.");
+ }
+
+ if (!Enum.IsDefined(typeof(ActivityStatusEnum), activityStatusId))
+ {
+ throw new ArgumentOutOfRangeException($"activityStatusId : {activityStatusId} does not exist within ActivityStatusEnum");
+ }
+
+ if (activityStatusIdsNotInUseInDB.Contains(activityStatusId))
+ {
+ throw new ArgumentOutOfRangeException($"activityStatusId: {activityStatusId} does not exist within the database definitions");
+ }
+
+ return await this.resourceService.GetResourceReferenceByActivityStatus(new List() { activityStatusId }, this.CurrentUserId.Value);
+ }
+
+ ///
+ /// Get resourceReferences that have certificates
+ ///
+ /// ResourceReferenceViewModels for matching resources.
+ [HttpGet("User/Certificates")]
+ public async Task> GetResourceReferencesByCertificates()
+ {
+ if (this.CurrentUserId == null)
+ {
+ throw new UnauthorizedAccessException("User Id required.");
+ }
+
+ return await this.resourceService.GetResourceReferencesForCertificates(this.CurrentUserId.Value);
}
}
}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user b/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user
new file mode 100644
index 000000000..b17387f00
--- /dev/null
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user
@@ -0,0 +1,9 @@
+
+
+
+ ProjectDebugger
+
+
+ IIS Local
+
+
\ No newline at end of file
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/SwaggerDefinitions/v1.3.0.json b/OpenAPI/LearningHub.Nhs.OpenApi/SwaggerDefinitions/v1.3.0.json
index d77f6b823..4fdbbba8b 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi/SwaggerDefinitions/v1.3.0.json
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/SwaggerDefinitions/v1.3.0.json
@@ -1,5 +1,5 @@
{
- "openapi": "3.0.1",
+ "openapi": "3.0.2",
"info": {
"title": "LearningHub.NHS.OpenAPI",
"version": "1.3.0",
@@ -295,6 +295,81 @@
}
}
}
+ },
+ "/Resource/User/{activityStatusId}": {
+ "get": {
+ "tags": [ "Resource" ],
+ "summary": "Get resource references by activity status",
+ "operationId": "GetResourceReferencesByActivityStatus",
+ "parameters": [
+ {
+ "name": "activityStatusId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "description": "The activity status Id to filter resource references. Valid values are Completed 3 (returned as Completed/Downloaded/Launched/Viewed), Incomplete 7 (returned as In progress), Passed 5, Failed 4."
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ResourceReferenceWithResourceDetailsViewModel"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad request: The activityStatusId provided is not valid."
+ },
+ "401": {
+ "description": "Unauthorized: User Id required."
+ },
+ "403": {
+ "description": "Forbidden: The activityStatusId is not defined within ActivityStatusEnum or is in the list of activityStatusIdsNotInUseInDB."
+ },
+ "500": {
+ "description": "Internal server error: An unexpected error occurred while processing the request."
+ }
+ }
+ }
+ },
+ "/Resource/User/Certificates": {
+ "get": {
+ "tags": [ "Resource" ],
+ "summary": "Get resource references where a major version has a certificate",
+ "operationId": "GetResourceReferencesByCertificates",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ResourceReferenceWithResourceDetailsViewModel"
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized: User Id required."
+ },
+ "500": {
+ "description": "Internal server error: An unexpected error occurred while processing the request."
+ }
+ }
+ }
}
},
"components": {
diff --git a/WebAPI/LearningHub.Nhs.API/LearningHub.Nhs.Api.csproj b/WebAPI/LearningHub.Nhs.API/LearningHub.Nhs.Api.csproj
index 073baa1ff..82b88c37a 100644
--- a/WebAPI/LearningHub.Nhs.API/LearningHub.Nhs.Api.csproj
+++ b/WebAPI/LearningHub.Nhs.API/LearningHub.Nhs.Api.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/WebAPI/LearningHub.Nhs.Api.Shared/LearningHub.Nhs.Api.Shared.csproj b/WebAPI/LearningHub.Nhs.Api.Shared/LearningHub.Nhs.Api.Shared.csproj
index 722cefdee..f38e0260e 100644
--- a/WebAPI/LearningHub.Nhs.Api.Shared/LearningHub.Nhs.Api.Shared.csproj
+++ b/WebAPI/LearningHub.Nhs.Api.Shared/LearningHub.Nhs.Api.Shared.csproj
@@ -9,7 +9,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/WebAPI/LearningHub.Nhs.Api.UnitTests/LearningHub.Nhs.Api.UnitTests.csproj b/WebAPI/LearningHub.Nhs.Api.UnitTests/LearningHub.Nhs.Api.UnitTests.csproj
index d47e3d417..f61e84c2f 100644
--- a/WebAPI/LearningHub.Nhs.Api.UnitTests/LearningHub.Nhs.Api.UnitTests.csproj
+++ b/WebAPI/LearningHub.Nhs.Api.UnitTests/LearningHub.Nhs.Api.UnitTests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.sqlproj b/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.sqlproj
index f19a40bbe..477a48869 100644
--- a/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.sqlproj
+++ b/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.sqlproj
@@ -200,9 +200,6 @@
-
-
-
@@ -518,12 +515,17 @@
+
+
+
+
+
@@ -594,4 +596,4 @@
-
+
\ No newline at end of file
diff --git a/WebAPI/LearningHub.Nhs.Database/Scripts/Post-Deploy/Script.PostDeployment.sql b/WebAPI/LearningHub.Nhs.Database/Scripts/Post-Deploy/Script.PostDeployment.sql
index 151c7c2c5..5d99e1884 100644
--- a/WebAPI/LearningHub.Nhs.Database/Scripts/Post-Deploy/Script.PostDeployment.sql
+++ b/WebAPI/LearningHub.Nhs.Database/Scripts/Post-Deploy/Script.PostDeployment.sql
@@ -81,4 +81,5 @@ UPDATE [resources].[ResourceVersion] SET CertificateEnabled = 0 WHERE VersionSta
:r .\Scripts\InitialiseDataForEmailTemplates.sql
:r .\Scripts\TD-2929_ActivityStatusUpdates.sql
:r .\Scripts\InitialiseDataForEmailTemplates.sql
-:r .\Scripts\AttributeData.sql
\ No newline at end of file
+:r .\Scripts\AttributeData.sql
+:r .\Scripts\PPSXFileType.sql
\ No newline at end of file
diff --git a/WebAPI/LearningHub.Nhs.Database/Scripts/Post-Deploy/Scripts/PPSXFileType.sql b/WebAPI/LearningHub.Nhs.Database/Scripts/Post-Deploy/Scripts/PPSXFileType.sql
new file mode 100644
index 000000000..d948664ad
--- /dev/null
+++ b/WebAPI/LearningHub.Nhs.Database/Scripts/Post-Deploy/Scripts/PPSXFileType.sql
@@ -0,0 +1,30 @@
+IF NOT EXISTS(SELECT Id FROM [resources].[FileType] where Extension ='ppsx')
+BEGIN
+INSERT INTO [resources].[FileType]
+ (Id,
+ [DefaultResourceTypeId]
+ ,[Name]
+ ,[Description]
+ ,[Extension]
+ ,[Icon]
+ ,[NotAllowed]
+ ,[Deleted]
+ ,[CreateUserId]
+ ,[CreateDate]
+ ,[AmendUserId]
+ ,[AmendDate])
+ VALUES
+ (70,
+ 9
+ ,'PowerPoint Open XML Slide Show'
+ ,'PowerPoint Open XML Slide Show'
+ ,'ppsx'
+ ,'a-mppoint-icon.svg'
+ ,0
+ ,0
+ ,57541
+ ,SYSDATETIMEOFFSET()
+ ,57541
+ ,SYSDATETIMEOFFSET())
+END
+GO
\ No newline at end of file
diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql
new file mode 100644
index 000000000..063110343
--- /dev/null
+++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql
@@ -0,0 +1,152 @@
+
+-------------------------------------------------------------------------------
+-- Author Phil T
+-- Created 04-07-24
+-- Purpose Return resource activity for each major version for user
+
+
+-- Description
+/*
+ This procedure returns a single entry per resource Id, selecting the most important one for that major version.
+ This is so users can still have a resourceActivity history following a majorVersion change
+
+ UserIds is nullable so that general resource activity can be searched for
+ ResourceIds is nullable so that all a users history can be searched for
+
+ When determining the resourceActivity statusDescription in the front end resourceTypeId is also required for changing completed statuses to resourceType specific ones
+
+ Currently if multiple rows meet the case criteria we retrieve the one with the highest Id which is also expected to be the ActivityEnd part of the activityStatus pair.
+
+*/
+-- Future Considerations
+/*
+ Because the activityResource should come in pairs one with ActivityStart populated and one with ActivityEnd populated
+ it could be desireable to join via LaunchResourceActivityId and coalesce the data in future.
+ Or/And coalesce where the case returns multiple rows.
+
+*/
+-- Notes
+ -- resourceId is used not originalResourceId
+
+
+-------------------------------------------------------------------------------
+
+-- Create the new stored procedure
+CREATE PROCEDURE [activity].[GetResourceActivityPerResourceMajorVersion]
+ @ResourceIds VARCHAR(MAX) = NULL,
+ @UserIds VARCHAR(MAX) = NULL
+AS
+BEGIN
+
+ -- Split the comma-separated list into a table of integers
+ DECLARE @ResourceIdTable TABLE (ResourceId INT);
+
+ IF @ResourceIds IS NOT NULL AND @ResourceIds <> ''
+ BEGIN
+ INSERT INTO @ResourceIdTable (ResourceId)
+ SELECT CAST(value AS INT)
+ FROM STRING_SPLIT(@ResourceIds, ',');
+ END;
+
+ -- Split the comma-separated list of UserIds into a table
+ DECLARE @UserIdTable TABLE (UserId INT);
+
+ IF @UserIds IS NOT NULL AND @UserIds <> ''
+ BEGIN
+ INSERT INTO @UserIdTable (UserId)
+ SELECT CAST(value AS INT)
+ FROM STRING_SPLIT(@UserIds, ',');
+ END;
+
+ WITH FilteredResourceActivities AS (
+ SELECT
+ ars.[Id],
+ ars.[UserId],
+ ars.[LaunchResourceActivityId],
+ ars.[ResourceId],
+ ars.[ResourceVersionId],
+ ars.[MajorVersion],
+ ars.[MinorVersion],
+ ars.[NodePathId],
+ ars.[ActivityStatusId],
+ ars.[ActivityStart],
+ ars.[ActivityEnd],
+ ars.[DurationSeconds],
+ ars.[Score],
+ ars.[Deleted],
+ ars.[CreateUserID],
+ ars.[CreateDate],
+ ars.[AmendUserID],
+ ars.[AmendDate]
+ FROM
+ [activity].[resourceactivity] ars
+ WHERE
+ (@UserIds IS NULL OR ars.userId IN (SELECT UserId FROM @UserIdTable) OR NOT EXISTS (SELECT 1 FROM @UserIdTable))
+ AND (@ResourceIds IS NULL OR @ResourceIds = '' OR ars.resourceId IN (SELECT ResourceId FROM @ResourceIdTable) OR NOT EXISTS (SELECT 1 FROM @ResourceIdTable))
+ AND ars.Deleted = 0
+ AND ars.ActivityStatusId NOT IN (1, 6, 2) -- These Ids are not in use - Launched, Downloaded, In Progress (stored as completed and incomplete then renamed in the application)
+ ),
+ RankedActivities AS (
+ SELECT
+ ra.[Id],
+ ra.[UserId],
+ ra.[LaunchResourceActivityId],
+ ra.[ResourceId],
+ ra.[ResourceVersionId],
+ ra.[MajorVersion],
+ ra.[MinorVersion],
+ ra.[NodePathId],
+ ra.[ActivityStatusId],
+ ra.[ActivityStart],
+ ra.[ActivityEnd],
+ ra.[DurationSeconds],
+ ra.[Score],
+ ra.[Deleted],
+ ra.[CreateUserID],
+ ra.[CreateDate],
+ ra.[AmendUserID],
+ ra.[AmendDate],
+ ROW_NUMBER() OVER (
+ PARTITION BY resourceId, userId, MajorVersion
+ ORDER BY
+ CASE
+ WHEN ActivityStatusId = 5 THEN 1 -- Passed
+ WHEN ActivityStatusId = 3 THEN 2 -- Completed
+ WHEN ActivityStatusId = 4 THEN 3 -- Failed
+ WHEN ActivityStatusId = 7 THEN 4 -- Incomplete
+ ELSE 5 -- shouldn't be any
+ END,
+ Id DESC -- we have two entries per interacting with a resource the start and the end, we are just returning the last entry made
+ -- there is the option of instead coalescing LaunchResourceActivityId, ActivityStart,ActivityEnd potentially via joining LaunchResourceActivityId and UserId
+ ) AS RowNum
+ FROM
+ FilteredResourceActivities ra
+ )
+ SELECT
+ ra.[Id],
+ ra.[UserId],
+ ra.[LaunchResourceActivityId],
+ ra.[ResourceId],
+ ra.[ResourceVersionId],
+ ra.[MajorVersion],
+ ra.[MinorVersion],
+ ra.[NodePathId],
+ ra.[ActivityStatusId],
+ ra.[ActivityStart],
+ ra.[ActivityEnd],
+ ra.[DurationSeconds],
+ ra.[Score],
+ ra.[Deleted],
+ ra.[CreateUserID],
+ ra.[CreateDate],
+ ra.[AmendUserID],
+ ra.[AmendDate]
+ FROM
+ RankedActivities ra
+ WHERE
+ RowNum = 1
+ order by MajorVersion desc;
+END;
+GO
+
+
diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserLearningActivities.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserLearningActivities.sql
index c96931937..15272dc4e 100644
--- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserLearningActivities.sql
+++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserLearningActivities.sql
@@ -10,6 +10,7 @@
-- Sarathlal 08-03-2024
-- Sarathlal 23-04-2024 TD-2954: Audio/Video/Assessment issue resolved and duplicate issue also resolved
-- Sarathlal 25-04-2024 TD-4067: Resource with muliple version issue resolved
+-- Arunima 26-07-2024 TD-4411: "Completed" filter along with "Assessment" doesn't display the correct results
-------------------------------------------------------------------------------
CREATE PROCEDURE [activity].[GetUserLearningActivities] (
@userId INT
@@ -271,9 +272,35 @@ FROM (
)
)
OR
- ([Res].[ResourceTypeId] IN (6,11) AND [ResourceActivity].[ActivityStatusId] = 3)
- OR ([Res].[ResourceTypeId] IN (11) AND [ResourceActivity].[ActivityStatusId] = 3 AND [AssessResVer].[AssessmentType]=1)
- --OR
+ ([Res].[ResourceTypeId] IN (6) AND [ResourceActivity].[ActivityStatusId] = 3)
+ OR (
+ EXISTS (SELECT 1 FROM @tmpActivityStatus WHERE ActivityStatusId = 3)
+ AND
+ (
+ [Res].[ResourceTypeId] = 11 AND [AssessResVer].[AssessmentType]=1
+ AND
+ EXISTS
+ (
+ SELECT 1
+ FROM [activity].[AssessmentResourceActivity] AS [AssessmentResourceActivity6]
+ WHERE
+ [AssessmentResourceActivity6].[Deleted] = 0
+ AND
+ [ResourceActivity].[Id] = [AssessmentResourceActivity6].[ResourceActivityId]
+ )
+ AND
+ (
+ (SELECT TOP(1)
+ [AssessmentResourceActivity7].[Score]
+ FROM [activity].[AssessmentResourceActivity] AS [AssessmentResourceActivity7]
+ WHERE
+ [AssessmentResourceActivity7].[Deleted] = 0
+ AND [ResourceActivity].[Id] = [AssessmentResourceActivity7].[ResourceActivityId]) >= 0.0
+ )
+ )
+
+ )
+ --OR
--(
-- ([Res].[ResourceTypeId] IN (1,5,10,12) AND [ResourceActivity].[ActivityStatusId] = 3)
-- AND
@@ -507,5 +534,3 @@ LEFT JOIN (
ORDER BY [t2].[ActivityStart] DESC, [t2].[Id], [t2].[Id0], [t2].[Id1], [t2].[Id2], [VideoResourceVersion].[Id], [AudeoResourceVersion].[Id], [t3].[Id], [t4].[Id], [t5].[Id], [t6].[Id], [t7].[Id], [t8].[Id], [t9].[Id], [t10].[Id], [t11].[Id]
END
-
-
diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserLearningActivitiesCount.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserLearningActivitiesCount.sql
index daa20e0e7..7ce53f78c 100644
--- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserLearningActivitiesCount.sql
+++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserLearningActivitiesCount.sql
@@ -9,6 +9,7 @@
-- Sarathlal 18-12-2023
-- Sarathlal 08-03-2024
-- Sarathlal 23-04-2024 TD-2954: Audio/Video/Assessment issue resolved and duplicate issue also resolved
+-- Arunima 26-07-2024 TD-4411: "Completed" filter along with "Assessment" doesn't display the correct results
-------------------------------------------------------------------------------
CREATE PROCEDURE [activity].[GetUserLearningActivitiesCount] (
@userId INT
@@ -186,10 +187,35 @@ FROM (
)
)
OR
- ([Res].[ResourceTypeId] IN (6,11) AND [ResourceActivity].[ActivityStatusId] = 3)
- OR ([Res].[ResourceTypeId] IN (11) AND [ResourceActivity].[ActivityStatusId] = 3 AND [AssessResVer].[AssessmentType]=1)
-
- --OR
+ ([Res].[ResourceTypeId] IN (6) AND [ResourceActivity].[ActivityStatusId] = 3)
+ OR (
+ EXISTS (SELECT 1 FROM @tmpActivityStatus WHERE ActivityStatusId = 3)
+ AND
+ (
+ [Res].[ResourceTypeId] = 11 AND [AssessResVer].[AssessmentType]=1
+ AND
+ EXISTS
+ (
+ SELECT 1
+ FROM [activity].[AssessmentResourceActivity] AS [AssessmentResourceActivity6]
+ WHERE
+ [AssessmentResourceActivity6].[Deleted] = 0
+ AND
+ [ResourceActivity].[Id] = [AssessmentResourceActivity6].[ResourceActivityId]
+ )
+ AND
+ (
+ (SELECT TOP(1)
+ [AssessmentResourceActivity7].[Score]
+ FROM [activity].[AssessmentResourceActivity] AS [AssessmentResourceActivity7]
+ WHERE
+ [AssessmentResourceActivity7].[Deleted] = 0
+ AND [ResourceActivity].[Id] = [AssessmentResourceActivity7].[ResourceActivityId]) >= 0.0
+ )
+ )
+
+ )
+ --OR
--(
-- ([Res].[ResourceTypeId] IN (1,5,10,12) AND [ResourceActivity].[ActivityStatusId] = 3)
-- AND
diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetAchievedCertificatedResourcesWithOptionalPagination.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetAchievedCertificatedResourcesWithOptionalPagination.sql
new file mode 100644
index 000000000..922480858
--- /dev/null
+++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetAchievedCertificatedResourcesWithOptionalPagination.sql
@@ -0,0 +1,118 @@
+
+-------------------------------------------------------------------------------
+-- Author PT
+-- Created 11 July 2024
+-- Purpose Get achieved certificated resources with optional pagination
+-- Description Extracted from the GetDashboardResources sproc to enable one source of truth for determining achieved certificated resources
+-- To support the GetDashboardResources it has pagination and to support other requests the default values disable pagination effects
+-------------------------------------------------------------------------------
+
+
+
+CREATE PROCEDURE [resources].[GetAchievedcertificatedResourcesWithOptionalPagination]
+ @UserId INT,
+
+ -- Default values disable pagination
+ @MaxRows INT = 2147483647, -- Warning! Magic number. To disable pagination by default.
+ @OffsetRows INT = 0,
+ @FetchRows INT = 2147483647, -- Warning! Magic number. To disable pagination by default.
+
+ @TotalRecords INT OUTPUT
+AS
+BEGIN
+
+ -- Step 1: Create a table variable to store intermediate results
+ DECLARE @MyActivity TABLE (
+ ResourceId INT,
+ ResourceActivityId INT
+ );
+
+ INSERT INTO @MyActivity
+ SELECT TOP (@MaxRows) ra.ResourceId, MAX(ra.Id) ResourceActivityId
+ FROM
+ /* resources with resource activity, resource activity determines if certificated*/
+ activity.ResourceActivity ra
+ JOIN [resources].[Resource] r ON ra.ResourceId = r.Id
+ JOIN [resources].[ResourceVersion] rv ON rv.Id = ra.ResourceVersionId
+
+ /* Determining if certificated scorm, assessment mark, media*/
+ LEFT JOIN [resources].[AssessmentResourceVersion] arv ON arv.ResourceVersionId = ra.ResourceVersionId
+ LEFT JOIN [activity].[AssessmentResourceActivity] ara ON ara.ResourceActivityId = ra.Id
+ LEFT JOIN [activity].[MediaResourceActivity] mar ON mar.ResourceActivityId = ra.Id
+ LEFT JOIN [activity].[ScormActivity] sa ON sa.ResourceActivityId = ra.Id
+
+ WHERE ra.UserId = @UserId AND rv.CertificateEnabled = 1 -- detemining if certificated
+ AND (
+ (r.ResourceTypeId IN (2, 7) AND ra.ActivityStatusId IN (3) /* resourceType 2 Audio and 7 is video activityStatusId 3 is completed */
+ OR ra.ActivityStart < '2020-09-07 00:00:00 +00:00' /* old activity assumed to be valid*/
+ OR mar.Id IS NOT NULL AND mar.PercentComplete = 100 /* media activity 100% complete*/
+ )
+ /* type 6 scorm elearning,*/
+ OR (r.ResourceTypeId = 6 AND (sa.CmiCoreLesson_status IN(3,5) OR (ra.ActivityStatusId IN(3, 5)))) /* activityStatus 3 and 5 are completed and passed */
+ /* 11 is assessment */
+ OR (r.ResourceTypeId = 11 AND ara.Score >= arv.PassMark OR ra.ActivityStatusId IN(3, 5)) /*assessment mark and activity status passed completed */
+ /* 1 Article, 5 Image, 8 Weblink 9 file, 10 case, 12 html */
+ OR (r.ResourceTypeId IN (1, 5, 8, 9, 10, 12) AND ra.ActivityStatusId IN (3))) /* Completed */
+ GROUP BY ra.ResourceId
+ ORDER BY ResourceActivityId DESC
+
+ SELECT r.Id AS ResourceId
+ ,( SELECT TOP 1 rr.OriginalResourceReferenceId
+ FROM [resources].[ResourceReference] rr
+ JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0
+ WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0
+ ) AS ResourceReferenceID
+ ,r.CurrentResourceVersionId AS ResourceVersionId
+ ,r.ResourceTypeId AS ResourceTypeId
+ ,rv.Title
+ ,rv.Description
+ ,CASE
+ WHEN r.ResourceTypeId = 7 THEN
+ (SELECT vrv.DurationInMilliseconds from [resources].[VideoResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId)
+ WHEN r.ResourceTypeId = 2 THEN
+ (SELECT vrv.DurationInMilliseconds from [resources].[AudioResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId)
+ ELSE
+ NULL
+ END AS DurationInMilliseconds
+ ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName
+ ,cnv.Url AS Url
+ ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl
+ ,cnv.RestrictedAccess
+ ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess
+ ,ub.Id AS BookMarkId
+ ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked
+ ,rs.AverageRating
+ ,rs.RatingCount
+ FROM @MyActivity ma
+ JOIN activity.ResourceActivity ra ON ra.id = ma.ResourceActivityId
+ JOIN resources.resourceversion rv ON rv.id = ra.ResourceVersionId AND rv.Deleted = 0
+ JOIN Resources.Resource r ON r.Id = rv.ResourceId
+ JOIN hierarchy.Publication p ON rv.PublicationId = p.Id AND p.Deleted = 0
+ JOIN resources.ResourceVersionRatingSummary rvrs ON rv.Id = rvrs.ResourceVersionId AND rvrs.Deleted = 0
+
+ /* Catalogue logic */
+ JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0
+ JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0
+ JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1
+ JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0
+ JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0
+
+ /* Book marks */
+ LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.ResourceReferenceId = (SELECT TOP 1 rr.OriginalResourceReferenceId
+ FROM [resources].[ResourceReference] rr
+ JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0
+ WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0)
+ LEFT JOIN ( SELECT DISTINCT CatalogueNodeId
+ FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId
+ WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId
+ LEFT JOIN resources.ResourceVersionRatingSummary rs ON rs.ResourceVersionId = rv.Id
+ ORDER BY ma.ResourceActivityId DESC, rv.Title
+
+ /* pagination logic */
+ OFFSET @OffsetRows ROWS
+ FETCH NEXT @FetchRows ROWS ONLY
+ SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END FROM @MyActivity
+ END;
+GO
+
+
diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyCertificatesDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyCertificatesDashboardResources.sql
index b75e45860..7beec0594 100644
--- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyCertificatesDashboardResources.sql
+++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyCertificatesDashboardResources.sql
@@ -6,6 +6,7 @@
-- Modification History
--
-- 24 Jun 2024 OA Initial Revision
+-- 31 Jun 2024 PT Extracting functionality of certification with optional pagination so can be used on openapi and be single source of truth
-------------------------------------------------------------------------------
CREATE PROCEDURE [resources].[GetMyCertificatesDashboardResources]
@@ -25,77 +26,11 @@ BEGIN
DECLARE @MaxRows INT = @MaxPageNUmber * @FetchRows
DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows
- DECLARE @MyActivity TABLE (ResourceId [int] NOT NULL PRIMARY KEY, ResourceActivityId [int] NOT NULL);
- DECLARE @Resources TABLE (ResourceId [int] NOT NULL PRIMARY KEY, ResourceActivityCount [int] NOT NULL);
+ EXEC [resources].[GetAchievedcertificatedResourcesWithOptionalPagination]
+ @UserId = @UserId,
+ @MaxRows= @MaxRows,
+ @OffsetRows = @OffsetRows,
+ @FetchRows = @FetchRows,
+ @TotalRecords = @TotalRecords;
- INSERT INTO @MyActivity
- SELECT TOP (@MaxRows) ra.ResourceId, MAX(ra.Id) ResourceActivityId
- FROM
- activity.ResourceActivity ra
- JOIN [resources].[Resource] r ON ra.ResourceId = r.Id
- JOIN [resources].[ResourceVersion] rv ON rv.Id = ra.ResourceVersionId
- LEFT JOIN [resources].[AssessmentResourceVersion] arv ON arv.ResourceVersionId = ra.ResourceVersionId
- LEFT JOIN [activity].[AssessmentResourceActivity] ara ON ara.ResourceActivityId = ra.Id
- LEFT JOIN [activity].[MediaResourceActivity] mar ON mar.ResourceActivityId = ra.Id
- LEFT JOIN [activity].[ScormActivity] sa ON sa.ResourceActivityId = ra.Id
- WHERE ra.UserId = @UserId AND rv.CertificateEnabled = 1
- AND (
- (r.ResourceTypeId IN (2, 7) AND ra.ActivityStatusId = 3 OR ra.ActivityStart < '2020-09-07 00:00:00 +00:00' OR mar.Id IS NOT NULL AND mar.PercentComplete = 100)
- OR (r.ResourceTypeId = 6 AND (sa.CmiCoreLesson_status IN(3,5) OR (ra.ActivityStatusId IN(3, 5))))
- OR ((r.ResourceTypeId = 11 AND arv.AssessmentType = 2) AND (ara.Score >= arv.PassMark OR ra.ActivityStatusId IN(3, 5)))
- OR ((r.ResourceTypeId = 11 AND arv.AssessmentType =1) AND (ara.Score >= arv.PassMark AND ra.ActivityStatusId IN(3, 5,7)))
- OR (r.ResourceTypeId IN (1, 5, 8, 9, 10, 12) AND ra.ActivityStatusId = 3))
- GROUP BY ra.ResourceId
- ORDER BY ResourceActivityId DESC
-
- SELECT r.Id AS ResourceId
- ,( SELECT TOP 1 rr.OriginalResourceReferenceId
- FROM [resources].[ResourceReference] rr
- JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0
- WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0
- ) AS ResourceReferenceID
- ,r.CurrentResourceVersionId AS ResourceVersionId
- ,r.ResourceTypeId AS ResourceTypeId
- ,rv.Title
- ,rv.Description
- ,CASE
- WHEN r.ResourceTypeId = 7 THEN
- (SELECT vrv.DurationInMilliseconds from [resources].[VideoResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId)
- WHEN r.ResourceTypeId = 2 THEN
- (SELECT vrv.DurationInMilliseconds from [resources].[AudioResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId)
- ELSE
- NULL
- END AS DurationInMilliseconds
- ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName
- ,cnv.Url AS Url
- ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl
- ,cnv.RestrictedAccess
- ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess
- ,ub.Id AS BookMarkId
- ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked
- ,rvrs.AverageRating
- ,rvrs.RatingCount
-FROM @MyActivity ma
-JOIN activity.ResourceActivity ra ON ra.id = ma.ResourceActivityId
-JOIN resources.resourceversion rv ON rv.id = ra.ResourceVersionId AND rv.Deleted = 0
-JOIN Resources.Resource r ON r.Id = rv.ResourceId
-JOIN hierarchy.Publication p ON rv.PublicationId = p.Id AND p.Deleted = 0
-JOIN resources.ResourceVersionRatingSummary rvrs ON rv.Id = rvrs.ResourceVersionId AND rvrs.Deleted = 0
-JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0
-JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0
-JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1
-JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0
-JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0
-LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.ResourceReferenceId = (SELECT TOP 1 rr.OriginalResourceReferenceId
- FROM [resources].[ResourceReference] rr
- JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0
- WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0)
-LEFT JOIN ( SELECT DISTINCT CatalogueNodeId
- FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId
- WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId
-ORDER BY ma.ResourceActivityId DESC, rv.Title
-OFFSET @OffsetRows ROWS
-FETCH NEXT @FetchRows ROWS ONLY
-
- SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END FROM @MyActivity
END
\ No newline at end of file
diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyLearningCertificatesDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyLearningCertificatesDashboardResources.sql
new file mode 100644
index 000000000..986e25423
--- /dev/null
+++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyLearningCertificatesDashboardResources.sql
@@ -0,0 +1,101 @@
+-------------------------------------------------------------------------------
+-- Author OA
+-- Created 24 JUN 2024 Nov 2020
+-- Purpose Break down the GetDashboardResources SP to smaller SP for a specific data type
+--
+-- Modification History
+--
+-- 24 Jun 2024 OA Initial Revision
+-------------------------------------------------------------------------------
+
+CREATE PROCEDURE [resources].[GetMyLearningCertificatesDashboardResources]
+ @UserId INT,
+ @PageNumber INT = 1,
+ @TotalRecords INT OUTPUT
+AS
+BEGIN
+ DECLARE @MaxPageNumber INT = 4
+
+ IF @PageNumber > 4
+ BEGIN
+ SET @PageNumber = @MaxPageNumber
+ END
+
+ DECLARE @FetchRows INT = 3
+ DECLARE @MaxRows INT = @MaxPageNUmber * @FetchRows
+ DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows
+
+ DECLARE @MyActivity TABLE (ResourceId [int] NOT NULL PRIMARY KEY, ResourceActivityId [int] NOT NULL);
+ DECLARE @Resources TABLE (ResourceId [int] NOT NULL PRIMARY KEY, ResourceActivityCount [int] NOT NULL);
+
+ INSERT INTO @MyActivity
+ SELECT TOP (@MaxRows) ra.ResourceId, MAX(ra.Id) ResourceActivityId
+ FROM
+ activity.ResourceActivity ra
+ JOIN [resources].[Resource] r ON ra.ResourceId = r.Id
+ JOIN [resources].[ResourceVersion] rv ON rv.Id = ra.ResourceVersionId
+ LEFT JOIN [resources].[AssessmentResourceVersion] arv ON arv.ResourceVersionId = ra.ResourceVersionId
+ LEFT JOIN [activity].[AssessmentResourceActivity] ara ON ara.ResourceActivityId = ra.Id
+ LEFT JOIN [activity].[MediaResourceActivity] mar ON mar.ResourceActivityId = ra.Id
+ LEFT JOIN [activity].[ScormActivity] sa ON sa.ResourceActivityId = ra.Id
+ WHERE ra.UserId = @UserId AND rv.CertificateEnabled = 1
+ AND (
+ (r.ResourceTypeId IN (2, 7) AND ra.ActivityStatusId = 3 OR ra.ActivityStart < '2020-09-07 00:00:00 +00:00' OR mar.Id IS NOT NULL AND mar.PercentComplete = 100)
+ OR (r.ResourceTypeId = 6 AND (sa.CmiCoreLesson_status IN(3,5) OR (ra.ActivityStatusId IN(3, 5))))
+ OR ((r.ResourceTypeId = 11 AND arv.AssessmentType = 2) AND (ara.Score >= arv.PassMark OR ra.ActivityStatusId IN(3, 5)))
+ OR ((r.ResourceTypeId = 11 AND arv.AssessmentType =1) AND (ara.Score >= arv.PassMark AND ra.ActivityStatusId IN(3, 5,7)))
+ OR (r.ResourceTypeId IN (1, 5, 8, 9, 10, 12) AND ra.ActivityStatusId = 3))
+ GROUP BY ra.ResourceId
+ ORDER BY ResourceActivityId DESC
+
+ SELECT r.Id AS ResourceId
+ ,( SELECT TOP 1 rr.OriginalResourceReferenceId
+ FROM [resources].[ResourceReference] rr
+ JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0
+ WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0
+ ) AS ResourceReferenceID
+ ,r.CurrentResourceVersionId AS ResourceVersionId
+ ,r.ResourceTypeId AS ResourceTypeId
+ ,rv.Title
+ ,rv.Description
+ ,CASE
+ WHEN r.ResourceTypeId = 7 THEN
+ (SELECT vrv.DurationInMilliseconds from [resources].[VideoResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId)
+ WHEN r.ResourceTypeId = 2 THEN
+ (SELECT vrv.DurationInMilliseconds from [resources].[AudioResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId)
+ ELSE
+ NULL
+ END AS DurationInMilliseconds
+ ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName
+ ,cnv.Url AS Url
+ ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl
+ ,cnv.RestrictedAccess
+ ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess
+ ,ub.Id AS BookMarkId
+ ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked
+ ,rvrs.AverageRating
+ ,rvrs.RatingCount
+FROM @MyActivity ma
+JOIN activity.ResourceActivity ra ON ra.id = ma.ResourceActivityId
+JOIN resources.resourceversion rv ON rv.id = ra.ResourceVersionId AND rv.Deleted = 0
+JOIN Resources.Resource r ON r.Id = rv.ResourceId
+JOIN hierarchy.Publication p ON rv.PublicationId = p.Id AND p.Deleted = 0
+JOIN resources.ResourceVersionRatingSummary rvrs ON rv.Id = rvrs.ResourceVersionId AND rvrs.Deleted = 0
+JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0
+JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0
+JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1
+JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0
+JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0
+LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.ResourceReferenceId = (SELECT TOP 1 rr.OriginalResourceReferenceId
+ FROM [resources].[ResourceReference] rr
+ JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0
+ WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0)
+LEFT JOIN ( SELECT DISTINCT CatalogueNodeId
+ FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId
+ WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId
+ORDER BY ma.ResourceActivityId DESC, rv.Title
+OFFSET @OffsetRows ROWS
+FETCH NEXT @FetchRows ROWS ONLY
+
+ SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END FROM @MyActivity
+END
\ No newline at end of file
diff --git a/WebAPI/LearningHub.Nhs.Repository.Interface/LearningHub.Nhs.Repository.Interface.csproj b/WebAPI/LearningHub.Nhs.Repository.Interface/LearningHub.Nhs.Repository.Interface.csproj
index 35d70c810..a2ac5f308 100644
--- a/WebAPI/LearningHub.Nhs.Repository.Interface/LearningHub.Nhs.Repository.Interface.csproj
+++ b/WebAPI/LearningHub.Nhs.Repository.Interface/LearningHub.Nhs.Repository.Interface.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/WebAPI/LearningHub.Nhs.Repository/LearningHub.Nhs.Repository.csproj b/WebAPI/LearningHub.Nhs.Repository/LearningHub.Nhs.Repository.csproj
index 5e7e65365..0f94d7953 100644
--- a/WebAPI/LearningHub.Nhs.Repository/LearningHub.Nhs.Repository.csproj
+++ b/WebAPI/LearningHub.Nhs.Repository/LearningHub.Nhs.Repository.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/WebAPI/LearningHub.Nhs.Repository/Resources/ResourceVersionRepository.cs b/WebAPI/LearningHub.Nhs.Repository/Resources/ResourceVersionRepository.cs
index 584da6344..cee3e6a07 100644
--- a/WebAPI/LearningHub.Nhs.Repository/Resources/ResourceVersionRepository.cs
+++ b/WebAPI/LearningHub.Nhs.Repository/Resources/ResourceVersionRepository.cs
@@ -690,7 +690,7 @@ public List GetContributions(int userId, ResourceContri
switch (dashboardType)
{
case "my-certificates":
- dashboardResources = this.DbContext.DashboardResourceDto.FromSqlRaw("resources.GetMyCertificatesDashboardResources @userId, @pageNumber, @totalRows output", param0, param1, param2).ToList();
+ dashboardResources = this.DbContext.DashboardResourceDto.FromSqlRaw("resources.GetMyLearningCertificatesDashboardResources @userId, @pageNumber, @totalRows output", param0, param1, param2).ToList();
break;
case "my-recent-completed":
dashboardResources = this.DbContext.DashboardResourceDto.FromSqlRaw("resources.GetMyRecentCompletedDashboardResources @userId, @pageNumber, @totalRows output", param0, param1, param2).ToList();
diff --git a/WebAPI/LearningHub.Nhs.Services.Interface/LearningHub.Nhs.Services.Interface.csproj b/WebAPI/LearningHub.Nhs.Services.Interface/LearningHub.Nhs.Services.Interface.csproj
index 5d2f278d4..59748eeb8 100644
--- a/WebAPI/LearningHub.Nhs.Services.Interface/LearningHub.Nhs.Services.Interface.csproj
+++ b/WebAPI/LearningHub.Nhs.Services.Interface/LearningHub.Nhs.Services.Interface.csproj
@@ -16,7 +16,7 @@
-
+
all
diff --git a/WebAPI/LearningHub.Nhs.Services.UnitTests/LearningHub.Nhs.Services.UnitTests.csproj b/WebAPI/LearningHub.Nhs.Services.UnitTests/LearningHub.Nhs.Services.UnitTests.csproj
index 9869ac4be..7f312c998 100644
--- a/WebAPI/LearningHub.Nhs.Services.UnitTests/LearningHub.Nhs.Services.UnitTests.csproj
+++ b/WebAPI/LearningHub.Nhs.Services.UnitTests/LearningHub.Nhs.Services.UnitTests.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/WebAPI/LearningHub.Nhs.Services/LearningHub.Nhs.Services.csproj b/WebAPI/LearningHub.Nhs.Services/LearningHub.Nhs.Services.csproj
index 44fefe844..e9fa30eef 100644
--- a/WebAPI/LearningHub.Nhs.Services/LearningHub.Nhs.Services.csproj
+++ b/WebAPI/LearningHub.Nhs.Services/LearningHub.Nhs.Services.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/WebAPI/LearningHub.Nhs.Services/ResourceService.cs b/WebAPI/LearningHub.Nhs.Services/ResourceService.cs
index 808bd8448..76d8df791 100644
--- a/WebAPI/LearningHub.Nhs.Services/ResourceService.cs
+++ b/WebAPI/LearningHub.Nhs.Services/ResourceService.cs
@@ -1772,7 +1772,8 @@ public async Task AddResourceVersionKeywordAsync(Re
bool doesKeywordAlreadyExist = await this.resourceVersionKeywordRepository.DoesResourceVersionKeywordAlreadyExistAsync(rvk.ResourceVersionId, rvk.Keyword);
if (doesKeywordAlreadyExist)
{
- return new LearningHubValidationResult(false, "This keyword has already been added.");
+ retVal.CreatedId = 0;
+ return retVal;
}
retVal.CreatedId = await this.resourceVersionKeywordRepository.CreateAsync(userId, rvk);
diff --git a/WebAPI/LearningHub.Nhs.Services/SearchService.cs b/WebAPI/LearningHub.Nhs.Services/SearchService.cs
index a247d9aa1..99ea20895 100644
--- a/WebAPI/LearningHub.Nhs.Services/SearchService.cs
+++ b/WebAPI/LearningHub.Nhs.Services/SearchService.cs
@@ -11,7 +11,7 @@ namespace LearningHub.Nhs.Services
using LearningHub.Nhs.Models.Entities.Analytics;
using LearningHub.Nhs.Models.Enums;
using LearningHub.Nhs.Models.Search;
- using LearningHub.Nhs.Models.Search.SearchFeedback;
+ using LearningHub.Nhs.Models.Search.SearchClick;
using LearningHub.Nhs.Models.Validation;
using LearningHub.Nhs.Services.Helpers;
using LearningHub.Nhs.Services.Interface;
@@ -512,7 +512,7 @@ public async Task CreateCatalogueSearchTermEvent(Ca
///
public async Task SendResourceSearchEventClickAsync(SearchActionResourceModel searchActionResourceModel)
{
- var searchClickPayloadModel = this.mapper.Map(searchActionResourceModel);
+ var searchClickPayloadModel = this.mapper.Map(searchActionResourceModel);
searchClickPayloadModel.TimeOfClick = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
searchClickPayloadModel.SearchSignal.ProfileSignature.ApplicationId = ApplicationId;
searchClickPayloadModel.SearchSignal.ProfileSignature.ProfileType = ProfileType;
@@ -532,7 +532,7 @@ public async Task SendResourceSearchEventClickAsync(SearchActionResourceMo
///
public async Task SendCatalogueSearchEventAsync(SearchActionCatalogueModel searchActionCatalogueModel)
{
- var searchClickPayloadModel = this.mapper.Map(searchActionCatalogueModel);
+ var searchClickPayloadModel = this.mapper.Map(searchActionCatalogueModel);
searchClickPayloadModel.TimeOfClick = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
searchClickPayloadModel.SearchSignal.ProfileSignature.ApplicationId = ApplicationId;
searchClickPayloadModel.SearchSignal.ProfileSignature.ProfileType = ProfileType;
@@ -596,7 +596,7 @@ public async Task GetAllCatalogueSearchResultsAsy
///
/// The .
///
- private async Task SendSearchEventClickAsync(SearchFeedbackPayloadModel searchClickPayloadModel, bool isResource)
+ private async Task SendSearchEventClickAsync(SearchClickPayloadModel searchClickPayloadModel, bool isResource)
{
var eventType = isResource ? "resource" : "catalog";
diff --git a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.ConsoleApp/LearningHub.Nhs.Migration.ConsoleApp.csproj b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.ConsoleApp/LearningHub.Nhs.Migration.ConsoleApp.csproj
index de8df1a58..539697d49 100644
--- a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.ConsoleApp/LearningHub.Nhs.Migration.ConsoleApp.csproj
+++ b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.ConsoleApp/LearningHub.Nhs.Migration.ConsoleApp.csproj
@@ -24,7 +24,7 @@
-
+
all
diff --git a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Interface/LearningHub.Nhs.Migration.Interface.csproj b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Interface/LearningHub.Nhs.Migration.Interface.csproj
index 52929fbd8..eb924766b 100644
--- a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Interface/LearningHub.Nhs.Migration.Interface.csproj
+++ b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Interface/LearningHub.Nhs.Migration.Interface.csproj
@@ -9,7 +9,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Models/LearningHub.Nhs.Migration.Models.csproj b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Models/LearningHub.Nhs.Migration.Models.csproj
index a3951c927..6f4c807b7 100644
--- a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Models/LearningHub.Nhs.Migration.Models.csproj
+++ b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Models/LearningHub.Nhs.Migration.Models.csproj
@@ -10,7 +10,7 @@
-
+
all
diff --git a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Repository/LearningHub.Nhs.Migration.Staging.Repository.csproj b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Repository/LearningHub.Nhs.Migration.Staging.Repository.csproj
index 7efd53fab..df5c3c6c3 100644
--- a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Repository/LearningHub.Nhs.Migration.Staging.Repository.csproj
+++ b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Repository/LearningHub.Nhs.Migration.Staging.Repository.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.UnitTests/LearningHub.Nhs.Migration.UnitTests.csproj b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.UnitTests/LearningHub.Nhs.Migration.UnitTests.csproj
index ad73a25cc..16a31717c 100644
--- a/WebAPI/MigrationTool/LearningHub.Nhs.Migration.UnitTests/LearningHub.Nhs.Migration.UnitTests.csproj
+++ b/WebAPI/MigrationTool/LearningHub.Nhs.Migration.UnitTests/LearningHub.Nhs.Migration.UnitTests.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/WebAPI/MigrationTool/LearningHub.Nhs.Migration/LearningHub.Nhs.Migration.csproj b/WebAPI/MigrationTool/LearningHub.Nhs.Migration/LearningHub.Nhs.Migration.csproj
index 861270f13..440ec325e 100644
--- a/WebAPI/MigrationTool/LearningHub.Nhs.Migration/LearningHub.Nhs.Migration.csproj
+++ b/WebAPI/MigrationTool/LearningHub.Nhs.Migration/LearningHub.Nhs.Migration.csproj
@@ -12,7 +12,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive