Skip to content

Commit

Permalink
Added report export to the UI
Browse files Browse the repository at this point in the history
  • Loading branch information
davewalker5 committed Nov 18, 2023
1 parent 4d0ce02 commit 2fa71e9
Show file tree
Hide file tree
Showing 38 changed files with 425 additions and 68 deletions.
4 changes: 2 additions & 2 deletions docker/api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/core/aspnet:latest
COPY musiccatalogue.api-1.18.0.0 /opt/musiccatalogue.api-1.18.0.0
WORKDIR /opt/musiccatalogue.api-1.18.0.0/bin
COPY musiccatalogue.api-1.19.0.0 /opt/musiccatalogue.api-1.19.0.0
WORKDIR /opt/musiccatalogue.api-1.19.0.0/bin
ENTRYPOINT [ "./MusicCatalogue.Api" ]
4 changes: 2 additions & 2 deletions docker/ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM node:20-alpine
COPY musiccatalogue.ui-1.18.0.0 /opt/musiccatalogue.ui-1.18.0.0
WORKDIR /opt/musiccatalogue.ui-1.18.0.0
COPY musiccatalogue.ui-1.19.0.0 /opt/musiccatalogue.ui-1.19.0.0
WORKDIR /opt/musiccatalogue.ui-1.19.0.0
RUN npm install
RUN npm run build
ENTRYPOINT [ "npm", "start" ]
51 changes: 49 additions & 2 deletions src/MusicCatalogue.Api/Controllers/ExportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,26 @@ namespace MusicCatalogue.Api.Controllers
public class ExportController : Controller
{
private readonly IBackgroundQueue<CatalogueExportWorkItem> _catalogueQueue;
private readonly IBackgroundQueue<ArtistStatisticsExportWorkItem> _artistStatisticsQueue;
private readonly IBackgroundQueue<GenreStatisticsExportWorkItem> _genreStatisticsQueue;
private readonly IBackgroundQueue<MonthlySpendExportWorkItem> _monthlySpendQueue;

public ExportController(IBackgroundQueue<CatalogueExportWorkItem> catalogueQueue)
public ExportController(
IBackgroundQueue<CatalogueExportWorkItem> catalogueQueue,
IBackgroundQueue<ArtistStatisticsExportWorkItem> artistStatisticsQueue,
IBackgroundQueue<GenreStatisticsExportWorkItem> genreStatisticsQueue,
IBackgroundQueue<MonthlySpendExportWorkItem> monthlySpendQueue
)
{
_catalogueQueue = catalogueQueue;
_artistStatisticsQueue = artistStatisticsQueue;
_genreStatisticsQueue = genreStatisticsQueue;
_monthlySpendQueue = monthlySpendQueue;
}

[HttpPost]
[Route("catalogue")]
public IActionResult Export([FromBody] CatalogueExportWorkItem item)
public IActionResult ExportCatalogue([FromBody] CatalogueExportWorkItem item)
{
// Set the job name used in the job status record
item.JobName = "Catalogue Export";
Expand All @@ -29,5 +40,41 @@ public IActionResult Export([FromBody] CatalogueExportWorkItem item)
_catalogueQueue.Enqueue(item);
return Accepted();
}

[HttpPost]
[Route("artiststatistics")]
public IActionResult ExportArtistStatisticsReport([FromBody] ArtistStatisticsExportWorkItem item)
{
// Set the job name used in the job status record
item.JobName = "Artist Statistics Export";

// Queue the work item
_artistStatisticsQueue.Enqueue(item);
return Accepted();
}

[HttpPost]
[Route("genrestatistics")]
public IActionResult ExportGenreStatisticsReport([FromBody] GenreStatisticsExportWorkItem item)
{
// Set the job name used in the job status record
item.JobName = "Genre Statistics Export";

// Queue the work item
_genreStatisticsQueue.Enqueue(item);
return Accepted();
}

[HttpPost]
[Route("monthlyspend")]
public IActionResult ExportMonthySpendReport([FromBody] MonthlySpendExportWorkItem item)
{
// Set the job name used in the job status record
item.JobName = "Monthly Spending Export";

// Queue the work item
_monthlySpendQueue.Enqueue(item);
return Accepted();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace MusicCatalogue.Api.Entities
{
public class ArtistStatisticsExportWorkItem : BackgroundWorkItem
{
public string FileName { get; set; } = "";
public bool WishList { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace MusicCatalogue.Api.Entities
{
public class GenreStatisticsExportWorkItem : BackgroundWorkItem
{
public string FileName { get; set; } = "";
public bool WishList { get; set; }
}
}
7 changes: 7 additions & 0 deletions src/MusicCatalogue.Api/Entities/MonthlySpendExportWorkItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace MusicCatalogue.Api.Entities
{
public class MonthlySpendExportWorkItem : BackgroundWorkItem
{
public string FileName { get; set; } = "";
}
}
6 changes: 3 additions & 3 deletions src/MusicCatalogue.Api/MusicCatalogue.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ReleaseVersion>1.18.0.0</ReleaseVersion>
<FileVersion>1.18.0.0</FileVersion>
<ProductVersion>1.18.0</ProductVersion>
<ReleaseVersion>1.19.0.0</ReleaseVersion>
<FileVersion>1.19.0.0</FileVersion>
<ProductVersion>1.19.0</ProductVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand Down
12 changes: 12 additions & 0 deletions src/MusicCatalogue.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ public static void Main(string[] args)
builder.Services.AddSingleton<IBackgroundQueue<CatalogueExportWorkItem>, BackgroundQueue<CatalogueExportWorkItem>>();
builder.Services.AddHostedService<CatalogueExportService>();

// Add the artist statistics exporter hosted service
builder.Services.AddSingleton<IBackgroundQueue<ArtistStatisticsExportWorkItem>, BackgroundQueue<ArtistStatisticsExportWorkItem>>();
builder.Services.AddHostedService<ArtistStatisticsExportService>();

// Add the genre statistics exporter hosted service
builder.Services.AddSingleton<IBackgroundQueue<GenreStatisticsExportWorkItem>, BackgroundQueue<GenreStatisticsExportWorkItem>>();
builder.Services.AddHostedService<GenreStatisticsExportService>();

// Add the monthly spend report exporter hosted service
builder.Services.AddSingleton<IBackgroundQueue<MonthlySpendExportWorkItem>, BackgroundQueue<MonthlySpendExportWorkItem>>();
builder.Services.AddHostedService<MonthlySpendExportService>();

// Configure JWT
byte[] key = Encoding.ASCII.GetBytes(settings!.Secret);
builder.Services.AddAuthentication(x =>
Expand Down
47 changes: 47 additions & 0 deletions src/MusicCatalogue.Api/Services/ArtistStatisticsExportService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Microsoft.Extensions.Options;
using MusicCatalogue.Api.Entities;
using MusicCatalogue.Api.Interfaces;
using MusicCatalogue.Entities.Config;
using MusicCatalogue.Entities.Interfaces;
using MusicCatalogue.Entities.Reporting;
using MusicCatalogue.Logic.DataExchange.Generic;
using System.Diagnostics.CodeAnalysis;

namespace MusicCatalogue.Api.Services
{
[ExcludeFromCodeCoverage]
public class ArtistStatisticsExportService : BackgroundQueueProcessor<ArtistStatisticsExportWorkItem>
{
private readonly MusicApplicationSettings _settings;
public ArtistStatisticsExportService(
ILogger<BackgroundQueueProcessor<ArtistStatisticsExportWorkItem>> logger,
IBackgroundQueue<ArtistStatisticsExportWorkItem> queue,
IServiceScopeFactory serviceScopeFactory,
IOptions<MusicApplicationSettings> settings)
: base(logger, queue, serviceScopeFactory)
{
_settings = settings.Value;
}

/// <summary>
/// Export the artist statistics report
/// </summary>
/// <param name="item"></param>
/// <param name="factory"></param>
/// <returns></returns>
protected override async Task ProcessWorkItem(ArtistStatisticsExportWorkItem item, IMusicCatalogueFactory factory)
{
// Get the report data
MessageLogger.LogInformation("Retrieving the artist statistics report for export");
var records = await factory.ArtistStatistics.GenerateReportAsync(item.WishList, 1, int.MaxValue);

// Construct the full path to the export file
var filePath = Path.Combine(_settings.ReportsExportPath, item.FileName);

// Export the report
var exporter = new CsvExporter<ArtistStatistics>();
exporter.Export(records, filePath, ',');
MessageLogger.LogInformation("Artist statistics report export completed");
}
}
}
4 changes: 3 additions & 1 deletion src/MusicCatalogue.Api/Services/BackgroundQueueProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,17 @@ protected override async Task ExecuteAsync(CancellationToken token)
var factory = scope.ServiceProvider.GetService<IMusicCatalogueFactory>();

// Create the job status record
var status = await factory.JobStatuses.AddAsync(item.JobName, item.ToString());
var status = await factory!.JobStatuses.AddAsync(item.JobName, item.ToString());

try
{
// Process the work item and, if successful, complete the job status record with
// no error
MessageLogger.LogInformation($"Processing work item {item.ToString()}");
await ProcessWorkItem(item, factory);
#pragma warning disable CS8625
await factory.JobStatuses.UpdateAsync(status.Id, null);
#pragma warning restore CS8625
MessageLogger.LogInformation($"Finished processing work item {item.ToString()}");
}
catch (Exception ex)
Expand Down
7 changes: 3 additions & 4 deletions src/MusicCatalogue.Api/Services/CatalogueExportService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,23 @@ public CatalogueExportService(
}

/// <summary>
/// Export all the sightings from the database
/// Export the catalogue
/// </summary>
/// <param name="item"></param>
/// <param name="factory"></param>
/// <returns></returns>
protected override async Task ProcessWorkItem(CatalogueExportWorkItem item, IMusicCatalogueFactory factory)
{
// Get the list of sightings to export
MessageLogger.LogInformation("Retrieving tracks for export");

// Use the file extension to determine which exporter to use
var extension = Path.GetExtension(item.FileName).ToLower();
IExporter? exporter = extension == ".xlsx" ? factory.XlsxExporter : factory.CsvExporter;
IExporter? exporter = extension == ".xlsx" ? factory.CatalogueXlsxExporter : factory.CatalogueCsvExporter;

// Construct the full path to the export file
var filePath = Path.Combine(_settings.CatalogueExportPath, item.FileName);

// Export the file
// Export the catalogue
await exporter.Export(filePath);
MessageLogger.LogInformation("Catalogue export completed");
}
Expand Down
45 changes: 45 additions & 0 deletions src/MusicCatalogue.Api/Services/GenreStatisticsExportService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Microsoft.Extensions.Options;
using MusicCatalogue.Api.Entities;
using MusicCatalogue.Api.Interfaces;
using MusicCatalogue.Entities.Config;
using MusicCatalogue.Entities.Interfaces;
using MusicCatalogue.Entities.Reporting;
using MusicCatalogue.Logic.DataExchange.Generic;

namespace MusicCatalogue.Api.Services
{
public class GenreStatisticsExportService : BackgroundQueueProcessor<GenreStatisticsExportWorkItem>
{
private readonly MusicApplicationSettings _settings;
public GenreStatisticsExportService(
ILogger<BackgroundQueueProcessor<GenreStatisticsExportWorkItem>> logger,
IBackgroundQueue<GenreStatisticsExportWorkItem> queue,
IServiceScopeFactory serviceScopeFactory,
IOptions<MusicApplicationSettings> settings)
: base(logger, queue, serviceScopeFactory)
{
_settings = settings.Value;
}

/// <summary>
/// Export the genre statistics report
/// </summary>
/// <param name="item"></param>
/// <param name="factory"></param>
/// <returns></returns>
protected override async Task ProcessWorkItem(GenreStatisticsExportWorkItem item, IMusicCatalogueFactory factory)
{
// Get the report data
MessageLogger.LogInformation("Retrieving the genre statistics report for export");
var records = await factory.GenreStatistics.GenerateReportAsync(item.WishList, 1, int.MaxValue);

// Construct the full path to the export file
var filePath = Path.Combine(_settings.ReportsExportPath, item.FileName);

// Export the report
var exporter = new CsvExporter<GenreStatistics>();
exporter.Export(records, filePath, ',');
MessageLogger.LogInformation("Genre statistics report export completed");
}
}
}
45 changes: 45 additions & 0 deletions src/MusicCatalogue.Api/Services/MonthlySpendExportService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Microsoft.Extensions.Options;
using MusicCatalogue.Api.Entities;
using MusicCatalogue.Api.Interfaces;
using MusicCatalogue.Entities.Config;
using MusicCatalogue.Entities.Interfaces;
using MusicCatalogue.Entities.Reporting;
using MusicCatalogue.Logic.DataExchange.Generic;

namespace MusicCatalogue.Api.Services
{
public class MonthlySpendExportService : BackgroundQueueProcessor<MonthlySpendExportWorkItem>
{
private readonly MusicApplicationSettings _settings;
public MonthlySpendExportService(
ILogger<BackgroundQueueProcessor<MonthlySpendExportWorkItem>> logger,
IBackgroundQueue<MonthlySpendExportWorkItem> queue,
IServiceScopeFactory serviceScopeFactory,
IOptions<MusicApplicationSettings> settings)
: base(logger, queue, serviceScopeFactory)
{
_settings = settings.Value;
}

/// <summary>
/// Export the monthly spend report
/// </summary>
/// <param name="item"></param>
/// <param name="factory"></param>
/// <returns></returns>
protected override async Task ProcessWorkItem(MonthlySpendExportWorkItem item, IMusicCatalogueFactory factory)
{
// Get the report data
MessageLogger.LogInformation("Retrieving the monthly spending report for export");
var records = await factory.MonthlySpend.GenerateReportAsync(false, 1, int.MaxValue);

// Construct the full path to the export file
var filePath = Path.Combine(_settings.ReportsExportPath, item.FileName);

// Export the report
var exporter = new CsvExporter<MonthlySpend>();
exporter.Export(records, filePath, ',');
MessageLogger.LogInformation("Monthly spending report export completed");
}
}
}
1 change: 1 addition & 0 deletions src/MusicCatalogue.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"MinimumLogLevel": "Info",
"Environment": "Development",
"CatalogueExportPath": "C:\\MyApps\\MusicCatalogue\\Export",
"ReportsExportPath": "C:\\MyApps\\MusicCatalogue\\Export\\Reports",
"ApiEndpoints": [
{
"EndpointType": "Albums",
Expand Down
4 changes: 2 additions & 2 deletions src/MusicCatalogue.Data/MusicCatalogue.Data.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>MusicCatalogue.Data</PackageId>
<PackageVersion>1.17.0.0</PackageVersion>
<PackageVersion>1.18.0.0</PackageVersion>
<Authors>Dave Walker</Authors>
<Copyright>Copyright (c) Dave Walker 2023</Copyright>
<Owners>Dave Walker</Owners>
Expand All @@ -17,7 +17,7 @@
<PackageProjectUrl>https://github.com/davewalker5/MusicCatalogue</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<ReleaseVersion>1.17.0.0</ReleaseVersion>
<ReleaseVersion>1.18.0.0</ReleaseVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class MusicApplicationSettings
public string LogFile { get; set; } = "";
public MusicCatalogueEnvironment Environment { get; set; }
public string CatalogueExportPath { get; set; } = "";
public string ReportsExportPath { get; set; } = "";
public List<ApiEndpoint> ApiEndpoints { get; set; } = new List<ApiEndpoint>();
public List<ApiServiceKey> ApiServiceKeys { get; set; } = new List<ApiServiceKey>();

Expand Down
11 changes: 11 additions & 0 deletions src/MusicCatalogue.Entities/DataExchange/ExportEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Diagnostics.CodeAnalysis;

namespace MusicCatalogue.Entities.DataExchange
{
[ExcludeFromCodeCoverage]
public class ExportEventArgs<T> : EventArgs where T : class
{
public long RecordCount { get; set; }
public T? RecordSource { get; set; }
}
}
10 changes: 10 additions & 0 deletions src/MusicCatalogue.Entities/Interfaces/ICsvExporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using MusicCatalogue.Entities.DataExchange;

namespace MusicCatalogue.Entities.Interfaces
{
public interface ICsvExporter<T> where T : class
{
event EventHandler<ExportEventArgs<T>> RecordExport;
void Export(IEnumerable<T> entities, string fileName, char separator);
}
}
Loading

0 comments on commit 2fa71e9

Please sign in to comment.