diff --git a/Atlas.ManualTesting.Common/Services/HttpRequester.cs b/Atlas.ManualTesting.Common/Services/HttpRequester.cs new file mode 100644 index 000000000..781b7aeb2 --- /dev/null +++ b/Atlas.ManualTesting.Common/Services/HttpRequester.cs @@ -0,0 +1,56 @@ +using Newtonsoft.Json; +using Polly; + +namespace Atlas.ManualTesting.Common.Services +{ + public class AtlasHttpResult + { + public bool WasSuccess => Result != null; + public TResult? Result { get; set; } + + public AtlasHttpResult(TResult? result) + { + Result = result; + } + } + + public abstract class AtlasHttpRequester + { + private readonly HttpClient httpRequestClient; + private readonly string requestUrl; + + protected AtlasHttpRequester(HttpClient httpRequestClient, string requestUrl) + { + this.httpRequestClient = httpRequestClient; + this.requestUrl = requestUrl; + } + + protected async Task> PostRequest(TRequest request) + { + var retryPolicy = Policy.Handle().RetryAsync(10); + + var requestResponse = await retryPolicy.ExecuteAndCaptureAsync( + async () => await SendRequest(request)); + + return new AtlasHttpResult(requestResponse.Result); + } + + private async Task SendRequest(TRequest request) + { + try + { + var response = await httpRequestClient.PostAsync( + requestUrl, new StringContent(JsonConvert.SerializeObject(request))); + response.EnsureSuccessStatusCode(); + + return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Failed to send request {request}. Details: {ex.Message} " + + "Re-attempting until success or re-attempt count reached."); + throw; + } + } + } +} \ No newline at end of file diff --git a/Atlas.MatchPrediction.Functions/Functions/Debug/GenotypeImputationFunctions.cs b/Atlas.MatchPrediction.Functions/Functions/Debug/GenotypeImputationFunctions.cs index cdbf48477..9aca9ae7e 100644 --- a/Atlas.MatchPrediction.Functions/Functions/Debug/GenotypeImputationFunctions.cs +++ b/Atlas.MatchPrediction.Functions/Functions/Debug/GenotypeImputationFunctions.cs @@ -51,7 +51,7 @@ public class GenotypeImputationFunctions { HlaTyping = input.SubjectInfo.HlaTyping.ToPhenotypeInfo().PrettyPrint(), MatchPredictionParameters = input.MatchPredictionParameters, - HaplotypeFrequencySet = frequencySet, + HaplotypeFrequencySet = frequencySet.ToClientHaplotypeFrequencySet(), GenotypeCount = imputedGenotypes.GenotypeLikelihoods.Count, SumOfLikelihoods = imputedGenotypes.SumOfLikelihoods, GenotypeLikelihoods = imputedGenotypes.GenotypeLikelihoods.ToSingleDelimitedString() diff --git a/Atlas.MatchPrediction.Functions/Functions/Debug/MatchCalculationFunctions.cs b/Atlas.MatchPrediction.Functions/Functions/Debug/MatchCalculationFunctions.cs index 87119a961..4addd52f4 100644 --- a/Atlas.MatchPrediction.Functions/Functions/Debug/MatchCalculationFunctions.cs +++ b/Atlas.MatchPrediction.Functions/Functions/Debug/MatchCalculationFunctions.cs @@ -69,7 +69,7 @@ public class MatchCalculationFunctions subjectResult.IsUnrepresented, subjectResult.GenotypeCount, subjectResult.SumOfLikelihoods, - set, + set.ToClientHaplotypeFrequencySet(), subjectInfo.HlaTyping.ToPhenotypeInfo().PrettyPrint()); } } diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Context/MatchPredictionValidationContext.cs b/Atlas.MatchPrediction.Test.Validation.Data/Context/MatchPredictionValidationContext.cs index 212f1f144..f510506b4 100644 --- a/Atlas.MatchPrediction.Test.Validation.Data/Context/MatchPredictionValidationContext.cs +++ b/Atlas.MatchPrediction.Test.Validation.Data/Context/MatchPredictionValidationContext.cs @@ -29,6 +29,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().SetUpModel(); modelBuilder.Entity().SetUpModel(); + modelBuilder.Entity().SetUpModel(); + modelBuilder.Entity().SetUpModel(); base.OnModelCreating(modelBuilder); } @@ -47,5 +49,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) public DbSet HomeworkSets { get; set; } public DbSet PatientDonorPairs { get; set; } + public DbSet ImputationSummaries { get; set; } + public DbSet SubjectGenotypes { get; set; } } } \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240429195932_AddSubjectGenotypeTables.Designer.cs b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240429195932_AddSubjectGenotypeTables.Designer.cs new file mode 100644 index 000000000..254424f11 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240429195932_AddSubjectGenotypeTables.Designer.cs @@ -0,0 +1,799 @@ +// +using System; +using Atlas.MatchPrediction.Test.Validation.Data.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Atlas.MatchPrediction.Test.Validation.Data.Migrations +{ + [DbContext(typeof(MatchPredictionValidationContext))] + [Migration("20240429195932_AddSubjectGenotypeTables")] + partial class AddSubjectGenotypeTables + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.LocusMatchDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("IsAntigenMatch_1") + .HasColumnType("bit"); + + b.Property("IsAntigenMatch_2") + .HasColumnType("bit"); + + b.Property("Locus") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("MatchConfidence_1") + .HasColumnType("nvarchar(32)"); + + b.Property("MatchConfidence_2") + .HasColumnType("nvarchar(32)"); + + b.Property("MatchCount") + .HasColumnType("int"); + + b.Property("MatchGrade_1") + .HasColumnType("nvarchar(128)"); + + b.Property("MatchGrade_2") + .HasColumnType("nvarchar(128)"); + + b.Property("MatchedDonor_Id") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MatchedDonor_Id"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("MatchedDonor_Id"), new[] { "Locus", "MatchConfidence_1", "MatchConfidence_2", "IsAntigenMatch_1", "IsAntigenMatch_2" }); + + b.ToTable("LocusMatchDetails"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DonorCode") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DonorHfSetPopulationId") + .HasColumnType("int"); + + b.Property("MatchPredictionResult") + .HasColumnType("nvarchar(max)"); + + b.Property("MatchingResult") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PatientHfSetPopulationId") + .HasColumnType("int"); + + b.Property("SearchRequestRecord_Id") + .HasColumnType("int"); + + b.Property("TotalMatchCount") + .HasColumnType("int"); + + b.Property("TypedLociCount") + .HasColumnType("int"); + + b.Property("WasDonorRepresented") + .HasColumnType("bit"); + + b.Property("WasPatientRepresented") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("SearchRequestRecord_Id"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("SearchRequestRecord_Id"), new[] { "DonorCode", "TotalMatchCount", "PatientHfSetPopulationId", "DonorHfSetPopulationId", "WasPatientRepresented", "WasDonorRepresented" }); + + b.ToTable("MatchedDonors"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonorProbability", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Locus") + .HasColumnType("nvarchar(10)"); + + b.Property("MatchedDonor_Id") + .HasColumnType("int"); + + b.Property("MismatchCount") + .HasColumnType("int"); + + b.Property("Probability") + .HasColumnType("decimal(6,5)"); + + b.Property("ProbabilityAsPercentage") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MatchedDonor_Id", "Locus", "MismatchCount"); + + b.ToTable("MatchProbabilities"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.TestDonorExportRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DataRefreshCompleted") + .HasColumnType("datetimeoffset"); + + b.Property("DataRefreshRecordId") + .HasColumnType("int"); + + b.Property("Exported") + .HasColumnType("datetimeoffset"); + + b.Property("Started") + .HasColumnType("datetimeoffset"); + + b.Property("WasDataRefreshSuccessful") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("TestDonorExportRecords"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.DonorGenotype", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("A_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("A_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("C_1") + .HasColumnType("nvarchar(32)"); + + b.Property("C_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_1") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("ImputationSummary_Id") + .HasColumnType("int"); + + b.Property("Likelihood") + .HasColumnType("decimal(21,20)"); + + b.HasKey("Id"); + + b.HasIndex("ImputationSummary_Id"); + + b.ToTable("DonorGenotypes"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.DonorImputationSummary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ExternalSubjectId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("GenotypeCount") + .HasColumnType("int"); + + b.Property("HfSetPopulationId") + .HasColumnType("int"); + + b.Property("HomeworkSet_Id") + .HasColumnType("int"); + + b.Property("SumOfLikelihoods") + .HasColumnType("decimal(21,20)"); + + b.Property("WasRepresented") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("HomeworkSet_Id"); + + b.HasIndex("ExternalSubjectId", "HomeworkSet_Id"); + + b.ToTable("DonorImputationSummaries"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.HomeworkSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("HlaNomenclatureVersion") + .IsRequired() + .HasColumnType("nvarchar(8)"); + + b.Property("MatchLoci") + .IsRequired() + .HasColumnType("nvarchar(128)"); + + b.Property("ResultsPath") + .IsRequired() + .HasColumnType("nvarchar(516)"); + + b.Property("SetName") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("SubmittedDateTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset") + .HasDefaultValueSql("GETUTCDATE()"); + + b.HasKey("Id"); + + b.HasIndex("SetName"); + + b.ToTable("HomeworkSets"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientDonorPair", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DidDonorHaveMissingHla") + .HasColumnType("bit"); + + b.Property("DidPatientHaveMissingHla") + .HasColumnType("bit"); + + b.Property("DonorId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("DonorImputationCompleted") + .HasColumnType("bit"); + + b.Property("HomeworkSet_Id") + .HasColumnType("int"); + + b.Property("IsProcessed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("MatchingGenotypesCalculated") + .HasColumnType("bit"); + + b.Property("PatientId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("PatientImputationCompleted") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("HomeworkSet_Id"); + + b.HasIndex("DonorId", "PatientId", "HomeworkSet_Id", "IsProcessed") + .IsUnique(); + + b.ToTable("PatientDonorPairs"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientGenotype", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("A_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("A_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("C_1") + .HasColumnType("nvarchar(32)"); + + b.Property("C_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_1") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("ImputationSummary_Id") + .HasColumnType("int"); + + b.Property("Likelihood") + .HasColumnType("decimal(21,20)"); + + b.HasKey("Id"); + + b.HasIndex("ImputationSummary_Id"); + + b.ToTable("PatientGenotypes"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientImputationSummary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ExternalSubjectId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("GenotypeCount") + .HasColumnType("int"); + + b.Property("HfSetPopulationId") + .HasColumnType("int"); + + b.Property("HomeworkSet_Id") + .HasColumnType("int"); + + b.Property("SumOfLikelihoods") + .HasColumnType("decimal(21,20)"); + + b.Property("WasRepresented") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("HomeworkSet_Id"); + + b.HasIndex("ExternalSubjectId", "HomeworkSet_Id"); + + b.ToTable("PatientImputationSummaries"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DonorId") + .HasColumnType("int"); + + b.Property("MatchPredictionAlgorithmRequestId") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("PatientId") + .HasColumnType("int"); + + b.Property("RequestErrors") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("DonorId"); + + b.HasIndex("PatientId"); + + b.HasIndex("MatchPredictionAlgorithmRequestId", "DonorId", "PatientId"); + + b.ToTable("MatchPredictionRequests"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionResults", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Locus") + .HasColumnType("nvarchar(10)"); + + b.Property("MatchPredictionRequestId") + .HasColumnType("int"); + + b.Property("MismatchCount") + .HasColumnType("int"); + + b.Property("Probability") + .HasColumnType("decimal(6,5)"); + + b.Property("ProbabilityAsPercentage") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MatchPredictionRequestId", "Locus", "MismatchCount"); + + b.ToTable("MatchPredictionResults"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.SearchSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreatedDateTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("DonorType") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("MatchLoci") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("MismatchCount") + .HasColumnType("int"); + + b.Property("SearchRequestsSubmitted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("TestDonorExportRecord_Id") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DonorType"); + + b.HasIndex("TestDonorExportRecord_Id"); + + b.ToTable("SearchSets"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("A_1") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("A_2") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("B_1") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("B_2") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("C_1") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("C_2") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DQB1_1") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DQB1_2") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DRB1_1") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DRB1_2") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DonorType") + .HasColumnType("nvarchar(10)"); + + b.Property("ExternalHfSetId") + .HasMaxLength(256) + .HasColumnType("int"); + + b.Property("ExternalId") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.HasKey("Id"); + + b.HasIndex("ExternalId") + .IsUnique(); + + b.ToTable("SubjectInfo"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.ValidationSearchRequestRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AtlasSearchIdentifier") + .IsRequired() + .HasColumnType("nvarchar(50)"); + + b.Property("DonorMismatchCount") + .HasColumnType("int"); + + b.Property("MatchedDonorCount") + .HasColumnType("int"); + + b.Property("PatientId") + .HasColumnType("int"); + + b.Property("SearchResultsRetrieved") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("SearchSet_Id") + .HasColumnType("int"); + + b.Property("WasSuccessful") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("AtlasSearchIdentifier"); + + b.HasIndex("PatientId"); + + b.HasIndex("SearchSet_Id"); + + b.HasIndex("WasSuccessful"); + + b.ToTable("SearchRequests"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.LocusMatchDetails", b => + { + b.HasOne("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", null) + .WithMany() + .HasForeignKey("MatchedDonor_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.ValidationSearchRequestRecord", null) + .WithMany() + .HasForeignKey("SearchRequestRecord_Id") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonorProbability", b => + { + b.HasOne("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", null) + .WithMany() + .HasForeignKey("MatchedDonor_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.DonorGenotype", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.DonorImputationSummary", null) + .WithMany() + .HasForeignKey("ImputationSummary_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.DonorImputationSummary", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.HomeworkSet", null) + .WithMany() + .HasForeignKey("HomeworkSet_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientDonorPair", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.HomeworkSet", null) + .WithMany() + .HasForeignKey("HomeworkSet_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientGenotype", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientImputationSummary", null) + .WithMany() + .HasForeignKey("ImputationSummary_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientImputationSummary", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.HomeworkSet", null) + .WithMany() + .HasForeignKey("HomeworkSet_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) + .WithMany() + .HasForeignKey("DonorId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) + .WithMany() + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionResults", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", null) + .WithMany() + .HasForeignKey("MatchPredictionRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.SearchSet", b => + { + b.HasOne("Atlas.ManualTesting.Common.Models.Entities.TestDonorExportRecord", null) + .WithMany() + .HasForeignKey("TestDonorExportRecord_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.ValidationSearchRequestRecord", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) + .WithMany() + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SearchSet", null) + .WithMany() + .HasForeignKey("SearchSet_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240429195932_AddSubjectGenotypeTables.cs b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240429195932_AddSubjectGenotypeTables.cs new file mode 100644 index 000000000..757a0b85d --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240429195932_AddSubjectGenotypeTables.cs @@ -0,0 +1,165 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Atlas.MatchPrediction.Test.Validation.Data.Migrations +{ + public partial class AddSubjectGenotypeTables : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "DonorImputationSummaries", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ExternalSubjectId = table.Column(type: "nvarchar(10)", nullable: false), + HfSetPopulationId = table.Column(type: "int", nullable: false), + WasRepresented = table.Column(type: "bit", nullable: false), + GenotypeCount = table.Column(type: "int", nullable: false), + SumOfLikelihoods = table.Column(type: "decimal(21,20)", nullable: false), + HomeworkSet_Id = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DonorImputationSummaries", x => x.Id); + table.ForeignKey( + name: "FK_DonorImputationSummaries_HomeworkSets_HomeworkSet_Id", + column: x => x.HomeworkSet_Id, + principalTable: "HomeworkSets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PatientImputationSummaries", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ExternalSubjectId = table.Column(type: "nvarchar(10)", nullable: false), + HfSetPopulationId = table.Column(type: "int", nullable: false), + WasRepresented = table.Column(type: "bit", nullable: false), + GenotypeCount = table.Column(type: "int", nullable: false), + SumOfLikelihoods = table.Column(type: "decimal(21,20)", nullable: false), + HomeworkSet_Id = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PatientImputationSummaries", x => x.Id); + table.ForeignKey( + name: "FK_PatientImputationSummaries_HomeworkSets_HomeworkSet_Id", + column: x => x.HomeworkSet_Id, + principalTable: "HomeworkSets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "DonorGenotypes", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + A_1 = table.Column(type: "nvarchar(32)", nullable: false), + A_2 = table.Column(type: "nvarchar(32)", nullable: false), + B_1 = table.Column(type: "nvarchar(32)", nullable: false), + B_2 = table.Column(type: "nvarchar(32)", nullable: false), + C_1 = table.Column(type: "nvarchar(32)", nullable: true), + C_2 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_1 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_2 = table.Column(type: "nvarchar(32)", nullable: true), + DRB1_1 = table.Column(type: "nvarchar(32)", nullable: false), + DRB1_2 = table.Column(type: "nvarchar(32)", nullable: false), + Likelihood = table.Column(type: "decimal(21,20)", nullable: true), + ImputationSummary_Id = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DonorGenotypes", x => x.Id); + table.ForeignKey( + name: "FK_DonorGenotypes_DonorImputationSummaries_ImputationSummary_Id", + column: x => x.ImputationSummary_Id, + principalTable: "DonorImputationSummaries", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PatientGenotypes", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + A_1 = table.Column(type: "nvarchar(32)", nullable: false), + A_2 = table.Column(type: "nvarchar(32)", nullable: false), + B_1 = table.Column(type: "nvarchar(32)", nullable: false), + B_2 = table.Column(type: "nvarchar(32)", nullable: false), + C_1 = table.Column(type: "nvarchar(32)", nullable: true), + C_2 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_1 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_2 = table.Column(type: "nvarchar(32)", nullable: true), + DRB1_1 = table.Column(type: "nvarchar(32)", nullable: false), + DRB1_2 = table.Column(type: "nvarchar(32)", nullable: false), + Likelihood = table.Column(type: "decimal(21,20)", nullable: true), + ImputationSummary_Id = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PatientGenotypes", x => x.Id); + table.ForeignKey( + name: "FK_PatientGenotypes_PatientImputationSummaries_ImputationSummary_Id", + column: x => x.ImputationSummary_Id, + principalTable: "PatientImputationSummaries", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_DonorGenotypes_ImputationSummary_Id", + table: "DonorGenotypes", + column: "ImputationSummary_Id"); + + migrationBuilder.CreateIndex( + name: "IX_DonorImputationSummaries_ExternalSubjectId_HomeworkSet_Id", + table: "DonorImputationSummaries", + columns: new[] { "ExternalSubjectId", "HomeworkSet_Id" }); + + migrationBuilder.CreateIndex( + name: "IX_DonorImputationSummaries_HomeworkSet_Id", + table: "DonorImputationSummaries", + column: "HomeworkSet_Id"); + + migrationBuilder.CreateIndex( + name: "IX_PatientGenotypes_ImputationSummary_Id", + table: "PatientGenotypes", + column: "ImputationSummary_Id"); + + migrationBuilder.CreateIndex( + name: "IX_PatientImputationSummaries_ExternalSubjectId_HomeworkSet_Id", + table: "PatientImputationSummaries", + columns: new[] { "ExternalSubjectId", "HomeworkSet_Id" }); + + migrationBuilder.CreateIndex( + name: "IX_PatientImputationSummaries_HomeworkSet_Id", + table: "PatientImputationSummaries", + column: "HomeworkSet_Id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "DonorGenotypes"); + + migrationBuilder.DropTable( + name: "PatientGenotypes"); + + migrationBuilder.DropTable( + name: "DonorImputationSummaries"); + + migrationBuilder.DropTable( + name: "PatientImputationSummaries"); + } + } +} diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430105231_RemoveHomeworkSetId.Designer.cs b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430105231_RemoveHomeworkSetId.Designer.cs new file mode 100644 index 000000000..ac38e9521 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430105231_RemoveHomeworkSetId.Designer.cs @@ -0,0 +1,771 @@ +// +using System; +using Atlas.MatchPrediction.Test.Validation.Data.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Atlas.MatchPrediction.Test.Validation.Data.Migrations +{ + [DbContext(typeof(MatchPredictionValidationContext))] + [Migration("20240430105231_RemoveHomeworkSetId")] + partial class RemoveHomeworkSetId + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.LocusMatchDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("IsAntigenMatch_1") + .HasColumnType("bit"); + + b.Property("IsAntigenMatch_2") + .HasColumnType("bit"); + + b.Property("Locus") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("MatchConfidence_1") + .HasColumnType("nvarchar(32)"); + + b.Property("MatchConfidence_2") + .HasColumnType("nvarchar(32)"); + + b.Property("MatchCount") + .HasColumnType("int"); + + b.Property("MatchGrade_1") + .HasColumnType("nvarchar(128)"); + + b.Property("MatchGrade_2") + .HasColumnType("nvarchar(128)"); + + b.Property("MatchedDonor_Id") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MatchedDonor_Id"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("MatchedDonor_Id"), new[] { "Locus", "MatchConfidence_1", "MatchConfidence_2", "IsAntigenMatch_1", "IsAntigenMatch_2" }); + + b.ToTable("LocusMatchDetails"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DonorCode") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DonorHfSetPopulationId") + .HasColumnType("int"); + + b.Property("MatchPredictionResult") + .HasColumnType("nvarchar(max)"); + + b.Property("MatchingResult") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PatientHfSetPopulationId") + .HasColumnType("int"); + + b.Property("SearchRequestRecord_Id") + .HasColumnType("int"); + + b.Property("TotalMatchCount") + .HasColumnType("int"); + + b.Property("TypedLociCount") + .HasColumnType("int"); + + b.Property("WasDonorRepresented") + .HasColumnType("bit"); + + b.Property("WasPatientRepresented") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("SearchRequestRecord_Id"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("SearchRequestRecord_Id"), new[] { "DonorCode", "TotalMatchCount", "PatientHfSetPopulationId", "DonorHfSetPopulationId", "WasPatientRepresented", "WasDonorRepresented" }); + + b.ToTable("MatchedDonors"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonorProbability", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Locus") + .HasColumnType("nvarchar(10)"); + + b.Property("MatchedDonor_Id") + .HasColumnType("int"); + + b.Property("MismatchCount") + .HasColumnType("int"); + + b.Property("Probability") + .HasColumnType("decimal(6,5)"); + + b.Property("ProbabilityAsPercentage") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MatchedDonor_Id", "Locus", "MismatchCount"); + + b.ToTable("MatchProbabilities"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.TestDonorExportRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DataRefreshCompleted") + .HasColumnType("datetimeoffset"); + + b.Property("DataRefreshRecordId") + .HasColumnType("int"); + + b.Property("Exported") + .HasColumnType("datetimeoffset"); + + b.Property("Started") + .HasColumnType("datetimeoffset"); + + b.Property("WasDataRefreshSuccessful") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("TestDonorExportRecords"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.DonorGenotype", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("A_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("A_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("C_1") + .HasColumnType("nvarchar(32)"); + + b.Property("C_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_1") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("ImputationSummary_Id") + .HasColumnType("int"); + + b.Property("Likelihood") + .HasColumnType("decimal(21,20)"); + + b.HasKey("Id"); + + b.HasIndex("ImputationSummary_Id"); + + b.ToTable("DonorGenotypes"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.DonorImputationSummary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ExternalSubjectId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("GenotypeCount") + .HasColumnType("int"); + + b.Property("HfSetPopulationId") + .HasColumnType("int"); + + b.Property("SumOfLikelihoods") + .HasColumnType("decimal(21,20)"); + + b.Property("WasRepresented") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ExternalSubjectId"); + + b.ToTable("DonorImputationSummaries"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.HomeworkSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("HlaNomenclatureVersion") + .IsRequired() + .HasColumnType("nvarchar(8)"); + + b.Property("MatchLoci") + .IsRequired() + .HasColumnType("nvarchar(128)"); + + b.Property("ResultsPath") + .IsRequired() + .HasColumnType("nvarchar(516)"); + + b.Property("SetName") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("SubmittedDateTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset") + .HasDefaultValueSql("GETUTCDATE()"); + + b.HasKey("Id"); + + b.HasIndex("SetName"); + + b.ToTable("HomeworkSets"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientDonorPair", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DidDonorHaveMissingHla") + .HasColumnType("bit"); + + b.Property("DidPatientHaveMissingHla") + .HasColumnType("bit"); + + b.Property("DonorId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("DonorImputationCompleted") + .HasColumnType("bit"); + + b.Property("HomeworkSet_Id") + .HasColumnType("int"); + + b.Property("IsProcessed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("MatchingGenotypesCalculated") + .HasColumnType("bit"); + + b.Property("PatientId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("PatientImputationCompleted") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("HomeworkSet_Id"); + + b.HasIndex("DonorId", "PatientId", "HomeworkSet_Id", "IsProcessed") + .IsUnique(); + + b.ToTable("PatientDonorPairs"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientGenotype", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("A_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("A_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("C_1") + .HasColumnType("nvarchar(32)"); + + b.Property("C_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_1") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("ImputationSummary_Id") + .HasColumnType("int"); + + b.Property("Likelihood") + .HasColumnType("decimal(21,20)"); + + b.HasKey("Id"); + + b.HasIndex("ImputationSummary_Id"); + + b.ToTable("PatientGenotypes"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientImputationSummary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ExternalSubjectId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("GenotypeCount") + .HasColumnType("int"); + + b.Property("HfSetPopulationId") + .HasColumnType("int"); + + b.Property("SumOfLikelihoods") + .HasColumnType("decimal(21,20)"); + + b.Property("WasRepresented") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ExternalSubjectId"); + + b.ToTable("PatientImputationSummaries"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DonorId") + .HasColumnType("int"); + + b.Property("MatchPredictionAlgorithmRequestId") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("PatientId") + .HasColumnType("int"); + + b.Property("RequestErrors") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("DonorId"); + + b.HasIndex("PatientId"); + + b.HasIndex("MatchPredictionAlgorithmRequestId", "DonorId", "PatientId"); + + b.ToTable("MatchPredictionRequests"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionResults", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Locus") + .HasColumnType("nvarchar(10)"); + + b.Property("MatchPredictionRequestId") + .HasColumnType("int"); + + b.Property("MismatchCount") + .HasColumnType("int"); + + b.Property("Probability") + .HasColumnType("decimal(6,5)"); + + b.Property("ProbabilityAsPercentage") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MatchPredictionRequestId", "Locus", "MismatchCount"); + + b.ToTable("MatchPredictionResults"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.SearchSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreatedDateTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("DonorType") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("MatchLoci") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("MismatchCount") + .HasColumnType("int"); + + b.Property("SearchRequestsSubmitted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("TestDonorExportRecord_Id") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DonorType"); + + b.HasIndex("TestDonorExportRecord_Id"); + + b.ToTable("SearchSets"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("A_1") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("A_2") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("B_1") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("B_2") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("C_1") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("C_2") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DQB1_1") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DQB1_2") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DRB1_1") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DRB1_2") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DonorType") + .HasColumnType("nvarchar(10)"); + + b.Property("ExternalHfSetId") + .HasMaxLength(256) + .HasColumnType("int"); + + b.Property("ExternalId") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.HasKey("Id"); + + b.HasIndex("ExternalId") + .IsUnique(); + + b.ToTable("SubjectInfo"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.ValidationSearchRequestRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AtlasSearchIdentifier") + .IsRequired() + .HasColumnType("nvarchar(50)"); + + b.Property("DonorMismatchCount") + .HasColumnType("int"); + + b.Property("MatchedDonorCount") + .HasColumnType("int"); + + b.Property("PatientId") + .HasColumnType("int"); + + b.Property("SearchResultsRetrieved") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("SearchSet_Id") + .HasColumnType("int"); + + b.Property("WasSuccessful") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("AtlasSearchIdentifier"); + + b.HasIndex("PatientId"); + + b.HasIndex("SearchSet_Id"); + + b.HasIndex("WasSuccessful"); + + b.ToTable("SearchRequests"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.LocusMatchDetails", b => + { + b.HasOne("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", null) + .WithMany() + .HasForeignKey("MatchedDonor_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.ValidationSearchRequestRecord", null) + .WithMany() + .HasForeignKey("SearchRequestRecord_Id") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonorProbability", b => + { + b.HasOne("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", null) + .WithMany() + .HasForeignKey("MatchedDonor_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.DonorGenotype", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.DonorImputationSummary", null) + .WithMany() + .HasForeignKey("ImputationSummary_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientDonorPair", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.HomeworkSet", null) + .WithMany() + .HasForeignKey("HomeworkSet_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientGenotype", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientImputationSummary", null) + .WithMany() + .HasForeignKey("ImputationSummary_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) + .WithMany() + .HasForeignKey("DonorId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) + .WithMany() + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionResults", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", null) + .WithMany() + .HasForeignKey("MatchPredictionRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.SearchSet", b => + { + b.HasOne("Atlas.ManualTesting.Common.Models.Entities.TestDonorExportRecord", null) + .WithMany() + .HasForeignKey("TestDonorExportRecord_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.ValidationSearchRequestRecord", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) + .WithMany() + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SearchSet", null) + .WithMany() + .HasForeignKey("SearchSet_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430105231_RemoveHomeworkSetId.cs b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430105231_RemoveHomeworkSetId.cs new file mode 100644 index 000000000..33b8be789 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430105231_RemoveHomeworkSetId.cs @@ -0,0 +1,115 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Atlas.MatchPrediction.Test.Validation.Data.Migrations +{ + public partial class RemoveHomeworkSetId : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_DonorImputationSummaries_HomeworkSets_HomeworkSet_Id", + table: "DonorImputationSummaries"); + + migrationBuilder.DropForeignKey( + name: "FK_PatientImputationSummaries_HomeworkSets_HomeworkSet_Id", + table: "PatientImputationSummaries"); + + migrationBuilder.DropIndex( + name: "IX_PatientImputationSummaries_ExternalSubjectId_HomeworkSet_Id", + table: "PatientImputationSummaries"); + + migrationBuilder.DropIndex( + name: "IX_PatientImputationSummaries_HomeworkSet_Id", + table: "PatientImputationSummaries"); + + migrationBuilder.DropIndex( + name: "IX_DonorImputationSummaries_ExternalSubjectId_HomeworkSet_Id", + table: "DonorImputationSummaries"); + + migrationBuilder.DropIndex( + name: "IX_DonorImputationSummaries_HomeworkSet_Id", + table: "DonorImputationSummaries"); + + migrationBuilder.DropColumn( + name: "HomeworkSet_Id", + table: "PatientImputationSummaries"); + + migrationBuilder.DropColumn( + name: "HomeworkSet_Id", + table: "DonorImputationSummaries"); + + migrationBuilder.CreateIndex( + name: "IX_PatientImputationSummaries_ExternalSubjectId", + table: "PatientImputationSummaries", + column: "ExternalSubjectId"); + + migrationBuilder.CreateIndex( + name: "IX_DonorImputationSummaries_ExternalSubjectId", + table: "DonorImputationSummaries", + column: "ExternalSubjectId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_PatientImputationSummaries_ExternalSubjectId", + table: "PatientImputationSummaries"); + + migrationBuilder.DropIndex( + name: "IX_DonorImputationSummaries_ExternalSubjectId", + table: "DonorImputationSummaries"); + + migrationBuilder.AddColumn( + name: "HomeworkSet_Id", + table: "PatientImputationSummaries", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "HomeworkSet_Id", + table: "DonorImputationSummaries", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateIndex( + name: "IX_PatientImputationSummaries_ExternalSubjectId_HomeworkSet_Id", + table: "PatientImputationSummaries", + columns: new[] { "ExternalSubjectId", "HomeworkSet_Id" }); + + migrationBuilder.CreateIndex( + name: "IX_PatientImputationSummaries_HomeworkSet_Id", + table: "PatientImputationSummaries", + column: "HomeworkSet_Id"); + + migrationBuilder.CreateIndex( + name: "IX_DonorImputationSummaries_ExternalSubjectId_HomeworkSet_Id", + table: "DonorImputationSummaries", + columns: new[] { "ExternalSubjectId", "HomeworkSet_Id" }); + + migrationBuilder.CreateIndex( + name: "IX_DonorImputationSummaries_HomeworkSet_Id", + table: "DonorImputationSummaries", + column: "HomeworkSet_Id"); + + migrationBuilder.AddForeignKey( + name: "FK_DonorImputationSummaries_HomeworkSets_HomeworkSet_Id", + table: "DonorImputationSummaries", + column: "HomeworkSet_Id", + principalTable: "HomeworkSets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_PatientImputationSummaries_HomeworkSets_HomeworkSet_Id", + table: "PatientImputationSummaries", + column: "HomeworkSet_Id", + principalTable: "HomeworkSets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430153041_CombinePatientAndDonorTables.Designer.cs b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430153041_CombinePatientAndDonorTables.Designer.cs new file mode 100644 index 000000000..9c2794086 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430153041_CombinePatientAndDonorTables.Designer.cs @@ -0,0 +1,674 @@ +// +using System; +using Atlas.MatchPrediction.Test.Validation.Data.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Atlas.MatchPrediction.Test.Validation.Data.Migrations +{ + [DbContext(typeof(MatchPredictionValidationContext))] + [Migration("20240430153041_CombinePatientAndDonorTables")] + partial class CombinePatientAndDonorTables + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.LocusMatchDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("IsAntigenMatch_1") + .HasColumnType("bit"); + + b.Property("IsAntigenMatch_2") + .HasColumnType("bit"); + + b.Property("Locus") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("MatchConfidence_1") + .HasColumnType("nvarchar(32)"); + + b.Property("MatchConfidence_2") + .HasColumnType("nvarchar(32)"); + + b.Property("MatchCount") + .HasColumnType("int"); + + b.Property("MatchGrade_1") + .HasColumnType("nvarchar(128)"); + + b.Property("MatchGrade_2") + .HasColumnType("nvarchar(128)"); + + b.Property("MatchedDonor_Id") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MatchedDonor_Id"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("MatchedDonor_Id"), new[] { "Locus", "MatchConfidence_1", "MatchConfidence_2", "IsAntigenMatch_1", "IsAntigenMatch_2" }); + + b.ToTable("LocusMatchDetails"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DonorCode") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DonorHfSetPopulationId") + .HasColumnType("int"); + + b.Property("MatchPredictionResult") + .HasColumnType("nvarchar(max)"); + + b.Property("MatchingResult") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PatientHfSetPopulationId") + .HasColumnType("int"); + + b.Property("SearchRequestRecord_Id") + .HasColumnType("int"); + + b.Property("TotalMatchCount") + .HasColumnType("int"); + + b.Property("TypedLociCount") + .HasColumnType("int"); + + b.Property("WasDonorRepresented") + .HasColumnType("bit"); + + b.Property("WasPatientRepresented") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("SearchRequestRecord_Id"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("SearchRequestRecord_Id"), new[] { "DonorCode", "TotalMatchCount", "PatientHfSetPopulationId", "DonorHfSetPopulationId", "WasPatientRepresented", "WasDonorRepresented" }); + + b.ToTable("MatchedDonors"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonorProbability", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Locus") + .HasColumnType("nvarchar(10)"); + + b.Property("MatchedDonor_Id") + .HasColumnType("int"); + + b.Property("MismatchCount") + .HasColumnType("int"); + + b.Property("Probability") + .HasColumnType("decimal(6,5)"); + + b.Property("ProbabilityAsPercentage") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MatchedDonor_Id", "Locus", "MismatchCount"); + + b.ToTable("MatchProbabilities"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.TestDonorExportRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DataRefreshCompleted") + .HasColumnType("datetimeoffset"); + + b.Property("DataRefreshRecordId") + .HasColumnType("int"); + + b.Property("Exported") + .HasColumnType("datetimeoffset"); + + b.Property("Started") + .HasColumnType("datetimeoffset"); + + b.Property("WasDataRefreshSuccessful") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("TestDonorExportRecords"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.HomeworkSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("HlaNomenclatureVersion") + .IsRequired() + .HasColumnType("nvarchar(8)"); + + b.Property("MatchLoci") + .IsRequired() + .HasColumnType("nvarchar(128)"); + + b.Property("ResultsPath") + .IsRequired() + .HasColumnType("nvarchar(516)"); + + b.Property("SetName") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("SubmittedDateTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset") + .HasDefaultValueSql("GETUTCDATE()"); + + b.HasKey("Id"); + + b.HasIndex("SetName"); + + b.ToTable("HomeworkSets"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.ImputationSummary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ExternalSubjectId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("GenotypeCount") + .HasColumnType("int"); + + b.Property("HfSetPopulationId") + .HasColumnType("int"); + + b.Property("SumOfLikelihoods") + .HasColumnType("decimal(21,20)"); + + b.Property("WasRepresented") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ExternalSubjectId"); + + b.ToTable("ImputationSummaries"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientDonorPair", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DidDonorHaveMissingHla") + .HasColumnType("bit"); + + b.Property("DidPatientHaveMissingHla") + .HasColumnType("bit"); + + b.Property("DonorId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("DonorImputationCompleted") + .HasColumnType("bit"); + + b.Property("HomeworkSet_Id") + .HasColumnType("int"); + + b.Property("IsProcessed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("MatchingGenotypesCalculated") + .HasColumnType("bit"); + + b.Property("PatientId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("PatientImputationCompleted") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("HomeworkSet_Id"); + + b.HasIndex("DonorId", "PatientId", "HomeworkSet_Id", "IsProcessed") + .IsUnique(); + + b.ToTable("PatientDonorPairs"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.SubjectGenotype", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("A_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("A_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("C_1") + .HasColumnType("nvarchar(32)"); + + b.Property("C_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_1") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("ImputationSummary_Id") + .HasColumnType("int"); + + b.Property("Likelihood") + .HasColumnType("decimal(21,20)"); + + b.HasKey("Id"); + + b.HasIndex("ImputationSummary_Id"); + + b.ToTable("SubjectGenotypes"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DonorId") + .HasColumnType("int"); + + b.Property("MatchPredictionAlgorithmRequestId") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("PatientId") + .HasColumnType("int"); + + b.Property("RequestErrors") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("DonorId"); + + b.HasIndex("PatientId"); + + b.HasIndex("MatchPredictionAlgorithmRequestId", "DonorId", "PatientId"); + + b.ToTable("MatchPredictionRequests"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionResults", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Locus") + .HasColumnType("nvarchar(10)"); + + b.Property("MatchPredictionRequestId") + .HasColumnType("int"); + + b.Property("MismatchCount") + .HasColumnType("int"); + + b.Property("Probability") + .HasColumnType("decimal(6,5)"); + + b.Property("ProbabilityAsPercentage") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MatchPredictionRequestId", "Locus", "MismatchCount"); + + b.ToTable("MatchPredictionResults"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.SearchSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreatedDateTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("DonorType") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("MatchLoci") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("MismatchCount") + .HasColumnType("int"); + + b.Property("SearchRequestsSubmitted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("TestDonorExportRecord_Id") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DonorType"); + + b.HasIndex("TestDonorExportRecord_Id"); + + b.ToTable("SearchSets"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("A_1") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("A_2") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("B_1") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("B_2") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("C_1") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("C_2") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DQB1_1") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DQB1_2") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DRB1_1") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DRB1_2") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DonorType") + .HasColumnType("nvarchar(10)"); + + b.Property("ExternalHfSetId") + .HasMaxLength(256) + .HasColumnType("int"); + + b.Property("ExternalId") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.HasKey("Id"); + + b.HasIndex("ExternalId") + .IsUnique(); + + b.ToTable("SubjectInfo"); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.ValidationSearchRequestRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AtlasSearchIdentifier") + .IsRequired() + .HasColumnType("nvarchar(50)"); + + b.Property("DonorMismatchCount") + .HasColumnType("int"); + + b.Property("MatchedDonorCount") + .HasColumnType("int"); + + b.Property("PatientId") + .HasColumnType("int"); + + b.Property("SearchResultsRetrieved") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("SearchSet_Id") + .HasColumnType("int"); + + b.Property("WasSuccessful") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("AtlasSearchIdentifier"); + + b.HasIndex("PatientId"); + + b.HasIndex("SearchSet_Id"); + + b.HasIndex("WasSuccessful"); + + b.ToTable("SearchRequests"); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.LocusMatchDetails", b => + { + b.HasOne("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", null) + .WithMany() + .HasForeignKey("MatchedDonor_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.ValidationSearchRequestRecord", null) + .WithMany() + .HasForeignKey("SearchRequestRecord_Id") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.ManualTesting.Common.Models.Entities.MatchedDonorProbability", b => + { + b.HasOne("Atlas.ManualTesting.Common.Models.Entities.MatchedDonor", null) + .WithMany() + .HasForeignKey("MatchedDonor_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientDonorPair", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.HomeworkSet", null) + .WithMany() + .HasForeignKey("HomeworkSet_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.SubjectGenotype", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.ImputationSummary", null) + .WithMany() + .HasForeignKey("ImputationSummary_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) + .WithMany() + .HasForeignKey("DonorId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) + .WithMany() + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionResults", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", null) + .WithMany() + .HasForeignKey("MatchPredictionRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.SearchSet", b => + { + b.HasOne("Atlas.ManualTesting.Common.Models.Entities.TestDonorExportRecord", null) + .WithMany() + .HasForeignKey("TestDonorExportRecord_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.ValidationSearchRequestRecord", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) + .WithMany() + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SearchSet", null) + .WithMany() + .HasForeignKey("SearchSet_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430153041_CombinePatientAndDonorTables.cs b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430153041_CombinePatientAndDonorTables.cs new file mode 100644 index 000000000..7da8a7921 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/20240430153041_CombinePatientAndDonorTables.cs @@ -0,0 +1,204 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Atlas.MatchPrediction.Test.Validation.Data.Migrations +{ + public partial class CombinePatientAndDonorTables : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "DonorGenotypes"); + + migrationBuilder.DropTable( + name: "PatientGenotypes"); + + migrationBuilder.DropTable( + name: "DonorImputationSummaries"); + + migrationBuilder.DropTable( + name: "PatientImputationSummaries"); + + migrationBuilder.CreateTable( + name: "ImputationSummaries", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ExternalSubjectId = table.Column(type: "nvarchar(10)", nullable: false), + HfSetPopulationId = table.Column(type: "int", nullable: false), + WasRepresented = table.Column(type: "bit", nullable: false), + GenotypeCount = table.Column(type: "int", nullable: false), + SumOfLikelihoods = table.Column(type: "decimal(21,20)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ImputationSummaries", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SubjectGenotypes", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + A_1 = table.Column(type: "nvarchar(32)", nullable: false), + A_2 = table.Column(type: "nvarchar(32)", nullable: false), + B_1 = table.Column(type: "nvarchar(32)", nullable: false), + B_2 = table.Column(type: "nvarchar(32)", nullable: false), + C_1 = table.Column(type: "nvarchar(32)", nullable: true), + C_2 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_1 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_2 = table.Column(type: "nvarchar(32)", nullable: true), + DRB1_1 = table.Column(type: "nvarchar(32)", nullable: false), + DRB1_2 = table.Column(type: "nvarchar(32)", nullable: false), + Likelihood = table.Column(type: "decimal(21,20)", nullable: true), + ImputationSummary_Id = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SubjectGenotypes", x => x.Id); + table.ForeignKey( + name: "FK_SubjectGenotypes_ImputationSummaries_ImputationSummary_Id", + column: x => x.ImputationSummary_Id, + principalTable: "ImputationSummaries", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ImputationSummaries_ExternalSubjectId", + table: "ImputationSummaries", + column: "ExternalSubjectId"); + + migrationBuilder.CreateIndex( + name: "IX_SubjectGenotypes_ImputationSummary_Id", + table: "SubjectGenotypes", + column: "ImputationSummary_Id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SubjectGenotypes"); + + migrationBuilder.DropTable( + name: "ImputationSummaries"); + + migrationBuilder.CreateTable( + name: "DonorImputationSummaries", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ExternalSubjectId = table.Column(type: "nvarchar(10)", nullable: false), + GenotypeCount = table.Column(type: "int", nullable: false), + HfSetPopulationId = table.Column(type: "int", nullable: false), + SumOfLikelihoods = table.Column(type: "decimal(21,20)", nullable: false), + WasRepresented = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DonorImputationSummaries", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "PatientImputationSummaries", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ExternalSubjectId = table.Column(type: "nvarchar(10)", nullable: false), + GenotypeCount = table.Column(type: "int", nullable: false), + HfSetPopulationId = table.Column(type: "int", nullable: false), + SumOfLikelihoods = table.Column(type: "decimal(21,20)", nullable: false), + WasRepresented = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PatientImputationSummaries", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "DonorGenotypes", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + A_1 = table.Column(type: "nvarchar(32)", nullable: false), + A_2 = table.Column(type: "nvarchar(32)", nullable: false), + B_1 = table.Column(type: "nvarchar(32)", nullable: false), + B_2 = table.Column(type: "nvarchar(32)", nullable: false), + C_1 = table.Column(type: "nvarchar(32)", nullable: true), + C_2 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_1 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_2 = table.Column(type: "nvarchar(32)", nullable: true), + DRB1_1 = table.Column(type: "nvarchar(32)", nullable: false), + DRB1_2 = table.Column(type: "nvarchar(32)", nullable: false), + ImputationSummary_Id = table.Column(type: "int", nullable: false), + Likelihood = table.Column(type: "decimal(21,20)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_DonorGenotypes", x => x.Id); + table.ForeignKey( + name: "FK_DonorGenotypes_DonorImputationSummaries_ImputationSummary_Id", + column: x => x.ImputationSummary_Id, + principalTable: "DonorImputationSummaries", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PatientGenotypes", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + A_1 = table.Column(type: "nvarchar(32)", nullable: false), + A_2 = table.Column(type: "nvarchar(32)", nullable: false), + B_1 = table.Column(type: "nvarchar(32)", nullable: false), + B_2 = table.Column(type: "nvarchar(32)", nullable: false), + C_1 = table.Column(type: "nvarchar(32)", nullable: true), + C_2 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_1 = table.Column(type: "nvarchar(32)", nullable: true), + DQB1_2 = table.Column(type: "nvarchar(32)", nullable: true), + DRB1_1 = table.Column(type: "nvarchar(32)", nullable: false), + DRB1_2 = table.Column(type: "nvarchar(32)", nullable: false), + ImputationSummary_Id = table.Column(type: "int", nullable: false), + Likelihood = table.Column(type: "decimal(21,20)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PatientGenotypes", x => x.Id); + table.ForeignKey( + name: "FK_PatientGenotypes_PatientImputationSummaries_ImputationSummary_Id", + column: x => x.ImputationSummary_Id, + principalTable: "PatientImputationSummaries", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_DonorGenotypes_ImputationSummary_Id", + table: "DonorGenotypes", + column: "ImputationSummary_Id"); + + migrationBuilder.CreateIndex( + name: "IX_DonorImputationSummaries_ExternalSubjectId", + table: "DonorImputationSummaries", + column: "ExternalSubjectId"); + + migrationBuilder.CreateIndex( + name: "IX_PatientGenotypes_ImputationSummary_Id", + table: "PatientGenotypes", + column: "ImputationSummary_Id"); + + migrationBuilder.CreateIndex( + name: "IX_PatientImputationSummaries_ExternalSubjectId", + table: "PatientImputationSummaries", + column: "ExternalSubjectId"); + } + } +} diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Migrations/MatchPredictionValidationContextModelSnapshot.cs b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/MatchPredictionValidationContextModelSnapshot.cs index 3e972f1e4..df9065d74 100644 --- a/Atlas.MatchPrediction.Test.Validation.Data/Migrations/MatchPredictionValidationContextModelSnapshot.cs +++ b/Atlas.MatchPrediction.Test.Validation.Data/Migrations/MatchPredictionValidationContextModelSnapshot.cs @@ -211,6 +211,37 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("HomeworkSets"); }); + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.ImputationSummary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ExternalSubjectId") + .IsRequired() + .HasColumnType("nvarchar(10)"); + + b.Property("GenotypeCount") + .HasColumnType("int"); + + b.Property("HfSetPopulationId") + .HasColumnType("int"); + + b.Property("SumOfLikelihoods") + .HasColumnType("decimal(21,20)"); + + b.Property("WasRepresented") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ExternalSubjectId"); + + b.ToTable("ImputationSummaries"); + }); + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.PatientDonorPair", b => { b.Property("Id") @@ -260,6 +291,63 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("PatientDonorPairs"); }); + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.SubjectGenotype", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("A_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("A_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("B_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("C_1") + .HasColumnType("nvarchar(32)"); + + b.Property("C_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_1") + .HasColumnType("nvarchar(32)"); + + b.Property("DQB1_2") + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_1") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("DRB1_2") + .IsRequired() + .HasColumnType("nvarchar(32)"); + + b.Property("ImputationSummary_Id") + .HasColumnType("int"); + + b.Property("Likelihood") + .HasColumnType("decimal(21,20)"); + + b.HasKey("Id"); + + b.HasIndex("ImputationSummary_Id"); + + b.ToTable("SubjectGenotypes"); + }); + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", b => { b.Property("Id") @@ -522,6 +610,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.SubjectGenotype", b => + { + b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.Homework.ImputationSummary", null) + .WithMany() + .HasForeignKey("ImputationSummary_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Atlas.MatchPrediction.Test.Validation.Data.Models.MatchPredictionRequest", b => { b.HasOne("Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo", null) diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Models/Homework/ImputationSummary.cs b/Atlas.MatchPrediction.Test.Validation.Data/Models/Homework/ImputationSummary.cs new file mode 100644 index 000000000..47dd3492d --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Models/Homework/ImputationSummary.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Atlas.MatchPrediction.Test.Validation.Data.Models.Homework +{ + public class ImputationSummary + { + public int Id { get; set; } + + [Column(TypeName = "nvarchar(10)")] + public string ExternalSubjectId { get; set; } + + public int HfSetPopulationId { get; set; } + public bool WasRepresented { get; set; } + public int GenotypeCount { get; set; } + + [Column(TypeName = "decimal(21,20)")] + public decimal SumOfLikelihoods { get; set; } + } + + internal static class ImputationSummaryBuilder + { + public static void SetUpModel(this EntityTypeBuilder modelBuilder) + { + modelBuilder.HasIndex(x => new { x.ExternalSubjectId }); + } + } +} \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Models/Homework/SubjectGenotype.cs b/Atlas.MatchPrediction.Test.Validation.Data/Models/Homework/SubjectGenotype.cs new file mode 100644 index 000000000..57743d2f3 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Models/Homework/SubjectGenotype.cs @@ -0,0 +1,58 @@ +using Atlas.Common.Sql.BulkInsert; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System.ComponentModel.DataAnnotations.Schema; +// ReSharper disable InconsistentNaming + +namespace Atlas.MatchPrediction.Test.Validation.Data.Models.Homework +{ + public class SubjectGenotype : IBulkInsertModel + { + public int Id { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string A_1 { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string A_2 { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string B_1 { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string B_2 { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string? C_1 { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string? C_2 { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string? DQB1_1 { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string? DQB1_2 { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string DRB1_1 { get; set; } + + [Column(TypeName = "nvarchar(32)")] + public string DRB1_2 { get; set; } + + [Column(TypeName = "decimal(21,20)")] + public decimal? Likelihood { get; set; } + + public int ImputationSummary_Id { get; set; } + } + + internal static class SubjectGenotypeBuilder + { + public static void SetUpModel(this EntityTypeBuilder modelBuilder) + { + modelBuilder + .HasOne() + .WithMany() + .HasForeignKey(x => x.ImputationSummary_Id); + } + } +} \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/HomeworkDeletionRepository.cs b/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/HomeworkDeletionRepository.cs index 388253e7c..a7850e6dc 100644 --- a/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/HomeworkDeletionRepository.cs +++ b/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/HomeworkDeletionRepository.cs @@ -20,18 +20,25 @@ public HomeworkDeletionRepository(string connectionString) /// public async Task DeleteAll() { + const string deleteGenotypes = "DELETE FROM SubjectGenotypes"; + const string deleteImputationSummaries = "DELETE FROM ImputationSummaries"; const string deleteAllPairs = "DELETE FROM PatientDonorPairs"; + const string deleteAllSets = "DELETE FROM HomeworkSets"; - await using (var connection = new SqlConnection(connectionString)) + var sqlCollection = new[] { - await connection.ExecuteAsync(deleteAllPairs); - } - - const string deleteAllSets = "DELETE FROM HomeworkSets"; + deleteGenotypes, + deleteImputationSummaries, + deleteAllPairs, + deleteAllSets + }; await using (var connection = new SqlConnection(connectionString)) { - await connection.ExecuteAsync(deleteAllSets); + foreach (var sql in sqlCollection) + { + await connection.ExecuteAsync(sql); + } } } } diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/HomeworkSetRepository.cs b/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/HomeworkSetRepository.cs index 3171da228..a493ff062 100644 --- a/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/HomeworkSetRepository.cs +++ b/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/HomeworkSetRepository.cs @@ -37,7 +37,7 @@ public async Task Add(string setName, string resultsPath, string matchLoci, await using (var connection = new SqlConnection(connectionString)) { - return (await connection.QueryAsync(sql, new { setName, resultsPath, matchLoci })).Single(); + return (await connection.QueryAsync(sql, new { setName, resultsPath, matchLoci, hlaNomenclatureVersion })).Single(); } } diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/ImputationSummaryRepository.cs b/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/ImputationSummaryRepository.cs new file mode 100644 index 000000000..f1ebcc1eb --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/ImputationSummaryRepository.cs @@ -0,0 +1,52 @@ +using Atlas.MatchPrediction.Test.Validation.Data.Context; +using Atlas.MatchPrediction.Test.Validation.Data.Models.Homework; +using Dapper; +using Microsoft.Data.SqlClient; + +namespace Atlas.MatchPrediction.Test.Validation.Data.Repositories.Homework +{ + public interface IImputationSummaryRepository + { + Task Add(ImputationSummary imputationSummary); + } + + public class ImputationSummaryRepository : IImputationSummaryRepository + { + private readonly string connectionString; + + public ImputationSummaryRepository(string connectionString) + { + this.connectionString = connectionString; + } + + public async Task Add(ImputationSummary imputationSummary) + { + const string sql = $@" + INSERT INTO {nameof(MatchPredictionValidationContext.ImputationSummaries)} ( + ExternalSubjectId, + HfSetPopulationId, + WasRepresented, + GenotypeCount, + SumOfLikelihoods) + VALUES ( + @{nameof(ImputationSummary.ExternalSubjectId)}, + @{nameof(ImputationSummary.HfSetPopulationId)}, + @{nameof(ImputationSummary.WasRepresented)}, + @{nameof(ImputationSummary.GenotypeCount)}, + @{nameof(ImputationSummary.SumOfLikelihoods)}); + SELECT CAST(SCOPE_IDENTITY() as int);"; + + await using (var connection = new SqlConnection(connectionString)) + { + return (await connection.QueryAsync(sql, new + { + imputationSummary.ExternalSubjectId, + imputationSummary.HfSetPopulationId, + imputationSummary.WasRepresented, + imputationSummary.GenotypeCount, + imputationSummary.SumOfLikelihoods + })).Single(); + } + } + } +} \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/SubjectGenotypeRepository.cs b/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/SubjectGenotypeRepository.cs new file mode 100644 index 000000000..f605551d6 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation.Data/Repositories/Homework/SubjectGenotypeRepository.cs @@ -0,0 +1,20 @@ +using Atlas.Common.Sql.BulkInsert; +using Atlas.MatchPrediction.Test.Validation.Data.Context; +using Atlas.MatchPrediction.Test.Validation.Data.Models.Homework; + +namespace Atlas.MatchPrediction.Test.Validation.Data.Repositories.Homework +{ + public interface ISubjectGenotypeRepository : IBulkInsertRepository + { + } + + public class SubjectGenotypeRepository : BulkInsertRepository, ISubjectGenotypeRepository + { + private const string TableName = nameof(MatchPredictionValidationContext.SubjectGenotypes); + + /// + public SubjectGenotypeRepository(string connectionString) : base(connectionString, TableName) + { + } + } +} \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation/DependencyInjection/ServiceConfiguration.cs b/Atlas.MatchPrediction.Test.Validation/DependencyInjection/ServiceConfiguration.cs index 70d394332..f426cb07f 100644 --- a/Atlas.MatchPrediction.Test.Validation/DependencyInjection/ServiceConfiguration.cs +++ b/Atlas.MatchPrediction.Test.Validation/DependencyInjection/ServiceConfiguration.cs @@ -21,6 +21,7 @@ using Atlas.MatchPrediction.Test.Validation.Settings; using Microsoft.Extensions.DependencyInjection; using System; +using Atlas.MatchPrediction.Test.Validation.Data.Models.Homework; using Atlas.MatchPrediction.Test.Validation.Data.Repositories.Homework; using Atlas.MatchPrediction.Test.Validation.Services.Exercise4.Homework; @@ -36,6 +37,7 @@ internal static class ServiceConfiguration Func fetchMessageServiceBusSettings, Func fetchMatchPredictionRequestSettings, Func fetchValidationSearchSettings, + Func fetchValidationHomeworkSettings, Func fetchMatchPredictionValidationSqlConnectionString, Func fetchMatchPredictionSqlConnectionString, Func fetchDonorImportSqlConnectionString) @@ -43,7 +45,8 @@ internal static class ServiceConfiguration services.RegisterSettings( fetchOutgoingMatchPredictionRequestSettings, fetchValidationAzureStorageSettings, - fetchValidationSearchSettings); + fetchValidationSearchSettings, + fetchValidationHomeworkSettings); services.RegisterDatabaseServices(fetchMatchPredictionValidationSqlConnectionString); @@ -61,11 +64,13 @@ internal static class ServiceConfiguration this IServiceCollection services, Func fetchOutgoingMatchPredictionRequestSettings, Func fetchValidationAzureStorageSettings, - Func fetchValidationSearchSettings) + Func fetchValidationSearchSettings, + Func fetchValidationHomeworkSettings) { services.MakeSettingsAvailableForUse(fetchOutgoingMatchPredictionRequestSettings); services.MakeSettingsAvailableForUse(fetchValidationAzureStorageSettings); services.MakeSettingsAvailableForUse(fetchValidationSearchSettings); + services.MakeSettingsAvailableForUse(fetchValidationHomeworkSettings); } private static void RegisterDatabaseServices(this IServiceCollection services, Func fetchSqlConnectionString) @@ -157,10 +162,17 @@ private static void RegisterDatabaseServices(this IServiceCollection services, F new HomeworkSetRepository(fetchSqlConnectionString(sp))); services.AddScoped(sp => new PatientDonorPairRepository(fetchSqlConnectionString(sp))); + services.AddScoped(sp => + new ImputationSummaryRepository(fetchSqlConnectionString(sp))); + services.AddScoped(sp => + new SubjectGenotypeRepository(fetchSqlConnectionString(sp))); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } } } \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation/Models/MatchLociExtensions.cs b/Atlas.MatchPrediction.Test.Validation/Models/MatchLociExtensions.cs index a66906f87..589892b40 100644 --- a/Atlas.MatchPrediction.Test.Validation/Models/MatchLociExtensions.cs +++ b/Atlas.MatchPrediction.Test.Validation/Models/MatchLociExtensions.cs @@ -32,5 +32,13 @@ public static LociInfo ToLociInfo(this string matchLoci) new LociInfo(false), (currentLociInfo, locus) => currentLociInfo.SetLocus(Enum.Parse(locus), true)); } + + public static ISet ToSet(this string matchLoci) + { + return matchLoci + .Split(MatchLociSeparator) + .Select(Enum.Parse) + .ToHashSet(); + } } } \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation/Models/SubjectGenotypeResult.cs b/Atlas.MatchPrediction.Test.Validation/Models/SubjectGenotypeResult.cs new file mode 100644 index 000000000..4dce21e94 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation/Models/SubjectGenotypeResult.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Atlas.MatchPrediction.Test.Validation.Data.Models; +using Atlas.MatchPrediction.Test.Validation.Data.Models.Homework; + +namespace Atlas.MatchPrediction.Test.Validation.Models +{ + internal class SubjectGenotypeResult + { + public bool HasMissingHla { get; } + + public SubjectInfo SubjectInfo { get; } + + /// + /// Will be `null` if the subject has not yet been imputed. + /// + public IEnumerable Genotypes { get; set; } + + public SubjectGenotypeResult(bool hasMissingHla, SubjectInfo subjectInfo) + { + HasMissingHla = hasMissingHla; + SubjectInfo = subjectInfo; + } + } +} \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/HomeworkProcessor.cs b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/HomeworkProcessor.cs index af9816dbd..e90bec421 100644 --- a/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/HomeworkProcessor.cs +++ b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/HomeworkProcessor.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Atlas.Common.Utils.Extensions; using Atlas.MatchPrediction.Test.Validation.Data.Repositories.Homework; -using Atlas.MatchPrediction.Test.Validation.Models; namespace Atlas.MatchPrediction.Test.Validation.Services.Exercise4.Homework { @@ -54,11 +53,9 @@ private async Task ProcessHomeworkSet(int homeworkSetId) return; } - var matchLoci = set.MatchLoci.ToLociInfo(); - foreach (var pdp in pdps) { - await pdpProcessor.Process(pdp, matchLoci); + await pdpProcessor.Process(pdp, set.MatchLoci, set.HlaNomenclatureVersion); } } } diff --git a/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/ImputationRequester.cs b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/ImputationRequester.cs new file mode 100644 index 000000000..dea092aa1 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/ImputationRequester.cs @@ -0,0 +1,61 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Atlas.Common.Public.Models.GeneticData.PhenotypeInfo; +using Atlas.Common.Public.Models.GeneticData.PhenotypeInfo.TransferModels; +using Atlas.Common.Public.Models.MatchPrediction; +using Atlas.Debug.Client.Models.MatchPrediction; +using Atlas.ManualTesting.Common.Services; +using Atlas.MatchPrediction.Test.Validation.Models; +using Atlas.MatchPrediction.Test.Validation.Settings; +using Microsoft.Extensions.Options; + +namespace Atlas.MatchPrediction.Test.Validation.Services.Exercise4.Homework +{ + public class HomeworkImputationRequest + { + public PhenotypeInfo SubjectHla { get; set; } + public int? ExternalHfSetId { get; set; } + public string MatchLoci { get; set; } + public string HlaVersion { get; set; } + } + + public interface IImputationRequester + { + Task> Request(HomeworkImputationRequest request); + } + + internal class ImputationRequester : AtlasHttpRequester, IImputationRequester + { + private static readonly HttpClient HttpRequestClient = new(); + + /// + public ImputationRequester(IOptions settings) + : base(HttpRequestClient, settings.Value.ImputationRequestUrl) + { + } + + /// + public async Task> Request(HomeworkImputationRequest request) + { + var imputationRequest = new GenotypeImputationRequest + { + SubjectInfo = new SubjectInfo + { + HlaTyping = request.SubjectHla.ToPhenotypeInfoTransfer(), + FrequencySetMetadata = new FrequencySetMetadata + { + EthnicityCode = request.ExternalHfSetId.ToString(), + RegistryCode = request.ExternalHfSetId.ToString() + } + }, + MatchPredictionParameters = new MatchPredictionParameters + { + AllowedLoci = request.MatchLoci.ToSet(), + MatchingAlgorithmHlaNomenclatureVersion = request.HlaVersion + } + }; + + return await PostRequest(imputationRequest); + } + } +} \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/MissingHlaChecker.cs b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/MissingHlaChecker.cs new file mode 100644 index 000000000..d221b6617 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/MissingHlaChecker.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Atlas.Common.Public.Models.GeneticData.PhenotypeInfo; +using Atlas.MatchPrediction.Test.Validation.Data.Models; +using Atlas.MatchPrediction.Test.Validation.Data.Repositories; + +namespace Atlas.MatchPrediction.Test.Validation.Services.Exercise4.Homework +{ + public interface IMissingHlaChecker + { + Task<(bool, SubjectInfo)> SubjectHasMissingHla(string externalId, bool isDonor, LociInfo matchLoci); + } + + internal class MissingHlaChecker : IMissingHlaChecker + { + private readonly ISubjectRepository subjectRepository; + + public MissingHlaChecker(ISubjectRepository subjectRepository) + { + this.subjectRepository = subjectRepository; + } + + public async Task<(bool, SubjectInfo)> SubjectHasMissingHla(string externalId, bool isDonor, LociInfo matchLoci) + { + var subject = await subjectRepository.GetByExternalId(externalId); + + // Missing subject was likely rejected during the initial import of test data + if (subject == null) + { + return (true, null); + } + + // Can assume that if donor is in the db, it has all the required loci + if (isDonor) + { + return (false, subject); + } + + // patient must be typed at all match loci + var patientHla = subject.ToPhenotypeInfo(); + var patientHasAllHla = matchLoci.AllAtLoci((locus, isRequired) => !isRequired || patientHla.GetLocus(locus).Position1And2NotNull()); + return (!patientHasAllHla, subject); + } + } +} \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/PatientDonorPairProcessor.cs b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/PatientDonorPairProcessor.cs index f80f7c8c3..61c79a1e1 100644 --- a/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/PatientDonorPairProcessor.cs +++ b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/PatientDonorPairProcessor.cs @@ -1,99 +1,107 @@ -using System.Threading.Tasks; +using System.Collections.Concurrent; +using System.Threading.Tasks; using Atlas.Common.Public.Models.GeneticData.PhenotypeInfo; -using Atlas.MatchPrediction.Test.Validation.Data.Models; using Atlas.MatchPrediction.Test.Validation.Data.Models.Homework; -using Atlas.MatchPrediction.Test.Validation.Data.Repositories; using Atlas.MatchPrediction.Test.Validation.Data.Repositories.Homework; +using Atlas.MatchPrediction.Test.Validation.Models; + +// ReSharper disable InconsistentNaming namespace Atlas.MatchPrediction.Test.Validation.Services.Exercise4.Homework { public interface IPatientDonorPairProcessor { - Task Process(PatientDonorPair pdp, LociInfo matchLoci); + Task Process(PatientDonorPair pdp, string matchLoci, string hlaVersion); } internal class PatientDonorPairProcessor : IPatientDonorPairProcessor { - private readonly ISubjectRepository subjectRepository; + private static readonly ConcurrentDictionary subjectResultCache = new(); + private readonly IPatientDonorPairRepository pdpRepository; + private readonly IMissingHlaChecker missingHlaChecker; + private readonly ISubjectGenotypesProcessor genotypesProcessor; public PatientDonorPairProcessor( - ISubjectRepository subjectRepository, - IPatientDonorPairRepository pdpRepository) + IPatientDonorPairRepository pdpRepository, + IMissingHlaChecker missingHlaChecker, + ISubjectGenotypesProcessor genotypesProcessor) { this.pdpRepository = pdpRepository; - this.subjectRepository = subjectRepository; + this.missingHlaChecker = missingHlaChecker; + this.genotypesProcessor = genotypesProcessor; } /// - public async Task Process(PatientDonorPair pdp, LociInfo matchLoci) + public async Task Process(PatientDonorPair pdp, string matchLoci, string hlaVersion) { - if (await PatientHasMissingHla(pdp, matchLoci)) return; + var matchLociInfo = matchLoci.ToLociInfo(); - if (await DonorHasMissingHla(pdp, matchLoci)) return; + var patientResult = await CheckPatientHasMissingHla(pdp, matchLociInfo); + if (patientResult.HasMissingHla) return; - // Else submit patient imputation request - - // Then submit donor imputation request + var donorResult = await CheckDonorHasMissingHla(pdp, matchLociInfo); + if (donorResult.HasMissingHla) return; + await Task.WhenAll( + Impute(pdp, patientResult, matchLoci, hlaVersion, true), + Impute(pdp, donorResult, matchLoci, hlaVersion, false) // Then submit matching genotypes request + ); } - private async Task PatientHasMissingHla(PatientDonorPair pdp, LociInfo matchLoci) + private async Task CheckPatientHasMissingHla(PatientDonorPair pdp, LociInfo matchLoci) { - var patientHasMissingHla = !await HasAllRequiredLoci(pdp.PatientId, false, matchLoci); - - // ReSharper disable once InvertIf - if (patientHasMissingHla) + async Task GetResult() { - pdp.DidPatientHaveMissingHla = true; - pdp.IsProcessed = true; - await UpdatePatientDonorPairRecord(pdp); + var (hasMissingHla, patientInfo) = await missingHlaChecker.SubjectHasMissingHla(pdp.PatientId, false, matchLoci); + pdp.DidPatientHaveMissingHla = hasMissingHla; + pdp.IsProcessed = hasMissingHla; + await UpdateRecord(pdp); + return new SubjectGenotypeResult(hasMissingHla, patientInfo); } - return patientHasMissingHla; + return subjectResultCache.GetOrAdd(pdp.PatientId, await GetResult()); } - private async Task DonorHasMissingHla(PatientDonorPair pdp, LociInfo matchLoci) + private async Task CheckDonorHasMissingHla(PatientDonorPair pdp, LociInfo matchLoci) { - var donorHasMissingHla = !await HasAllRequiredLoci(pdp.DonorId, true, matchLoci); - - // ReSharper disable once InvertIf - if (donorHasMissingHla) + async Task GetResult() { - pdp.DidDonorHaveMissingHla = true; - pdp.IsProcessed = true; - await UpdatePatientDonorPairRecord(pdp); + var (hasMissingHla, donorInfo) = await missingHlaChecker.SubjectHasMissingHla(pdp.DonorId, false, matchLoci); + pdp.DidDonorHaveMissingHla = hasMissingHla; + pdp.IsProcessed = hasMissingHla; + await UpdateRecord(pdp); + return new SubjectGenotypeResult(hasMissingHla, donorInfo); } - return donorHasMissingHla; + return subjectResultCache.GetOrAdd(pdp.DonorId, await GetResult()); } - /// - /// Will return false if either the subject is missing required HLA, - /// or if the subject does not exist, as this suggests it was not imported due to missing required HLA. - /// - private async Task HasAllRequiredLoci(string externalId, bool isDonor, LociInfo matchLoci) + private async Task Impute( + PatientDonorPair pdp, + SubjectGenotypeResult result, + string matchLoci, + string hlaVersion, + bool isPatient) { - var subject = await subjectRepository.GetByExternalId(externalId); + // Only request imputation if subject has not been processed before + // This step should update the cache as well + result.Genotypes ??= await genotypesProcessor.RequestAndSaveImputation(result.SubjectInfo, matchLoci, hlaVersion); - if (subject == null) + if (isPatient) { - return false; + pdp.PatientImputationCompleted = true; } - - // Can assume that if donor is in the db, it has all the required loci - if (isDonor) + else { - return true; + pdp.DonorImputationCompleted = true; } - - // patient must be typed at all match loci - var patientHla = subject.ToPhenotypeInfo(); - return matchLoci.AllAtLoci((locus, isRequired) => !isRequired || patientHla.GetLocus(locus).Position1And2NotNull()); + + await UpdateRecord(pdp); } - private async Task UpdatePatientDonorPairRecord(PatientDonorPair pdp) + private async Task UpdateRecord(PatientDonorPair pdp) { await pdpRepository.UpdateEditableFields(pdp); } diff --git a/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/SubjectGenotypesProcessor.cs b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/SubjectGenotypesProcessor.cs new file mode 100644 index 000000000..4c7c2abf9 --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation/Services/Exercise4/Homework/SubjectGenotypesProcessor.cs @@ -0,0 +1,111 @@ +using Atlas.Debug.Client.Models.MatchPrediction; +using Atlas.MatchPrediction.Test.Validation.Data.Models; +using Atlas.MatchPrediction.Test.Validation.Data.Models.Homework; +using Atlas.MatchPrediction.Test.Validation.Data.Repositories.Homework; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SubjectInfo = Atlas.MatchPrediction.Test.Validation.Data.Models.SubjectInfo; + +namespace Atlas.MatchPrediction.Test.Validation.Services.Exercise4.Homework +{ + internal interface ISubjectGenotypesProcessor + { + /// `null` means the imputation request failed. + /// Empty collection means success, but no genotypes found, which is a valid response. + Task> RequestAndSaveImputation( + SubjectInfo subjectInfo, + string matchLoci, + string hlaVersion); + } + + internal class SubjectGenotypesProcessor : ISubjectGenotypesProcessor + { + private readonly IImputationRequester imputationRequester; + private readonly IImputationSummaryRepository summaryRepo; + private readonly ISubjectGenotypeRepository subjectGenotypeRepo; + + public SubjectGenotypesProcessor( + IImputationRequester imputationRequester, + IImputationSummaryRepository summaryRepo, + ISubjectGenotypeRepository subjectGenotypeRepo) + { + this.imputationRequester = imputationRequester; + this.summaryRepo = summaryRepo; + this.subjectGenotypeRepo = subjectGenotypeRepo; + } + + public async Task> RequestAndSaveImputation( + SubjectInfo subjectInfo, + string matchLoci, + string hlaVersion) + { + var subjectId = subjectInfo.ExternalId; + + var response = await imputationRequester.Request(new HomeworkImputationRequest + { + SubjectHla = subjectInfo.ToPhenotypeInfo(), + ExternalHfSetId = subjectInfo.ExternalHfSetId, + MatchLoci = matchLoci, + HlaVersion = hlaVersion + }); + + // if response is not successful, return, the PDP will remain as "unprocessed" in the db + if (response is not { WasSuccess: true }) + { + System.Diagnostics.Debug.WriteLine($"Imputation request for {subjectId} was not successful."); + return null; + } + + var summaryId = await summaryRepo.Add(BuildSummary(subjectId, response.Result!)); + var genotypes = SplitIntoGenotypes(response.Result!.GenotypeLikelihoods, summaryId).ToList(); + await subjectGenotypeRepo.BulkInsert(genotypes); + return genotypes; + } + + private static ImputationSummary BuildSummary( + string externalSubjectId, + GenotypeImputationResponse genotypeResponse) + { + return new ImputationSummary + { + ExternalSubjectId = externalSubjectId, + HfSetPopulationId = genotypeResponse.HaplotypeFrequencySet.PopulationId, + WasRepresented = !genotypeResponse.IsUnrepresented, + GenotypeCount = genotypeResponse.GenotypeCount, + SumOfLikelihoods = genotypeResponse.SumOfLikelihoods + }; + } + + private static IEnumerable SplitIntoGenotypes( + string genotypesAsString, int imputationSummaryId) + { + // split genotypesAsString into individual genotypes by splitting by \r\n + // first line contains header so will be skipped + var genotypeStrings = genotypesAsString + .Split("\r\n") + .Skip(1) + .Where(s => !string.IsNullOrEmpty(s)); + + foreach (var genotypeString in genotypeStrings) + { + var genotypeParts = genotypeString.Split(","); + yield return new SubjectGenotype + { + A_1 = genotypeParts[0], + A_2 = genotypeParts[1], + B_1 = genotypeParts[2], + B_2 = genotypeParts[3], + C_1 = genotypeParts[4], + C_2 = genotypeParts[5], + DQB1_1 = genotypeParts[6], + DQB1_2 = genotypeParts[7], + DRB1_1 = genotypeParts[8], + DRB1_2 = genotypeParts[9], + Likelihood = decimal.Parse(genotypeParts[10]), + ImputationSummary_Id = imputationSummaryId + }; + } + } + } +} diff --git a/Atlas.MatchPrediction.Test.Validation/Settings/ValidationHomeworkSettings.cs b/Atlas.MatchPrediction.Test.Validation/Settings/ValidationHomeworkSettings.cs new file mode 100644 index 000000000..7d7db570b --- /dev/null +++ b/Atlas.MatchPrediction.Test.Validation/Settings/ValidationHomeworkSettings.cs @@ -0,0 +1,8 @@ +namespace Atlas.MatchPrediction.Test.Validation.Settings +{ + internal class ValidationHomeworkSettings + { + public string ImputationRequestUrl { get; set; } + public string MatchingGenotypesRequestUrl { get; set; } + } +} \ No newline at end of file diff --git a/Atlas.MatchPrediction.Test.Validation/Startup.cs b/Atlas.MatchPrediction.Test.Validation/Startup.cs index 0a45e3938..7b979027b 100644 --- a/Atlas.MatchPrediction.Test.Validation/Startup.cs +++ b/Atlas.MatchPrediction.Test.Validation/Startup.cs @@ -29,6 +29,7 @@ public override void Configure(IFunctionsHostBuilder builder) OptionsReaderFor(), OptionsReaderFor(), OptionsReaderFor(), + OptionsReaderFor(), ConnectionStringReader("MatchPredictionValidation:Sql"), ConnectionStringReader("MatchPrediction:Sql"), ConnectionStringReader("DonorImport:Sql")); @@ -39,6 +40,7 @@ private static void RegisterSettings(IServiceCollection services) services.RegisterAsOptions("OutgoingMatchPredictionRequests"); services.RegisterAsOptions("AzureStorage"); services.RegisterAsOptions("DataRefresh"); + services.RegisterAsOptions("Homework"); services.RegisterAsOptions("MessagingServiceBus"); services.RegisterAsOptions("MatchPredictionRequests"); services.RegisterAsOptions("Search"); diff --git a/Atlas.MatchPrediction.Test.Validation/local.setting.template.json b/Atlas.MatchPrediction.Test.Validation/local.setting.template.json index 5d7193967..c4a6f4934 100644 --- a/Atlas.MatchPrediction.Test.Validation/local.setting.template.json +++ b/Atlas.MatchPrediction.Test.Validation/local.setting.template.json @@ -13,6 +13,9 @@ "FUNCTIONS_WORKER_RUNTIME": "dotnet", + "Homework:ImputationRequestUrl": "override-this", + "Homework:MatchingGenotypesRequestUrl": "override-this", + "OutgoingMatchPredictionRequests:RequestBatchSize": 1000, "OutgoingMatchPredictionRequests:RequestUrl": "http://localhost:7085/api/BatchMatchPredictionRequests", diff --git a/Atlas.MatchPrediction/ExternalInterface/Models/HaplotypeFrequencySet/HaplotypeFrequencySet.cs b/Atlas.MatchPrediction/ExternalInterface/Models/HaplotypeFrequencySet/HaplotypeFrequencySet.cs index 7c3f29675..585bcc448 100644 --- a/Atlas.MatchPrediction/ExternalInterface/Models/HaplotypeFrequencySet/HaplotypeFrequencySet.cs +++ b/Atlas.MatchPrediction/ExternalInterface/Models/HaplotypeFrequencySet/HaplotypeFrequencySet.cs @@ -9,7 +9,7 @@ public class HaplotypeFrequencySet public string Name { get; set; } public int PopulationId { get; set; } - internal Client.Models.Search.Results.MatchPrediction.HaplotypeFrequencySet ToClientHaplotypeFrequencySet() + public Client.Models.Search.Results.MatchPrediction.HaplotypeFrequencySet ToClientHaplotypeFrequencySet() { return new Client.Models.Search.Results.MatchPrediction.HaplotypeFrequencySet {