Skip to content
This repository has been archived by the owner on Sep 4, 2023. It is now read-only.

Commit

Permalink
Abstracted format logic from exporters to main logic (#62)
Browse files Browse the repository at this point in the history
* Abstracted format logic from exporters to main logic

* Remove typo

* Remove unused variable

* Renamed Course- prefix to Module, Kpi and Assignment class & Renamed Kpi to Outcome

* Renamed ModuleExporterDataCollection to ModuleDataPackager

* Resolved GetExportData List/IAsyncEnumerable type conflict

* Added generic data model for exporters

* Rename ModuleData to ExportData
  • Loading branch information
HansenSven authored and Typiqally committed May 26, 2023
1 parent a5e6270 commit 3dc1593
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 154 deletions.
4 changes: 2 additions & 2 deletions Epsilon.Abstractions/Export/ICanvasModuleExporter.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Epsilon.Canvas.Abstractions.Model;
using Epsilon.Abstractions.Model;

namespace Epsilon.Abstractions.Export;

public interface ICanvasModuleExporter : IExporter<IAsyncEnumerable<ModuleOutcomeResultCollection>>
public interface ICanvasModuleExporter : IExporter<ExportData>
{

}
9 changes: 9 additions & 0 deletions Epsilon.Abstractions/Export/IExportDataPackager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Epsilon.Abstractions.Model;
using Epsilon.Canvas.Abstractions.Model;

namespace Epsilon.Abstractions.Export;

public interface IExportDataPackager
{
public Task<ExportData> GetExportData(IAsyncEnumerable<ModuleOutcomeResultCollection> data);
}
8 changes: 8 additions & 0 deletions Epsilon.Abstractions/Model/CourseAssignment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Epsilon.Abstractions.Model
{
public class CourseAssignment
{
public string Name { get; set; }
public string Score { get; set; }
}
}
8 changes: 8 additions & 0 deletions Epsilon.Abstractions/Model/CourseModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Epsilon.Abstractions.Model
{
public class CourseModule
{
public string Name { get; set; }
public IEnumerable<CourseOutcome> Kpis { get; set; }
}
}
8 changes: 8 additions & 0 deletions Epsilon.Abstractions/Model/CourseOutcome.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Epsilon.Abstractions.Model
{
public class CourseOutcome
{
public string Name { get; set; }
public IEnumerable<CourseAssignment> Assignments { get; set; }
}
}
13 changes: 13 additions & 0 deletions Epsilon.Abstractions/Model/ExportData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Epsilon.Abstractions.Model
{
public class ExportData
{
public IEnumerable<CourseModule> CourseModules { get; set; }
}
}
10 changes: 7 additions & 3 deletions Epsilon.Cli/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,24 @@ public class Startup : IHostedService
private readonly CanvasSettings _canvasSettings;
private readonly ICanvasModuleCollectionFetcher _collectionFetcher;
private readonly IModuleExporterCollection _exporterCollection;
private readonly IExportDataPackager _exporterDataCollection;

public Startup(
ILogger<Startup> logger,
IHostApplicationLifetime lifetime,
IOptions<CanvasSettings> canvasSettings,
IOptions<ExportOptions> exportSettings,
ICanvasModuleCollectionFetcher collectionFetcher,
IModuleExporterCollection exporterCollection)
IModuleExporterCollection exporterCollection,
IExportDataPackager exporterDataCollection)
{
_logger = logger;
_canvasSettings = canvasSettings.Value;
_exportOptions = exportSettings.Value;
_lifetime = lifetime;
_collectionFetcher = collectionFetcher;
_exporterCollection = exporterCollection;
_exporterDataCollection = exporterDataCollection;
}

public Task StartAsync(CancellationToken cancellationToken)
Expand Down Expand Up @@ -61,12 +64,13 @@ private async Task ExecuteAsync()
_lifetime.StopApplication();
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 formats = _exportOptions.Formats.Split(",");
var exporters = _exporterCollection.DetermineExporters(formats).ToArray();
Expand All @@ -77,7 +81,7 @@ private async Task ExecuteAsync()
{
_logger.LogInformation("Exporting to {Format} using {Exporter}...", format, exporter.GetType().Name);
// ReSharper disable once PossibleMultipleEnumeration
await exporter.Export(items, format);
await exporter.Export(formattedItems, format);
}
}
catch (Exception ex)
Expand Down
59 changes: 59 additions & 0 deletions Epsilon/Export/ExportDataPackager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Diagnostics;
using Epsilon.Abstractions.Export;
using Epsilon.Abstractions.Model;
using Epsilon.Canvas.Abstractions.Model;

namespace Epsilon.Export;

public class ExportDataPackager : IExportDataPackager
{
public async Task<ExportData> GetExportData(IAsyncEnumerable<ModuleOutcomeResultCollection> data)
{
var output = new List<CourseModule>();

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;

Debug.Assert(links != null, nameof(links) + " != null");

var alignments = links.AlignmentsDictionary;
var outcomes = links.OutcomesDictionary;

var moduleKpis = new List<CourseOutcome>();

foreach (var (outcomeId, outcome) in outcomes)
{
var assignmentIds = item.Collection.OutcomeResults
.Where(o => o.Link.Outcome == outcomeId && o.Grade() != null)
.Select(o => o.Link.Assignment);

if (assignmentIds.Any())
{
var assignments = assignmentIds
.Select(assignmentId => new CourseAssignment
{
Name = alignments[assignmentId].Name + " | " + alignments[assignmentId].Url,
Score = item.Collection.OutcomeResults
.First(o => o.Link.Outcome == outcomeId && o.Link.Assignment == assignmentId)
.Grade() ?? "N/A"
})
.ToList();

moduleKpis.Add(new CourseOutcome
{
Name = outcome.Title + " " + outcome.ShortDescription(),
Assignments = assignments,
});
}
}

module.Kpis = moduleKpis;

output.Add(module);
}

return new ExportData { CourseModules = output };
}
}
31 changes: 13 additions & 18 deletions Epsilon/Export/Exporters/ConsoleModuleExporter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Diagnostics;
using Epsilon.Abstractions.Export;
using Epsilon.Canvas.Abstractions.Model;
using Epsilon.Abstractions.Export;
using Epsilon.Abstractions.Model;
using Microsoft.Extensions.Logging;

namespace Epsilon.Export.Exporters;
Expand All @@ -13,29 +12,25 @@ public ConsoleModuleExporter(ILogger<ConsoleModuleExporter> logger)
{
_logger = logger;
}

public IEnumerable<string> Formats { get; } = new[] { "console", "logs" };

public async Task Export(IAsyncEnumerable<ModuleOutcomeResultCollection> data, string format)
public async Task Export(ExportData data, string format)
{
await foreach (var item in data)
foreach (var module in data.CourseModules)
{
_logger.LogInformation("Module: {Name}", item.Module.Name);

var links = item.Collection.Links;
var alignments = links?.AlignmentsDictionary;
var outcomes = links?.OutcomesDictionary;
_logger.LogInformation("--------------------------------");
_logger.LogInformation("Module: {Module}", module.Name);

Debug.Assert(alignments != null, nameof(alignments) + " != null");
Debug.Assert(outcomes != null, nameof(outcomes) + " != null");

foreach (var alignment in alignments.Values)
foreach (var kpi in module.Kpis)
{
_logger.LogInformation("Alignment: {Alignment}", alignment.Name);
_logger.LogInformation("");
_logger.LogInformation("KPI: {Kpi}", kpi.Name);

foreach (var result in item.Collection.OutcomeResults.Where(o => o.Link.Alignment == alignment.Id && o.Link.Outcome != null))
foreach (var assignment in kpi.Assignments)
{
_logger.LogInformation("- {OutcomeName} {Score}", outcomes[result.Link.Outcome!].Title, result.Grade());
_logger.LogInformation("- Assignment: {Assignment}", assignment.Name);
_logger.LogInformation(" Score: {Score}", assignment.Score);
}
}
}
Expand Down
52 changes: 16 additions & 36 deletions Epsilon/Export/Exporters/CsvModuleExporter.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System.Data;
using System.Diagnostics;
using Epsilon.Abstractions.Export;
using Epsilon.Canvas.Abstractions.Model;
using Microsoft.Extensions.Options;
using Epsilon.Abstractions.Model;

namespace Epsilon.Export.Exporters;

Expand All @@ -17,9 +16,9 @@ public CsvModuleExporter(IOptions<ExportOptions> options)

public IEnumerable<string> Formats { get; } = new[] { "csv" };

public async Task Export(IAsyncEnumerable<ModuleOutcomeResultCollection> data, string format)
public async Task Export(ExportData data, string format)
{
var dt = await CreateDataTable(data);
var dt = CreateDataTable(data.CourseModules);

var stream = new StreamWriter($"{_options.FormattedOutputName}.{format}", false);
WriteHeader(stream, dt);
Expand All @@ -28,46 +27,27 @@ public async Task Export(IAsyncEnumerable<ModuleOutcomeResultCollection> data, s
stream.Close();
}

private static async Task<DataTable> CreateDataTable(IAsyncEnumerable<ModuleOutcomeResultCollection> items)
private static DataTable CreateDataTable(IEnumerable<CourseModule> data)
{
var dt = new DataTable();

dt.Columns.Add("Result Id", typeof(int));
dt.Columns.Add("Assignment Id", typeof(string));
dt.Columns.Add("Assignment", typeof(string));
dt.Columns.Add("KPI", typeof(string));
dt.Columns.Add("Score", typeof(string));
dt.Columns.Add("Module", typeof(string));

await foreach (var item in items)
var dataTable = new DataTable();

dataTable.Columns.Add("Module", typeof(string));
dataTable.Columns.Add("KPI", typeof(string));
dataTable.Columns.Add("Assignment", typeof(string));
dataTable.Columns.Add("Score", typeof(string));

foreach (var module in data)
{
var links = item.Collection.Links;

Debug.Assert(links?.OutcomesDictionary != null, "links?.OutcomesDictionary != null");
Debug.Assert(links.AlignmentsDictionary != null, "links.AlignmentsDictionary != null");

foreach (var result in item.Collection.OutcomeResults)
foreach (var kpi in module.Kpis)
{
Debug.Assert(result.Link != null, "result.Link != null");
var outcome = links.OutcomesDictionary[result.Link.Outcome!];
var alignment = links.AlignmentsDictionary[result.Link.Alignment!];
var grade = result.Grade();

if (grade != null)
foreach (var assignment in kpi.Assignments)
{
dt.Rows.Add(
outcome.Id,
alignment.Id,
alignment.Name,
outcome.Title,
result.Grade(),
item.Module.Name
);
dataTable.Rows.Add(module.Name, kpi.Name, assignment.Name, assignment.Score);
}
}
}

return dt;
return dataTable;
}

private static void WriteHeader(TextWriter writer, DataTable dt)
Expand Down
63 changes: 11 additions & 52 deletions Epsilon/Export/Exporters/ExcelModuleExporter.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System.Diagnostics;
using System.Text;
using Epsilon.Abstractions.Export;
using Epsilon.Canvas.Abstractions.Model;
using Epsilon.Abstractions.Export;
using Epsilon.Abstractions.Model;
using ExcelLibrary.SpreadSheet;
using Microsoft.Extensions.Options;

Expand All @@ -18,65 +16,26 @@ public ExcelModuleExporter(IOptions<ExportOptions> options)

public IEnumerable<string> Formats { get; } = new[] { "xls", "xlsx", "excel" };

public async Task Export(IAsyncEnumerable<ModuleOutcomeResultCollection> data, string format)
public async Task Export(ExportData data, string format)
{
var workbook = new Workbook();

await foreach (var item in data.Where(static m => m.Collection.OutcomeResults.Any()))
foreach (var module in data.CourseModules)
{
var worksheet = new Worksheet(item.Module.Name);
var worksheet = new Worksheet(module.Name);
var kpis = module.Kpis;

//Because reasons @source https://stackoverflow.com/a/8127642
for (var i = 0; i < 100; i++)
{
worksheet.Cells[i, 0] = new Cell("");
}

var links = item.Collection.Links;

Debug.Assert(links != null, nameof(links) + " != null");

var alignments = links.AlignmentsDictionary;
var outcomes = links.OutcomesDictionary;

//Add headers
worksheet.Cells[0, 0] = new Cell("KPI");
worksheet.Cells[0, 1] = new Cell("Assignment(s)");
worksheet.Cells[0, 2] = new Cell("Score");

//Adding all the outcomes.

var index = 1;
foreach (var (outcomeId, outcome) in outcomes)
for (var i = 0; i < kpis.Count(); i++)
{
var assignmentIds = item.Collection.OutcomeResults
.Where(o => o.Link.Outcome == outcomeId && o.Grade() != null)
.Select(static o => o.Link.Assignment)
.ToArray();

if (assignmentIds.Any())
{
worksheet.Cells[index, 0] = new Cell($"{outcome.Title} {outcome.ShortDescription()}");

var cellValueBuilder = new StringBuilder();

foreach (var (_, alignment) in alignments.Where(a => assignmentIds.Contains(a.Key)))
{
cellValueBuilder.AppendLine($"{alignment.Name} {alignment.Url}");
}

worksheet.Cells[index, 1] = new Cell(cellValueBuilder.ToString());

var cellValueOutComeResultsBuilder = new StringBuilder();
foreach (var outcomeResult in item.Collection.OutcomeResults.Where(result =>
result.Link.Outcome == outcomeId))
{
cellValueOutComeResultsBuilder.AppendLine(outcomeResult.Grade());
}
var kpi = kpis.ElementAt(i);

worksheet.Cells[index, 2] = new Cell(cellValueOutComeResultsBuilder.ToString());
index++;
}
worksheet.Cells[i + 1, 0] = new Cell(kpi.Name);
worksheet.Cells[i + 1, 1] = new Cell(string.Join('\n', kpi.Assignments.Select(a => a.Name)));
worksheet.Cells[i + 1, 2] = new Cell(string.Join('\n', kpi.Assignments.Select(a => a.Score)));
}

worksheet.Cells.ColumnWidth[0, 0] = 500;
Expand Down
Loading

0 comments on commit 3dc1593

Please sign in to comment.