100 lines
3.4 KiB
C#
100 lines
3.4 KiB
C#
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);
|