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)); } /// Formatunabhängige Schülerdaten, die ein konkreter Handler erzeugt. 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 static readonly ImportFormatId LessonStudentListCsv = new("lesson-student-list.csv"); public static readonly ImportFormatId MarksPerLessonCsv = new("marks-per-lesson.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 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 Entries { get; } public IReadOnlyList Messages { get; } public IReadOnlyList Conflicts { get; } public IReadOnlyList GroupAssignments { get; } public Guid? TargetGroupId { 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 entries, IEnumerable messages, IEnumerable conflicts, IEnumerable groupAssignments, Guid? targetGroupId, string existingStudentsFingerprint, string groupStateFingerprint) { FormatId = formatId; FormatName = formatName; Entries = ReadOnly(entries); Messages = ReadOnly(messages); Conflicts = ReadOnly(conflicts); GroupAssignments = ReadOnly(groupAssignments); TargetGroupId = targetGroupId; ExistingStudentsFingerprint = existingStudentsFingerprint; GroupStateFingerprint = groupStateFingerprint; } private static IReadOnlyList ReadOnly(IEnumerable values) => new ReadOnlyCollection(values.ToList()); } public sealed record StudentImportApplyResult( int CreatedStudents, int UpdatedStudents, int MatchedExistingStudents, int SkippedStudents, int CreatedMemberships, IReadOnlyList CreatedStudentIds);