feat: add student master data import
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using LehrerApp.Core.Models;
|
||||
|
||||
namespace LehrerApp.Core.Importing;
|
||||
|
||||
public sealed record ImportedStudentContact
|
||||
{
|
||||
public string? Email { get; init; }
|
||||
public string? MobilePhone { get; init; }
|
||||
public string? Phone { get; init; }
|
||||
public string? Street { get; init; }
|
||||
public string? PostalCode { get; init; }
|
||||
public string? City { get; init; }
|
||||
|
||||
public bool HasData => new[] { Email, MobilePhone, Phone, Street, PostalCode, City }
|
||||
.Any(value => !string.IsNullOrWhiteSpace(value));
|
||||
}
|
||||
|
||||
/// <summary>Formatunabhängige Schülerdaten, die ein konkreter Handler erzeugt.</summary>
|
||||
public sealed record ImportedStudent
|
||||
{
|
||||
public required string FirstName { get; init; }
|
||||
public required string LastName { get; init; }
|
||||
public DateOnly? DateOfBirth { get; init; }
|
||||
public Gender? Gender { get; init; }
|
||||
public string? ExternalId { get; init; }
|
||||
public string? GroupName { get; init; }
|
||||
public ImportedStudentContact? Contact { get; init; }
|
||||
public string? SourceReference { get; init; }
|
||||
}
|
||||
|
||||
public static class StudentImportFormats
|
||||
{
|
||||
public static readonly ImportFormatId MasterDataCsv = new("student-master-data.csv");
|
||||
}
|
||||
|
||||
public enum StudentImportResolutionKind { CreateNew, UseExisting, Skip }
|
||||
|
||||
public sealed record StudentImportResolution(
|
||||
StudentImportResolutionKind Kind,
|
||||
Guid? ExistingStudentId = null);
|
||||
|
||||
public sealed record StudentImportEntry(
|
||||
string Id,
|
||||
ImportedStudent Student,
|
||||
StudentImportResolution? AutomaticResolution,
|
||||
string? ConflictId,
|
||||
IReadOnlyList<string> FieldsToSupplement);
|
||||
|
||||
public sealed record StudentImportGroupAssignment(
|
||||
string SourceGroupName,
|
||||
Guid? AutomaticGroupId,
|
||||
string? ConflictId);
|
||||
|
||||
public sealed class StudentImportPreview
|
||||
{
|
||||
public ImportFormatId FormatId { get; }
|
||||
public string FormatName { get; }
|
||||
public IReadOnlyList<StudentImportEntry> Entries { get; }
|
||||
public IReadOnlyList<ImportMessage> Messages { get; }
|
||||
public IReadOnlyList<ImportConflict> Conflicts { get; }
|
||||
public IReadOnlyList<StudentImportGroupAssignment> GroupAssignments { get; }
|
||||
internal string ExistingStudentsFingerprint { get; }
|
||||
internal string GroupStateFingerprint { get; }
|
||||
|
||||
public bool CanApply =>
|
||||
Entries.Count > 0 && Messages.All(message => message.Severity != ImportMessageSeverity.Error);
|
||||
|
||||
internal StudentImportPreview(
|
||||
ImportFormatId formatId,
|
||||
string formatName,
|
||||
IEnumerable<StudentImportEntry> entries,
|
||||
IEnumerable<ImportMessage> messages,
|
||||
IEnumerable<ImportConflict> conflicts,
|
||||
IEnumerable<StudentImportGroupAssignment> groupAssignments,
|
||||
string existingStudentsFingerprint,
|
||||
string groupStateFingerprint)
|
||||
{
|
||||
FormatId = formatId;
|
||||
FormatName = formatName;
|
||||
Entries = ReadOnly(entries);
|
||||
Messages = ReadOnly(messages);
|
||||
Conflicts = ReadOnly(conflicts);
|
||||
GroupAssignments = ReadOnly(groupAssignments);
|
||||
ExistingStudentsFingerprint = existingStudentsFingerprint;
|
||||
GroupStateFingerprint = groupStateFingerprint;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<T> ReadOnly<T>(IEnumerable<T> values) =>
|
||||
new ReadOnlyCollection<T>(values.ToList());
|
||||
}
|
||||
|
||||
public sealed record StudentImportApplyResult(
|
||||
int CreatedStudents,
|
||||
int UpdatedStudents,
|
||||
int MatchedExistingStudents,
|
||||
int SkippedStudents,
|
||||
int CreatedMemberships,
|
||||
IReadOnlyList<Guid> CreatedStudentIds);
|
||||
Reference in New Issue
Block a user