MatDenDagen/Services/ExportService.cs
+57
-8
diff --git a/MatDenDagen/Services/ExportService.cs b/MatDenDagen/Services/ExportService.cs
index 0608019..5622907 100644
@@ -1,7 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using MatDenDagen.Infrastructure.Storage.BlobStorage;
@@ -38,6 +42,11 @@ public sealed class ExportService(
.Submissions.Include(s => s.Answers)
.Include(s => s.Uploads)
.ToArrayAsync(cancellationToken);
var participants = await questionnaireContext.Participants.ToDictionaryAsync(
p => p.Id,
p => p.Name,
cancellationToken
);
{
var entry = archive.CreateEntry("export.md");
@@ -48,27 +57,46 @@ public sealed class ExportService(
$"""
# Export
Svar från {submissions.Length} deltagare
{submissions.Length} svar
""".AsMemory(),
cancellationToken
);
}
foreach (var submission in submissions)
{
var participant = await questionnaireContext.Participants.SingleAsync(
p => p.Id == submission.Participant,
var entry = archive.CreateEntry("export.json");
await using var entryStream = await entry.OpenAsync(cancellationToken);
await JsonSerializer.SerializeAsync(
entryStream,
submissions.Select(s =>
{
var participantName = participants[s.Participant];
var participantFolder = RemoveInvalidFileNameCharacters(participantName);
return new ExportSubmission(
s.Id,
participantName,
s.Answers.Select(a => new ExportAnswer(questions[a.QuestionId], a.Text)),
s.Uploads.Select(u => Path.Join(participantFolder, RemoveInvalidFileNameCharacters(u.Name)))
);
}),
ExportJsonSerializerContext.Default.IEnumerableExportSubmission,
cancellationToken
);
}
foreach (var submission in submissions)
{
var participantName = participants[submission.Participant];
var participantFolder = RemoveInvalidFileNameCharacters(participantName);
{
var entry = archive.CreateEntry(Path.Join(participant.Name, "svar.md"));
var entry = archive.CreateEntry(Path.Join(participantFolder, $"{submission.Id}.md"));
await using var entryStream = await entry.OpenAsync(cancellationToken);
await using var writer = new StreamWriter(entryStream);
await writer.WriteLineAsync(
$"""
# {participant.Name}
# {participantName}
""".AsMemory(),
cancellationToken
@@ -78,7 +106,10 @@ public sealed class ExportService(
{
if (!questions.TryGetValue(answer.QuestionId, out var question))
{
logger.LogError("An answer to {QuestionId} exist, but no question.", answer.QuestionId);
logger.LogError(
"An answer to {QuestionId} exist, but the question does not.",
answer.QuestionId
);
question = answer.QuestionId.ToString();
}
@@ -96,7 +127,9 @@ public sealed class ExportService(
foreach (var upload in submission.Uploads)
{
var entry = archive.CreateEntry(Path.Join(participant.Name, upload.Name));
var entry = archive.CreateEntry(
Path.Join(participantFolder, RemoveInvalidFileNameCharacters(upload.Name))
);
await using var entryStream = await entry.OpenAsync(cancellationToken);
var blobStream = blobStorage.GetBlob(upload.Id);
@@ -116,6 +149,9 @@ public sealed class ExportService(
}
}
}
private static string RemoveInvalidFileNameCharacters(string fileName) =>
string.Join("_", fileName.Split(Path.GetInvalidPathChars()));
}
public static class ExportServiceCollectionExtensions
@@ -123,3 +159,16 @@ public static class ExportServiceCollectionExtensions
public static IServiceCollection AddExportService(this IServiceCollection services) =>
services.AddTransient<ExportService>();
}
public sealed record ExportSubmission(
Guid Id,
string Participant,
IEnumerable<ExportAnswer> Answers,
IEnumerable<string> Uploads
);
public sealed record ExportAnswer(string Question, string Answer);
[JsonSerializable(typeof(IEnumerable<ExportSubmission>))]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, WriteIndented = true)]
public sealed partial class ExportJsonSerializerContext : JsonSerializerContext;