From 6793e76c00820e98b9d7caeae1a8336f9315271a Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Fri, 17 Mar 2023 13:00:02 +0100
Subject: [PATCH 01/15] Created Page model and Service interface

---
 Epsilon.Canvas.Abstractions/Model/Page.cs     | 21 +++++++++++++++++++
 .../Service/IPageHttpService.cs               | 10 +++++++++
 2 files changed, 31 insertions(+)
 create mode 100644 Epsilon.Canvas.Abstractions/Model/Page.cs
 create mode 100644 Epsilon.Canvas.Abstractions/Service/IPageHttpService.cs

diff --git a/Epsilon.Canvas.Abstractions/Model/Page.cs b/Epsilon.Canvas.Abstractions/Model/Page.cs
new file mode 100644
index 00000000..05b1ad77
--- /dev/null
+++ b/Epsilon.Canvas.Abstractions/Model/Page.cs
@@ -0,0 +1,21 @@
+using System.Text.Json.Serialization;
+
+namespace Epsilon.Canvas.Abstractions.Model;
+
+public record Page(
+    [property: JsonPropertyName("title")] string? Title,
+    [property: JsonPropertyName("created_at")] string? CreatedAt,
+    [property: JsonPropertyName("url")] string? Url,
+    [property: JsonPropertyName("editing_roles")] string? EditingRoles,
+    [property: JsonPropertyName("page_id")] string? PageId,
+    [property: JsonPropertyName("last_edited_by")] string? LastEditedBy,
+    [property: JsonPropertyName("published")] string? Published,
+    [property: JsonPropertyName("hide_from_students")] string? HideFromStudents,
+    [property: JsonPropertyName("front_page")] string? FrontPage,
+    [property: JsonPropertyName("html_url")] string? HTMLUrl,
+    [property: JsonPropertyName("todo_date")] string? TodoDate,
+    [property: JsonPropertyName("publish_at")] string? PublishAt,
+    [property: JsonPropertyName("updated_at")] string? UpdatedAt,
+    [property: JsonPropertyName("locked_for_user")] string? LockedForUser,
+    [property: JsonPropertyName("body")] string? Body
+);
\ No newline at end of file
diff --git a/Epsilon.Canvas.Abstractions/Service/IPageHttpService.cs b/Epsilon.Canvas.Abstractions/Service/IPageHttpService.cs
new file mode 100644
index 00000000..9c22d8cf
--- /dev/null
+++ b/Epsilon.Canvas.Abstractions/Service/IPageHttpService.cs
@@ -0,0 +1,10 @@
+using Epsilon.Canvas.Abstractions.Model;
+
+namespace Epsilon.Canvas.Abstractions.Service;
+
+public interface IPageHttpService
+{
+    Task<Page?> GetPageByName(int courseId, string pageName);
+
+    Task<IEnumerable<Page>?> GetAll(int courseId, IEnumerable<string> include);
+}
\ No newline at end of file

From 2e6e2731f242ffd6c5907a95a11a858ab490da4e Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Fri, 17 Mar 2023 14:34:06 +0100
Subject: [PATCH 02/15] Created PageHttpService.cs

---
 Epsilon.Abstractions/Model/CoursePage.cs      | 20 +++++++++++
 Epsilon.Canvas.Abstractions/Model/Page.cs     | 17 +++++-----
 .../CanvasModuleCollectionFetcher.cs          |  6 +++-
 .../CanvasServiceCollectionExtensions.cs      |  1 +
 Epsilon.Canvas/Service/PageHttpService.cs     | 33 +++++++++++++++++++
 5 files changed, 67 insertions(+), 10 deletions(-)
 create mode 100644 Epsilon.Abstractions/Model/CoursePage.cs
 create mode 100644 Epsilon.Canvas/Service/PageHttpService.cs

diff --git a/Epsilon.Abstractions/Model/CoursePage.cs b/Epsilon.Abstractions/Model/CoursePage.cs
new file mode 100644
index 00000000..dc68fac2
--- /dev/null
+++ b/Epsilon.Abstractions/Model/CoursePage.cs
@@ -0,0 +1,20 @@
+namespace Epsilon.Abstractions.Model;
+
+public class CoursePage
+{
+    public string Title { get; set; }
+    public string CreatedAt { get; set; }
+    public string Url { get; set; }
+    public string EditingRoles { get; set; }
+    public string PageId { get; set; }
+    public string LastEditedBy { get; set; }
+    public string Published { get; set; }
+    public string HideFromStudents { get; set; }
+    public string FrontPage { get; set; }
+    public string HTMLUrl { get; set; }
+    public string TodoDate { get; set; }
+    public string PublishedAt { get; set; }
+    public string UpdatedAt { get; set; }
+    public string LockedForUser { get; set; }
+    public string Body { get; set; }
+}
\ No newline at end of file
diff --git a/Epsilon.Canvas.Abstractions/Model/Page.cs b/Epsilon.Canvas.Abstractions/Model/Page.cs
index 05b1ad77..2a82460d 100644
--- a/Epsilon.Canvas.Abstractions/Model/Page.cs
+++ b/Epsilon.Canvas.Abstractions/Model/Page.cs
@@ -7,15 +7,14 @@ public record Page(
     [property: JsonPropertyName("created_at")] string? CreatedAt,
     [property: JsonPropertyName("url")] string? Url,
     [property: JsonPropertyName("editing_roles")] string? EditingRoles,
-    [property: JsonPropertyName("page_id")] string? PageId,
-    [property: JsonPropertyName("last_edited_by")] string? LastEditedBy,
-    [property: JsonPropertyName("published")] string? Published,
-    [property: JsonPropertyName("hide_from_students")] string? HideFromStudents,
-    [property: JsonPropertyName("front_page")] string? FrontPage,
+    [property: JsonPropertyName("page_id")] int? PageId,
+    [property: JsonPropertyName("published")] bool? Published,
+    [property: JsonPropertyName("hide_from_students")] bool? HideFromStudents,
+    [property: JsonPropertyName("front_page")] bool? FrontPage,
     [property: JsonPropertyName("html_url")] string? HTMLUrl,
-    [property: JsonPropertyName("todo_date")] string? TodoDate,
-    [property: JsonPropertyName("publish_at")] string? PublishAt,
-    [property: JsonPropertyName("updated_at")] string? UpdatedAt,
-    [property: JsonPropertyName("locked_for_user")] string? LockedForUser,
+    [property: JsonPropertyName("todo_date")] DateTime? TodoDate,
+    [property: JsonPropertyName("publish_at")] DateTime? PublishAt,
+    [property: JsonPropertyName("updated_at")] DateTime? UpdatedAt,
+    [property: JsonPropertyName("locked_for_user")] bool? LockedForUser,
     [property: JsonPropertyName("body")] string? Body
 );
\ No newline at end of file
diff --git a/Epsilon.Canvas/CanvasModuleCollectionFetcher.cs b/Epsilon.Canvas/CanvasModuleCollectionFetcher.cs
index 2c6c7db0..136469dd 100644
--- a/Epsilon.Canvas/CanvasModuleCollectionFetcher.cs
+++ b/Epsilon.Canvas/CanvasModuleCollectionFetcher.cs
@@ -10,19 +10,23 @@ public class CanvasModuleCollectionFetcher : ICanvasModuleCollectionFetcher
 {
     private readonly IModuleHttpService _moduleService;
     private readonly IOutcomeHttpService _outcomeService;
+    private readonly IPageHttpService _pageService;
 
     public CanvasModuleCollectionFetcher(
         ILogger<CanvasModuleCollectionFetcher> logger,
         IModuleHttpService moduleService,
-        IOutcomeHttpService outcomeService
+        IOutcomeHttpService outcomeService,
+        IPageHttpService pageService
     )
     {
         _moduleService = moduleService;
         _outcomeService = outcomeService;
+        _pageService = pageService;
     }
 
     public async IAsyncEnumerable<ModuleOutcomeResultCollection> GetAll(int courseId, IEnumerable<string>? allowedModules)
     {
+        var pages = await _pageService.GetPageByName(courseId,"front_page");
         var response = await _outcomeService.GetResults(courseId, new[] { "outcomes", "alignments" });
         var modules = await _moduleService.GetAll(courseId, new[] { "items" });
 
diff --git a/Epsilon.Canvas/CanvasServiceCollectionExtensions.cs b/Epsilon.Canvas/CanvasServiceCollectionExtensions.cs
index 9888c720..04989038 100644
--- a/Epsilon.Canvas/CanvasServiceCollectionExtensions.cs
+++ b/Epsilon.Canvas/CanvasServiceCollectionExtensions.cs
@@ -32,6 +32,7 @@ public static IServiceCollection AddCanvas(this IServiceCollection services, ICo
         services.AddHttpClient<IAssignmentHttpService, AssignmentHttpService>(CanvasHttpClient);
         services.AddHttpClient<IOutcomeHttpService, OutcomeHttpService>(CanvasHttpClient);
         services.AddHttpClient<ISubmissionHttpService, SubmissionHttpService>(CanvasHttpClient);
+        services.AddHttpClient<IPageHttpService, PageHttpService>(CanvasHttpClient);
 
         services.AddScoped<ILinkHeaderConverter, LinkHeaderConverter>();
         
diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
new file mode 100644
index 00000000..ad017066
--- /dev/null
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -0,0 +1,33 @@
+using System.Net.Http.Json;
+using Epsilon.Abstractions.Http;
+using Epsilon.Canvas.Abstractions.Converter;
+using Epsilon.Canvas.Abstractions.Model;
+using Epsilon.Canvas.Abstractions.Service;
+
+namespace Epsilon.Canvas.Service;
+
+public class PageHttpService : HttpService, IPageHttpService
+{
+    private readonly ILinkHeaderConverter _headerConverter;
+
+    public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter) : base(client)
+    {
+        _headerConverter = headerConverter;
+    }
+
+    public async Task<Page?> GetPageByName(int courseId, string pageName)
+    {
+        var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
+        var response = await Client.SendAsync(request);
+
+        return await response.Content.ReadFromJsonAsync<Page>();
+    }
+
+    public async Task<IEnumerable<Page>?> GetAll(int courseId, IEnumerable<string> include)
+    {
+        var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
+        var response = await Client.SendAsync(request);
+
+        return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
+    }
+}
\ No newline at end of file

From a2036fbf29fef96e3ba7f3f0897bc94df8afbb22 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Fri, 24 Mar 2023 13:42:40 +0100
Subject: [PATCH 03/15] Added GetPageImages method

---
 Epsilon.Canvas/Epsilon.Canvas.csproj      |  1 +
 Epsilon.Canvas/Service/PageHttpService.cs | 85 +++++++++++++++++++++--
 2 files changed, 81 insertions(+), 5 deletions(-)

diff --git a/Epsilon.Canvas/Epsilon.Canvas.csproj b/Epsilon.Canvas/Epsilon.Canvas.csproj
index c4c618d3..5077c19b 100644
--- a/Epsilon.Canvas/Epsilon.Canvas.csproj
+++ b/Epsilon.Canvas/Epsilon.Canvas.csproj
@@ -12,6 +12,7 @@
     </ItemGroup>
 
     <ItemGroup>
+      <PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
       <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
       <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
       <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index ad017066..8c503737 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -1,8 +1,10 @@
-using System.Net.Http.Json;
+using System.Net;
+using System.Net.Http.Json;
 using Epsilon.Abstractions.Http;
 using Epsilon.Canvas.Abstractions.Converter;
 using Epsilon.Canvas.Abstractions.Model;
 using Epsilon.Canvas.Abstractions.Service;
+using HtmlAgilityPack;
 
 namespace Epsilon.Canvas.Service;
 
@@ -18,16 +20,89 @@ public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter)
     public async Task<Page?> GetPageByName(int courseId, string pageName)
     {
         var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
-        var response = await Client.SendAsync(request);
+        var response = await Client.SendAsync(request); 
+        
+        if(response.StatusCode == HttpStatusCode.NotFound)
+        {
+            throw new Exception("Page not found");
+        }
+        
+        if(response.StatusCode == HttpStatusCode.Forbidden)
+        {
+            throw new Exception("Page forbidden");
+        }
+        
+        if(response.StatusCode == HttpStatusCode.Unauthorized)
+        {
+            throw new Exception("Page unauthorized");
+        }
+
+        if (response.StatusCode == HttpStatusCode.OK)
+        {
+            var page = await response.Content.ReadFromJsonAsync<Page>();
+            this.GetPageImages(page);
+            return page;
+        }
 
-        return await response.Content.ReadFromJsonAsync<Page>();
+        return null;
     }
 
     public async Task<IEnumerable<Page>?> GetAll(int courseId, IEnumerable<string> include)
     {
         var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
         var response = await Client.SendAsync(request);
+        
+        if(response.StatusCode == HttpStatusCode.NotFound)
+        {
+            throw new Exception("Not found");
+        }
+        
+        if(response.StatusCode == HttpStatusCode.Forbidden)
+        {
+            throw new Exception("Forbidden");
+        }
+        
+        if(response.StatusCode == HttpStatusCode.Unauthorized)
+        {
+            throw new Exception("Unauthorized");
+        }
 
-        return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
+        if (response.StatusCode == HttpStatusCode.OK)
+        {
+            return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
+        }
+        
+        return null;
+    }
+
+    private async Task GetPageImages(Page page)
+    {
+        var htmlDoc = new HtmlDocument();
+        htmlDoc.LoadHtml(page.Body);
+    
+        string imageSrc = htmlDoc.DocumentNode
+            .SelectNodes("//img")
+            .First()
+            .Attributes["src"].Value;
+        
+        string imageAlt = htmlDoc.DocumentNode
+            .SelectNodes("//img")
+            .First()
+            .Attributes["alt"].Value;
+        
+        if(imageSrc == null || imageAlt == null)
+        {
+            throw new Exception("No image found");
+        }
+        
+        try
+        {
+            byte[] imageBytes = await Client.GetByteArrayAsync(imageSrc);
+            await File.WriteAllBytesAsync(imageAlt, imageBytes);
+        }
+        catch(Exception e)
+        { 
+            throw new Exception(e.Message);
+        }
     }
-}
\ No newline at end of file
+}

From 20a7e0d83f2037736b1f6703a2bec3471e264829 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Fri, 24 Mar 2023 15:25:31 +0100
Subject: [PATCH 04/15] Changed GetPageImages so that it replaces the img src
 with the base64 string of the image.

---
 Epsilon.Canvas/Service/PageHttpService.cs | 34 ++++++++---------------
 1 file changed, 12 insertions(+), 22 deletions(-)

diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index 8c503737..92f566c5 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -75,34 +75,24 @@ public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter)
         return null;
     }
 
-    private async Task GetPageImages(Page page)
+    private async Task<string> GetPageImages(Page page)
     {
         var htmlDoc = new HtmlDocument();
         htmlDoc.LoadHtml(page.Body);
-    
-        string imageSrc = htmlDoc.DocumentNode
-            .SelectNodes("//img")
-            .First()
-            .Attributes["src"].Value;
         
-        string imageAlt = htmlDoc.DocumentNode
-            .SelectNodes("//img")
-            .First()
-            .Attributes["alt"].Value;
-        
-        if(imageSrc == null || imageAlt == null)
-        {
-            throw new Exception("No image found");
-        }
-        
-        try
+        foreach (var node in htmlDoc.DocumentNode.SelectNodes("//img"))
         {
+            string imageSrc = htmlDoc.DocumentNode
+                .SelectNodes("//img")
+                .First()
+                .Attributes["src"].Value;
+
             byte[] imageBytes = await Client.GetByteArrayAsync(imageSrc);
-            await File.WriteAllBytesAsync(imageAlt, imageBytes);
-        }
-        catch(Exception e)
-        { 
-            throw new Exception(e.Message);
+            string imageBase64 = Convert.ToBase64String(imageBytes);
+
+            node.SetAttributeValue("src", $"data:image/jpeg;base64,{imageBase64}");
         }
+
+        return htmlDoc.DocumentNode.WriteTo();
     }
 }

From 76daec6d43b558320f0877a9a79766f692ae759c Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Mon, 27 Mar 2023 14:05:16 +0200
Subject: [PATCH 05/15] Added persona to word exporter

---
 .../Export/IExportDataPackager.cs             |  2 +-
 Epsilon.Abstractions/Http/HttpService.cs      |  2 +-
 Epsilon.Abstractions/Model/ExportData.cs      |  3 +-
 .../Service/IFileHttpService.cs               |  7 ++
 .../Service/IPageHttpService.cs               |  2 +-
 .../CanvasModuleCollectionFetcher.cs          | 15 ++--
 .../CanvasServiceCollectionExtensions.cs      |  6 +-
 Epsilon.Canvas/Epsilon.Canvas.csproj          |  1 -
 Epsilon.Canvas/Service/FileHttpService.cs     | 27 +++++++
 Epsilon.Canvas/Service/PageHttpService.cs     | 80 +++++--------------
 Epsilon.Cli/Startup.cs                        | 12 ++-
 Epsilon/Epsilon.csproj                        |  1 -
 Epsilon/Export/ExportDataPackager.cs          | 33 ++++++--
 .../Export/Exporters/WordModuleExporter.cs    | 66 ++++++++++++---
 .../CoreServiceCollectionExtensions.cs        |  2 +
 15 files changed, 161 insertions(+), 98 deletions(-)
 create mode 100644 Epsilon.Canvas.Abstractions/Service/IFileHttpService.cs
 create mode 100644 Epsilon.Canvas/Service/FileHttpService.cs

diff --git a/Epsilon.Abstractions/Export/IExportDataPackager.cs b/Epsilon.Abstractions/Export/IExportDataPackager.cs
index f4e0aafd..079d987e 100644
--- a/Epsilon.Abstractions/Export/IExportDataPackager.cs
+++ b/Epsilon.Abstractions/Export/IExportDataPackager.cs
@@ -5,5 +5,5 @@ namespace Epsilon.Abstractions.Export;
 
 public interface IExportDataPackager
 {
-    public Task<ExportData> GetExportData(IAsyncEnumerable<ModuleOutcomeResultCollection> data);
+    public Task<ExportData> GetExportData();
 }
\ No newline at end of file
diff --git a/Epsilon.Abstractions/Http/HttpService.cs b/Epsilon.Abstractions/Http/HttpService.cs
index c5643701..0c803fa2 100644
--- a/Epsilon.Abstractions/Http/HttpService.cs
+++ b/Epsilon.Abstractions/Http/HttpService.cs
@@ -4,5 +4,5 @@ public abstract class HttpService
 {
     protected HttpService(HttpClient client) => Client = client;
 
-    protected HttpClient Client { get; }
+    public HttpClient Client { get; }
 }
\ No newline at end of file
diff --git a/Epsilon.Abstractions/Model/ExportData.cs b/Epsilon.Abstractions/Model/ExportData.cs
index 51558856..fe1b7493 100644
--- a/Epsilon.Abstractions/Model/ExportData.cs
+++ b/Epsilon.Abstractions/Model/ExportData.cs
@@ -2,6 +2,7 @@
 {
     public class ExportData
     {
+        public string PersonaHtml { get; set; } = string.Empty;
         public IEnumerable<CourseModule> CourseModules { get; set; } = Enumerable.Empty<CourseModule>();
     }
-}
+}
\ No newline at end of file
diff --git a/Epsilon.Canvas.Abstractions/Service/IFileHttpService.cs b/Epsilon.Canvas.Abstractions/Service/IFileHttpService.cs
new file mode 100644
index 00000000..f5dd1635
--- /dev/null
+++ b/Epsilon.Canvas.Abstractions/Service/IFileHttpService.cs
@@ -0,0 +1,7 @@
+namespace Epsilon.Canvas.Abstractions.Service;
+
+public interface IFileHttpService
+{
+    Task<byte[]> GetFileByteArray(string url);
+    HttpClient Client { get; }
+}
\ No newline at end of file
diff --git a/Epsilon.Canvas.Abstractions/Service/IPageHttpService.cs b/Epsilon.Canvas.Abstractions/Service/IPageHttpService.cs
index 9c22d8cf..a9390d6c 100644
--- a/Epsilon.Canvas.Abstractions/Service/IPageHttpService.cs
+++ b/Epsilon.Canvas.Abstractions/Service/IPageHttpService.cs
@@ -4,7 +4,7 @@ namespace Epsilon.Canvas.Abstractions.Service;
 
 public interface IPageHttpService
 {
-    Task<Page?> GetPageByName(int courseId, string pageName);
+    Task<string> GetPageByName(int courseId, string pageName);
 
     Task<IEnumerable<Page>?> GetAll(int courseId, IEnumerable<string> include);
 }
\ No newline at end of file
diff --git a/Epsilon.Canvas/CanvasModuleCollectionFetcher.cs b/Epsilon.Canvas/CanvasModuleCollectionFetcher.cs
index 136469dd..b38db8ae 100644
--- a/Epsilon.Canvas/CanvasModuleCollectionFetcher.cs
+++ b/Epsilon.Canvas/CanvasModuleCollectionFetcher.cs
@@ -10,25 +10,22 @@ public class CanvasModuleCollectionFetcher : ICanvasModuleCollectionFetcher
 {
     private readonly IModuleHttpService _moduleService;
     private readonly IOutcomeHttpService _outcomeService;
-    private readonly IPageHttpService _pageService;
 
     public CanvasModuleCollectionFetcher(
         ILogger<CanvasModuleCollectionFetcher> logger,
         IModuleHttpService moduleService,
-        IOutcomeHttpService outcomeService,
-        IPageHttpService pageService
+        IOutcomeHttpService outcomeService
     )
     {
         _moduleService = moduleService;
         _outcomeService = outcomeService;
-        _pageService = pageService;
     }
 
-    public async IAsyncEnumerable<ModuleOutcomeResultCollection> GetAll(int courseId, IEnumerable<string>? allowedModules)
+    public async IAsyncEnumerable<ModuleOutcomeResultCollection> GetAll(int courseId,
+        IEnumerable<string>? allowedModules)
     {
-        var pages = await _pageService.GetPageByName(courseId,"front_page");
-        var response = await _outcomeService.GetResults(courseId, new[] { "outcomes", "alignments" });
-        var modules = await _moduleService.GetAll(courseId, new[] { "items" });
+        var response = await _outcomeService.GetResults(courseId, new[] {"outcomes", "alignments"});
+        var modules = await _moduleService.GetAll(courseId, new[] {"items"});
 
         Debug.Assert(response != null, nameof(response) + " != null");
         Debug.Assert(modules != null, nameof(modules) + " != null");
@@ -45,7 +42,7 @@ public async IAsyncEnumerable<ModuleOutcomeResultCollection> GetAll(int courseId
 
                 yield return new ModuleOutcomeResultCollection(module, new OutcomeResultCollection(
                     response.OutcomeResults.Where(r => ids.Contains(r.Link.Alignment)),
-                    response.Links with { Alignments = response.Links.Alignments.Where(a => ids.Contains(a.Id)) }
+                    response.Links with {Alignments = response.Links.Alignments.Where(a => ids.Contains(a.Id))}
                 ));
             }
         }
diff --git a/Epsilon.Canvas/CanvasServiceCollectionExtensions.cs b/Epsilon.Canvas/CanvasServiceCollectionExtensions.cs
index 04989038..31b6664c 100644
--- a/Epsilon.Canvas/CanvasServiceCollectionExtensions.cs
+++ b/Epsilon.Canvas/CanvasServiceCollectionExtensions.cs
@@ -24,7 +24,8 @@ public static IServiceCollection AddCanvas(this IServiceCollection services, ICo
                 var settings = provider.GetRequiredService<IOptions<CanvasSettings>>().Value;
 
                 client.BaseAddress = settings.ApiUrl;
-                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", settings.AccessToken);
+                client.DefaultRequestHeaders.Authorization =
+                    new AuthenticationHeaderValue("Bearer", settings.AccessToken);
             });
 
         services.AddHttpClient<IPaginatorHttpService, PaginatorHttpService>(CanvasHttpClient);
@@ -33,9 +34,10 @@ public static IServiceCollection AddCanvas(this IServiceCollection services, ICo
         services.AddHttpClient<IOutcomeHttpService, OutcomeHttpService>(CanvasHttpClient);
         services.AddHttpClient<ISubmissionHttpService, SubmissionHttpService>(CanvasHttpClient);
         services.AddHttpClient<IPageHttpService, PageHttpService>(CanvasHttpClient);
+        services.AddHttpClient<IFileHttpService, FileHttpService>(CanvasHttpClient);
 
         services.AddScoped<ILinkHeaderConverter, LinkHeaderConverter>();
-        
+
         services.AddScoped<ICanvasModuleCollectionFetcher, CanvasModuleCollectionFetcher>();
 
         return services;
diff --git a/Epsilon.Canvas/Epsilon.Canvas.csproj b/Epsilon.Canvas/Epsilon.Canvas.csproj
index 5077c19b..69096ea1 100644
--- a/Epsilon.Canvas/Epsilon.Canvas.csproj
+++ b/Epsilon.Canvas/Epsilon.Canvas.csproj
@@ -16,7 +16,6 @@
       <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
       <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
       <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
-      <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
       <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
       <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
     </ItemGroup>
diff --git a/Epsilon.Canvas/Service/FileHttpService.cs b/Epsilon.Canvas/Service/FileHttpService.cs
new file mode 100644
index 00000000..37ad63d1
--- /dev/null
+++ b/Epsilon.Canvas/Service/FileHttpService.cs
@@ -0,0 +1,27 @@
+using System.Net;
+using System.Net.Http.Json;
+using Epsilon.Abstractions.Http;
+using Epsilon.Canvas.Abstractions.Converter;
+using Epsilon.Canvas.Abstractions.Model;
+using Epsilon.Canvas.Abstractions.Service;
+using HtmlAgilityPack;
+
+namespace Epsilon.Canvas.Service;
+
+public class FileHttpService : HttpService, IFileHttpService
+{
+    private readonly ILinkHeaderConverter _headerConverter;
+
+    public FileHttpService(HttpClient client, ILinkHeaderConverter headerConverter) : base(client)
+    {
+        _headerConverter = headerConverter;
+    }
+
+    public async Task<byte[]> GetFileByteArray(string url)
+    {
+        if (url == null)
+            throw new ArgumentNullException(nameof(url));
+
+        return await Client.GetByteArrayAsync(url);
+    }
+}
\ No newline at end of file
diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index 92f566c5..d524d3a2 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -4,7 +4,6 @@
 using Epsilon.Canvas.Abstractions.Converter;
 using Epsilon.Canvas.Abstractions.Model;
 using Epsilon.Canvas.Abstractions.Service;
-using HtmlAgilityPack;
 
 namespace Epsilon.Canvas.Service;
 
@@ -17,32 +16,22 @@ public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter)
         _headerConverter = headerConverter;
     }
 
-    public async Task<Page?> GetPageByName(int courseId, string pageName)
+    public async Task<string?> GetPageByName(int courseId, string pageName)
     {
         var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
-        var response = await Client.SendAsync(request); 
-        
-        if(response.StatusCode == HttpStatusCode.NotFound)
-        {
-            throw new Exception("Page not found");
-        }
-        
-        if(response.StatusCode == HttpStatusCode.Forbidden)
-        {
-            throw new Exception("Page forbidden");
-        }
-        
-        if(response.StatusCode == HttpStatusCode.Unauthorized)
-        {
-            throw new Exception("Page unauthorized");
-        }
+        var response = await Client.SendAsync(request);
+
+        if (response.StatusCode == HttpStatusCode.NotFound)
+            throw new Exception("Not found");
+
+        if (response.StatusCode == HttpStatusCode.Forbidden)
+            throw new Exception("Forbidden");
+
+        if (response.StatusCode == HttpStatusCode.Unauthorized)
+            throw new Exception("Unauthorized");
 
         if (response.StatusCode == HttpStatusCode.OK)
-        {
-            var page = await response.Content.ReadFromJsonAsync<Page>();
-            this.GetPageImages(page);
-            return page;
-        }
+            return (await response.Content.ReadFromJsonAsync<Page>()).Body;
 
         return null;
     }
@@ -51,48 +40,19 @@ public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter)
     {
         var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
         var response = await Client.SendAsync(request);
-        
-        if(response.StatusCode == HttpStatusCode.NotFound)
-        {
+
+        if (response.StatusCode == HttpStatusCode.NotFound)
             throw new Exception("Not found");
-        }
-        
-        if(response.StatusCode == HttpStatusCode.Forbidden)
-        {
+
+        if (response.StatusCode == HttpStatusCode.Forbidden)
             throw new Exception("Forbidden");
-        }
-        
-        if(response.StatusCode == HttpStatusCode.Unauthorized)
-        {
+
+        if (response.StatusCode == HttpStatusCode.Unauthorized)
             throw new Exception("Unauthorized");
-        }
 
         if (response.StatusCode == HttpStatusCode.OK)
-        {
             return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
-        }
-        
-        return null;
-    }
-
-    private async Task<string> GetPageImages(Page page)
-    {
-        var htmlDoc = new HtmlDocument();
-        htmlDoc.LoadHtml(page.Body);
-        
-        foreach (var node in htmlDoc.DocumentNode.SelectNodes("//img"))
-        {
-            string imageSrc = htmlDoc.DocumentNode
-                .SelectNodes("//img")
-                .First()
-                .Attributes["src"].Value;
-
-            byte[] imageBytes = await Client.GetByteArrayAsync(imageSrc);
-            string imageBase64 = Convert.ToBase64String(imageBytes);
 
-            node.SetAttributeValue("src", $"data:image/jpeg;base64,{imageBase64}");
-        }
-
-        return htmlDoc.DocumentNode.WriteTo();
+        return null;
     }
-}
+}
\ No newline at end of file
diff --git a/Epsilon.Cli/Startup.cs b/Epsilon.Cli/Startup.cs
index e026bc23..37abc68c 100644
--- a/Epsilon.Cli/Startup.cs
+++ b/Epsilon.Cli/Startup.cs
@@ -15,7 +15,6 @@ public class Startup : IHostedService
     private readonly IHostApplicationLifetime _lifetime;
     private readonly ExportOptions _exportOptions;
     private readonly CanvasSettings _canvasSettings;
-    private readonly ICanvasModuleCollectionFetcher _collectionFetcher;
     private readonly IModuleExporterCollection _exporterCollection;
     private readonly IExportDataPackager _exporterDataCollection;
 
@@ -24,7 +23,6 @@ public Startup(
         IHostApplicationLifetime lifetime,
         IOptions<CanvasSettings> canvasSettings,
         IOptions<ExportOptions> exportSettings,
-        ICanvasModuleCollectionFetcher collectionFetcher,
         IModuleExporterCollection exporterCollection,
         IExportDataPackager exporterDataCollection)
     {
@@ -32,7 +30,6 @@ public Startup(
         _canvasSettings = canvasSettings.Value;
         _exportOptions = exportSettings.Value;
         _lifetime = lifetime;
-        _collectionFetcher = collectionFetcher;
         _exporterCollection = exporterCollection;
         _exporterDataCollection = exporterDataCollection;
     }
@@ -65,12 +62,11 @@ private async Task ExecuteAsync()
                 return;
             }
 
-            var modules = _exportOptions.Modules?.Split(",");
             _logger.LogInformation("Targeting Canvas course: {CourseId}, at {Url}", _canvasSettings.CourseId,
                 _canvasSettings.ApiUrl);
             _logger.LogInformation("Downloading results, this may take a few seconds...");
-            var items = _collectionFetcher.GetAll(_canvasSettings.CourseId, modules);
-            var formattedItems = await _exporterDataCollection.GetExportData(items);
+
+            var formattedItems = await _exporterDataCollection.GetExportData();
 
             var formats = _exportOptions.Formats.Split(",");
             var exporters = _exporterCollection.DetermineExporters(formats).ToArray();
@@ -82,7 +78,9 @@ private async Task ExecuteAsync()
                 _logger.LogInformation("Exporting to {Format} using {Exporter}...", format, exporter.GetType().Name);
                 var stream = await exporter.Export(formattedItems, format);
 
-                await using var fileStream = new FileStream($"{_exportOptions.FormattedOutputName}.{exporter.FileExtension}", FileMode.Create, FileAccess.Write);
+                await using var fileStream =
+                    new FileStream($"{_exportOptions.FormattedOutputName}.{exporter.FileExtension}", FileMode.Create,
+                        FileAccess.Write);
 
                 stream.Position = 0; // Reset position to zero to prepare for copy
                 await stream.CopyToAsync(fileStream);
diff --git a/Epsilon/Epsilon.csproj b/Epsilon/Epsilon.csproj
index 56da7f5e..120f0714 100644
--- a/Epsilon/Epsilon.csproj
+++ b/Epsilon/Epsilon.csproj
@@ -17,7 +17,6 @@
         <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
         <PackageReference Include="System.Linq.Async" Version="6.0.1" />
         <PackageReference Include="DocumentFormat.OpenXml" Version="2.18.0" />
-        <PackageReference Include="System.Linq.Async" Version="6.0.1" />
     </ItemGroup>
 
 </Project>
diff --git a/Epsilon/Export/ExportDataPackager.cs b/Epsilon/Export/ExportDataPackager.cs
index a9f94d7d..9e75afd7 100644
--- a/Epsilon/Export/ExportDataPackager.cs
+++ b/Epsilon/Export/ExportDataPackager.cs
@@ -1,19 +1,42 @@
 using System.Diagnostics;
 using Epsilon.Abstractions.Export;
 using Epsilon.Abstractions.Model;
-using Epsilon.Canvas.Abstractions.Model;
+using Epsilon.Canvas;
+using Epsilon.Canvas.Abstractions;
+using Epsilon.Canvas.Abstractions.Service;
+using Microsoft.Extensions.Options;
 
 namespace Epsilon.Export;
 
 public class ExportDataPackager : IExportDataPackager
 {
-    public async Task<ExportData> GetExportData(IAsyncEnumerable<ModuleOutcomeResultCollection> data)
+    private readonly ICanvasModuleCollectionFetcher _moduleCollectionFetcher;
+    private readonly IPageHttpService _pageService;
+    private readonly ExportOptions _exportOptions;
+    private readonly CanvasSettings _canvasSettings;
+
+    public ExportDataPackager(ICanvasModuleCollectionFetcher moduleCollectionFetcher, IPageHttpService pageService,
+        IOptions<CanvasSettings> canvasSettings, IOptions<ExportOptions> exportOptions)
+    {
+        _moduleCollectionFetcher = moduleCollectionFetcher;
+        _pageService = pageService;
+        _exportOptions = exportOptions.Value;
+        _canvasSettings = canvasSettings.Value;
+    }
+
+    public async Task<ExportData> GetExportData()
     {
+        var modules = _exportOptions.Modules?.Split(",");
+        var courseId = _canvasSettings.CourseId;
+
+        var moduleOutcomes = _moduleCollectionFetcher.GetAll(courseId, modules);
+        var personaHtml = await _pageService.GetPageByName(courseId, "front_page");
+
         var output = new List<CourseModule>();
 
-        await foreach (var item in data.Where(m => m.Collection.OutcomeResults.Any()))
+        await foreach (var item in moduleOutcomes.Where(m => m.Collection.OutcomeResults.Any()))
         {
-            var module = new CourseModule { Name = item.Module.Name };
+            var module = new CourseModule {Name = item.Module.Name};
             var links = item.Collection.Links;
 
             Debug.Assert(links != null, nameof(links) + " != null");
@@ -56,6 +79,6 @@ public async Task<ExportData> GetExportData(IAsyncEnumerable<ModuleOutcomeResult
             output.Add(module);
         }
 
-        return new ExportData { CourseModules = output };
+        return new ExportData {CourseModules = output, PersonaHtml = personaHtml};
     }
 }
\ No newline at end of file
diff --git a/Epsilon/Export/Exporters/WordModuleExporter.cs b/Epsilon/Export/Exporters/WordModuleExporter.cs
index f9a36be3..fa50d377 100644
--- a/Epsilon/Export/Exporters/WordModuleExporter.cs
+++ b/Epsilon/Export/Exporters/WordModuleExporter.cs
@@ -4,18 +4,20 @@
 using DocumentFormat.OpenXml.Wordprocessing;
 using Epsilon.Abstractions.Export;
 using Epsilon.Abstractions.Model;
+using Epsilon.Canvas.Abstractions.Service;
+using HtmlAgilityPack;
 
 namespace Epsilon.Export.Exporters;
 
 public class WordModuleExporter : ICanvasModuleExporter
 {
     private static readonly TableBorders s_defaultBorders = new(
-        new TopBorder { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 3 },
-        new BottomBorder { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 3 },
-        new LeftBorder { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 3 },
-        new RightBorder { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 3 },
-        new InsideHorizontalBorder { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 6 },
-        new InsideVerticalBorder { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 6 }
+        new TopBorder {Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 3},
+        new BottomBorder {Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 3},
+        new LeftBorder {Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 3},
+        new RightBorder {Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 3},
+        new InsideHorizontalBorder {Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 6},
+        new InsideVerticalBorder {Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 6}
     );
 
     private static readonly TableProperties s_defaultTableProperties = new(s_defaultBorders);
@@ -26,10 +28,15 @@ public class WordModuleExporter : ICanvasModuleExporter
         CreateTextCell("Score")
     );
 
-    public IEnumerable<string> Formats { get; } = new[] { "word", "docx" };
-
+    private readonly IFileHttpService _fileService;
+    public IEnumerable<string> Formats { get; } = new[] {"word", "docx"};
     public string FileExtension => "docx";
 
+    public WordModuleExporter(IFileHttpService fileService)
+    {
+        _fileService = fileService;
+    }
+
     public async Task<Stream> Export(ExportData data, string format)
     {
         var stream = new MemoryStream();
@@ -42,6 +49,25 @@ public async Task<Stream> Export(ExportData data, string format)
         var cellValueBuilder = new StringBuilder();
         var cellValueOutComeResultsBuilder = new StringBuilder();
 
+        var altChunkId = "HomePage";
+
+        var personaHTML = new HtmlDocument();
+        personaHTML.LoadHtml(data.PersonaHtml);
+
+        var ms = new MemoryStream(new UTF8Encoding(true).GetPreamble()
+            .Concat(Encoding.UTF8.GetBytes($"<html>{personaHTML.Text}</html>")).ToArray());
+
+        var formatImportPart =
+            document.MainDocumentPart.AddAlternativeFormatImportPart(
+                AlternativeFormatImportPartType.Html, altChunkId);
+
+        formatImportPart.FeedData(ms);
+        AltChunk altChunk = new AltChunk();
+        altChunk.Id = altChunkId;
+
+        body?.Append(altChunk);
+        body?.Append(new Paragraph(new Run(new Break() {Type = BreakValues.Page})));
+
         foreach (var module in data.CourseModules)
         {
             body?.Append(CreateText(module.Name));
@@ -86,6 +112,28 @@ public async Task<Stream> Export(ExportData data, string format)
 
     private static TableCell CreateTextCell(string text) => new(
         CreateText(text),
-        new TableCellProperties(new TableCellWidth { Type = TableWidthUnitValues.Auto })
+        new TableCellProperties(new TableCellWidth {Type = TableWidthUnitValues.Auto})
     );
+
+    private async Task ReplaceSrcWithBase64String(HtmlDocument htmlDoc)
+    {
+        if (htmlDoc == null)
+            throw new ArgumentNullException(nameof(htmlDoc));
+
+        foreach (var node in htmlDoc.DocumentNode.SelectNodes("//img"))
+        {
+            var imageSrc = node
+                .SelectNodes("//img")
+                .First()
+                .Attributes["src"].Value;
+
+            if (imageSrc == null)
+                throw new ArgumentNullException(nameof(imageSrc));
+
+            var imageBytes = await _fileService.GetFileByteArray(imageSrc);
+            var imageBase64 = Convert.ToBase64String(imageBytes);
+
+            node.SetAttributeValue("src", $"data:image/jpeg;base64,{imageBase64}");
+        }
+    }
 }
\ No newline at end of file
diff --git a/Epsilon/Extensions/CoreServiceCollectionExtensions.cs b/Epsilon/Extensions/CoreServiceCollectionExtensions.cs
index 63515668..493a4f34 100644
--- a/Epsilon/Extensions/CoreServiceCollectionExtensions.cs
+++ b/Epsilon/Extensions/CoreServiceCollectionExtensions.cs
@@ -1,5 +1,7 @@
 using Epsilon.Abstractions.Export;
 using Epsilon.Canvas;
+using Epsilon.Canvas.Abstractions.Service;
+using Epsilon.Canvas.Service;
 using Epsilon.Export;
 using Epsilon.Export.Exporters;
 using Microsoft.Extensions.Configuration;

From c6f351539334a377f462288c319708d7d56b4708 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Mon, 27 Mar 2023 15:39:42 +0200
Subject: [PATCH 06/15] Added dispose

---
 Epsilon.Canvas/Service/PageHttpService.cs      | 2 ++
 Epsilon/Export/Exporters/WordModuleExporter.cs | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index d524d3a2..f8b780bf 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -20,6 +20,7 @@ public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter)
     {
         var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
         var response = await Client.SendAsync(request);
+        request.Dispose();
 
         if (response.StatusCode == HttpStatusCode.NotFound)
             throw new Exception("Not found");
@@ -40,6 +41,7 @@ public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter)
     {
         var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
         var response = await Client.SendAsync(request);
+        request.Dispose();
 
         if (response.StatusCode == HttpStatusCode.NotFound)
             throw new Exception("Not found");
diff --git a/Epsilon/Export/Exporters/WordModuleExporter.cs b/Epsilon/Export/Exporters/WordModuleExporter.cs
index fa50d377..8dad919b 100644
--- a/Epsilon/Export/Exporters/WordModuleExporter.cs
+++ b/Epsilon/Export/Exporters/WordModuleExporter.cs
@@ -54,6 +54,7 @@ public async Task<Stream> Export(ExportData data, string format)
         var personaHTML = new HtmlDocument();
         personaHTML.LoadHtml(data.PersonaHtml);
 
+
         var ms = new MemoryStream(new UTF8Encoding(true).GetPreamble()
             .Concat(Encoding.UTF8.GetBytes($"<html>{personaHTML.Text}</html>")).ToArray());
 
@@ -66,6 +67,7 @@ public async Task<Stream> Export(ExportData data, string format)
         altChunk.Id = altChunkId;
 
         body?.Append(altChunk);
+        ms.DisposeAsync();
         body?.Append(new Paragraph(new Run(new Break() {Type = BreakValues.Page})));
 
         foreach (var module in data.CourseModules)

From e7c57c8dd43782069860013d21af420f4812b059 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Mon, 27 Mar 2023 17:06:40 +0200
Subject: [PATCH 07/15] Removed redundant code and renamed method

---
 Epsilon.Canvas/Service/FileHttpService.cs     |  8 +------
 Epsilon.Canvas/Service/PageHttpService.cs     | 23 +------------------
 .../Export/Exporters/WordModuleExporter.cs    |  5 +---
 3 files changed, 3 insertions(+), 33 deletions(-)

diff --git a/Epsilon.Canvas/Service/FileHttpService.cs b/Epsilon.Canvas/Service/FileHttpService.cs
index 37ad63d1..9208a74d 100644
--- a/Epsilon.Canvas/Service/FileHttpService.cs
+++ b/Epsilon.Canvas/Service/FileHttpService.cs
@@ -10,18 +10,12 @@ namespace Epsilon.Canvas.Service;
 
 public class FileHttpService : HttpService, IFileHttpService
 {
-    private readonly ILinkHeaderConverter _headerConverter;
-
-    public FileHttpService(HttpClient client, ILinkHeaderConverter headerConverter) : base(client)
+    public FileHttpService(HttpClient client) : base(client)
     {
-        _headerConverter = headerConverter;
     }
 
     public async Task<byte[]> GetFileByteArray(string url)
     {
-        if (url == null)
-            throw new ArgumentNullException(nameof(url));
-
         return await Client.GetByteArrayAsync(url);
     }
 }
\ No newline at end of file
diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index f8b780bf..69dd1113 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -9,11 +9,8 @@ namespace Epsilon.Canvas.Service;
 
 public class PageHttpService : HttpService, IPageHttpService
 {
-    private readonly ILinkHeaderConverter _headerConverter;
-
-    public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter) : base(client)
+    public PageHttpService(HttpClient client) : base(client)
     {
-        _headerConverter = headerConverter;
     }
 
     public async Task<string?> GetPageByName(int courseId, string pageName)
@@ -22,15 +19,6 @@ public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter)
         var response = await Client.SendAsync(request);
         request.Dispose();
 
-        if (response.StatusCode == HttpStatusCode.NotFound)
-            throw new Exception("Not found");
-
-        if (response.StatusCode == HttpStatusCode.Forbidden)
-            throw new Exception("Forbidden");
-
-        if (response.StatusCode == HttpStatusCode.Unauthorized)
-            throw new Exception("Unauthorized");
-
         if (response.StatusCode == HttpStatusCode.OK)
             return (await response.Content.ReadFromJsonAsync<Page>()).Body;
 
@@ -43,15 +31,6 @@ public PageHttpService(HttpClient client, ILinkHeaderConverter headerConverter)
         var response = await Client.SendAsync(request);
         request.Dispose();
 
-        if (response.StatusCode == HttpStatusCode.NotFound)
-            throw new Exception("Not found");
-
-        if (response.StatusCode == HttpStatusCode.Forbidden)
-            throw new Exception("Forbidden");
-
-        if (response.StatusCode == HttpStatusCode.Unauthorized)
-            throw new Exception("Unauthorized");
-
         if (response.StatusCode == HttpStatusCode.OK)
             return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
 
diff --git a/Epsilon/Export/Exporters/WordModuleExporter.cs b/Epsilon/Export/Exporters/WordModuleExporter.cs
index 8dad919b..64f1689b 100644
--- a/Epsilon/Export/Exporters/WordModuleExporter.cs
+++ b/Epsilon/Export/Exporters/WordModuleExporter.cs
@@ -117,11 +117,8 @@ public async Task<Stream> Export(ExportData data, string format)
         new TableCellProperties(new TableCellWidth {Type = TableWidthUnitValues.Auto})
     );
 
-    private async Task ReplaceSrcWithBase64String(HtmlDocument htmlDoc)
+    private async Task ReplaceImageSrcWithBase64String(HtmlDocument htmlDoc)
     {
-        if (htmlDoc == null)
-            throw new ArgumentNullException(nameof(htmlDoc));
-
         foreach (var node in htmlDoc.DocumentNode.SelectNodes("//img"))
         {
             var imageSrc = node

From 4e7e5e798ca87434f822117eb406026aae3826f6 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Mon, 27 Mar 2023 17:24:55 +0200
Subject: [PATCH 08/15] Wrapped HttpRequestMessage in try catch block to ensure
 that the request gets disposed even when an error is thrown.

---
 Epsilon.Canvas/Service/FileHttpService.cs | 17 +++++++-
 Epsilon.Canvas/Service/PageHttpService.cs | 48 +++++++++++++++++------
 2 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/Epsilon.Canvas/Service/FileHttpService.cs b/Epsilon.Canvas/Service/FileHttpService.cs
index 9208a74d..1a17eb4e 100644
--- a/Epsilon.Canvas/Service/FileHttpService.cs
+++ b/Epsilon.Canvas/Service/FileHttpService.cs
@@ -16,6 +16,21 @@ public FileHttpService(HttpClient client) : base(client)
 
     public async Task<byte[]> GetFileByteArray(string url)
     {
-        return await Client.GetByteArrayAsync(url);
+        var client = new HttpClient();
+        var response = new byte[0];
+        try
+        {
+            response = await Client.GetByteArrayAsync(url);
+        }
+        catch (Exception e)
+        {
+            throw new Exception($"Error in GetFileByteArray: {e.Message}");
+        }
+        finally
+        {
+            client?.Dispose();
+        }
+
+        return response;
     }
 }
\ No newline at end of file
diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index 69dd1113..13d4a35f 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -1,5 +1,6 @@
 using System.Net;
 using System.Net.Http.Json;
+using System.Runtime.InteropServices;
 using Epsilon.Abstractions.Http;
 using Epsilon.Canvas.Abstractions.Converter;
 using Epsilon.Canvas.Abstractions.Model;
@@ -15,24 +16,47 @@ public PageHttpService(HttpClient client) : base(client)
 
     public async Task<string?> GetPageByName(int courseId, string pageName)
     {
-        var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
-        var response = await Client.SendAsync(request);
-        request.Dispose();
-
-        if (response.StatusCode == HttpStatusCode.OK)
-            return (await response.Content.ReadFromJsonAsync<Page>()).Body;
+        var request = new HttpRequestMessage();
+        try
+        {
+            request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
+            var response = await Client.SendAsync(request);
+            request.Dispose();
+
+            if (response.StatusCode == HttpStatusCode.OK)
+                return (await response.Content.ReadFromJsonAsync<Page>()).Body;
+        }
+        catch (Exception e)
+        {
+            throw new Exception($"Error in GetPageByName: {e.Message}");
+        }
+        finally
+        {
+            request?.Dispose();
+        }
 
         return null;
     }
 
     public async Task<IEnumerable<Page>?> GetAll(int courseId, IEnumerable<string> include)
     {
-        var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
-        var response = await Client.SendAsync(request);
-        request.Dispose();
-
-        if (response.StatusCode == HttpStatusCode.OK)
-            return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
+        var request = new HttpRequestMessage();
+        try
+        {
+            request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
+            var response = await Client.SendAsync(request);
+
+            if (response.StatusCode == HttpStatusCode.OK)
+                return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
+        }
+        catch (Exception e)
+        {
+            throw new Exception($"Error in GetAll: {e.Message}");
+        }
+        finally
+        {
+            request?.Dispose();
+        }
 
         return null;
     }

From 4e09649acaf9d7f8bb4cb9520e9b4b67cb1901c0 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Mon, 27 Mar 2023 17:46:39 +0200
Subject: [PATCH 09/15] Changed HttpClient to protected, renamed HTMLUrl to
 HtmlUrl to match code convention, modified Httpservices and changed
 FileService to iEnumerable<byte>.

---
 Epsilon.Abstractions/Http/HttpService.cs                | 2 +-
 Epsilon.Abstractions/Model/CoursePage.cs                | 2 +-
 Epsilon.Canvas.Abstractions/Service/IFileHttpService.cs | 3 +--
 Epsilon.Canvas/Service/FileHttpService.cs               | 2 +-
 Epsilon.Canvas/Service/PageHttpService.cs               | 2 --
 Epsilon/Export/Exporters/WordModuleExporter.cs          | 2 +-
 6 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/Epsilon.Abstractions/Http/HttpService.cs b/Epsilon.Abstractions/Http/HttpService.cs
index 0c803fa2..c5643701 100644
--- a/Epsilon.Abstractions/Http/HttpService.cs
+++ b/Epsilon.Abstractions/Http/HttpService.cs
@@ -4,5 +4,5 @@ public abstract class HttpService
 {
     protected HttpService(HttpClient client) => Client = client;
 
-    public HttpClient Client { get; }
+    protected HttpClient Client { get; }
 }
\ No newline at end of file
diff --git a/Epsilon.Abstractions/Model/CoursePage.cs b/Epsilon.Abstractions/Model/CoursePage.cs
index dc68fac2..50ab8728 100644
--- a/Epsilon.Abstractions/Model/CoursePage.cs
+++ b/Epsilon.Abstractions/Model/CoursePage.cs
@@ -11,7 +11,7 @@ public class CoursePage
     public string Published { get; set; }
     public string HideFromStudents { get; set; }
     public string FrontPage { get; set; }
-    public string HTMLUrl { get; set; }
+    public string HtmlUrl { get; set; }
     public string TodoDate { get; set; }
     public string PublishedAt { get; set; }
     public string UpdatedAt { get; set; }
diff --git a/Epsilon.Canvas.Abstractions/Service/IFileHttpService.cs b/Epsilon.Canvas.Abstractions/Service/IFileHttpService.cs
index f5dd1635..8afbe732 100644
--- a/Epsilon.Canvas.Abstractions/Service/IFileHttpService.cs
+++ b/Epsilon.Canvas.Abstractions/Service/IFileHttpService.cs
@@ -2,6 +2,5 @@ namespace Epsilon.Canvas.Abstractions.Service;
 
 public interface IFileHttpService
 {
-    Task<byte[]> GetFileByteArray(string url);
-    HttpClient Client { get; }
+    Task<IEnumerable<byte>?> GetFileByteArray(string url);
 }
\ No newline at end of file
diff --git a/Epsilon.Canvas/Service/FileHttpService.cs b/Epsilon.Canvas/Service/FileHttpService.cs
index 1a17eb4e..60ef8bfe 100644
--- a/Epsilon.Canvas/Service/FileHttpService.cs
+++ b/Epsilon.Canvas/Service/FileHttpService.cs
@@ -14,7 +14,7 @@ public FileHttpService(HttpClient client) : base(client)
     {
     }
 
-    public async Task<byte[]> GetFileByteArray(string url)
+    public async Task<IEnumerable<byte>?> GetFileByteArray(string url)
     {
         var client = new HttpClient();
         var response = new byte[0];
diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index 13d4a35f..0c6f8e2b 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -1,8 +1,6 @@
 using System.Net;
 using System.Net.Http.Json;
-using System.Runtime.InteropServices;
 using Epsilon.Abstractions.Http;
-using Epsilon.Canvas.Abstractions.Converter;
 using Epsilon.Canvas.Abstractions.Model;
 using Epsilon.Canvas.Abstractions.Service;
 
diff --git a/Epsilon/Export/Exporters/WordModuleExporter.cs b/Epsilon/Export/Exporters/WordModuleExporter.cs
index 64f1689b..29c4bfbc 100644
--- a/Epsilon/Export/Exporters/WordModuleExporter.cs
+++ b/Epsilon/Export/Exporters/WordModuleExporter.cs
@@ -130,7 +130,7 @@ private async Task ReplaceImageSrcWithBase64String(HtmlDocument htmlDoc)
                 throw new ArgumentNullException(nameof(imageSrc));
 
             var imageBytes = await _fileService.GetFileByteArray(imageSrc);
-            var imageBase64 = Convert.ToBase64String(imageBytes);
+            var imageBase64 = Convert.ToBase64String(imageBytes.ToArray());
 
             node.SetAttributeValue("src", $"data:image/jpeg;base64,{imageBase64}");
         }

From 7d0ae5661bec4c1dd8cd03650e8a3f3b4dd69ba7 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Mon, 27 Mar 2023 18:03:59 +0200
Subject: [PATCH 10/15] Changed to using

---
 Epsilon.Canvas/Service/FileHttpService.cs | 13 +++-----
 Epsilon.Canvas/Service/PageHttpService.cs | 37 ++++++++++-------------
 2 files changed, 20 insertions(+), 30 deletions(-)

diff --git a/Epsilon.Canvas/Service/FileHttpService.cs b/Epsilon.Canvas/Service/FileHttpService.cs
index 60ef8bfe..866a493c 100644
--- a/Epsilon.Canvas/Service/FileHttpService.cs
+++ b/Epsilon.Canvas/Service/FileHttpService.cs
@@ -16,21 +16,16 @@ public FileHttpService(HttpClient client) : base(client)
 
     public async Task<IEnumerable<byte>?> GetFileByteArray(string url)
     {
-        var client = new HttpClient();
-        var response = new byte[0];
         try
         {
-            response = await Client.GetByteArrayAsync(url);
+            using var httpClient = new HttpClient();
+            {
+                return await Client.GetByteArrayAsync(url);
+            }
         }
         catch (Exception e)
         {
             throw new Exception($"Error in GetFileByteArray: {e.Message}");
         }
-        finally
-        {
-            client?.Dispose();
-        }
-
-        return response;
     }
 }
\ No newline at end of file
diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index 0c6f8e2b..c58d4761 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -14,47 +14,42 @@ public PageHttpService(HttpClient client) : base(client)
 
     public async Task<string?> GetPageByName(int courseId, string pageName)
     {
-        var request = new HttpRequestMessage();
         try
         {
-            request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
-            var response = await Client.SendAsync(request);
-            request.Dispose();
-
-            if (response.StatusCode == HttpStatusCode.OK)
-                return (await response.Content.ReadFromJsonAsync<Page>()).Body;
+            using var httpClient = new HttpClient();
+            {
+                var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
+                var response = await httpClient.SendAsync(request);
+
+                if (response.StatusCode == HttpStatusCode.OK)
+                    return (await response.Content.ReadFromJsonAsync<Page>()).Body;
+            }
         }
         catch (Exception e)
         {
             throw new Exception($"Error in GetPageByName: {e.Message}");
         }
-        finally
-        {
-            request?.Dispose();
-        }
 
         return null;
     }
 
     public async Task<IEnumerable<Page>?> GetAll(int courseId, IEnumerable<string> include)
     {
-        var request = new HttpRequestMessage();
         try
         {
-            request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
-            var response = await Client.SendAsync(request);
-
-            if (response.StatusCode == HttpStatusCode.OK)
-                return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
+            using var httpClient = new HttpClient();
+            {
+                var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
+                var response = await Client.SendAsync(request);
+
+                if (response.StatusCode == HttpStatusCode.OK)
+                    return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
+            }
         }
         catch (Exception e)
         {
             throw new Exception($"Error in GetAll: {e.Message}");
         }
-        finally
-        {
-            request?.Dispose();
-        }
 
         return null;
     }

From 061c6f8e90361db49965f10804fb39ab4fb27670 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Mon, 27 Mar 2023 18:10:41 +0200
Subject: [PATCH 11/15] Change on previous commit

---
 Epsilon.Canvas/Service/FileHttpService.cs |  5 +----
 Epsilon.Canvas/Service/PageHttpService.cs | 14 +++++---------
 2 files changed, 6 insertions(+), 13 deletions(-)

diff --git a/Epsilon.Canvas/Service/FileHttpService.cs b/Epsilon.Canvas/Service/FileHttpService.cs
index 866a493c..91c1d3b8 100644
--- a/Epsilon.Canvas/Service/FileHttpService.cs
+++ b/Epsilon.Canvas/Service/FileHttpService.cs
@@ -18,10 +18,7 @@ public FileHttpService(HttpClient client) : base(client)
     {
         try
         {
-            using var httpClient = new HttpClient();
-            {
-                return await Client.GetByteArrayAsync(url);
-            }
+            return await Client.GetByteArrayAsync(url);
         }
         catch (Exception e)
         {
diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index c58d4761..ab21a82c 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -16,14 +16,11 @@ public PageHttpService(HttpClient client) : base(client)
     {
         try
         {
-            using var httpClient = new HttpClient();
-            {
-                var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
-                var response = await httpClient.SendAsync(request);
+            var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
+            using var response = await Client.SendAsync(request);
 
-                if (response.StatusCode == HttpStatusCode.OK)
-                    return (await response.Content.ReadFromJsonAsync<Page>()).Body;
-            }
+            if (response.StatusCode == HttpStatusCode.OK)
+                return (await response.Content.ReadFromJsonAsync<Page>()).Body;
         }
         catch (Exception e)
         {
@@ -37,10 +34,9 @@ public PageHttpService(HttpClient client) : base(client)
     {
         try
         {
-            using var httpClient = new HttpClient();
             {
                 var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
-                var response = await Client.SendAsync(request);
+                using var response = await Client.SendAsync(request);
 
                 if (response.StatusCode == HttpStatusCode.OK)
                     return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();

From eca97d2a59f0ffb470c955df06c35f57c153cab4 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Mon, 27 Mar 2023 18:19:35 +0200
Subject: [PATCH 12/15] fix

---
 Epsilon.Canvas/Service/PageHttpService.cs      | 4 ++--
 Epsilon/Export/Exporters/WordModuleExporter.cs | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index ab21a82c..8ea24d1a 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -16,7 +16,7 @@ public PageHttpService(HttpClient client) : base(client)
     {
         try
         {
-            var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
+            using var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
             using var response = await Client.SendAsync(request);
 
             if (response.StatusCode == HttpStatusCode.OK)
@@ -35,7 +35,7 @@ public PageHttpService(HttpClient client) : base(client)
         try
         {
             {
-                var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
+                using var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
                 using var response = await Client.SendAsync(request);
 
                 if (response.StatusCode == HttpStatusCode.OK)
diff --git a/Epsilon/Export/Exporters/WordModuleExporter.cs b/Epsilon/Export/Exporters/WordModuleExporter.cs
index 29c4bfbc..21f15538 100644
--- a/Epsilon/Export/Exporters/WordModuleExporter.cs
+++ b/Epsilon/Export/Exporters/WordModuleExporter.cs
@@ -55,7 +55,7 @@ public async Task<Stream> Export(ExportData data, string format)
         personaHTML.LoadHtml(data.PersonaHtml);
 
 
-        var ms = new MemoryStream(new UTF8Encoding(true).GetPreamble()
+        using var ms = new MemoryStream(new UTF8Encoding(true).GetPreamble()
             .Concat(Encoding.UTF8.GetBytes($"<html>{personaHTML.Text}</html>")).ToArray());
 
         var formatImportPart =

From a1fcadcef587f80b0a1af41ed8965728bec3b6bf Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Tue, 28 Mar 2023 17:23:43 +0200
Subject: [PATCH 13/15] Removed generic exception

---
 Epsilon.Canvas/Service/FileHttpService.cs |  9 +-----
 Epsilon.Canvas/Service/PageHttpService.cs | 36 +++++++----------------
 2 files changed, 11 insertions(+), 34 deletions(-)

diff --git a/Epsilon.Canvas/Service/FileHttpService.cs b/Epsilon.Canvas/Service/FileHttpService.cs
index 91c1d3b8..121a70cc 100644
--- a/Epsilon.Canvas/Service/FileHttpService.cs
+++ b/Epsilon.Canvas/Service/FileHttpService.cs
@@ -16,13 +16,6 @@ public FileHttpService(HttpClient client) : base(client)
 
     public async Task<IEnumerable<byte>?> GetFileByteArray(string url)
     {
-        try
-        {
-            return await Client.GetByteArrayAsync(url);
-        }
-        catch (Exception e)
-        {
-            throw new Exception($"Error in GetFileByteArray: {e.Message}");
-        }
+        return await Client.GetByteArrayAsync(url);
     }
 }
\ No newline at end of file
diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs
index 8ea24d1a..5b2bfd0f 100644
--- a/Epsilon.Canvas/Service/PageHttpService.cs
+++ b/Epsilon.Canvas/Service/PageHttpService.cs
@@ -14,38 +14,22 @@ public PageHttpService(HttpClient client) : base(client)
 
     public async Task<string?> GetPageByName(int courseId, string pageName)
     {
-        try
-        {
-            using var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
-            using var response = await Client.SendAsync(request);
-
-            if (response.StatusCode == HttpStatusCode.OK)
-                return (await response.Content.ReadFromJsonAsync<Page>()).Body;
-        }
-        catch (Exception e)
-        {
-            throw new Exception($"Error in GetPageByName: {e.Message}");
-        }
+        using var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/{pageName}");
+        using var response = await Client.SendAsync(request);
+
+        if (response.StatusCode == HttpStatusCode.OK)
+            return (await response.Content.ReadFromJsonAsync<Page>()).Body;
 
         return null;
     }
 
     public async Task<IEnumerable<Page>?> GetAll(int courseId, IEnumerable<string> include)
     {
-        try
-        {
-            {
-                using var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
-                using var response = await Client.SendAsync(request);
-
-                if (response.StatusCode == HttpStatusCode.OK)
-                    return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
-            }
-        }
-        catch (Exception e)
-        {
-            throw new Exception($"Error in GetAll: {e.Message}");
-        }
+        using var request = new HttpRequestMessage(HttpMethod.Get, $"v1/courses/{courseId}/pages");
+        using var response = await Client.SendAsync(request);
+
+        if (response.StatusCode == HttpStatusCode.OK)
+            return await response.Content.ReadFromJsonAsync<IEnumerable<Page>>();
 
         return null;
     }

From 3bfa27fb92611cb94faaf30d86ccef6ee83fca1f Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Tue, 28 Mar 2023 18:55:36 +0200
Subject: [PATCH 14/15] fix

---
 Epsilon/Export/ExportDataPackager.cs | 23 ++++++++---------------
 1 file changed, 8 insertions(+), 15 deletions(-)

diff --git a/Epsilon/Export/ExportDataPackager.cs b/Epsilon/Export/ExportDataPackager.cs
index 9e75afd7..6bad505c 100644
--- a/Epsilon/Export/ExportDataPackager.cs
+++ b/Epsilon/Export/ExportDataPackager.cs
@@ -2,7 +2,7 @@
 using Epsilon.Abstractions.Export;
 using Epsilon.Abstractions.Model;
 using Epsilon.Canvas;
-using Epsilon.Canvas.Abstractions;
+using Epsilon.Canvas.Abstractions.Model;
 using Epsilon.Canvas.Abstractions.Service;
 using Microsoft.Extensions.Options;
 
@@ -10,31 +10,24 @@ namespace Epsilon.Export;
 
 public class ExportDataPackager : IExportDataPackager
 {
-    private readonly ICanvasModuleCollectionFetcher _moduleCollectionFetcher;
     private readonly IPageHttpService _pageService;
-    private readonly ExportOptions _exportOptions;
     private readonly CanvasSettings _canvasSettings;
 
-    public ExportDataPackager(ICanvasModuleCollectionFetcher moduleCollectionFetcher, IPageHttpService pageService,
-        IOptions<CanvasSettings> canvasSettings, IOptions<ExportOptions> exportOptions)
+    public ExportDataPackager(IPageHttpService pageService,
+        IOptions<CanvasSettings> canvasSettings)
     {
-        _moduleCollectionFetcher = moduleCollectionFetcher;
         _pageService = pageService;
-        _exportOptions = exportOptions.Value;
         _canvasSettings = canvasSettings.Value;
     }
 
-    public async Task<ExportData> GetExportData()
+    public async Task<ExportData> GetExportData(IAsyncEnumerable<ModuleOutcomeResultCollection> data)
     {
-        var modules = _exportOptions.Modules?.Split(",");
         var courseId = _canvasSettings.CourseId;
-
-        var moduleOutcomes = _moduleCollectionFetcher.GetAll(courseId, modules);
         var personaHtml = await _pageService.GetPageByName(courseId, "front_page");
 
         var output = new List<CourseModule>();
 
-        await foreach (var item in moduleOutcomes.Where(m => m.Collection.OutcomeResults.Any()))
+        await foreach (var item in data.Where(m => m.Collection.OutcomeResults.Any()))
         {
             var module = new CourseModule {Name = item.Module.Name};
             var links = item.Collection.Links;
@@ -44,7 +37,7 @@ public async Task<ExportData> GetExportData()
             var alignments = links.AlignmentsDictionary;
             var outcomes = links.OutcomesDictionary;
 
-            var moduleKpis = new List<CourseOutcome>();
+            var moduleOutcomes = new List<CourseOutcome>();
 
             foreach (var (outcomeId, outcome) in outcomes)
             {
@@ -65,7 +58,7 @@ public async Task<ExportData> GetExportData()
                         })
                         .ToList();
 
-                    moduleKpis.Add(new CourseOutcome
+                    moduleOutcomes.Add(new CourseOutcome
                     {
                         Name = outcome.Title,
                         Description = outcome.ShortDescription(),
@@ -74,7 +67,7 @@ public async Task<ExportData> GetExportData()
                 }
             }
 
-            module.Kpis = moduleKpis;
+            module.Outcomes = moduleOutcomes;
 
             output.Add(module);
         }

From 3659942facf4e55a6cbdd836321868323738c914 Mon Sep 17 00:00:00 2001
From: Koen Janssen <mail@koenjanssen.dev>
Date: Tue, 28 Mar 2023 19:01:05 +0200
Subject: [PATCH 15/15] Added throw exception to suppress testing error.

---
 Epsilon/Export/ExportDataPackager.cs           | 5 +++++
 Epsilon/Export/Exporters/WordModuleExporter.cs | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/Epsilon/Export/ExportDataPackager.cs b/Epsilon/Export/ExportDataPackager.cs
index 6bad505c..9aaca7b5 100644
--- a/Epsilon/Export/ExportDataPackager.cs
+++ b/Epsilon/Export/ExportDataPackager.cs
@@ -20,6 +20,11 @@ public ExportDataPackager(IPageHttpService pageService,
         _canvasSettings = canvasSettings.Value;
     }
 
+    public ExportDataPackager()
+    {
+        throw new NotImplementedException();
+    }
+
     public async Task<ExportData> GetExportData(IAsyncEnumerable<ModuleOutcomeResultCollection> data)
     {
         var courseId = _canvasSettings.CourseId;
diff --git a/Epsilon/Export/Exporters/WordModuleExporter.cs b/Epsilon/Export/Exporters/WordModuleExporter.cs
index 1604800b..036c627a 100644
--- a/Epsilon/Export/Exporters/WordModuleExporter.cs
+++ b/Epsilon/Export/Exporters/WordModuleExporter.cs
@@ -37,6 +37,11 @@ public WordModuleExporter(IFileHttpService fileService)
         _fileService = fileService;
     }
 
+    public WordModuleExporter()
+    {
+        throw new NotImplementedException();
+    }
+
     public async Task<Stream> Export(ExportData data, string format)
     {
         var stream = new MemoryStream();