Files
predavanja/Events-WebApi/Events.FilesAPI/Features/Certificates/Synchronize/CertificateFileGenerator.cs
2026-05-10 23:39:55 +02:00

124 lines
4.3 KiB
C#

using System.Globalization;
using Events.FilesAPI.Infrastructure.Options;
using Events.WebAPI.Handlers.EF.Data.Postgres;
using Events.WebAPI.Handlers.EF.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
namespace Events.FilesAPI.Features.Certificates.Synchronize;
public sealed class CertificateFileGenerator(
EventsContext context,
IHostEnvironment hostEnvironment,
IOptions<GeneratedFilesOptions> generatedFilesOptions,
ILogger<CertificateFileGenerator> logger)
{
public async Task GenerateAsync(int eventId, int personId, CancellationToken cancellationToken)
{
var registrations = await context.Set<Registration>()
.AsNoTracking()
.Where(r => r.EventId == eventId && r.PersonId == personId)
.OrderBy(r => r.Sport.Name)
.Select(r => new CertificateRegistrationData
{
EventId = r.EventId,
EventName = r.Event.Name,
EventDate = r.Event.EventDate,
PersonId = r.PersonId,
FirstName = r.Person.FirstName,
LastName = r.Person.LastName,
FirstNameTranscription = r.Person.FirstNameTranscription,
LastNameTranscription = r.Person.LastNameTranscription,
SportName = r.Sport.Name
})
.ToListAsync(cancellationToken);
string certificatePath = GetCertificatePath(eventId, personId);
if (registrations.Count == 0)
{
DeleteCertificateIfExists(certificatePath);
return;
}
Directory.CreateDirectory(Path.GetDirectoryName(certificatePath)!);
byte[] pdfBytes = CertificatePdfDocumentWriter.CreateCertificate(BuildModel(registrations));
await File.WriteAllBytesAsync(certificatePath, pdfBytes, cancellationToken);
logger.LogInformation(
"Registration certificate generated for event #{EventId}, person #{PersonId} at {Path}",
eventId,
personId,
certificatePath);
}
private string GetCertificatePath(int eventId, int personId)
{
string rootPath = Path.IsPathRooted(generatedFilesOptions.Value.OutputPath)
? generatedFilesOptions.Value.OutputPath
: Path.GetFullPath(Path.Combine(hostEnvironment.ContentRootPath, generatedFilesOptions.Value.OutputPath));
return Path.Combine(rootPath, eventId.ToString(CultureInfo.InvariantCulture), $"{eventId}-{personId}.pdf");
}
private void DeleteCertificateIfExists(string certificatePath)
{
DeleteFileIfExists(certificatePath, "Registration certificate deleted at {Path}");
string? directory = Path.GetDirectoryName(certificatePath);
if (!string.IsNullOrWhiteSpace(directory) &&
Directory.Exists(directory) &&
!Directory.EnumerateFileSystemEntries(directory).Any())
{
Directory.Delete(directory);
}
}
private void DeleteFileIfExists(string path, string logMessage)
{
if (!File.Exists(path))
return;
File.Delete(path);
logger.LogInformation(logMessage, path);
}
private static CertificatePdfModel BuildModel(IReadOnlyList<CertificateRegistrationData> registrations)
{
CertificateRegistrationData first = registrations[0];
string originalFullName = $"{first.FirstName} {first.LastName}".Trim();
string transcriptionFullName = $"{first.FirstNameTranscription} {first.LastNameTranscription}".Trim();
var sports = registrations
.Select(r => r.SportName)
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(name => name, StringComparer.OrdinalIgnoreCase)
.ToList();
return new CertificatePdfModel
{
Title = "Certificate of participation",
PersonFullName = originalFullName,
PersonFullNameTranscription = transcriptionFullName,
EventName = first.EventName,
EventDate = first.EventDate,
EventId = first.EventId,
PersonId = first.PersonId,
SportNames = sports
};
}
private sealed class CertificateRegistrationData
{
public int EventId { get; init; }
public string EventName { get; init; } = string.Empty;
public DateOnly EventDate { get; init; }
public int PersonId { get; init; }
public string? FirstName { get; init; } = string.Empty;
public string? LastName { get; init; } = string.Empty;
public string FirstNameTranscription { get; init; } = string.Empty;
public string LastNameTranscription { get; init; } = string.Empty;
public string SportName { get; init; } = string.Empty;
}
}