Skip to content

Commit 76aedd7

Browse files
author
Binon
committed
Moodel course discoverable in LH
1 parent e1cdfb3 commit 76aedd7

File tree

5 files changed

+270
-90
lines changed

5 files changed

+270
-90
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
namespace LearningHub.Nhs.WebUI.Helpers;
2+
3+
/// <summary>
4+
/// Represents the types of resources available in Moodle.
5+
/// </summary>
6+
public enum ResourceTypeEnumMoodle
7+
{
8+
/// <summary>
9+
/// The undefined resource type.
10+
/// </summary>
11+
Undefined,
12+
13+
/// <summary>
14+
/// The article resource type.
15+
/// </summary>
16+
Article,
17+
18+
/// <summary>
19+
/// The audio resource type.
20+
/// </summary>
21+
Audio,
22+
23+
/// <summary>
24+
/// The embedded resource type.
25+
/// </summary>
26+
Embedded,
27+
28+
/// <summary>
29+
/// The equipment resource type.
30+
/// </summary>
31+
Equipment,
32+
33+
/// <summary>
34+
/// The image resource type.
35+
/// </summary>
36+
Image,
37+
38+
/// <summary>
39+
/// The SCORM resource type.
40+
/// </summary>
41+
Scorm,
42+
43+
/// <summary>
44+
/// The video resource type.
45+
/// </summary>
46+
Video,
47+
48+
/// <summary>
49+
/// The web link resource type.
50+
/// </summary>
51+
WebLink,
52+
53+
/// <summary>
54+
/// The generic file resource type.
55+
/// </summary>
56+
GenericFile,
57+
58+
/// <summary>
59+
/// The clinical case resource type.
60+
/// </summary>
61+
Case,
62+
63+
/// <summary>
64+
/// The assessment resource type.
65+
/// </summary>
66+
Assessment,
67+
68+
/// <summary>
69+
/// The HTML resource type.
70+
/// </summary>
71+
Html,
72+
73+
/// <summary>
74+
/// The Moodle resource type.
75+
/// </summary>
76+
Moodle,
77+
78+
/// <summary>
79+
/// The Moodle course resource type.
80+
/// </summary>
81+
Course,
82+
}

LearningHub.Nhs.WebUI/Helpers/UtilityHelper.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@ public static class UtilityHelper
3232
{ "html", ResourceTypeEnum.Html },
3333
};
3434

35+
/// TODO: Remove this method after adding to Moodle resource types to models project.
36+
/// <summary>
37+
/// Findwise Moodle resource type dictionary.
38+
/// </summary>
39+
public static readonly Dictionary<string, ResourceTypeEnumMoodle> FindwiseResourceMoodleTypeDict = new Dictionary<string, ResourceTypeEnumMoodle>()
40+
{
41+
{ "video", ResourceTypeEnumMoodle.Video },
42+
{ "article", ResourceTypeEnumMoodle.Article },
43+
{ "case", ResourceTypeEnumMoodle.Case },
44+
{ "weblink", ResourceTypeEnumMoodle.WebLink },
45+
{ "audio", ResourceTypeEnumMoodle.Audio },
46+
{ "scorm", ResourceTypeEnumMoodle.Scorm },
47+
{ "assessment", ResourceTypeEnumMoodle.Assessment },
48+
{ "genericfile", ResourceTypeEnumMoodle.GenericFile },
49+
{ "image", ResourceTypeEnumMoodle.Image },
50+
{ "html", ResourceTypeEnumMoodle.Html },
51+
{ "moodle", ResourceTypeEnumMoodle.Course },
52+
};
53+
3554
/// <summary>
3655
/// The FormatTwitterDate.
3756
/// </summary>
@@ -147,6 +166,54 @@ public static string GetPrettifiedResourceTypeName(ResourceTypeEnum resourceType
147166
}
148167
}
149168

169+
/// TODO: Remove this method after adding to Moodle resource types to models project.
170+
/// <summary>
171+
/// Returns a prettified resource type name, suitable for display in the UI. Includes video/audio duration string.
172+
/// </summary>
173+
/// <param name="resourceType">The resource type.</param>
174+
/// <param name="durationInMilliseconds">The media duration in milliseconds.</param>
175+
/// <returns>The resource type name, and duration if applicable.</returns>
176+
public static string GetPrettifiedResourceTypeNameMoodle(ResourceTypeEnumMoodle resourceType, int? durationInMilliseconds = 0)
177+
{
178+
switch (resourceType)
179+
{
180+
case ResourceTypeEnumMoodle.Assessment:
181+
return "Assessment";
182+
case ResourceTypeEnumMoodle.Article:
183+
return "Article";
184+
case ResourceTypeEnumMoodle.Audio:
185+
string durationText = GetDurationText(durationInMilliseconds ?? 0);
186+
durationText = string.IsNullOrEmpty(durationText) ? string.Empty : " - " + durationText;
187+
return "Audio" + durationText;
188+
case ResourceTypeEnumMoodle.Equipment:
189+
return "Equipment";
190+
case ResourceTypeEnumMoodle.Image:
191+
return "Image";
192+
case ResourceTypeEnumMoodle.Scorm:
193+
return "elearning";
194+
case ResourceTypeEnumMoodle.Video:
195+
durationText = GetDurationText(durationInMilliseconds ?? 0);
196+
durationText = string.IsNullOrEmpty(durationText) ? string.Empty : " - " + durationText;
197+
return "Video" + durationText;
198+
case ResourceTypeEnumMoodle.WebLink:
199+
return "Web link";
200+
case ResourceTypeEnumMoodle.GenericFile:
201+
return "File";
202+
case ResourceTypeEnumMoodle.Embedded:
203+
return "Embedded";
204+
case ResourceTypeEnumMoodle.Case:
205+
return "Case";
206+
case ResourceTypeEnumMoodle.Html:
207+
return "HTML";
208+
case ResourceTypeEnumMoodle.Moodle:
209+
return "Course";
210+
case ResourceTypeEnumMoodle.Course:
211+
return "Course";
212+
default:
213+
return "File";
214+
}
215+
}
216+
150217
/// <summary>
151218
/// Returns a prettified resource type name, suitable for display in the UI. Excludes video/audio duration string.
152219
/// </summary>

LearningHub.Nhs.WebUI/Services/SearchService.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ public async Task<SearchResultViewModel> PerformSearch(IPrincipal user, SearchRe
163163
{
164164
var filter = filters.Where(x => x.DisplayName == filteritem).FirstOrDefault();
165165

166-
if (filter != null && UtilityHelper.FindwiseResourceTypeDict.ContainsKey(filter.DisplayName))
166+
if (filter != null && UtilityHelper.FindwiseResourceMoodleTypeDict.ContainsKey(filter.DisplayName))
167167
{
168-
var resourceTypeEnum = UtilityHelper.FindwiseResourceTypeDict[filter.DisplayName];
169-
var searchfilter = new SearchFilterModel() { DisplayName = UtilityHelper.GetPrettifiedResourceTypeName(resourceTypeEnum), Count = filter.Count, Value = filteritem, Selected = searchRequest.Filters?.Contains(filter.DisplayName) ?? false };
168+
var resourceTypeEnum = UtilityHelper.FindwiseResourceMoodleTypeDict[filter.DisplayName];
169+
var searchfilter = new SearchFilterModel() { DisplayName = UtilityHelper.GetPrettifiedResourceTypeNameMoodle(resourceTypeEnum), Count = filter.Count, Value = filteritem, Selected = searchRequest.Filters?.Contains(filter.DisplayName) ?? false };
170170
searchfilters.Add(searchfilter);
171171
}
172172
}
Lines changed: 113 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@model LearningHub.Nhs.WebUI.Models.Search.SearchResultViewModel
2+
@inject Microsoft.Extensions.Configuration.IConfiguration Configuration;
23

34
@using System.Linq;
45
@using System.Web;
@@ -10,99 +11,124 @@
1011
@using LearningHub.Nhs.Models.Search.SearchClick;
1112

1213
@{
13-
var resourceResult = Model.ResourceSearchResult;
14-
var pagingModel = Model.ResourceResultPaging;
15-
var index = pagingModel.CurrentPage * pagingModel.PageSize;
16-
var searchString = HttpUtility.UrlEncode(Model.SearchString);
17-
18-
string GetUrl(int resourceReferenceId, int itemIndex, int nodePathId, SearchClickPayloadModel payload)
19-
{
20-
var searchSignal = payload?.SearchSignal;
21-
string groupId = HttpUtility.UrlEncode(Model.GroupId.ToString());
22-
string searchSignalQueryEncoded = HttpUtility.UrlEncode(HttpUtility.UrlDecode(searchSignal?.Query));
23-
24-
return $@"/search/record-resource-click?url=/Resource/{resourceReferenceId}&nodePathId={nodePathId}&itemIndex={payload?.HitNumber}
14+
var resourceResult = Model.ResourceSearchResult;
15+
var pagingModel = Model.ResourceResultPaging;
16+
var index = pagingModel.CurrentPage * pagingModel.PageSize;
17+
var searchString = HttpUtility.UrlEncode(Model.SearchString);
18+
19+
string GetUrl(int resourceReferenceId, int itemIndex, int nodePathId, SearchClickPayloadModel payload)
20+
{
21+
var searchSignal = payload?.SearchSignal;
22+
string groupId = HttpUtility.UrlEncode(Model.GroupId.ToString());
23+
string searchSignalQueryEncoded = HttpUtility.UrlEncode(HttpUtility.UrlDecode(searchSignal?.Query));
24+
25+
return $@"/search/record-resource-click?url=/Resource/{resourceReferenceId}&nodePathId={nodePathId}&itemIndex={payload?.HitNumber}
2526
&pageIndex={pagingModel.CurrentPage}&totalNumberOfHits={payload?.SearchSignal?.Stats?.TotalHits}&searchText={searchString}&resourceReferenceId={resourceReferenceId}
2627
&groupId={groupId}&searchId={searchSignal?.SearchId}&timeOfSearch={searchSignal?.TimeOfSearch}&userQuery={HttpUtility.UrlEncode(searchSignal.UserQuery)}
2728
&query={searchSignalQueryEncoded}&title={payload?.DocumentFields?.Title}";
28-
}
29+
}
30+
31+
string GetMoodleCourseUrl(string courseId)
32+
{
33+
var prefix = "M";
34+
if (courseId.StartsWith(prefix))
35+
{
36+
courseId = courseId.Replace(prefix, ""); ;
37+
}
38+
39+
var apiBaseUrl = Configuration["MoodleAPIConfig:BaseUrl"];
40+
string path = $"course/view.php";
41+
string returnUrl = $@"{apiBaseUrl}/{path}?id={courseId}";
2942

30-
bool showCatalogueFieldsInResources = ViewBag.ShowCatalogueFieldsInResources == null || ViewBag.ShowCatalogueFieldsInResources == true;
31-
bool resourceAccessLevelFilterSelected = resourceResult.SearchResourceAccessLevelFilters.Any(f => f.Selected);
43+
return returnUrl;
44+
}
45+
46+
bool showCatalogueFieldsInResources = ViewBag.ShowCatalogueFieldsInResources == null || ViewBag.ShowCatalogueFieldsInResources == true;
47+
bool resourceAccessLevelFilterSelected = resourceResult.SearchResourceAccessLevelFilters.Any(f => f.Selected);
3248
}
3349

3450
@foreach (var item in resourceResult.DocumentModel)
3551
{
36-
var provider = item.Providers?.FirstOrDefault();
37-
38-
<div class="resource-item nhsuk-list-item--border nhsuk-u-padding-bottom-4 nhsuk-u-margin-bottom-4">
39-
<h3 class="nhsuk-heading-xs nhsuk-u-margin-bottom-2">
40-
<a class="nhsuk-card__link" href="@GetUrl(item.ResourceReferenceId, index, item.NodePathId??0, item.Click.Payload)">@item.Title</a>
41-
</h3>
42-
43-
@if (provider != null)
44-
{
45-
<div>
46-
<div class="provider-details">
47-
<img src="~/images/provider-logos/@provider.Logo" alt="@provider.Name" class="logo" />
48-
<span class="title">@ProviderHelper.GetProviderString(provider.Name)</span>
49-
</div>
50-
</div>
51-
}
52-
@if (item.CatalogueRestrictedAccess && !Model.HideRestrictedBadge && showCatalogueFieldsInResources)
53-
{
54-
<p class="nhsuk-body-s nhsuk-u-margin-bottom-2 nhsuk-u-font-weight-bold">
55-
@((item.CatalogueHasAccess || this.User.IsInRole("Administrator")) ? "Access Granted" : "Access restricted")
56-
</p>
57-
}
58-
59-
@if (!resourceAccessLevelFilterSelected)
60-
{
61-
<div class="nhsuk-body-s nhsuk-u-margin-bottom-2">
62-
<div class="nhsuk-u-margin-right-4">
63-
<strong>Audience access level: </strong>
64-
@ResourceAccessLevelHelper.GetResourceAccessLevelText((ResourceAccessibilityEnum)item.ResourceAccessLevel)
65-
</div>
66-
</div>
67-
}
68-
69-
70-
<div class="resource-info nhsuk-body-s nhsuk-u-margin-bottom-2">
71-
<div class="nhsuk-u-margin-right-4">
72-
<strong>Type: </strong>
73-
@UtilityHelper.GetPrettifiedResourceTypeName(UtilityHelper.ToEnum<ResourceTypeEnum>(item.ResourceType), 0)
74-
</div>
75-
<div>
76-
@await Html.PartialAsync("../Shared/_StarRating.cshtml", item.Rating)
77-
</div>
78-
</div>
79-
80-
<p class="nhsuk-body-s nhsuk-u-margin-bottom-2 resource-description line-clamp-3">
81-
@item.Description
82-
</p>
83-
84-
<div class="resource-author">
85-
@if (!string.IsNullOrWhiteSpace(item.CatalogueBadgeUrl) && showCatalogueFieldsInResources)
86-
{
87-
<img class="resource-catalogue-badge" src="/search/image/@item.CatalogueBadgeUrl" alt="Provider's catalogue badge"/>
88-
}
89-
90-
@if (!string.IsNullOrEmpty(item.CatalogueName) && !this.Model.CatalogueId.HasValue && showCatalogueFieldsInResources)
91-
{
92-
<div class="nhsuk-u-margin-right-3">
93-
<a href="@("/Catalogue/" + item.CatalogueUrl)" class="catalogue-resource-search-result-name">@item.CatalogueName</a>
94-
</div>
95-
}
96-
97-
<div>
98-
@UtilityHelper.GetAttribution(item.Authors)
99-
@if (!string.IsNullOrEmpty(item.AuthoredDate))
100-
{
101-
@UtilityHelper.GetInOn(item.AuthoredDate)
102-
@: @item.AuthoredDate
103-
}
104-
</div>
105-
</div>
106-
</div>
107-
index++;
52+
var provider = item.Providers?.FirstOrDefault();
53+
54+
<div class="resource-item nhsuk-list-item--border nhsuk-u-padding-bottom-4 nhsuk-u-margin-bottom-4">
55+
<h3 class="nhsuk-heading-xs nhsuk-u-margin-bottom-2">
56+
@if (item.ResourceType == "moodle")
57+
{
58+
<a class="nhsuk-card__link" href="@GetMoodleCourseUrl(item.Id)">@item.Title</a>
59+
}
60+
else
61+
{
62+
<a class="nhsuk-card__link" href="@GetUrl(item.ResourceReferenceId, index, item.NodePathId??0, item.Click.Payload)">@item.Title</a>
63+
}
64+
</h3>
65+
66+
@if (provider != null)
67+
{
68+
<div>
69+
<div class="provider-details">
70+
<img src="~/images/provider-logos/@provider.Logo" alt="@provider.Name" class="logo" />
71+
<span class="title">@ProviderHelper.GetProviderString(provider.Name)</span>
72+
</div>
73+
</div>
74+
}
75+
@if (item.CatalogueRestrictedAccess && !Model.HideRestrictedBadge && showCatalogueFieldsInResources)
76+
{
77+
<p class="nhsuk-body-s nhsuk-u-margin-bottom-2 nhsuk-u-font-weight-bold">
78+
@((item.CatalogueHasAccess || this.User.IsInRole("Administrator")) ? "Access Granted" : "Access restricted")
79+
</p>
80+
}
81+
82+
@if (!resourceAccessLevelFilterSelected)
83+
{
84+
<div class="nhsuk-body-s nhsuk-u-margin-bottom-2">
85+
<div class="nhsuk-u-margin-right-4">
86+
<strong>Audience access level: </strong>
87+
@ResourceAccessLevelHelper.GetResourceAccessLevelText((ResourceAccessibilityEnum)item.ResourceAccessLevel)
88+
</div>
89+
</div>
90+
}
91+
92+
93+
<div class="resource-info nhsuk-body-s nhsuk-u-margin-bottom-2">
94+
<div class="nhsuk-u-margin-right-4">
95+
<strong>Type: </strong>
96+
@UtilityHelper.GetPrettifiedResourceTypeNameMoodle(UtilityHelper.ToEnum<ResourceTypeEnumMoodle>(item.ResourceType), 0)
97+
</div>
98+
<div>
99+
@if (item.ResourceType != "moodle")
100+
{
101+
@await Html.PartialAsync("../Shared/_StarRating.cshtml", item.Rating)
102+
}
103+
</div>
104+
</div>
105+
106+
<p class="nhsuk-body-s nhsuk-u-margin-bottom-2 resource-description line-clamp-3">
107+
@item.Description
108+
</p>
109+
110+
<div class="resource-author">
111+
@if (!string.IsNullOrWhiteSpace(item.CatalogueBadgeUrl) && showCatalogueFieldsInResources)
112+
{
113+
<img class="resource-catalogue-badge" src="/search/image/@item.CatalogueBadgeUrl" alt="Provider's catalogue badge" />
114+
}
115+
116+
@if (!string.IsNullOrEmpty(item.CatalogueName) && !this.Model.CatalogueId.HasValue && showCatalogueFieldsInResources)
117+
{
118+
<div class="nhsuk-u-margin-right-3">
119+
<a href="@("/Catalogue/" + item.CatalogueUrl)" class="catalogue-resource-search-result-name">@item.CatalogueName</a>
120+
</div>
121+
}
122+
123+
<div>
124+
@UtilityHelper.GetAttribution(item.Authors)
125+
@if (!string.IsNullOrEmpty(item.AuthoredDate))
126+
{
127+
@UtilityHelper.GetInOn(item.AuthoredDate)
128+
@: @item.AuthoredDate
129+
}
130+
</div>
131+
</div>
132+
</div>
133+
index++;
108134
}

0 commit comments

Comments
 (0)