From 35cda50ad74fbcf4eaab6a38d4c3a6a33a0a6296 Mon Sep 17 00:00:00 2001 From: Simon Sarasova Date: Sun, 2 Jun 2024 08:43:39 +0000 Subject: [PATCH] Improved the creation procedures, encoding format, and graphical presentation of genetic analyses. Map lists have been replaced by custom objects. --- Changelog.md | 1 + Contributors.md | 2 +- documentation/Whitepaper.md | 2 +- gui/helpGui.go | 44 +- gui/manageGeneticsGui.go | 200 +- gui/viewAnalysisGui_Couple.go | 521 ++-- gui/viewAnalysisGui_Person.go | 412 +-- gui/viewProfileGui.go | 222 +- internal/encoding/encoding.go | 29 +- internal/genetics/companyAnalysis/23andMe.go | 50 +- .../createGeneticAnalysis.go | 2575 ++++++++--------- .../createGeneticAnalysis_test.go | 66 +- .../geneticAnalysis/geneticAnalysis.go | 318 ++ internal/genetics/myAnalyses/myAnalyses.go | 243 +- .../myChosenAnalysis/myChosenAnalysis.go | 58 +- internal/genetics/myCouples/myCouples.go | 78 +- internal/genetics/myGenomes/myGenomes.go | 59 +- internal/genetics/myPeople/myPeople.go | 4 +- .../prepareRawGenomes/prepareRawGenomes.go | 62 +- .../readGeneticAnalysis.go | 1830 ++++-------- .../sampleAnalyses/SampleCoupleAnalysis.json | 712 ----- .../SampleCoupleAnalysis.messagepack | Bin 0 -> 11391 bytes .../sampleAnalyses/SamplePerson1Analysis.json | 922 ------ .../SamplePerson1Analysis.messagepack | Bin 0 -> 12030 bytes .../sampleAnalyses/SamplePerson2Analysis.json | 923 ------ .../SamplePerson2Analysis.messagepack | Bin 0 -> 10050 bytes .../genetics/sampleAnalyses/sampleAnalyses.go | 31 +- .../sampleAnalyses/sampleAnalyses_test.go | 12 +- internal/helpers/helpers.go | 24 + internal/helpers/helpers_test.go | 1 - .../network/myMateCriteria/myMateCriteria.go | 4 +- .../calculatedAttributes.go | 150 +- .../myProfileExports/myProfileExports.go | 19 +- .../geneticReferences_test.go | 6 +- .../polygenicDiseases/breastCancer.go | 2 +- .../createGeneticModels.go | 3 +- .../createSampleGeneticAnalyses/.gitignore | 6 +- .../createSampleGeneticAnalyses.go | 12 +- 38 files changed, 3376 insertions(+), 6227 deletions(-) create mode 100644 internal/genetics/geneticAnalysis/geneticAnalysis.go delete mode 100644 internal/genetics/sampleAnalyses/SampleCoupleAnalysis.json create mode 100644 internal/genetics/sampleAnalyses/SampleCoupleAnalysis.messagepack delete mode 100644 internal/genetics/sampleAnalyses/SamplePerson1Analysis.json create mode 100644 internal/genetics/sampleAnalyses/SamplePerson1Analysis.messagepack delete mode 100644 internal/genetics/sampleAnalyses/SamplePerson2Analysis.json create mode 100644 internal/genetics/sampleAnalyses/SamplePerson2Analysis.messagepack diff --git a/Changelog.md b/Changelog.md index 76dafcf..0b2100d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Small and insignificant changes may not be included in this log. ## Unversioned Changes +* Improved the creation procedures, encoding format, and graphical presentation of genetic analyses. Map lists have been replaced by custom objects. - *Simon Sarasova* * Upgraded Circl to version 1.3.8. - *Simon Sarasova* ## Version 0.60 diff --git a/Contributors.md b/Contributors.md index d8520c6..f5a0c97 100644 --- a/Contributors.md +++ b/Contributors.md @@ -9,4 +9,4 @@ Many other people have written code for modules which are imported by Seekia. Th Name | Date Of First Commit | Number Of Commits --- | --- | --- -Simon Sarasova | June 13, 2023 | 247 \ No newline at end of file +Simon Sarasova | June 13, 2023 | 248 \ No newline at end of file diff --git a/documentation/Whitepaper.md b/documentation/Whitepaper.md index a122bcf..1b67813 100644 --- a/documentation/Whitepaper.md +++ b/documentation/Whitepaper.md @@ -232,7 +232,7 @@ If two people who have the same recessive monogenic disease breed, their offspri In order to prevent people with recessive monogenic diseases from being conceived, we must prevent people who have any defects in genes for the same diseases from breeding with each other. This practice only requires reducing each person's pool of potential mates by a small amount (~5% in 2024), but will result in a drastic reduction in the prevalence of recessive monogenic disorders within the human species. -A Person analysis describes a person’s probability of having each monogenic disease and their probability of passing a disease variant for each disease. A Couple analysis will report on the offspring's probability of having each monogenic disease. Users can share their monogenic disease probabilities on their profiles, and users can filter and sort users by their offspring's probability of having a monogenic disease. +A Person analysis describes if a person has each monogenic disease and their probability of passing a disease variant for each disease. A Couple analysis will report on the offspring's probability of having each monogenic disease. Users can share their monogenic disease probabilities on their profiles, and users can filter and sort users by their offspring's probability of having a monogenic disease. Users have 2 options for filtering their offspring's monogenic disease probability: 0% and <100%. diff --git a/gui/helpGui.go b/gui/helpGui.go index 6dcae3f..38e8202 100644 --- a/gui/helpGui.go +++ b/gui/helpGui.go @@ -338,6 +338,33 @@ func setNumberOfTestedVariantsExplainerPage(window fyne.Window, previousPage fun setPageContent(page, window) } +func setNumberOfPhasedLociExplainerPage(window fyne.Window, previousPage func()){ + + title := getPageTitleCentered("Help - Phased Loci") + + backButton := getBackButtonCentered(previousPage) + + subtitle := getPageSubtitleCentered("Number of Phased Loci") + + description1 := getLabelCentered("Each monogenic disease has a set of variants Seekia tests for.") + description2 := getLabelCentered("These variants are specific mutations that cause each monogenic disease.") + description3 := getLabelCentered("Each variant effects a specific locus of the genome.") + description4 := getLabelCentered("Each genome file reports the values of loci.") + description5 := getLabelCentered("A reported value can either be phased or unphased.") + description6 := getLabelCentered("Phased values provide more information than unphased values.") + description7 := getLabelCentered("The greater the number of phased loci is, the more accurate a genetic analysis will be.") + description8 := getLabelCentered("Import a phased genome for the most accurate genetic analysis results.") + description9 := getLabelCentered("Use a genome sequencing provider which produces phased genome files.") + + //TODO: Explain that phased genomes don't always increase accuracy, and the results may still be fully accurate + // For example, if you have no mutations or if you only have 1 mutation on 1 locus, + // a phased sequence has no impact on your disease analysis results. + + page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8, description9) + + setPageContent(page, window) +} + func setCoupleGenomePairExplainerPage(window fyne.Window, previousPage func()){ @@ -378,20 +405,21 @@ func setPersonProbabilityOfPassingVariantExplainerPage(window fyne.Window, previ setPageContent(page, window) } -func setPersonProbabilityOfHavingMonogenicDiseaseExplainerPage(window fyne.Window, previousPage func()){ +func setPersonHasMonogenicDiseaseExplainerPage(window fyne.Window, previousPage func()){ - title := getPageTitleCentered("Help - Person Monogenic Disease Probability") + title := getPageTitleCentered("Help - Person Has Monogenic Disease") backButton := getBackButtonCentered(previousPage) - subtitle := getPageSubtitleCentered("Probability Of Having Monogenic Disease") + subtitle := getPageSubtitleCentered("Person Has Monogenic Disease") - description1 := getLabelCentered("This is the probability that the analyzed person has this monogenic disease.") - description2 := getLabelCentered("This probability becomes more accurate if more variants are tested.") - description3 := getLabelCentered("The person's imported genomes may not contain locations for some variants.") - description4 := getLabelCentered("Seekia also does not test for many rare disease variants.") + description1 := getLabelCentered("This describes if the analyzed person has this monogenic disease.") + description2 := getLabelCentered("This result becomes more accurate if more variants are tested.") + description3 := getLabelCentered("It also become more accurate in certain circumstances if the tested loci are phased.") + description4 := getLabelCentered("The person's imported genomes may not contain locations for some variants.") + description5 := getLabelCentered("Seekia also does not test for many rare disease variants.") - page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4) + page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5) setPageContent(page, window) } diff --git a/gui/manageGeneticsGui.go b/gui/manageGeneticsGui.go index 5115746..f95713a 100644 --- a/gui/manageGeneticsGui.go +++ b/gui/manageGeneticsGui.go @@ -12,6 +12,8 @@ import "fyne.io/fyne/v2/layout" import "fyne.io/fyne/v2/dialog" import "fyne.io/fyne/v2/data/binding" +import "seekia/internal/appMemory" +import "seekia/internal/encoding" import "seekia/internal/genetics/myGenomes" import "seekia/internal/genetics/myPeople" import "seekia/internal/genetics/myCouples" @@ -19,7 +21,6 @@ import "seekia/internal/genetics/myAnalyses" import "seekia/internal/genetics/readRawGenomes" import "seekia/internal/genetics/sampleAnalyses" import "seekia/internal/helpers" -import "seekia/internal/appMemory" import "seekia/internal/localFilesystem" import "time" @@ -94,13 +95,13 @@ func setViewSampleGeneticAnalysesPage(window fyne.Window, previousPage func()){ personIdentifier := "111111111111111111111111111111" - analysisMapList, err := sampleAnalyses.GetSamplePerson1Analysis() + analysisObject, err := sampleAnalyses.GetSamplePerson1Analysis() if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } - setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisMapList, 1, currentPage) + setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisObject, 1, currentPage) }) personAnalysisButtonWithIcon := container.NewGridWithColumns(1, personIcon, personAnalysisButton) @@ -112,28 +113,28 @@ func setViewSampleGeneticAnalysesPage(window fyne.Window, previousPage func()){ } coupleAnalysisButton := widget.NewButton("Couple Analysis", func(){ - personAIdentifier := "111111111111111111111111111111" - personBIdentifier := "222222222222222222222222222222" + person1Identifier := "111111111111111111111111111111" + person2Identifier := "222222222222222222222222222222" - person1AnalysisMapList, err := sampleAnalyses.GetSamplePerson1Analysis() + person1AnalysisObject, err := sampleAnalyses.GetSamplePerson1Analysis() if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } - person2AnalysisMapList, err := sampleAnalyses.GetSamplePerson2Analysis() + person2AnalysisObject, err := sampleAnalyses.GetSamplePerson2Analysis() if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } - coupleAnalysisMapList, err := sampleAnalyses.GetSampleCoupleAnalysis() + coupleAnalysisObject, err := sampleAnalyses.GetSampleCoupleAnalysis() if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } - setViewCoupleGeneticAnalysisPage(window, personAIdentifier, personBIdentifier, person1AnalysisMapList, person2AnalysisMapList, coupleAnalysisMapList, 1, 1, currentPage) + setViewCoupleGeneticAnalysisPage(window, person1Identifier, person2Identifier, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, 1, 1, currentPage) }) coupleAnalysisButtonWithIcon := container.NewGridWithColumns(1, coupleIcon, coupleAnalysisButton) @@ -597,11 +598,16 @@ func setManagePersonGenomesPage(window fyne.Window, personIdentifier string, pre companyNameLabel := widget.NewLabel(companyName) - genomeIdentifier, exists := genomeMap["GenomeIdentifier"] + genomeIdentifierHex, exists := genomeMap["GenomeIdentifier"] if (exists == false){ return nil, errors.New("Malformed myGenomesMapList: Item missing GenomeIdentifier") } + genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex) + if (err != nil){ + return nil, errors.New("Malformed myGenomesMapList: Item contains invalid GenomeIdentifier: " + genomeIdentifierHex) + } + manageButton := widget.NewButtonWithIcon("Manage", theme.VisibilityIcon(), func(){ setManageGenomePage(window, genomeIdentifier, currentPage) }) @@ -709,7 +715,7 @@ func setImportRawGenomePage(window fyne.Window, personIdentifier string, previou } // This provides a page to manage a person's Genome -func setManageGenomePage(window fyne.Window, genomeIdentifier string, previousPage func()){ +func setManageGenomePage(window fyne.Window, genomeIdentifier [16]byte, previousPage func()){ currentPage := func(){setManageGenomePage(window, genomeIdentifier, previousPage)} @@ -791,7 +797,7 @@ func setManageGenomePage(window fyne.Window, genomeIdentifier string, previousPa return } importedTimeLabel := getItalicLabelCentered("Imported " + importedTimeAgo + ".") - + deleteGenomeButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Genome", theme.DeleteIcon(), func(){ setConfirmDeletePersonGenomePage(window, personIdentifier, genomeIdentifier, currentPage, previousPage) })) @@ -801,7 +807,7 @@ func setManageGenomePage(window fyne.Window, genomeIdentifier string, previousPa setPageContent(page, window) } -func setConfirmDeletePersonGenomePage(window fyne.Window, personIdentifier string, genomeIdentifier string, previousPage func(), nextPage func()){ +func setConfirmDeletePersonGenomePage(window fyne.Window, personIdentifier string, genomeIdentifier [16]byte, previousPage func(), nextPage func()){ currentPage := func(){setConfirmDeletePersonGenomePage(window, personIdentifier, genomeIdentifier, previousPage, nextPage)} @@ -972,7 +978,7 @@ func setAnalyzePersonGeneticsPage(window fyne.Window, personIdentifier string, p viewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("View Analysis", theme.VisibilityIcon(), func(){ - analysisFound, analysisMapList, err := myAnalyses.GetGeneticAnalysis(newestAnalysisIdentifier) + analysisFound, analysisObject, err := myAnalyses.GetPersonGeneticAnalysis(newestAnalysisIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return @@ -982,7 +988,7 @@ func setAnalyzePersonGeneticsPage(window fyne.Window, personIdentifier string, p return } - setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisMapList, numberOfGenomesInAnalysis, currentPage) + setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisObject, numberOfGenomesInAnalysis, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), analysisReadyLabel, personAnalyzedRow, numberOfGenomesInAnalysisRow, timeAgoPerformedLabel, viewAnalysisButton) @@ -1193,38 +1199,38 @@ func setManageCouplesPage(window fyne.Window, previousPage func()){ for _, coupleMap := range myCouplesMapList{ - personAIdentifier, exists := coupleMap["PersonAIdentifier"] + person1Identifier, exists := coupleMap["Person1Identifier"] if (exists == false) { - return nil, errors.New("Malformed myCoupleAnalysesMapList: Item missing personAIdentifier") + return nil, errors.New("Malformed myCoupleAnalysesMapList: Item missing Person1Identifier") } - personBIdentifier, exists := coupleMap["PersonBIdentifier"] + person2Identifier, exists := coupleMap["Person2Identifier"] if (exists == false) { - return nil, errors.New("Malformed myCoupleAnalysesMapList: Item missing personBIdentifier") + return nil, errors.New("Malformed myCoupleAnalysesMapList: Item missing Person2Identifier") } - personFound, personAName, _, _, err := myPeople.GetPersonInfo(personAIdentifier) + personFound, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier) if (err != nil) { return nil, err } if (personFound == false){ return nil, errors.New("Couple person not found.") } - personFound, personBName, _, _, err := myPeople.GetPersonInfo(personBIdentifier) + personFound, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier) if (err != nil) { return nil, err } if (personFound == false){ return nil, errors.New("Couple person not found.") } - personANameTrimmed, _, err := helpers.TrimAndFlattenString(personAName, 15) + person1NameTrimmed, _, err := helpers.TrimAndFlattenString(person1Name, 15) if (err != nil) { return nil, err } - personBNameTrimmed, _, err := helpers.TrimAndFlattenString(personBName, 15) + person2NameTrimmed, _, err := helpers.TrimAndFlattenString(person2Name, 15) if (err != nil) { return nil, err } - coupleName := personANameTrimmed + " + " + personBNameTrimmed + coupleName := person1NameTrimmed + " + " + person2NameTrimmed manageCoupleButton := widget.NewButton(coupleName, func(){ - setManageCouplePage(window, personAIdentifier, personBIdentifier, currentPage) + setManageCouplePage(window, person1Identifier, person2Identifier, currentPage) }) manageButtonsGrid.Add(manageCoupleButton) @@ -1374,16 +1380,16 @@ func setCreateCouplePage(window fyne.Window, previousPage func()){ return } - personAIdentifier := selectedPeopleList[0] - personBIdentifier := selectedPeopleList[1] + person1Identifier := selectedPeopleList[0] + person2Identifier := selectedPeopleList[1] - _, err = myCouples.AddCouple(personAIdentifier, personBIdentifier) + _, err = myCouples.AddCouple(person1Identifier, person2Identifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } - setManageCouplePage(window, personAIdentifier, personBIdentifier, previousPage) + setManageCouplePage(window, person1Identifier, person2Identifier, previousPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), choosePeopleGrid, createCoupleButton) @@ -1392,36 +1398,36 @@ func setCreateCouplePage(window fyne.Window, previousPage func()){ } -func setManageCouplePage(window fyne.Window, personAIdentifier string, personBIdentifier string, previousPage func()){ +func setManageCouplePage(window fyne.Window, person1Identifier string, person2Identifier string, previousPage func()){ - currentPage := func(){setManageCouplePage(window, personAIdentifier, personBIdentifier, previousPage)} + currentPage := func(){setManageCouplePage(window, person1Identifier, person2Identifier, previousPage)} title := getPageTitleCentered("Genetics - Manage Couple") backButton := getBackButtonCentered(previousPage) - personAFound, personAName, _, _, err := myPeople.GetPersonInfo(personAIdentifier) + person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - if (personAFound == false){ - setErrorEncounteredPage(window, errors.New("setManageCouplePage called with unknown personAIdentifier"), previousPage) + if (person1Found == false){ + setErrorEncounteredPage(window, errors.New("setManageCouplePage called with unknown person1Identifier"), previousPage) return } - personBFound, personBName, _, _, err := myPeople.GetPersonInfo(personBIdentifier) + person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - if (personBFound == false){ - setErrorEncounteredPage(window, errors.New("setManageCouplePage called with unknown personBIdentifier"), previousPage) + if (person2Found == false){ + setErrorEncounteredPage(window, errors.New("setManageCouplePage called with unknown person2Identifier"), previousPage) return } coupleNameLabel := getLabelCentered("Couple Name:") - coupleNameText := getBoldLabelCentered(personAName + " + " + personBName) + coupleNameText := getBoldLabelCentered(person1Name + " + " + person2Name) analyzeIcon, err := getFyneImageIcon("Stats") if (err != nil){ @@ -1429,12 +1435,12 @@ func setManageCouplePage(window fyne.Window, personAIdentifier string, personBId return } analyzeGeneticsButton := widget.NewButton("Analyze Genetics", func(){ - setAnalyzeCoupleGeneticsPage(window, personAIdentifier, personBIdentifier, currentPage) + setAnalyzeCoupleGeneticsPage(window, person1Identifier, person2Identifier, currentPage) }) analyzeGeneticsButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, analyzeIcon, analyzeGeneticsButton)) deleteCoupleButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Couple", theme.DeleteIcon(), func(){ - setDeleteCouplePage(window, personAIdentifier, personBIdentifier, currentPage, previousPage) + setDeleteCouplePage(window, person1Identifier, person2Identifier, currentPage, previousPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), coupleNameLabel, coupleNameText, widget.NewSeparator(), analyzeGeneticsButtonWithIcon, widget.NewSeparator(), deleteCoupleButton) @@ -1442,9 +1448,9 @@ func setManageCouplePage(window fyne.Window, personAIdentifier string, personBId setPageContent(page, window) } -func setDeleteCouplePage(window fyne.Window, personAIdentifier string, personBIdentifier string, previousPage func(), nextPage func()){ +func setDeleteCouplePage(window fyne.Window, person1Identifier string, person2Identifier string, previousPage func(), nextPage func()){ - currentPage := func(){setDeleteCouplePage(window, personAIdentifier, personBIdentifier, previousPage, nextPage)} + currentPage := func(){setDeleteCouplePage(window, person1Identifier, person2Identifier, previousPage, nextPage)} title := getPageTitleCentered("Genetics - Delete Couple") @@ -1452,29 +1458,29 @@ func setDeleteCouplePage(window fyne.Window, personAIdentifier string, personBId subtitle := getPageSubtitleCentered("Delete Couple") - personAFound, personAName, _, _, err := myPeople.GetPersonInfo(personAIdentifier) + person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - if (personAFound == false){ - setErrorEncounteredPage(window, errors.New("setDeleteGenomeCouplePage called with unknown personAIdentifier"), previousPage) + if (person1Found == false){ + setErrorEncounteredPage(window, errors.New("setDeleteGenomeCouplePage called with unknown person1Identifier"), previousPage) return } - personBFound, personBName, _, _, err := myPeople.GetPersonInfo(personBIdentifier) + person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - if (personBFound == false){ - setErrorEncounteredPage(window, errors.New("setDeleteGenomeCouplePage called with unknown personBIdentifier"), previousPage) + if (person2Found == false){ + setErrorEncounteredPage(window, errors.New("setDeleteGenomeCouplePage called with unknown person2Identifier"), previousPage) return } description := getLabelCentered("Confirm to delete this couple?") - coupleName := personAName + " + " + personBName + coupleName := person1Name + " + " + person2Name coupleNameLabel := widget.NewLabel("Couple Name:") coupleNameText := getBoldLabel(coupleName) @@ -1483,7 +1489,7 @@ func setDeleteCouplePage(window fyne.Window, personAIdentifier string, personBId deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){ - err := myCouples.DeleteCouple(personAIdentifier, personBIdentifier) + err := myCouples.DeleteCouple(person1Identifier, person2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return @@ -1498,15 +1504,15 @@ func setDeleteCouplePage(window fyne.Window, personAIdentifier string, personBId } -func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPersonAIdentifier string, inputPersonBIdentifier string, previousPage func()){ +func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPerson1Identifier string, inputPerson2Identifier string, previousPage func()){ appMemory.SetMemoryEntry("CurrentViewedPage", "AnalyzeCoupleGeneticsPage") - currentPage := func(){setAnalyzeCoupleGeneticsPage(window, inputPersonAIdentifier, inputPersonBIdentifier, previousPage)} + currentPage := func(){setAnalyzeCoupleGeneticsPage(window, inputPerson1Identifier, inputPerson2Identifier, previousPage)} // We have to sort the person identifiers so they are in the same order as they exist in the couple analysis - personAIdentifier, personBIdentifier, err := myAnalyses.GetPeopleIdentifiersSortedForCouple(inputPersonAIdentifier, inputPersonBIdentifier) + person1Identifier, person2Identifier, err := myAnalyses.GetPeopleIdentifiersSortedForCouple(inputPerson1Identifier, inputPerson2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -1518,54 +1524,54 @@ func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPersonAIdentifier str description := getLabelCentered("Analyze this couple's genetics.") - personAFound, personAName, _, _, err := myPeople.GetPersonInfo(personAIdentifier) + person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - if (personAFound == false){ + if (person1Found == false){ setErrorEncounteredPage(window, errors.New("setAnalyzeCoupleGeneticsPage called with missing person"), previousPage) return } - personBFound, personBName, _, _, err := myPeople.GetPersonInfo(personBIdentifier) + person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - if (personBFound == false){ + if (person2Found == false){ setErrorEncounteredPage(window, errors.New("setAnalyzeCoupleGeneticsPage called with missing person"), previousPage) return } - allPersonARawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(personAIdentifier) + allPerson1RawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(person1Identifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } - allPersonBRawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(personBIdentifier) + allPerson2RawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(person2Identifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } - if (len(allPersonARawGenomeIdentifiersList) == 0 || len(allPersonBRawGenomeIdentifiersList) == 0){ + if (len(allPerson1RawGenomeIdentifiersList) == 0 || len(allPerson2RawGenomeIdentifiersList) == 0){ // At least one of the people is missing genomes getGenomesMissingText := func()string{ - if (len(allPersonARawGenomeIdentifiersList) == 0 && len(allPersonBRawGenomeIdentifiersList) == 0){ - missingLabelText := personAName + " and " + personBName + " have no genomes." + if (len(allPerson1RawGenomeIdentifiersList) == 0 && len(allPerson2RawGenomeIdentifiersList) == 0){ + missingLabelText := person1Name + " and " + person2Name + " have no genomes." return missingLabelText } - if (len(allPersonARawGenomeIdentifiersList) == 0 && len(allPersonBRawGenomeIdentifiersList) != 0){ + if (len(allPerson1RawGenomeIdentifiersList) == 0 && len(allPerson2RawGenomeIdentifiersList) != 0){ - missingLabelText := personAName + " has no genomes." + missingLabelText := person1Name + " has no genomes." return missingLabelText } - // len(allPersonARawGenomeIdentifiersList) != 0 && len(allPersonBRawGenomeIdentifiersList) == 0){ + // len(allPerson1RawGenomeIdentifiersList) != 0 && len(allPerson2RawGenomeIdentifiersList) == 0){ - missingLabelText := personBName + " has no genomes." + missingLabelText := person2Name + " has no genomes." return missingLabelText } @@ -1579,35 +1585,35 @@ func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPersonAIdentifier str return } - anyPersonAAnalysisFound, newestPersonAAnalysisIdentifier, _, newestPersonAAnalysisListOfGenomesAnalyzed, newerPersonAAnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(personAIdentifier) + anyPerson1AnalysisFound, newestPerson1AnalysisIdentifier, _, newestPerson1AnalysisListOfGenomesAnalyzed, newerPerson1AnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(person1Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - anyPersonBAnalysisFound, newestPersonBAnalysisIdentifier, _, newestPersonBAnalysisListOfGenomesAnalyzed, newerPersonBAnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(personBIdentifier) + anyPerson2AnalysisFound, newestPerson2AnalysisIdentifier, _, newestPerson2AnalysisListOfGenomesAnalyzed, newerPerson2AnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(person2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - anyCoupleAnalysisFound, newestCoupleAnalysisIdentifier, timeOfNewestAnalysis, newestCoupleAnalysisListOfGenomesAnalyzed_PersonA, newestCoupleAnalysisListOfGenomesAnalyzed_PersonB, newerCoupleAnalysisVersionAvailable, err := myAnalyses.GetCoupleNewestGeneticAnalysisInfo(personAIdentifier, personBIdentifier) + anyCoupleAnalysisFound, newestCoupleAnalysisIdentifier, timeOfNewestAnalysis, newestCoupleAnalysisListOfGenomesAnalyzed_Person1, newestCoupleAnalysisListOfGenomesAnalyzed_Person2, newerCoupleAnalysisVersionAvailable, err := myAnalyses.GetCoupleNewestGeneticAnalysisInfo(person1Identifier, person2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - if (anyCoupleAnalysisFound == false || anyPersonAAnalysisFound == false || anyPersonBAnalysisFound == false){ + if (anyCoupleAnalysisFound == false || anyPerson1AnalysisFound == false || anyPerson2AnalysisFound == false){ // We will skip to the ConfirmPerformAnalysis page - setConfirmPerformCoupleAnalysisPage(window, personAIdentifier, personBIdentifier, previousPage, currentPage) + setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, previousPage, currentPage) return } - if (newerPersonAAnalysisVersionAvailable == true || newerPersonBAnalysisVersionAvailable == true || newerCoupleAnalysisVersionAvailable == true){ + if (newerPerson1AnalysisVersionAvailable == true || newerPerson2AnalysisVersionAvailable == true || newerCoupleAnalysisVersionAvailable == true){ description1 := getLabelCentered("A new analysis method is available!") description2 := getLabelCentered("You must perform a new analysis to see the couple's new results.") performNewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform New Analysis", theme.NavigateNextIcon(), func(){ - setConfirmPerformCoupleAnalysisPage(window, personAIdentifier, personBIdentifier, currentPage, currentPage) + setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, currentPage, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), description1, description2, performNewAnalysisButton) @@ -1619,22 +1625,22 @@ func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPersonAIdentifier str // This will return true if a newer analysis can be performed using newly imported genomes getAnalysisIsMissingGenomesBool := func()(bool, error){ - genomesAreIdentical := helpers.CheckIfTwoListsContainIdenticalItems(allPersonARawGenomeIdentifiersList, newestPersonAAnalysisListOfGenomesAnalyzed) + genomesAreIdentical := helpers.CheckIfTwoListsContainIdenticalItems(allPerson1RawGenomeIdentifiersList, newestPerson1AnalysisListOfGenomesAnalyzed) if (genomesAreIdentical == false){ return true, nil } - genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPersonBRawGenomeIdentifiersList, newestPersonBAnalysisListOfGenomesAnalyzed) + genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPerson2RawGenomeIdentifiersList, newestPerson2AnalysisListOfGenomesAnalyzed) if (genomesAreIdentical == false){ return true, nil } - genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPersonARawGenomeIdentifiersList, newestCoupleAnalysisListOfGenomesAnalyzed_PersonA) + genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPerson1RawGenomeIdentifiersList, newestCoupleAnalysisListOfGenomesAnalyzed_Person1) if (genomesAreIdentical == false){ return true, nil } - genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPersonBRawGenomeIdentifiersList, newestCoupleAnalysisListOfGenomesAnalyzed_PersonB) + genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPerson2RawGenomeIdentifiersList, newestCoupleAnalysisListOfGenomesAnalyzed_Person2) if (genomesAreIdentical == false){ return true, nil } @@ -1652,7 +1658,7 @@ func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPersonAIdentifier str description1 := getLabelCentered("At least 1 person in the couple has imported/deleted a genome.") description2 := getLabelCentered("A new analysis must be performed.") performAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform Analysis", theme.NavigateNextIcon(), func(){ - setConfirmPerformCoupleAnalysisPage(window, personAIdentifier, personBIdentifier, currentPage, currentPage) + setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, currentPage, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), description1, description2, performAnalysisButton) @@ -1664,12 +1670,12 @@ func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPersonAIdentifier str analysisReadyLabel := getBoldLabelCentered("Genetic Analysis Complete!") coupleAnalyzedLabel := widget.NewLabel("Couple Analyzed:") - coupleName := personAName + " + " + personBName + coupleName := person1Name + " + " + person2Name coupleNameLabel := getBoldLabel(coupleName) personAnalyzedRow := container.NewHBox(layout.NewSpacer(), coupleAnalyzedLabel, coupleNameLabel, layout.NewSpacer()) - numberOfPersonAGenomesInAnalysis := len(newestPersonAAnalysisListOfGenomesAnalyzed) - numberOfPersonBGenomesInAnalysis := len(newestPersonBAnalysisListOfGenomesAnalyzed) + numberOfPerson1GenomesInAnalysis := len(newestPerson1AnalysisListOfGenomesAnalyzed) + numberOfPerson2GenomesInAnalysis := len(newestPerson2AnalysisListOfGenomesAnalyzed) timeAgoPerformed, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeOfNewestAnalysis, false) if (err != nil){ @@ -1681,27 +1687,27 @@ func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPersonAIdentifier str viewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("View Analysis", theme.VisibilityIcon(), func(){ - personAAnalysisFound, personAAnalysisMapList, err := myAnalyses.GetGeneticAnalysis(newestPersonAAnalysisIdentifier) + person1AnalysisFound, person1AnalysisObject, err := myAnalyses.GetPersonGeneticAnalysis(newestPerson1AnalysisIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } - if (personAAnalysisFound == false){ + if (person1AnalysisFound == false){ setErrorEncounteredPage(window, errors.New("Person analysis not found after being found already."), currentPage) return } - personBAnalysisFound, personBAnalysisMapList, err := myAnalyses.GetGeneticAnalysis(newestPersonBAnalysisIdentifier) + person2AnalysisFound, person2AnalysisObject, err := myAnalyses.GetPersonGeneticAnalysis(newestPerson2AnalysisIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } - if (personBAnalysisFound == false){ + if (person2AnalysisFound == false){ setErrorEncounteredPage(window, errors.New("Person analysis not found after being found already."), currentPage) return } - coupleAnalysisFound, coupleAnalysisMapList, err := myAnalyses.GetGeneticAnalysis(newestCoupleAnalysisIdentifier) + coupleAnalysisFound, coupleAnalysisObject, err := myAnalyses.GetCoupleGeneticAnalysis(newestCoupleAnalysisIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return @@ -1711,7 +1717,7 @@ func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPersonAIdentifier str return } - setViewCoupleGeneticAnalysisPage(window, personAIdentifier, personBIdentifier, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, numberOfPersonAGenomesInAnalysis, numberOfPersonBGenomesInAnalysis, currentPage) + setViewCoupleGeneticAnalysisPage(window, person1Identifier, person2Identifier, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, numberOfPerson1GenomesInAnalysis, numberOfPerson2GenomesInAnalysis, currentPage) })) @@ -1721,45 +1727,45 @@ func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPersonAIdentifier str } -func setConfirmPerformCoupleAnalysisPage(window fyne.Window, personAIdentifier string, personBIdentifier string, previousPage func(), pageToVisitAfter func()){ +func setConfirmPerformCoupleAnalysisPage(window fyne.Window, person1Identifier string, person2Identifier string, previousPage func(), pageToVisitAfter func()){ appMemory.SetMemoryEntry("CurrentViewedPage", "ConfirmPerformCoupleGeneticAnalysisPage") - currentPage := func(){setConfirmPerformCoupleAnalysisPage(window, personAIdentifier, personBIdentifier, previousPage, pageToVisitAfter)} + currentPage := func(){setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, previousPage, pageToVisitAfter)} title := getPageTitleCentered("Genetics - Perform Couple Analysis") backButton := getBackButtonCentered(previousPage) - allPersonAGenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(personAIdentifier) + allPerson1GenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(person1Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - numberOfPersonAGenomes := len(allPersonAGenomesMapList) + numberOfPerson1Genomes := len(allPerson1GenomesMapList) - if (numberOfPersonAGenomes == 0){ + if (numberOfPerson1Genomes == 0){ setErrorEncounteredPage(window, errors.New("setConfirmPerformCoupleAnalysisPage called with person who has no genomes."), previousPage) return } - allPersonBGenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(personBIdentifier) + allPerson2GenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(person2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - numberOfPersonBGenomes := len(allPersonBGenomesMapList) + numberOfPerson2Genomes := len(allPerson2GenomesMapList) - if (numberOfPersonBGenomes == 0){ + if (numberOfPerson2Genomes == 0){ setErrorEncounteredPage(window, errors.New("setConfirmPerformCoupleAnalysisPage called with person who has no genomes."), previousPage) return } // Now we check if there is an analysis running - anyProcessFound, processIdentifier, err := myAnalyses.GetCoupleGeneticAnalysisProcessIdentifier(personAIdentifier, personBIdentifier) + anyProcessFound, processIdentifier, err := myAnalyses.GetCoupleGeneticAnalysisProcessIdentifier(person1Identifier, person2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -1800,7 +1806,7 @@ func setConfirmPerformCoupleAnalysisPage(window fyne.Window, personAIdentifier s startAnalysisFunction := func(){ - newProcessIdentifier, err := myAnalyses.StartCreateNewCoupleGeneticAnalysis(personAIdentifier, personBIdentifier) + newProcessIdentifier, err := myAnalyses.StartCreateNewCoupleGeneticAnalysis(person1Identifier, person2Identifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return diff --git a/gui/viewAnalysisGui_Couple.go b/gui/viewAnalysisGui_Couple.go index e3c160d..26a4e51 100644 --- a/gui/viewAnalysisGui_Couple.go +++ b/gui/viewAnalysisGui_Couple.go @@ -14,88 +14,91 @@ import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/resources/geneticReferences/traits" import "seekia/internal/appMemory" +import "seekia/internal/encoding" +import "seekia/internal/genetics/geneticAnalysis" import "seekia/internal/genetics/myGenomes" import "seekia/internal/genetics/myPeople" import "seekia/internal/genetics/readGeneticAnalysis" import "seekia/internal/helpers" -import "strings" +import "slices" import "errors" -func setViewCoupleGeneticAnalysisPage(window fyne.Window, personAIdentifier string, personBIdentifier string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, numberOfPersonAGenomesAnalyzed int, numberOfPersonBGenomesAnalyzed int, previousPage func()){ +func setViewCoupleGeneticAnalysisPage(window fyne.Window, person1Identifier string, person2Identifier string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, numberOfPerson1GenomesAnalyzed int, numberOfPerson2GenomesAnalyzed int, previousPage func()){ appMemory.SetMemoryEntry("CurrentViewedPage", "ViewCoupleGeneticAnalysisPage") - currentPage := func(){setViewCoupleGeneticAnalysisPage(window, personAIdentifier, personBIdentifier, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, numberOfPersonAGenomesAnalyzed, numberOfPersonBGenomesAnalyzed, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisPage(window, person1Identifier, person2Identifier, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, numberOfPerson1GenomesAnalyzed, numberOfPerson2GenomesAnalyzed, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis") backButton := getBackButtonCentered(previousPage) - warningLabel := getBoldLabelCentered("WARNING: Results are not accurate!") + warningLabel1 := getBoldLabelCentered("WARNING: Results are not accurate!") + warningLabel2 := getBoldLabelCentered("The analysis algorithms have known issues that are being worked on.") - personAFound, personAName, _, _, err := myPeople.GetPersonInfo(personAIdentifier) + person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } - if (personAFound == false){ + if (person1Found == false){ setErrorEncounteredPage(window, errors.New("Couple person A not found."), previousPage) return } - personBFound, personBName, _, _, err := myPeople.GetPersonInfo(personBIdentifier) + person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } - if (personBFound == false){ + if (person2Found == false){ setErrorEncounteredPage(window, errors.New("Couple person B not found."), previousPage) return } coupleNameLabel := widget.NewLabel("Couple Name:") - coupleNameText := getBoldLabel(personAName + " + " + personBName) + coupleNameText := getBoldLabel(person1Name + " + " + person2Name) coupleNameRow := container.NewHBox(layout.NewSpacer(), coupleNameLabel, coupleNameText, layout.NewSpacer()) numberOfAnalyzedGenomesLabel := getLabelCentered("Number of Analyzed Genomes:") - personANameLabel := widget.NewLabel(personAName + ":") - personANumberOfGenomesAnalyzedString := helpers.ConvertIntToString(numberOfPersonAGenomesAnalyzed) - personANumberOfGenomesAnalyzedLabel := getBoldLabel(personANumberOfGenomesAnalyzedString) - personANumberOfAnalyzedGenomesRow := container.NewHBox(layout.NewSpacer(), personANameLabel, personANumberOfGenomesAnalyzedLabel, layout.NewSpacer()) + person1NameLabel := widget.NewLabel(person1Name + ":") + person1NumberOfGenomesAnalyzedString := helpers.ConvertIntToString(numberOfPerson1GenomesAnalyzed) + person1NumberOfGenomesAnalyzedLabel := getBoldLabel(person1NumberOfGenomesAnalyzedString) + person1NumberOfAnalyzedGenomesRow := container.NewHBox(layout.NewSpacer(), person1NameLabel, person1NumberOfGenomesAnalyzedLabel, layout.NewSpacer()) - personBNameLabel := widget.NewLabel(personBName + ":") - personBNumberOfGenomesAnalyzedString := helpers.ConvertIntToString(numberOfPersonBGenomesAnalyzed) - personBNumberOfGenomesAnalyzedLabel := getBoldLabel(personBNumberOfGenomesAnalyzedString) - personBNumberOfAnalyzedGenomesRow := container.NewHBox(layout.NewSpacer(), personBNameLabel, personBNumberOfGenomesAnalyzedLabel, layout.NewSpacer()) + person2NameLabel := widget.NewLabel(person2Name + ":") + person2NumberOfGenomesAnalyzedString := helpers.ConvertIntToString(numberOfPerson2GenomesAnalyzed) + person2NumberOfGenomesAnalyzedLabel := getBoldLabel(person2NumberOfGenomesAnalyzedString) + person2NumberOfAnalyzedGenomesRow := container.NewHBox(layout.NewSpacer(), person2NameLabel, person2NumberOfGenomesAnalyzedLabel, layout.NewSpacer()) generalButton := widget.NewButton("General", func(){ //TODO: Offspring inbred rating (kinship of parents), ancestry showUnderConstructionDialog(window) }) monogenicDiseasesButton := widget.NewButton("Monogenic Diseases", func(){ - setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, currentPage) + setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, currentPage) }) polygenicDiseasesButton := widget.NewButton("Polygenic Diseases", func(){ - setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, currentPage) + setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, currentPage) }) traitsButton := widget.NewButton("Traits", func(){ - setViewCoupleGeneticAnalysisTraitsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, currentPage) + setViewCoupleGeneticAnalysisTraitsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, currentPage) }) categoryButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, generalButton, monogenicDiseasesButton, polygenicDiseasesButton, traitsButton)) - page := container.NewVBox(title, backButton, widget.NewSeparator(), warningLabel, widget.NewSeparator(), coupleNameRow, widget.NewSeparator(), numberOfAnalyzedGenomesLabel, personANumberOfAnalyzedGenomesRow, personBNumberOfAnalyzedGenomesRow, widget.NewSeparator(), categoryButtonsGrid) + page := container.NewVBox(title, backButton, widget.NewSeparator(), warningLabel1, warningLabel2, widget.NewSeparator(), coupleNameRow, widget.NewSeparator(), numberOfAnalyzedGenomesLabel, person1NumberOfAnalyzedGenomesRow, person2NumberOfAnalyzedGenomesRow, widget.NewSeparator(), categoryButtonsGrid) setPageContent(page, window) } -func setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, previousPage func()){ +func setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis - Monogenic Diseases") @@ -107,7 +110,7 @@ func setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, perso getMonogenicDiseasesGrid := func()(*fyne.Container, error){ - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ return nil, err } emptyLabelA := widget.NewLabel("") @@ -132,13 +135,15 @@ func setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, perso for _, diseaseName := range monogenicDiseaseNamesList{ - pair1GenomeIdentifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier + pair1GenomeIdentifierSlice := slices.Concat(pair1Person1GenomeIdentifier[:], pair1Person2GenomeIdentifier[:]) - genomeProbabilitiesKnown, _, genomePairOffspringProbabilityOfHavingDisease, _, _, conflictExists, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, pair1GenomeIdentifier, secondGenomePairExists) + pair1GenomeIdentifier := [32]byte(pair1GenomeIdentifierSlice) + + offspringHasDiseaseProbabilityIsKnown, _, genomePairOffspringProbabilityOfHavingDisease, _, _, _, conflictExists, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, pair1GenomeIdentifier) if (err != nil) { return nil, err } getOffspringProbabilityOfHavingDiseaseText := func()string{ - if (genomeProbabilitiesKnown == false){ + if (offspringHasDiseaseProbabilityIsKnown == false){ result := translate("Unknown") return result } @@ -159,7 +164,7 @@ func setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, perso conflictExistsColumn.Add(conflictExistsLabel) viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, currentPage) + setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, currentPage) })) viewButtonsColumn.Add(viewDetailsButton) @@ -204,15 +209,15 @@ func setViewCoupleGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, perso } -func setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, diseaseName string, previousPage func()){ +func setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, previousPage)} title := getPageTitleCentered("Viewing Couple Analysis - " + diseaseName) backButton := getBackButtonCentered(previousPage) - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -264,15 +269,17 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, offspringProbabilityOfHavingVariantColumn := container.NewVBox(offspringProbabilityOfLabel2, havingVariantLabel, widget.NewSeparator()) viewOffspringVariantButtonsColumn := container.NewVBox(emptyLabel5, emptyLabel6, widget.NewSeparator()) - addGenomePairRow := func(genomePairName string, personAGenomeIdentifier string, personBGenomeIdentifier string)error{ + addGenomePairRow := func(genomePairName string, person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{ - genomePairIdentifier := personAGenomeIdentifier + "+" + personBGenomeIdentifier + genomePairIdentifierSlice := slices.Concat(person1GenomeIdentifier[:], person2GenomeIdentifier[:]) - genomeProbabilitiesKnown, _, genomePairOffspringProbabilityOfHavingDisease, _, genomePairOffspringProbabilityOfHavingAVariant, _, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, genomePairIdentifier, secondGenomePairExists) + genomePairIdentifier := [32]byte(genomePairIdentifierSlice) + + offspringProbabilityOfHavingDiseaseIsKnown, _, genomePairOffspringProbabilityOfHavingDisease, offspringProbabilityOfHavingAVariantIsKnown, _, genomePairOffspringProbabilityOfHavingAVariant, _, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) if (err != nil) { return err } getOffspringProbabilityOfHavingDiseaseText := func()string{ - if (genomeProbabilitiesKnown == false){ + if (offspringProbabilityOfHavingDiseaseIsKnown == false){ result := translate("Unknown") return result } @@ -284,7 +291,7 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, getOffspringProbabilityOfHavingAVariantText := func()string{ - if (genomeProbabilitiesKnown == false){ + if (offspringProbabilityOfHavingAVariantIsKnown == false){ result := translate("Unknown") return result } @@ -300,11 +307,11 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, offspringProbabilityOfHavingAVariantLabel := getBoldLabelCentered(offspringProbabilityOfHavingAVariantText) viewGenomePairButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) }) viewOffspringVariantsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCoupleMonogenicDiseaseVariantsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleMonogenicDiseaseVariantsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) }) viewGenomePairButtonsColumn.Add(viewGenomePairButton) @@ -322,14 +329,14 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, return nil } - err = addGenomePairRow("Pair 1", pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier) + err = addGenomePairRow("Pair 1", pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } if (secondGenomePairExists == true){ - err := addGenomePairRow("Pair 2", pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier) + err := addGenomePairRow("Pair 2", pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -355,9 +362,9 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, } -func setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, diseaseName string, genomePairIdentifier string, genomePairName string, previousPage func()){ +func setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, previousPage)} title := getPageTitleCentered("Viewing Couple Genome Pair Info") @@ -379,6 +386,15 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window fy }) genomePairRow := container.NewHBox(layout.NewSpacer(), genomePairLabel, genomePairNameLabel, genomePairHelpButton, layout.NewSpacer()) + diseaseVariantsMap, err := monogenicDiseases.GetMonogenicDiseaseVariantsMap(diseaseName) + if (err != nil){ + setErrorEncounteredPage(window, err, previousPage) + return + } + + totalNumberOfVariants := len(diseaseVariantsMap) + totalNumberOfVariantsString := helpers.ConvertIntToString(totalNumberOfVariants) + emptyLabelA := widget.NewLabel("") personNameLabel := getItalicLabelCentered("Person Name") @@ -400,9 +416,9 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window fy numberOfVariantsTestedColumn := container.NewVBox(numberOfLabel, variantsTestedLabel, widget.NewSeparator()) viewGenomeButtonsColumn := container.NewVBox(emptyLabelC, emptyLabelD, widget.NewSeparator()) - addGenomeRow := func(isPersonA bool, personName string, inputGenomeIdentifier string)error{ + addGenomeRow := func(isPerson1 bool, personName string, inputGenomeIdentifier [16]byte)error{ - personAnalysisGenomeIdentifier, personHasMultipleGenomes, genomeIsCombined, combinedType, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPersonA, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, inputGenomeIdentifier) + personAnalysisGenomeIdentifier, _, genomeIsCombined, combinedType, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, inputGenomeIdentifier) if (err != nil) { return err } personNameTrimmed, _, err := helpers.TrimAndFlattenString(personName, 10) @@ -410,23 +426,23 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window fy personNameLabel := getBoldLabelCentered(personNameTrimmed) - getPersonAnalysisMapList := func()[]map[string]string{ - if (isPersonA == true){ - return personAAnalysisMapList + getPersonAnalysisObject := func()geneticAnalysis.PersonAnalysis{ + if (isPerson1 == true){ + return person1AnalysisObject } - return personBAnalysisMapList + return person2AnalysisObject } - personAnalysisMapList := getPersonAnalysisMapList() + personAnalysisObject := getPersonAnalysisObject() getGenomeName := func()(string, error){ if (genomeIsCombined == false){ - + genomeFound, _, _, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(personAnalysisGenomeIdentifier) if (err != nil) { return "", err } if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisMapList not found.") + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") } return companyName, nil @@ -440,7 +456,7 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window fy genomeNameLabel := getBoldLabelCentered(genomeName) - probabilitiesKnown, _, _, _, probabilityOfPassingAVariantFormatted, personNumberOfVariantsTested, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisMapList, diseaseName, personAnalysisGenomeIdentifier, personHasMultipleGenomes) + probabilitiesKnown, _, _, probabilityOfPassingAVariantFormatted, personNumberOfVariantsTested, _, _, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier) if (err != nil) { return err } getProbabilityOfPassingAVariantText := func()string{ @@ -454,14 +470,14 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window fy return probabilityOfPassingAVariantFormatted } genomeProbabilityOfPassingAVariantText := getProbabilityOfPassingAVariantText() - + genomeProbabilityOfPassingAVariantLabel := getBoldLabelCentered(genomeProbabilityOfPassingAVariantText) personNumberOfVariantsTestedString := helpers.ConvertIntToString(personNumberOfVariantsTested) - genomeNumberOfVariantsTestedLabel := getBoldLabelCentered(personNumberOfVariantsTestedString) + genomeNumberOfVariantsTestedLabel := getBoldLabelCentered(personNumberOfVariantsTestedString + "/" + totalNumberOfVariantsString) viewGenomeButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGenomeMonogenicDiseaseVariantsPage(window, personAnalysisMapList, personAnalysisGenomeIdentifier, genomeName, diseaseName, currentPage) + setViewPersonGenomeMonogenicDiseaseVariantsPage(window, personAnalysisObject, personAnalysisGenomeIdentifier, genomeName, diseaseName, currentPage) }) personNameColumn.Add(personNameLabel) @@ -479,18 +495,14 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window fy return nil } - personAGenomeIdentifier, personBGenomeIdentifier, found := strings.Cut(genomePairIdentifier, "+") - if (found == false){ - setErrorEncounteredPage(window, errors.New("setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage called with invalid genomePairIdentifier"), previousPage) - return - } + person1GenomeIdentifier, person2GenomeIdentifier := helpers.Split32ByteArrayInHalf(genomePairIdentifier) - err := addGenomeRow(true, personAName, personAGenomeIdentifier) + err = addGenomeRow(true, person1Name, person1GenomeIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - err = addGenomeRow(false, personBName, personBGenomeIdentifier) + err = addGenomeRow(false, person2Name, person2GenomeIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -515,11 +527,11 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window fy // This function provides a page to view the offspring disease variant probabilities for a particular genome pair from a couple genetic analysis -func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, diseaseName string, genomePairIdentifier string, genomePairName string, previousPage func()){ +func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){ setLoadingScreen(window, "Loading Disease Variants", "Loading disease variants...") - currentPage := func(){setViewCoupleMonogenicDiseaseVariantsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, previousPage)} + currentPage := func(){setViewCoupleMonogenicDiseaseVariantsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, previousPage)} title := getPageTitleCentered("View Offspring Disease Variants - " + diseaseName) @@ -534,36 +546,39 @@ func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, personAName s genomePairLabel := widget.NewLabel("Genome Pair:") genomePairNameLabel := getBoldLabel(genomePairName) viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) }) genomePairRow := container.NewHBox(layout.NewSpacer(), genomePairLabel, genomePairNameLabel, viewGenomePairInfoButton, layout.NewSpacer()) - getNumberOfVariantsTested := func()(int, error){ + //Outputs: + // -int: Number of variants tested + // -int: Number of loci tested + // -int: Number of phased loci + getVariantAndLociInfo := func()(int, int, int, error){ - personAGenomeIdentifier, personBGenomeIdentifier, found := strings.Cut(genomePairIdentifier, "+") - if (found == false){ - return 0, errors.New("setViewCoupleMonogenicDiseaseVariantsPage called with invalid genomePairIdentifier: " + genomePairIdentifier) - } + person1GenomeIdentifier, person2GenomeIdentifier := helpers.Split32ByteArrayInHalf(genomePairIdentifier) - personAAnalysisGenomeIdentifier, personAHasMultipleGenomes, _, _, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(true, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, personAGenomeIdentifier) - if (err != nil) { return 0, err } + person1AnalysisGenomeIdentifier, _, _, _, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(true, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, person1GenomeIdentifier) + if (err != nil) { return 0, 0, 0, err } - _, _, _, _, _, personANumberOfVariantsTested, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAAnalysisMapList, diseaseName, personAAnalysisGenomeIdentifier, personAHasMultipleGenomes) - if (err != nil) { return 0, err } + _, _, _, _, person1NumberOfVariantsTested, person1NumberOfLociTested, person1NumberOfPhasedLoci, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(person1AnalysisObject, diseaseName, person1AnalysisGenomeIdentifier) + if (err != nil) { return 0, 0, 0, err } - personBAnalysisGenomeIdentifier, personBHasMultipleGenomes, _, _, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(false, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, personBGenomeIdentifier) - if (err != nil) { return 0, err } + person2AnalysisGenomeIdentifier, _, _, _, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(false, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, person2GenomeIdentifier) + if (err != nil) { return 0, 0, 0, err } - _, _, _, _, _, personBNumberOfVariantsTested, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personBAnalysisMapList, diseaseName, personBAnalysisGenomeIdentifier, personBHasMultipleGenomes) - if (err != nil) { return 0, err } + _, _, _, _, person2NumberOfVariantsTested, person2NumberOfLociTested, person2NumberOfPhasedLoci, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(person2AnalysisObject, diseaseName, person2AnalysisGenomeIdentifier) + if (err != nil) { return 0, 0, 0, err } - numberOfVariantsTested := personANumberOfVariantsTested + personBNumberOfVariantsTested + numberOfVariantsTested := person1NumberOfVariantsTested + person2NumberOfVariantsTested + numberOfLociTested := person1NumberOfLociTested + person2NumberOfLociTested + numberOfPhasedLoci := person1NumberOfPhasedLoci + person2NumberOfPhasedLoci - return numberOfVariantsTested, nil + return numberOfVariantsTested, numberOfLociTested, numberOfPhasedLoci, nil } - numberOfVariantsTested, err := getNumberOfVariantsTested() + numberOfVariantsTested, numberOfLociTested, numberOfPhasedLoci, err := getVariantAndLociInfo() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -588,6 +603,16 @@ func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, personAName s variantsTestedRow := container.NewHBox(layout.NewSpacer(), variantsTestedLabel, variantsTestedText, variantsTestedHelpButton, layout.NewSpacer()) + numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) + numberOfPhasedLociString := helpers.ConvertIntToString(numberOfPhasedLoci) + + phasedLociLabel := widget.NewLabel("Phased Loci:") + phasedLociText := getBoldLabel(numberOfPhasedLociString + "/" + numberOfLociTestedString) + phasedLociHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + setNumberOfPhasedLociExplainerPage(window, currentPage) + }) + phasedLociRow := container.NewHBox(layout.NewSpacer(), phasedLociLabel, phasedLociText, phasedLociHelpButton, layout.NewSpacer()) + //TODO: Add navigation with pages getVariantsGrid := func()(*fyne.Container, error){ @@ -613,16 +638,21 @@ func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, personAName s probabilityOf2MutationsColumn := container.NewVBox(probabilityOfLabelC, twoMutationsLabel, widget.NewSeparator()) variantInfoButtonsColumn := container.NewVBox(emptyLabelB, emptyLabelC, widget.NewSeparator()) - addVariantRow := func(variantIdentifier string)error{ + addVariantRow := func(variantIdentifierHex string)error{ - variantObject, exists := diseaseVariantsMap[variantIdentifier] + variantObject, exists := diseaseVariantsMap[variantIdentifierHex] if (exists == false){ return errors.New("Cannot add variant row: Variant missing from diseaseVariantsMap") } + variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex) + if (err != nil){ + return errors.New("addVariantRow called with invalid variantIdentifier: " + variantIdentifierHex) + } + variantName := variantObject.VariantNames[0] - offspringProbabilitiesKnown, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, probabilityOf0MutationsFormatted, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, probabilityOf1MutationFormatted, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, probabilityOf2MutationsFormatted, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, variantIdentifier, genomePairIdentifier) + offspringProbabilitiesKnown, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, probabilityOf0MutationsFormatted, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, probabilityOf1MutationFormatted, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, probabilityOf2MutationsFormatted, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, variantIdentifier, genomePairIdentifier) if (err != nil) { return err } getProbabilityOf0MutationsText := func()string{ @@ -680,7 +710,7 @@ func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, personAName s probabilityOf2MutationsLabel := getBoldLabelCentered(probabilityOf2MutationsText) viewVariantDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, variantIdentifier, currentPage) + setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, variantIdentifier, currentPage) }) variantNameColumn.Add(variantNameLabel) @@ -703,28 +733,33 @@ func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, personAName s variantsWithZeroRiskPartiallyKnownList := make([]string, 0) variantsWithUnknownRiskList := make([]string, 0) - for variantIdentifier, _ := range diseaseVariantsMap{ + for variantIdentifierHex, _ := range diseaseVariantsMap{ - probabilitesKnown, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, _, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, _, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, _, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, variantIdentifier, genomePairIdentifier) + variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex) + if (err != nil){ + return nil, errors.New("diseaseVariantsMap contains invalid variantIdentifier: " + variantIdentifierHex) + } + + probabilitesKnown, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, _, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, _, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, _, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, variantIdentifier, genomePairIdentifier) if (err != nil) { return nil, err } if (probabilitesKnown == false){ - variantsWithUnknownRiskList = append(variantsWithUnknownRiskList, variantIdentifier) + variantsWithUnknownRiskList = append(variantsWithUnknownRiskList, variantIdentifierHex) continue } - + if (probabilityOf1MutationLowerBound != 0 || probabilityOf2MutationsLowerBound != 0){ - variantsWithNonZeroRiskList = append(variantsWithNonZeroRiskList, variantIdentifier) + variantsWithNonZeroRiskList = append(variantsWithNonZeroRiskList, variantIdentifierHex) continue } // Risk is either 0 or partially unknown if (probabilityOf0MutationsLowerBound == 100 && probabilityOf0MutationsUpperBound == 100 && probabilityOf1MutationLowerBound == 0 && probabilityOf1MutationUpperBound == 0 && probabilityOf2MutationsLowerBound == 0 && probabilityOf2MutationsUpperBound == 0){ - variantsWithZeroRiskFullyKnownList = append(variantsWithZeroRiskFullyKnownList, variantIdentifier) + variantsWithZeroRiskFullyKnownList = append(variantsWithZeroRiskFullyKnownList, variantIdentifierHex) continue } - - variantsWithZeroRiskPartiallyKnownList = append(variantsWithZeroRiskPartiallyKnownList, variantIdentifier) + + variantsWithZeroRiskPartiallyKnownList = append(variantsWithZeroRiskPartiallyKnownList, variantIdentifierHex) } // We sort each list so the variants show up in the same order each time @@ -766,7 +801,7 @@ func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, personAName s return } - page := container.NewVBox(title, backButton, widget.NewSeparator(), description1Row, widget.NewSeparator(), genomePairRow, widget.NewSeparator(), variantsTestedRow, widget.NewSeparator(), variantsGrid) + page := container.NewVBox(title, backButton, widget.NewSeparator(), description1Row, widget.NewSeparator(), genomePairRow, widget.NewSeparator(), variantsTestedRow, widget.NewSeparator(), phasedLociRow, widget.NewSeparator(), variantsGrid) setPageContent(page, window) } @@ -775,15 +810,17 @@ func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, personAName s // This function provides a page to view the details of a specific variant from a genetic analysis // It will show the variant details for all of the couple's genome pairs -func setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, diseaseName string, variantIdentifier string, previousPage func()){ +func setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, variantIdentifier [3]byte, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, variantIdentifier, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, variantIdentifier, previousPage)} title := getPageTitleCentered("Disease Variant Details - " + diseaseName) backButton := getBackButtonCentered(previousPage) - variantObject, err := monogenicDiseases.GetMonogenicDiseaseVariantObject(diseaseName, variantIdentifier) + variantIdentifierHex := encoding.EncodeBytesToHexString(variantIdentifier[:]) + + variantObject, err := monogenicDiseases.GetMonogenicDiseaseVariantObject(diseaseName, variantIdentifierHex) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -796,7 +833,7 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne. variantNameLabel := widget.NewLabel("Variant Name:") variantNameText := getBoldLabel(variantName) variantInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewMonogenicDiseaseVariantDetailsPage(window, diseaseName, variantIdentifier, currentPage) + setViewMonogenicDiseaseVariantDetailsPage(window, diseaseName, variantIdentifierHex, currentPage) }) variantNameRow := container.NewHBox(layout.NewSpacer(), variantNameLabel, variantNameText, variantInfoButton, layout.NewSpacer()) @@ -823,9 +860,9 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne. probabilityOf1MutationColumn := container.NewVBox(probabilityOfLabelB, oneMutationLabel, widget.NewSeparator()) probablityOf2MutationsColumn := container.NewVBox(probabilityOfLabelC, twoMutationsLabel, widget.NewSeparator()) - addGenomePairRow := func(genomePairName string, genomePairIdentifier string)error{ + addGenomePairRow := func(genomePairName string, genomePairIdentifier [32]byte)error{ - probabilitiesKnown, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, probabilityOf0MutationsFormatted, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, probabilityOf1MutationFormatted, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, probabilityOf2MutationsFormatted, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, variantIdentifier, genomePairIdentifier) + probabilitiesKnown, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, probabilityOf0MutationsFormatted, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, probabilityOf1MutationFormatted, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, probabilityOf2MutationsFormatted, err := readGeneticAnalysis.GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, variantIdentifier, genomePairIdentifier) if (err != nil) { return err } getProbabilityOf0MutationsText := func()string{ @@ -877,7 +914,7 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne. probabilityOf2MutationsText := getProbabilityOf2MutationsText() viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleGeneticAnalysisMonogenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) }) genomePairNameLabel := getBoldLabelCentered(genomePairName) @@ -901,17 +938,17 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne. return nil } - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ return nil, err } - genomePair1Identifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier + genomePair1Identifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) err = addGenomePairRow("Pair 1", genomePair1Identifier) if (err != nil) { return nil, err } if (secondGenomePairExists == true){ - genomePair2Identifier := pair2PersonAGenomeIdentifier + "+" + pair2PersonBGenomeIdentifier + genomePair2Identifier := helpers.JoinTwo16ByteArrays(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) err := addGenomePairRow("Pair 2", genomePair2Identifier) if (err != nil) { return nil, err } @@ -934,9 +971,9 @@ func setViewCoupleGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne. } -func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, previousPage func()){ +func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis - Polygenic Diseases") @@ -946,7 +983,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso getDiseasesGrid := func()(*fyne.Container, error){ - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ return nil, err } diseaseNameLabel := getItalicLabelCentered("Disease Name") @@ -967,14 +1004,14 @@ func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso for _, diseaseName := range diseaseNamesList{ - mainGenomePairIdentifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier + mainGenomePairIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) - offspringRiskScoreKnown, _, offspringRiskScoreFormatted, _, conflictExists, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, mainGenomePairIdentifier, secondGenomePairExists) + offspringRiskScoreKnown, _, offspringRiskScoreFormatted, _, conflictExists, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, mainGenomePairIdentifier) if (err != nil) { return nil, err } getRiskScoreLabelText := func()string{ - if (offspringRiskScoreKnown == false){ + if (offspringRiskScoreKnown == false){ result := translate("Unknown") return result @@ -996,7 +1033,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso conflictExistsColumn.Add(conflictExistsLabel) viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, currentPage) + setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, currentPage) })) viewButtonsColumn.Add(viewDetailsButton) @@ -1040,15 +1077,15 @@ func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso setPageContent(page, window) } -func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, diseaseName string, previousPage func()){ +func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, previousPage)} title := getPageTitleCentered("Viewing Couple Analysis - " + diseaseName) backButton := getBackButtonCentered(previousPage) - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -1093,11 +1130,11 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, viewLifetimeRiskButtonsColumn := container.NewVBox(emptyLabelC, widget.NewSeparator()) viewOffspringLociButtonsColumn := container.NewVBox(emptyLabelD, widget.NewSeparator()) - addGenomePairRow := func(genomePairName string, personAGenomeIdentifier string, personBGenomeIdentifier string)error{ + addGenomePairRow := func(genomePairName string, person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{ - genomePairIdentifier := personAGenomeIdentifier + "+" + personBGenomeIdentifier + genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier) - offspringRiskScoreKnown, _, offspringRiskScoreFormatted, _, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, genomePairIdentifier, secondGenomePairExists) + offspringRiskScoreKnown, _, offspringRiskScoreFormatted, _, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) if (err != nil) { return err } getRiskScoreLabelText := func()string{ @@ -1118,7 +1155,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, offspringRiskScoreLabel := getBoldLabelCentered(riskScoreLabelText) viewGenomePairButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) }) viewOffspringLifetimeRiskButton := widget.NewButtonWithIcon("", theme.HistoryIcon(), func(){ @@ -1147,7 +1184,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, }) viewOffspringLociButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCouplePolygenicDiseaseLociPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, currentPage) + setViewCouplePolygenicDiseaseLociPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) }) viewGenomePairButtonsColumn.Add(viewGenomePairButton) @@ -1164,14 +1201,14 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, return nil } - err = addGenomePairRow("Pair 1", pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier) + err = addGenomePairRow("Pair 1", pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } if (secondGenomePairExists == true){ - err := addGenomePairRow("Pair 2", pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier) + err := addGenomePairRow("Pair 2", pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -1192,9 +1229,9 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, } -func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, diseaseName string, genomePairIdentifier string, genomePairName string, previousPage func()){ +func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, previousPage)} title := getPageTitleCentered("Viewing Couple Genome Pair Info") @@ -1237,9 +1274,9 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy numberOfLociTestedColumn := container.NewVBox(numberOfLabel, lociTestedLabel, widget.NewSeparator()) viewGenomeButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) - addGenomeRow := func(isPersonA bool, personName string, inputGenomeIdentifier string)error{ + addGenomeRow := func(isPerson1 bool, personName string, inputGenomeIdentifier [16]byte)error{ - personAnalysisGenomeIdentifier, personHasMultipleGenomes, genomeIsCombined, combinedType, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPersonA, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, inputGenomeIdentifier) + personAnalysisGenomeIdentifier, _, genomeIsCombined, combinedType, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, inputGenomeIdentifier) if (err != nil) { return err } personNameTrimmed, _, err := helpers.TrimAndFlattenString(personName, 10) @@ -1247,23 +1284,23 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy personNameLabel := getBoldLabelCentered(personNameTrimmed) - getPersonAnalysisMapList := func()[]map[string]string{ - if (isPersonA == true){ - return personAAnalysisMapList + getPersonAnalysisObject := func()geneticAnalysis.PersonAnalysis{ + if (isPerson1 == true){ + return person1AnalysisObject } - return personBAnalysisMapList + return person2AnalysisObject } - personAnalysisMapList := getPersonAnalysisMapList() + personAnalysisObject := getPersonAnalysisObject() getGenomeName := func()(string, error){ if (genomeIsCombined == false){ - + genomeFound, _, _, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(personAnalysisGenomeIdentifier) if (err != nil) { return "", err } if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisMapList not found.") + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") } return companyName, nil @@ -1277,7 +1314,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy genomeNameLabel := getBoldLabelCentered(genomeName) - personRiskScoreKnown, _, personRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisMapList, diseaseName, personAnalysisGenomeIdentifier, personHasMultipleGenomes) + personRiskScoreKnown, _, personRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier) if (err != nil) { return err } getPersonRiskScoreLabelText := func()string{ @@ -1296,7 +1333,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy numberOfLociTestedText := getBoldLabelCentered(numberOfLociTestedString) viewGenomeButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGenomePolygenicDiseaseLociPage(window, personAnalysisMapList, diseaseName, personAnalysisGenomeIdentifier, genomeName, currentPage) + setViewPersonGenomePolygenicDiseaseLociPage(window, personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier, genomeName, currentPage) }) personNameColumn.Add(personNameLabel) @@ -1314,18 +1351,14 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy return nil } - personAGenomeIdentifier, personBGenomeIdentifier, found := strings.Cut(genomePairIdentifier, "+") - if (found == false){ - setErrorEncounteredPage(window, errors.New("setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage called with invalid genomePairIdentifier"), previousPage) - return - } + person1GenomeIdentifier, person2GenomeIdentifier := helpers.Split32ByteArrayInHalf(genomePairIdentifier) - err := addGenomeRow(true, personAName, personAGenomeIdentifier) + err := addGenomeRow(true, person1Name, person1GenomeIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - err = addGenomeRow(false, personBName, personBGenomeIdentifier) + err = addGenomeRow(false, person2Name, person2GenomeIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -1351,11 +1384,11 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy // This function provides a page to view the couple offspring locus probabilities for a particular genome pair -func setViewCouplePolygenicDiseaseLociPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, diseaseName string, genomePairIdentifier string, genomePairName string, previousPage func()){ +func setViewCouplePolygenicDiseaseLociPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){ setLoadingScreen(window, "Loading Polygenic Disease Loci", "Loading disease loci...") - currentPage := func(){setViewCouplePolygenicDiseaseLociPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, previousPage)} + currentPage := func(){setViewCouplePolygenicDiseaseLociPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, previousPage)} title := getPageTitleCentered("View Offspring Disease Loci - " + diseaseName) @@ -1370,18 +1403,12 @@ func setViewCouplePolygenicDiseaseLociPage(window fyne.Window, personAName strin genomePairLabel := widget.NewLabel("Genome Pair:") genomePairNameLabel := getBoldLabel(genomePairName) viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) }) genomePairRow := container.NewHBox(layout.NewSpacer(), genomePairLabel, genomePairNameLabel, viewGenomePairInfoButton, layout.NewSpacer()) - _, _, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - _, _, _, numberOfLociTested, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, genomePairIdentifier, secondGenomePairExists) + _, _, _, numberOfLociTested, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -1421,18 +1448,21 @@ func setViewCouplePolygenicDiseaseLociPage(window fyne.Window, personAName strin offspringOddsRatioColumn := container.NewVBox(offspringOddsRatioLabel, widget.NewSeparator()) lociInfoButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) - addLocusRow := func(locusIdentifier string)error{ + addLocusRow := func(locusIdentifierHex string)error{ - locusObject, exists := diseaseLociMap[locusIdentifier] + locusObject, exists := diseaseLociMap[locusIdentifierHex] if (exists == false) { - return errors.New("Cannot add locus row: Locus not found in diseaseLociMap: " + locusIdentifier) + return errors.New("Cannot add locus row: Locus not found in diseaseLociMap: " + locusIdentifierHex) } + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return err } + locusRSID := locusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusName := "rs" + locusRSIDString - offspringRiskWeightKnown, offspringRiskWeight, offspringOddsRatioKnown, _, offspringOddsRatioFormatted, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, locusIdentifier, genomePairIdentifier) + offspringRiskWeightKnown, offspringRiskWeight, offspringOddsRatioKnown, _, offspringOddsRatioFormatted, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, locusIdentifier, genomePairIdentifier) if (err != nil) { return err } getOffspringRiskWeightText := func()string{ @@ -1469,7 +1499,7 @@ func setViewCouplePolygenicDiseaseLociPage(window fyne.Window, personAName strin locusOddsRatioLabel := getBoldLabelCentered(offspringOddsRatioText) viewLocusDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, locusIdentifier, currentPage) + setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, locusIdentifier, currentPage) }) locusNameColumn.Add(locusNameLabel) @@ -1488,15 +1518,18 @@ func setViewCouplePolygenicDiseaseLociPage(window fyne.Window, personAName strin lociWithKnownRiskWeightList := make([]string, 0) lociWithUnknownRiskWeightList := make([]string, 0) - for locusIdentifier, _ := range diseaseLociMap{ + for locusIdentifierHex, _ := range diseaseLociMap{ - offspringRiskWeightKnown, _, _, _, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, locusIdentifier, genomePairIdentifier) + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return nil, err } + + offspringRiskWeightKnown, _, _, _, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, locusIdentifier, genomePairIdentifier) if (err != nil) { return nil, err } if (offspringRiskWeightKnown == true){ - lociWithKnownRiskWeightList = append(lociWithKnownRiskWeightList, locusIdentifier) + lociWithKnownRiskWeightList = append(lociWithKnownRiskWeightList, locusIdentifierHex) } else { - lociWithUnknownRiskWeightList = append(lociWithUnknownRiskWeightList, locusIdentifier) + lociWithUnknownRiskWeightList = append(lociWithUnknownRiskWeightList, locusIdentifierHex) } } @@ -1547,15 +1580,17 @@ func setViewCouplePolygenicDiseaseLociPage(window fyne.Window, personAName strin // This function provides a page to view the details of a specific locus from a genetic analysis // It will show the locus details for all of the couple's genome pairs -func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, diseaseName string, locusIdentifier string, previousPage func()){ +func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, locusIdentifier [3]byte, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, locusIdentifier, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, locusIdentifier, previousPage)} title := getPageTitleCentered("Disease Locus Details - " + diseaseName) backButton := getBackButtonCentered(previousPage) - locusObject, err := polygenicDiseases.GetPolygenicDiseaseLocusObject(diseaseName, locusIdentifier) + locusIdentifierHex := encoding.EncodeBytesToHexString(locusIdentifier[:]) + + locusObject, err := polygenicDiseases.GetPolygenicDiseaseLocusObject(diseaseName, locusIdentifierHex) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -1566,11 +1601,11 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi locusName := "rs" + locusRSIDString description := getLabelCentered("Below is the locus analysis for the couple.") - + locusNameLabel := widget.NewLabel("Locus Name:") locusNameText := getBoldLabel(locusName) locusInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifier, currentPage) + setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifierHex, currentPage) }) locusNameRow := container.NewHBox(layout.NewSpacer(), locusNameLabel, locusNameText, locusInfoButton, layout.NewSpacer()) @@ -1589,9 +1624,9 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi offspringRiskWeightColumn := container.NewVBox(offspringRiskWeightLabel, widget.NewSeparator()) offspringOddsRatioColumn := container.NewVBox(offspringOddsRatioLabel, widget.NewSeparator()) - addGenomePairRow := func(genomePairName string, genomePairIdentifier string)error{ + addGenomePairRow := func(genomePairName string, genomePairIdentifier [32]byte)error{ - offspringRiskWeightKnown, offspringRiskWeight, offspringOddsRatioKnown, _, offspringOddsRatioFormatted, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, locusIdentifier, genomePairIdentifier) + offspringRiskWeightKnown, offspringRiskWeight, offspringOddsRatioKnown, _, offspringOddsRatioFormatted, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, locusIdentifier, genomePairIdentifier) if (err != nil) { return err } getOffspringRiskWeightText := func()string{ @@ -1619,7 +1654,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi offspringOddsRatioText := getOffspringOddsRatioText() viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, diseaseName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) }) genomePairNameLabel := getBoldLabelCentered(genomePairName) @@ -1640,17 +1675,17 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi return nil } - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ return nil, err } - genomePair1Identifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier + genomePair1Identifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) err = addGenomePairRow("Pair 1", genomePair1Identifier) if (err != nil) { return nil, err } if (secondGenomePairExists == true){ - genomePair2Identifier := pair2PersonAGenomeIdentifier + "+" + pair2PersonBGenomeIdentifier + genomePair2Identifier := helpers.JoinTwo16ByteArrays(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) err := addGenomePairRow("Pair 2", genomePair2Identifier) if (err != nil) { return nil, err } @@ -1667,7 +1702,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi offspringRiskWeightColumn.Add(offspringRiskWeightHelpButton) offspringOddsRatioColumn.Add(offspringOddsRatioHelpButton) - + genomesContainer := container.NewHBox(layout.NewSpacer(), viewGenomePairInfoButtonsColumn, genomePairNameColumn, offspringRiskWeightColumn, offspringOddsRatioColumn, layout.NewSpacer()) return genomesContainer, nil @@ -1685,9 +1720,9 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi } -func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, previousPage func()){ +func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisTraitsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisTraitsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis - Traits") @@ -1697,9 +1732,9 @@ func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, personAName stri getTraitsGrid := func()(*fyne.Container, error){ - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ return nil, err } - + traitNameLabel := getItalicLabelCentered("Trait Name") offspringOutcomeScoresLabel := getItalicLabelCentered("Offspring Outcome Scores") @@ -1729,9 +1764,9 @@ func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, personAName stri continue } - mainGenomePairIdentifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier - - offspringOutcomeScoresKnown, offspringAverageOutcomeScoresMap, _, conflictExists, err := readGeneticAnalysis.GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisMapList, traitName, mainGenomePairIdentifier, secondGenomePairExists) + mainGenomePairIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) + + offspringOutcomeScoresKnown, offspringAverageOutcomeScoresMap, _, conflictExists, err := readGeneticAnalysis.GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, mainGenomePairIdentifier) if (err != nil) { return nil, err } // We add all of the columns except for the trait outcomes column, which may be multiple rows high @@ -1742,7 +1777,7 @@ func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, personAName stri conflictExistsLabel := getBoldLabelCentered(conflictExistsString) viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCoupleGeneticAnalysisTraitDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, previousPage) + setViewCoupleGeneticAnalysisTraitDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, currentPage) })) traitNameColumn.Add(traitNameText) @@ -1829,15 +1864,15 @@ func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, personAName stri -func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, traitName string, previousPage func()){ +func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisTraitDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisTraitDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, previousPage)} title := getPageTitleCentered("Viewing Couple Analysis - " + traitName) backButton := getBackButtonCentered(previousPage) - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -1880,19 +1915,19 @@ func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, personANam offspringOutcomeScoresColumn := container.NewVBox(offspringOutcomeScoresLabel, widget.NewSeparator()) viewOffspringRulesButtonsColumn := container.NewVBox(emptyLabelC, widget.NewSeparator()) - addGenomePairRow := func(genomePairName string, personAGenomeIdentifier string, personBGenomeIdentifier string)error{ + addGenomePairRow := func(genomePairName string, person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{ - genomePairIdentifier := personAGenomeIdentifier + "+" + personBGenomeIdentifier + genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier) genomePairNameLabel := getBoldLabelCentered(genomePairName) viewGenomePairButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage) }) viewOffspringRulesButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCoupleTraitRulesPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleTraitRulesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage) }) // We add all of the columns except for the trait rule column, which may be multiple rows high @@ -1901,7 +1936,7 @@ func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, personANam pairNameColumn.Add(genomePairNameLabel) viewOffspringRulesButtonsColumn.Add(viewOffspringRulesButton) - offspringOutcomeScoresKnown, offspringOutcomeScoresMap, _, _, err := readGeneticAnalysis.GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisMapList, traitName, genomePairIdentifier, secondGenomePairExists) + offspringOutcomeScoresKnown, offspringOutcomeScoresMap, _, _, err := readGeneticAnalysis.GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier) if (err != nil) { return err } if (offspringOutcomeScoresKnown == false){ @@ -1950,14 +1985,14 @@ func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, personANam return nil } - err = addGenomePairRow("Pair 1", pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier) + err = addGenomePairRow("Pair 1", pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } if (secondGenomePairExists == true){ - err := addGenomePairRow("Pair 2", pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier) + err := addGenomePairRow("Pair 2", pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -1978,9 +2013,9 @@ func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, personANam } -func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, traitName string, genomePairIdentifier string, genomePairName string, previousPage func()){ +func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, genomePairIdentifier, genomePairName, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, previousPage)} title := getPageTitleCentered("Viewing Couple Genome Pair Info") @@ -2023,9 +2058,9 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, numberOfRulesTestedColumn := container.NewVBox(numberOfLabel, rulesTestedLabel, widget.NewSeparator()) viewGenomeButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) - addGenomeRow := func(isPersonA bool, personName string, inputGenomeIdentifier string)error{ + addGenomeRow := func(isPerson1 bool, personName string, inputGenomeIdentifier [16]byte)error{ - personAnalysisGenomeIdentifier, personHasMultipleGenomes, genomeIsCombined, combinedType, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPersonA, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, inputGenomeIdentifier) + personAnalysisGenomeIdentifier, _, genomeIsCombined, combinedType, err := readGeneticAnalysis.GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, inputGenomeIdentifier) if (err != nil) { return err } personNameTrimmed, _, err := helpers.TrimAndFlattenString(personName, 10) @@ -2033,14 +2068,14 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, personNameLabel := getBoldLabelCentered(personNameTrimmed) - getPersonAnalysisMapList := func()[]map[string]string{ - if (isPersonA == true){ - return personAAnalysisMapList + getPersonAnalysisObject := func()geneticAnalysis.PersonAnalysis{ + if (isPerson1 == true){ + return person1AnalysisObject } - return personBAnalysisMapList + return person2AnalysisObject } - personAnalysisMapList := getPersonAnalysisMapList() + personAnalysisObject := getPersonAnalysisObject() getGenomeName := func()(string, error){ @@ -2049,7 +2084,7 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, genomeFound, _, _, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(personAnalysisGenomeIdentifier) if (err != nil) { return "", err } if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisMapList not found.") + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") } return companyName, nil @@ -2065,14 +2100,14 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, // We add all of the columns except for the trait rule column, which may be multiple rows high - _, anyTraitRuleTested, outcomeScoresMap, numberOfRulesTested, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(personAnalysisMapList, traitName, personAnalysisGenomeIdentifier, personHasMultipleGenomes) + _, anyTraitRuleTested, outcomeScoresMap, numberOfRulesTested, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, personAnalysisGenomeIdentifier) if (err != nil) { return err } numberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested) numberOfRulesTestedText := getBoldLabelCentered(numberOfRulesTestedString) viewGenomeButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGenomeTraitRulesPage(window, personAnalysisMapList, traitName, personAnalysisGenomeIdentifier, genomeName, currentPage) + setViewPersonGenomeTraitRulesPage(window, personAnalysisObject, traitName, personAnalysisGenomeIdentifier, genomeName, currentPage) }) personNameColumn.Add(personNameLabel) @@ -2100,7 +2135,7 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, if (exists == false){ return errors.New("Outcome name not found in outcome scores map after being found already.") } - + outcomeScoreString := helpers.ConvertIntToString(outcomeScore) outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString) @@ -2130,18 +2165,14 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, return nil } - personAGenomeIdentifier, personBGenomeIdentifier, found := strings.Cut(genomePairIdentifier, "+") - if (found == false){ - setErrorEncounteredPage(window, errors.New("setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage called with invalid genomePairIdentifier"), previousPage) - return - } + person1GenomeIdentifier, person2GenomeIdentifier := helpers.Split32ByteArrayInHalf(genomePairIdentifier) - err := addGenomeRow(true, personAName, personAGenomeIdentifier) + err := addGenomeRow(true, person1Name, person1GenomeIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - err = addGenomeRow(false, personBName, personBGenomeIdentifier) + err = addGenomeRow(false, person2Name, person2GenomeIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -2161,7 +2192,7 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, genomesGrid := container.NewHBox(layout.NewSpacer(), personNameColumn, genomeNameColumn, outcomeScoresColumn, numberOfRulesTestedColumn, viewGenomeButtonsColumn, layout.NewSpacer()) - page := container.NewVBox(title, backButton, widget.NewSeparator(), description, traitNameRow, genomePairRow, widget.NewSeparator(), genomesGrid) + page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), traitNameRow, widget.NewSeparator(), genomePairRow, widget.NewSeparator(), genomesGrid) setPageContent(page, window) } @@ -2169,11 +2200,11 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, // This function provides a page to view the couple offspring rule probabilities for a particular genome pair -func setViewCoupleTraitRulesPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, traitName string, genomePairIdentifier string, genomePairName string, previousPage func()){ +func setViewCoupleTraitRulesPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){ setLoadingScreen(window, "Loading Trait Rules", "Loading trait rules...") - currentPage := func(){setViewCoupleTraitRulesPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, genomePairIdentifier, genomePairName, previousPage)} + currentPage := func(){setViewCoupleTraitRulesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, previousPage)} title := getPageTitleCentered("View Offspring Trait Rules - " + traitName) @@ -2188,18 +2219,12 @@ func setViewCoupleTraitRulesPage(window fyne.Window, personAName string, personB genomePairLabel := widget.NewLabel("Genome Pair:") genomePairNameLabel := getBoldLabel(genomePairName) viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage) }) genomePairRow := container.NewHBox(layout.NewSpacer(), genomePairLabel, genomePairNameLabel, viewGenomePairInfoButton, layout.NewSpacer()) - _, _, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - _, _, numberOfRulesTested, _, err := readGeneticAnalysis.GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisMapList, traitName, genomePairIdentifier, secondGenomePairExists) + _, _, numberOfRulesTested, _, err := readGeneticAnalysis.GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -2243,15 +2268,18 @@ func setViewCoupleTraitRulesPage(window fyne.Window, personAName string, personB offspringProbabilityOfPassingRuleColumn := container.NewVBox(offspringProbabilityOfLabel, passingRuleLabel, widget.NewSeparator()) ruleInfoButtonsColumn := container.NewVBox(emptyLabelC, emptyLabelD, widget.NewSeparator()) - addRuleRow := func(ruleIdentifier string)error{ + addRuleRow := func(ruleIdentifierHex string)error{ - ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifier) - - offspringRuleProbabilityKnown, _, offspringProbabilityOfPassingRuleFormatted, err := readGeneticAnalysis.GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisMapList, traitName, ruleIdentifier, genomePairIdentifier) + ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifierHex) + + ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex) + if (err != nil) { return err } + + offspringRuleProbabilityKnown, _, offspringProbabilityOfPassingRuleFormatted, err := readGeneticAnalysis.GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier) if (err != nil) { return err } viewRuleDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, ruleIdentifier, currentPage) + setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, ruleIdentifier, currentPage) }) getProbabilityOfPassingRuleText := func()string{ @@ -2273,9 +2301,9 @@ func setViewCoupleTraitRulesPage(window fyne.Window, personAName string, personB offspringProbabilityOfPassingRuleColumn.Add(probabilityOfPassingRuleTextLabel) ruleInfoButtonsColumn.Add(viewRuleDetailsButton) - traitRuleObject, exists := traitRulesMap[ruleIdentifier] + traitRuleObject, exists := traitRulesMap[ruleIdentifierHex] if (exists == false){ - return errors.New("Trait rule not found after being found already: " + ruleIdentifier) + return errors.New("Trait rule not found after being found already: " + ruleIdentifierHex) } ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap @@ -2331,14 +2359,17 @@ func setViewCoupleTraitRulesPage(window fyne.Window, personAName string, personB rulesWithKnownProbabilityList := make([]string, 0) rulesWithUnknownProbabilityList := make([]string, 0) - for ruleIdentifier, _ := range traitRulesMap{ + for ruleIdentifierHex, _ := range traitRulesMap{ - offspringRuleProbabilityKnown, _, _, err := readGeneticAnalysis.GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisMapList, traitName, ruleIdentifier, genomePairIdentifier) + ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex) + if (err != nil) { return nil, err } + + offspringRuleProbabilityKnown, _, _, err := readGeneticAnalysis.GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier) if (err != nil) { return nil, err } if (offspringRuleProbabilityKnown == true){ - rulesWithKnownProbabilityList = append(rulesWithKnownProbabilityList, ruleIdentifier) + rulesWithKnownProbabilityList = append(rulesWithKnownProbabilityList, ruleIdentifierHex) } else { - rulesWithUnknownProbabilityList = append(rulesWithUnknownProbabilityList, ruleIdentifier) + rulesWithUnknownProbabilityList = append(rulesWithUnknownProbabilityList, ruleIdentifierHex) } } @@ -2389,20 +2420,22 @@ func setViewCoupleTraitRulesPage(window fyne.Window, personAName string, personB // This function implements a page to view the details of a specific rule from a genetic analysis // It will show the rule details for all of the couple's genome pairs -func setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, personAName string, personBName string, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, traitName string, ruleIdentifier string, previousPage func()){ +func setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, previousPage func()){ - currentPage := func(){setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, ruleIdentifier, previousPage)} + currentPage := func(){setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, ruleIdentifier, previousPage)} title := getPageTitleCentered("Trait Rule Details - " + traitName) backButton := getBackButtonCentered(previousPage) description := getLabelCentered("Below is the trait rule analysis for the couple.") - + + ruleIdentifierHex := encoding.EncodeBytesToHexString(ruleIdentifier[:]) + ruleIdentifierLabel := widget.NewLabel("Rule Identifier:") - ruleIdentifierText := getBoldLabel(ruleIdentifier) + ruleIdentifierText := getBoldLabel(ruleIdentifierHex) ruleInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewTraitRuleDetailsPage(window, traitName, ruleIdentifier, currentPage) + setViewTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage) }) ruleIdentifierRow := container.NewHBox(layout.NewSpacer(), ruleIdentifierLabel, ruleIdentifierText, ruleInfoButton, layout.NewSpacer()) @@ -2421,9 +2454,9 @@ func setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, person genomePairNameColumn := container.NewVBox(emptyLabelC, genomePairLabel, widget.NewSeparator()) offspringProbabilityOfPassingRuleColumn := container.NewVBox(offspringProbabilityOfLabel, passingRuleLabel, widget.NewSeparator()) - addGenomePairRow := func(genomePairName string, genomePairIdentifier string)error{ + addGenomePairRow := func(genomePairName string, genomePairIdentifier [32]byte)error{ - offspringRuleProbabilityKnown, _, offspringProbabilityOfPassingRuleFormatted, err := readGeneticAnalysis.GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisMapList, traitName, ruleIdentifier, genomePairIdentifier) + offspringRuleProbabilityKnown, _, offspringProbabilityOfPassingRuleFormatted, err := readGeneticAnalysis.GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier) if (err != nil) { return err } getOffspringProbabilityOfPassingRuleText := func()string{ @@ -2439,7 +2472,7 @@ func setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, person offspringProbabilityOfPassingRuleText := getOffspringProbabilityOfPassingRuleText() viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, personAName, personBName, personAAnalysisMapList, personBAnalysisMapList, coupleAnalysisMapList, traitName, genomePairIdentifier, genomePairName, currentPage) + setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage) }) genomePairNameLabel := getBoldLabelCentered(genomePairName) @@ -2457,22 +2490,22 @@ func setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, person return nil } - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ return nil, err } - genomePair1Identifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier + genomePair1Identifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) err = addGenomePairRow("Pair 1", genomePair1Identifier) if (err != nil) { return nil, err } if (secondGenomePairExists == true){ - genomePair2Identifier := pair2PersonAGenomeIdentifier + "+" + pair2PersonBGenomeIdentifier + genomePair2Identifier := helpers.JoinTwo16ByteArrays(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) err := addGenomePairRow("Pair 2", genomePair2Identifier) if (err != nil) { return nil, err } } - + genomesContainer := container.NewHBox(layout.NewSpacer(), viewGenomePairInfoButtonsColumn, genomePairNameColumn, offspringProbabilityOfPassingRuleColumn, layout.NewSpacer()) return genomesContainer, nil diff --git a/gui/viewAnalysisGui_Person.go b/gui/viewAnalysisGui_Person.go index 4487d75..9bd70d5 100644 --- a/gui/viewAnalysisGui_Person.go +++ b/gui/viewAnalysisGui_Person.go @@ -18,6 +18,8 @@ import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/resources/geneticReferences/traits" import "seekia/internal/appMemory" +import "seekia/internal/encoding" +import "seekia/internal/genetics/geneticAnalysis" import "seekia/internal/genetics/myGenomes" import "seekia/internal/genetics/myPeople" import "seekia/internal/genetics/readGeneticAnalysis" @@ -25,17 +27,18 @@ import "seekia/internal/helpers" import "errors" -func setViewPersonGeneticAnalysisPage(window fyne.Window, personIdentifier string, analysisMapList []map[string]string, numberOfGenomesAnalyzed int, previousPage func()){ +func setViewPersonGeneticAnalysisPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, numberOfGenomesAnalyzed int, previousPage func()){ appMemory.SetMemoryEntry("CurrentViewedPage", "ViewGeneticAnalysisPage") - currentPage := func(){setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisMapList, numberOfGenomesAnalyzed, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisObject, numberOfGenomesAnalyzed, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis") backButton := getBackButtonCentered(previousPage) - warningLabel := getBoldLabelCentered("WARNING: Results are not accurate!") + warningLabel1 := getBoldLabelCentered("WARNING: Results are not accurate!") + warningLabel2 := getBoldLabelCentered("The analysis algorithms have known issues that are being worked on.") personFound, personName, _, _, err := myPeople.GetPersonInfo(personIdentifier) if (err != nil){ @@ -62,26 +65,26 @@ func setViewPersonGeneticAnalysisPage(window fyne.Window, personIdentifier strin showUnderConstructionDialog(window) }) monogenicDiseasesButton := widget.NewButton("Monogenic Diseases", func(){ - setViewPersonGeneticAnalysisMonogenicDiseasesPage(window, analysisMapList, currentPage) + setViewPersonGeneticAnalysisMonogenicDiseasesPage(window, analysisObject, currentPage) }) polygenicDiseasesButton := widget.NewButton("Polygenic Diseases", func(){ - setViewPersonGeneticAnalysisPolygenicDiseasesPage(window, personIdentifier, analysisMapList, currentPage) + setViewPersonGeneticAnalysisPolygenicDiseasesPage(window, personIdentifier, analysisObject, currentPage) }) traitsButton := widget.NewButton("Traits", func(){ - setViewPersonGeneticAnalysisTraitsPage(window, personIdentifier, analysisMapList, currentPage) + setViewPersonGeneticAnalysisTraitsPage(window, personIdentifier, analysisObject, currentPage) }) categoryButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, generalButton, monogenicDiseasesButton, polygenicDiseasesButton, traitsButton)) - page := container.NewVBox(title, backButton, widget.NewSeparator(), warningLabel, widget.NewSeparator(), personNameRow, numberOfAnalyzedGenomesRow, widget.NewSeparator(), categoryButtonsGrid) + page := container.NewVBox(title, backButton, widget.NewSeparator(), warningLabel1, warningLabel2, widget.NewSeparator(), personNameRow, numberOfAnalyzedGenomesRow, widget.NewSeparator(), categoryButtonsGrid) setPageContent(page, window) } -func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analysisMapList []map[string]string, previousPage func()){ +func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analysisObject geneticAnalysis.PersonAnalysis, previousPage func()){ - currentPage := func(){setViewPersonGeneticAnalysisMonogenicDiseasesPage(window, analysisMapList, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisMonogenicDiseasesPage(window, analysisObject, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis - Monogenic Diseases") @@ -91,13 +94,13 @@ func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analy getMonogenicDiseasesContainer := func()(*fyne.Container, error){ - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisMapList) + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject) if (err != nil){ return nil, err } // Outputs: - // -string: Main genome identifier (Is either the combined Only exclude conflicts genome or the only genome) + // -[16]byte: Main genome identifier (Is either the combined Only exclude conflicts genome or the only genome) // -error - getMainGenomeIdentifier := func()(string, error){ + getMainGenomeIdentifier := func()([16]byte, error){ if (multipleGenomesExist == true){ return onlyExcludeConflictsGenomeIdentifier, nil @@ -115,10 +118,10 @@ func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analy diseaseNameLabel := getItalicLabelCentered("Disease Name") emptyLabelA := widget.NewLabel("") - probabilityOfLabelA := getItalicLabelCentered("Probability of") - havingDiseaseLabel := getItalicLabelCentered("Having Disease") + personLabel := getItalicLabelCentered("Person") + hasDiseaseLabel := getItalicLabelCentered("Has Disease") - probabilityOfLabelB := getItalicLabelCentered("Probability of") + probabilityOfLabel := getItalicLabelCentered("Probability of") passingVariantLabel := getItalicLabelCentered("Passing Variant") emptyLabelB := widget.NewLabel("") @@ -128,8 +131,8 @@ func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analy emptyLabelD := widget.NewLabel("") diseaseNameColumn := container.NewVBox(emptyLabelA, diseaseNameLabel, widget.NewSeparator()) - probabilityOfHavingDiseaseColumn := container.NewVBox(probabilityOfLabelA, havingDiseaseLabel, widget.NewSeparator()) - probabilityOfPassingVariantColumn := container.NewVBox(probabilityOfLabelB, passingVariantLabel, widget.NewSeparator()) + personHasDiseaseColumn := container.NewVBox(personLabel, hasDiseaseLabel, widget.NewSeparator()) + probabilityOfPassingVariantColumn := container.NewVBox(probabilityOfLabel, passingVariantLabel, widget.NewSeparator()) conflictExistsColumn := container.NewVBox(emptyLabelB, conflictExistsLabel, widget.NewSeparator()) viewButtonsColumn := container.NewVBox(emptyLabelC, emptyLabelD, widget.NewSeparator()) @@ -138,22 +141,24 @@ func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analy for _, diseaseName := range monogenicDiseaseNamesList{ - probabilitiesKnown, _, probabilityOfHavingDiseaseFormatted, _, probabilityOfPassingAVariantFormatted, _, conflictExistsBool, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(analysisMapList, diseaseName, mainGenomeIdentifier, multipleGenomesExist) + diseaseInfoIsKnown, personHasDisease, _, probabilityOfPassingAVariantFormatted, _, _, _, conflictExistsBool, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier) if (err != nil) { return nil, err } - getProbabilityOfHavingDiseaseText := func()string{ - if (probabilitiesKnown == false){ + getPersonHasDiseaseText := func()string{ + if (diseaseInfoIsKnown == false){ result := translate("Unknown") return result } - return probabilityOfHavingDiseaseFormatted + personHasDiseaseString := helpers.ConvertBoolToYesOrNoString(personHasDisease) + + return personHasDiseaseString } - probabilityOfHavingDiseaseText := getProbabilityOfHavingDiseaseText() + personHasDiseaseText := getPersonHasDiseaseText() getProbabilityOfPassingAVariantText := func()string{ - if (probabilitiesKnown == false){ + if (diseaseInfoIsKnown == false){ result := translate("Unknown") return result } @@ -166,8 +171,8 @@ func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analy diseaseNameText := getBoldLabelCentered(diseaseName) diseaseNameColumn.Add(diseaseNameText) - probabilityOfHavingDiseaseLabel := getBoldLabelCentered(probabilityOfHavingDiseaseText) - probabilityOfHavingDiseaseColumn.Add(probabilityOfHavingDiseaseLabel) + personHasDiseaseLabel := getBoldLabelCentered(personHasDiseaseText) + personHasDiseaseColumn.Add(personHasDiseaseLabel) probabilityOfPassingVariantLabel := getBoldLabelCentered(probabilityOfPassingAVariantText) probabilityOfPassingVariantColumn.Add(probabilityOfPassingVariantLabel) @@ -177,28 +182,28 @@ func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analy conflictExistsColumn.Add(conflictExistsLabel) viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window, analysisMapList, diseaseName, currentPage) + setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window, analysisObject, diseaseName, currentPage) })) viewButtonsColumn.Add(viewDetailsButton) diseaseNameColumn.Add(widget.NewSeparator()) - probabilityOfHavingDiseaseColumn.Add(widget.NewSeparator()) + personHasDiseaseColumn.Add(widget.NewSeparator()) probabilityOfPassingVariantColumn.Add(widget.NewSeparator()) conflictExistsColumn.Add(widget.NewSeparator()) viewButtonsColumn.Add(widget.NewSeparator()) } - probabilityOfHavingDiseaseHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPersonProbabilityOfHavingMonogenicDiseaseExplainerPage(window, currentPage) + personHasDiseaseHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + setPersonHasMonogenicDiseaseExplainerPage(window, currentPage) }) - probabilityOfHavingDiseaseColumn.Add(probabilityOfHavingDiseaseHelpButton) + personHasDiseaseColumn.Add(personHasDiseaseHelpButton) probabilityOfPassingVariantHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPersonProbabilityOfPassingVariantExplainerPage(window, currentPage) }) probabilityOfPassingVariantColumn.Add(probabilityOfPassingVariantHelpButton) - diseasesContainer := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, probabilityOfHavingDiseaseColumn, probabilityOfPassingVariantColumn) + diseasesContainer := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, personHasDiseaseColumn, probabilityOfPassingVariantColumn) if (multipleGenomesExist == true){ @@ -228,15 +233,15 @@ func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analy } -func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, analysisMapList []map[string]string, diseaseName string, previousPage func()){ +func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, analysisObject geneticAnalysis.PersonAnalysis, diseaseName string, previousPage func()){ - currentPage := func(){setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window, analysisMapList, diseaseName, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window, analysisObject, diseaseName, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis - " + diseaseName) backButton := getBackButtonCentered(previousPage) - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisMapList) + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -269,47 +274,61 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, getGenomesContainer := func()(*fyne.Container, error){ + diseaseVariantsMap, err := monogenicDiseases.GetMonogenicDiseaseVariantsMap(diseaseName) + if (err != nil){ return nil, err } + + totalNumberOfVariants := len(diseaseVariantsMap) + totalNumberOfVariantsString := helpers.ConvertIntToString(totalNumberOfVariants) + + + emptyLabelA := widget.NewLabel("") - genomeNameLabel := getItalicLabelCentered("Genome Name") + genomeNameLabel := getItalicLabelCentered("Genome Name") - probabilityOfLabelA := getItalicLabelCentered("Probability of") - havingDiseaseLabel := getItalicLabelCentered("Having Disease") + personLabel := getItalicLabelCentered("Person") + hasDiseaseLabel := getItalicLabelCentered("Has Disease") - probabilityOfLabelB := getItalicLabelCentered("Probability of") + probabilityOfLabel := getItalicLabelCentered("Probability of") passingVariantLabel := getItalicLabelCentered("Passing Variant") - numberOfLabel := getItalicLabelCentered("Number of") + numberOfLabel1 := getItalicLabelCentered("Number of") variantsTestedLabel := getItalicLabelCentered("Variants Tested") + numberOfLabel2 := getItalicLabelCentered("Number of") + phasedLociLabel := getItalicLabelCentered("Phased Loci") + emptyLabelB := widget.NewLabel("") emptyLabelC := widget.NewLabel("") genomeNameColumn := container.NewVBox(emptyLabelA, genomeNameLabel, widget.NewSeparator()) - probabilityOfHavingDiseaseColumn := container.NewVBox(probabilityOfLabelA, havingDiseaseLabel, widget.NewSeparator()) - probabilityOfPassingAVariantColumn := container.NewVBox(probabilityOfLabelB, passingVariantLabel, widget.NewSeparator()) - numberOfVariantsTestedColumn := container.NewVBox(numberOfLabel, variantsTestedLabel, widget.NewSeparator()) + personHasDiseaseColumn := container.NewVBox(personLabel, hasDiseaseLabel, widget.NewSeparator()) + probabilityOfPassingAVariantColumn := container.NewVBox(probabilityOfLabel, passingVariantLabel, widget.NewSeparator()) + numberOfVariantsTestedColumn := container.NewVBox(numberOfLabel1, variantsTestedLabel, widget.NewSeparator()) + numberOfPhasedLociColumn := container.NewVBox(numberOfLabel2, phasedLociLabel, widget.NewSeparator()) viewButtonsColumn := container.NewVBox(emptyLabelB, emptyLabelC, widget.NewSeparator()) - addGenomeRow := func(genomeName string, genomeIdentifier string, isACombinedGenome bool)error{ + addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ - probabilitiesKnown, _, probabilityOfHavingDiseaseFormatted, _, probabilityOfPassingAVariantFormatted, numberOfVariantsTested, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(analysisMapList, diseaseName, genomeIdentifier, multipleGenomesExist) + diseaseInfoIsKnown, personHasDisease, _, probabilityOfPassingAVariantFormatted, numberOfVariantsTested, numberOfLociTested, numberOfPhasedLoci, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier) if (err != nil) { return err } - getProbabilityOfHavingDiseaseText := func()string{ - if (probabilitiesKnown == false){ + getPersonHasDiseaseText := func()string{ + if (diseaseInfoIsKnown == false){ result := translate("Unknown") return result } - return probabilityOfHavingDiseaseFormatted + personHasDiseaseString := helpers.ConvertBoolToYesOrNoString(personHasDisease) + + return personHasDiseaseString } - probabilityOfHavingDiseaseText := getProbabilityOfHavingDiseaseText() + personHasDiseaseText := getPersonHasDiseaseText() getProbabilityOfPassingAVariantText := func()string{ - if (probabilitiesKnown == false){ + if (diseaseInfoIsKnown == false){ result := translate("Unknown") return result @@ -339,23 +358,33 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, genomeNameCell := getGenomeNameCell() genomeNameColumn.Add(genomeNameCell) - probabilityOfHavingDiseaseLabel := getBoldLabelCentered(probabilityOfHavingDiseaseText) - probabilityOfHavingDiseaseColumn.Add(probabilityOfHavingDiseaseLabel) + personHasDiseaseLabel := getBoldLabelCentered(personHasDiseaseText) + personHasDiseaseColumn.Add(personHasDiseaseLabel) probabilityOfPassingAVariantLabel := getBoldLabelCentered(probabilityOfPassingAVariantText) probabilityOfPassingAVariantColumn.Add(probabilityOfPassingAVariantLabel) numberOfVariantsTestedString := helpers.ConvertIntToString(numberOfVariantsTested) - numberOfVariantsTestedLabel := getBoldLabelCentered(numberOfVariantsTestedString) + numberOfVariantsTestedFormatted := numberOfVariantsTestedString + "/" + totalNumberOfVariantsString + numberOfVariantsTestedLabel := getBoldLabelCentered(numberOfVariantsTestedFormatted) numberOfVariantsTestedColumn.Add(numberOfVariantsTestedLabel) - viewButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){setViewPersonGenomeMonogenicDiseaseVariantsPage(window, analysisMapList, genomeIdentifier, genomeName, diseaseName, currentPage)}) + numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) + numberOfPhasedLociString := helpers.ConvertIntToString(numberOfPhasedLoci) + numberOfPhasedLociFormatted := numberOfPhasedLociString + "/" + numberOfLociTestedString + numberOfPhasedLociLabel := getBoldLabelCentered(numberOfPhasedLociFormatted) + numberOfPhasedLociColumn.Add(numberOfPhasedLociLabel) + + viewButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ + setViewPersonGenomeMonogenicDiseaseVariantsPage(window, analysisObject, genomeIdentifier, genomeName, diseaseName, currentPage) + }) viewButtonsColumn.Add(viewButton) genomeNameColumn.Add(widget.NewSeparator()) - probabilityOfHavingDiseaseColumn.Add(widget.NewSeparator()) + personHasDiseaseColumn.Add(widget.NewSeparator()) probabilityOfPassingAVariantColumn.Add(widget.NewSeparator()) numberOfVariantsTestedColumn.Add(widget.NewSeparator()) + numberOfPhasedLociColumn.Add(widget.NewSeparator()) viewButtonsColumn.Add(widget.NewSeparator()) return nil @@ -377,7 +406,7 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, genomeFound, _, timeGenomeWasExported, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier) if (err != nil) { return "", err } if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisMapList not found.") + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") } if (multipleGenomesExist == false){ @@ -401,10 +430,10 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, if (err != nil){ return nil, err } } - probabilityOfHavingDiseaseHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPersonProbabilityOfHavingMonogenicDiseaseExplainerPage(window, currentPage) + personHasDiseaseHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + setPersonHasMonogenicDiseaseExplainerPage(window, currentPage) }) - probabilityOfHavingDiseaseColumn.Add(probabilityOfHavingDiseaseHelpButton) + personHasDiseaseColumn.Add(personHasDiseaseHelpButton) probabilityOfPassingAVariantHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPersonProbabilityOfPassingVariantExplainerPage(window, currentPage) @@ -416,7 +445,12 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, }) numberOfVariantsTestedColumn.Add(numberOfVariantsTestedHelpButton) - genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, probabilityOfHavingDiseaseColumn, probabilityOfPassingAVariantColumn, numberOfVariantsTestedColumn, viewButtonsColumn, layout.NewSpacer()) + numberOfPhasedLociHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + setNumberOfPhasedLociExplainerPage(window, currentPage) + }) + numberOfPhasedLociColumn.Add(numberOfPhasedLociHelpButton) + + genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, personHasDiseaseColumn, probabilityOfPassingAVariantColumn, numberOfVariantsTestedColumn, numberOfPhasedLociColumn, viewButtonsColumn, layout.NewSpacer()) return genomesContainer, nil } @@ -428,17 +462,17 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window, } page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), genomesContainer) - + setPageContent(page, window) } // This page is used to view the person's variants for a particular genome -func setViewPersonGenomeMonogenicDiseaseVariantsPage(window fyne.Window, geneticAnalysisMapList []map[string]string, genomeIdentifier string, genomeName string, diseaseName string, previousPage func()){ +func setViewPersonGenomeMonogenicDiseaseVariantsPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, genomeIdentifier [16]byte, genomeName string, diseaseName string, previousPage func()){ setLoadingScreen(window, "Loading Disease Variants", "Loading disease variants...") - currentPage := func(){setViewPersonGenomeMonogenicDiseaseVariantsPage(window, geneticAnalysisMapList, genomeIdentifier, genomeName, diseaseName, previousPage)} + currentPage := func(){setViewPersonGenomeMonogenicDiseaseVariantsPage(window, geneticAnalysisObject, genomeIdentifier, genomeName, diseaseName, previousPage)} title := getPageTitleCentered("View Monogenic Disease Variants - " + diseaseName) @@ -468,13 +502,7 @@ func setViewPersonGenomeMonogenicDiseaseVariantsPage(window fyne.Window, genetic genomeNameRow := getGenomeNameRow() - _, multipleGenomesExist, _, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisMapList) - if (err != nil) { - setErrorEncounteredPage(window, err, previousPage) - return - } - - _, _, _, _, _, numberOfVariantsTested, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(geneticAnalysisMapList, diseaseName, genomeIdentifier, multipleGenomesExist) + _, _, _, _, numberOfVariantsTested, numberOfLociTested, numberOfPhasedLoci, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, genomeIdentifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -499,6 +527,16 @@ func setViewPersonGenomeMonogenicDiseaseVariantsPage(window fyne.Window, genetic variantsTestedRow := container.NewHBox(layout.NewSpacer(), variantsTestedLabel, variantsTestedText, variantsTestedHelpButton, layout.NewSpacer()) + numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) + numberOfPhasedLociString := helpers.ConvertIntToString(numberOfPhasedLoci) + + phasedLociLabel := widget.NewLabel("Phased Loci:") + phasedLociText := getBoldLabel(numberOfPhasedLociString + "/" + numberOfLociTestedString) + phasedLociHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + setNumberOfPhasedLociExplainerPage(window, currentPage) + }) + phasedLociRow := container.NewHBox(layout.NewSpacer(), phasedLociLabel, phasedLociText, phasedLociHelpButton, layout.NewSpacer()) + //TODO: Add navigation buttons and pages getVariantsGrid := func()(*fyne.Container, error){ @@ -513,16 +551,19 @@ func setViewPersonGenomeMonogenicDiseaseVariantsPage(window fyne.Window, genetic numberOfMutationsColumn := container.NewVBox(numberOfMutationsLabel, widget.NewSeparator()) viewButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) - addVariantRow := func(variantIdentifier string)error{ + addVariantRow := func(variantIdentifierHex string)error{ - variantObject, exists := diseaseVariantsMap[variantIdentifier] + variantObject, exists := diseaseVariantsMap[variantIdentifierHex] if (exists == false) { return errors.New("Cannot add variantRow: diseaseVariantsMap missing variant.") } variantName := variantObject.VariantNames[0] - numberOfMutationsIsKnown, genomeNumberOfMutations, err := readGeneticAnalysis.GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(geneticAnalysisMapList, diseaseName, variantIdentifier, genomeIdentifier) + variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex) + if (err != nil) { return err } + + numberOfMutationsIsKnown, genomeNumberOfMutations, err := readGeneticAnalysis.GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, variantIdentifier, genomeIdentifier) if (err != nil) { return err } variantNameLabel := getBoldLabelCentered(variantName) @@ -563,7 +604,7 @@ func setViewPersonGenomeMonogenicDiseaseVariantsPage(window fyne.Window, genetic numberOfMutationsColumn.Add(genomeNumberOfMutationsLabel) viewMutationButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window, geneticAnalysisMapList, diseaseName, variantIdentifier, currentPage) + setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window, geneticAnalysisObject, diseaseName, variantIdentifier, currentPage) }) viewButtonsColumn.Add(viewMutationButton) @@ -580,25 +621,28 @@ func setViewPersonGenomeMonogenicDiseaseVariantsPage(window fyne.Window, genetic variantsList_0Mutations := make([]string, 0) variantsList_Unknown := make([]string, 0) - for variantIdentifier, _ := range diseaseVariantsMap{ + for variantIdentifierHex, _ := range diseaseVariantsMap{ - numberOfMutationsIsKnown, genomeNumberOfMutations, err := readGeneticAnalysis.GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(geneticAnalysisMapList, diseaseName, variantIdentifier, genomeIdentifier) + variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex) + if (err != nil) { return nil, err } + + numberOfMutationsIsKnown, genomeNumberOfMutations, err := readGeneticAnalysis.GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, variantIdentifier, genomeIdentifier) if (err != nil) { return nil, err } if (numberOfMutationsIsKnown == false){ - variantsList_Unknown = append(variantsList_Unknown, variantIdentifier) + variantsList_Unknown = append(variantsList_Unknown, variantIdentifierHex) continue } if (genomeNumberOfMutations == 0){ - variantsList_0Mutations = append(variantsList_0Mutations, variantIdentifier) + variantsList_0Mutations = append(variantsList_0Mutations, variantIdentifierHex) } else if (genomeNumberOfMutations == 1) { - variantsList_1Mutation = append(variantsList_1Mutation, variantIdentifier) + variantsList_1Mutation = append(variantsList_1Mutation, variantIdentifierHex) } else if (genomeNumberOfMutations == 2){ - variantsList_2Mutations = append(variantsList_2Mutations, variantIdentifier) + variantsList_2Mutations = append(variantsList_2Mutations, variantIdentifierHex) } else { return nil, errors.New("GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis returning invalid genome number of mutations") @@ -655,7 +699,7 @@ func setViewPersonGenomeMonogenicDiseaseVariantsPage(window fyne.Window, genetic return } - page := container.NewVBox(title, backButton, widget.NewSeparator(), description1Row, widget.NewSeparator(), genomeNameRow, widget.NewSeparator(), variantsTestedRow, widget.NewSeparator(), variantsGrid) + page := container.NewVBox(title, backButton, widget.NewSeparator(), description1Row, widget.NewSeparator(), genomeNameRow, widget.NewSeparator(), variantsTestedRow, widget.NewSeparator(), phasedLociRow, widget.NewSeparator(), variantsGrid) setPageContent(page, window) } @@ -663,15 +707,17 @@ func setViewPersonGenomeMonogenicDiseaseVariantsPage(window fyne.Window, genetic // This page will show the details of a specific variant from a person's genetic analysis // It will show the variant details for all of the person's genomes -func setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne.Window, geneticAnalysisMapList []map[string]string, diseaseName string, variantIdentifier string, previousPage func()){ +func setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, variantIdentifier [3]byte, previousPage func()){ - currentPage := func(){setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window, geneticAnalysisMapList, diseaseName, variantIdentifier, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window, geneticAnalysisObject, diseaseName, variantIdentifier, previousPage)} title := getPageTitleCentered("Monogenic Disease Variant Details - " + diseaseName) backButton := getBackButtonCentered(previousPage) - variantObject, err := monogenicDiseases.GetMonogenicDiseaseVariantObject(diseaseName, variantIdentifier) + variantIdentifierHex := encoding.EncodeBytesToHexString(variantIdentifier[:]) + + variantObject, err := monogenicDiseases.GetMonogenicDiseaseVariantObject(diseaseName, variantIdentifierHex) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -684,13 +730,13 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne. variantNameLabel := widget.NewLabel("Variant Name:") variantNameText := getBoldLabel(variantName) variantNameHelpButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewMonogenicDiseaseVariantDetailsPage(window, diseaseName, variantIdentifier, currentPage) + setViewMonogenicDiseaseVariantDetailsPage(window, diseaseName, variantIdentifierHex, currentPage) }) variantNameRow := container.NewHBox(layout.NewSpacer(), variantNameLabel, variantNameText, variantNameHelpButton, layout.NewSpacer()) getGenomesHaveVariantGrid := func()(*fyne.Container, error){ - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisMapList) + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject) if (err != nil) { return nil, err } genomeNameLabel := getItalicLabelCentered("Genome Name") @@ -701,9 +747,9 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne. genomeHasMutationColumn := container.NewVBox(genomeHasMutationLabel, widget.NewSeparator()) numberOfMutationsColumn := container.NewVBox(numberOfMutationsLabel, widget.NewSeparator()) - addGenomeRow := func(genomeName string, genomeIdentifier string, isACombinedGenome bool)error{ + addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ - genomeMutationsKnown, genomeNumberOfMutations, err := readGeneticAnalysis.GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(geneticAnalysisMapList, diseaseName, variantIdentifier, genomeIdentifier) + genomeMutationsKnown, genomeNumberOfMutations, err := readGeneticAnalysis.GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, variantIdentifier, genomeIdentifier) if (err != nil) { return err } getGenomeNameCell := func()*fyne.Container{ @@ -785,7 +831,7 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne. genomeFound, _, timeGenomeWasExported, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier) if (err != nil) { return "", err } if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisMapList not found.") + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") } if (multipleGenomesExist == false){ @@ -836,9 +882,9 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne. } -func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, personIdentifier string, analysisMapList []map[string]string, previousPage func()){ +func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, previousPage func()){ - currentPage := func(){setViewPersonGeneticAnalysisPolygenicDiseasesPage(window, personIdentifier, analysisMapList, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisPolygenicDiseasesPage(window, personIdentifier, analysisObject, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis - Polygenic Diseases") @@ -848,13 +894,13 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso getPolygenicDiseasesContainer := func()(*fyne.Container, error){ - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisMapList) + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject) if (err != nil){ return nil, err } // Outputs: - // -string: Main genome identifier (Is either the combined Only exclude conflicts genome or the only genome) + // -[16]byte: Main genome identifier (Is either the combined Only exclude conflicts genome or the only genome) // -error - getMainGenomeIdentifier := func()(string, error){ + getMainGenomeIdentifier := func()([16]byte, error){ if (multipleGenomesExist == true){ return onlyExcludeConflictsGenomeIdentifier, nil @@ -889,7 +935,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso diseaseNameText := getBoldLabelCentered(diseaseName) - personRiskScoreKnown, _, personRiskScoreFormatted, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisMapList, diseaseName, mainGenomeIdentifier, multipleGenomesExist) + personRiskScoreKnown, _, personRiskScoreFormatted, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier) if (err != nil) { return nil, err } getPersonRiskScoreLabelText := func()string{ @@ -911,7 +957,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso conflictExistsLabel := getBoldLabelCentered(conflictExistsString) viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window, personIdentifier, analysisMapList, diseaseName, currentPage) + setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window, personIdentifier, analysisObject, diseaseName, currentPage) })) diseaseNameColumn.Add(diseaseNameText) @@ -961,15 +1007,15 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso -func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, personIdentifier string, analysisMapList []map[string]string, diseaseName string, previousPage func()){ +func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, diseaseName string, previousPage func()){ - currentPage := func(){setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window, personIdentifier, analysisMapList, diseaseName, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window, personIdentifier, analysisObject, diseaseName, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis - " + diseaseName) backButton := getBackButtonCentered(previousPage) - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisMapList) + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -1001,7 +1047,14 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, diseaseInfoButton, layout.NewSpacer()) getGenomesContainer := func()(*fyne.Container, error){ - + + diseaseLociMap, err := polygenicDiseases.GetPolygenicDiseaseLociMap(diseaseName) + if (err != nil){ return nil, err } + + totalNumberOfLoci := len(diseaseLociMap) + totalNumberOfLociString := helpers.ConvertIntToString(totalNumberOfLoci) + + emptyLabelA := widget.NewLabel("") genomeNameLabel := getItalicLabelCentered("Genome Name") @@ -1023,7 +1076,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, viewLifetimeRiskButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) viewLociButtonsColumn := container.NewVBox(emptyLabelF, emptyLabelG, widget.NewSeparator()) - addGenomeRow := func(genomeName string, genomeIdentifier string, isACombinedGenome bool)error{ + addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ getGenomeNameCell := func()*fyne.Container{ if (isACombinedGenome == false){ @@ -1043,7 +1096,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, genomeNameCell := getGenomeNameCell() - diseaseRiskScoreKnown, _, diseaseRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisMapList, diseaseName, genomeIdentifier, multipleGenomesExist) + diseaseRiskScoreKnown, _, diseaseRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier) if (err != nil) { return err } getRiskScoreLabelText := func()string{ @@ -1061,7 +1114,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, riskScoreLabel := getBoldLabelCentered(genomeRiskScoreLabelText) genomeNumberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) - numberOfLociTestedLabel := getBoldLabelCentered(genomeNumberOfLociTestedString) + numberOfLociTestedLabel := getBoldLabelCentered(genomeNumberOfLociTestedString + "/" + totalNumberOfLociString) viewLifetimeRiskButton := widget.NewButtonWithIcon("", theme.HistoryIcon(), func(){ @@ -1087,7 +1140,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, }) viewLociButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGenomePolygenicDiseaseLociPage(window, analysisMapList, diseaseName, genomeIdentifier, genomeName, currentPage) + setViewPersonGenomePolygenicDiseaseLociPage(window, analysisObject, diseaseName, genomeIdentifier, genomeName, currentPage) }) genomeNameColumn.Add(genomeNameCell) @@ -1120,7 +1173,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, genomeFound, _, timeGenomeWasExported, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier) if (err != nil) { return "", err } if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisMapList not found.") + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") } if (multipleGenomesExist == false){ @@ -1310,11 +1363,11 @@ func setViewPersonPolygenicDiseaseLifetimeProbabilitiesPage(window fyne.Window, // This function provides a page to view the polygenic disease loci for a particular genome from a genetic analysis -func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnalysisMapList []map[string]string, diseaseName string, genomeIdentifier string, genomeName string, previousPage func()){ +func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte, genomeName string, previousPage func()){ setLoadingScreen(window, "Loading Polygenic Disease Loci", "Loading disease loci...") - currentPage := func(){setViewPersonGenomePolygenicDiseaseLociPage(window, geneticAnalysisMapList, diseaseName, genomeIdentifier, genomeName, previousPage)} + currentPage := func(){setViewPersonGenomePolygenicDiseaseLociPage(window, geneticAnalysisObject, diseaseName, genomeIdentifier, genomeName, previousPage)} title := getPageTitleCentered("View Disease Loci - " + diseaseName) @@ -1357,30 +1410,36 @@ func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnal lociList_NegativeWeight := make([]string, 0) lociList_UnknownWeight := make([]string, 0) - for locusIdentifier, _ := range diseaseLociMap{ + for locusIdentifierHex, _ := range diseaseLociMap{ - locusRiskWeightIsKnown, genomeLocusRiskWeight, _, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisMapList, diseaseName, locusIdentifier, genomeIdentifier) + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil){ + setErrorEncounteredPage(window, err, previousPage) + return + } + + locusRiskWeightIsKnown, genomeLocusRiskWeight, _, _, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } if (locusRiskWeightIsKnown == false){ - lociList_UnknownWeight = append(lociList_UnknownWeight, locusIdentifier) + lociList_UnknownWeight = append(lociList_UnknownWeight, locusIdentifierHex) continue } numberOfLociTested += 1 if (genomeLocusRiskWeight > 0){ - lociList_PositiveWeight = append(lociList_PositiveWeight, locusIdentifier) + lociList_PositiveWeight = append(lociList_PositiveWeight, locusIdentifierHex) } else if (genomeLocusRiskWeight == 0) { - lociList_ZeroWeight = append(lociList_ZeroWeight, locusIdentifier) + lociList_ZeroWeight = append(lociList_ZeroWeight, locusIdentifierHex) } else { // genomeLocusRiskWeight < 0 - lociList_NegativeWeight = append(lociList_NegativeWeight, locusIdentifier) + lociList_NegativeWeight = append(lociList_NegativeWeight, locusIdentifierHex) } } @@ -1409,11 +1468,11 @@ func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnal oddsRatioColumn := container.NewVBox(oddsRatioLabel, widget.NewSeparator()) viewButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) - addLocusRow := func(locusIdentifier string)error{ + addLocusRow := func(locusIdentifierHex string)error{ - diseaseLocusObject, exists := diseaseLociMap[locusIdentifier] + diseaseLocusObject, exists := diseaseLociMap[locusIdentifierHex] if (exists == false) { - return errors.New("Cannot add locusRow: diseaseLociMap missing locus: " + locusIdentifier) + return errors.New("Cannot add locusRow: diseaseLociMap missing locus: " + locusIdentifierHex) } locusRSID := diseaseLocusObject.LocusRSID @@ -1422,7 +1481,10 @@ func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnal locusNameLabel := getBoldLabelCentered(locusName) - locusRiskWeightIsKnown, genomeLocusRiskWeight, _, locusOddsRatioIsKnown, _, locusOddsRatioFormatted, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisMapList, diseaseName, locusIdentifier, genomeIdentifier) + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return err } + + locusRiskWeightIsKnown, genomeLocusRiskWeight, _, _, locusOddsRatioIsKnown, _, locusOddsRatioFormatted, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) if (err != nil) { return err } getGenomeLocusRiskWeightText := func()string{ @@ -1453,7 +1515,7 @@ func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnal genomeLocusOddsRatioLabel := getBoldLabelCentered(locusOddsRatioText) viewLocusButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, geneticAnalysisMapList, diseaseName, locusIdentifier, currentPage) + setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, geneticAnalysisObject, diseaseName, locusIdentifier, currentPage) }) locusNameColumn.Add(locusNameLabel) @@ -1529,15 +1591,17 @@ func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnal // This function provides a page to view the details of a specific locus from a person genetic analysis // It will show the locus details for all of the person's genomes -func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Window, geneticAnalysisMapList []map[string]string, diseaseName string, locusIdentifier string, previousPage func()){ +func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, locusIdentifier [3]byte, previousPage func()){ - currentPage := func(){setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, geneticAnalysisMapList, diseaseName, locusIdentifier, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, geneticAnalysisObject, diseaseName, locusIdentifier, previousPage)} title := getPageTitleCentered("Disease Locus Details - " + diseaseName) backButton := getBackButtonCentered(previousPage) - locusObject, err := polygenicDiseases.GetPolygenicDiseaseLocusObject(diseaseName, locusIdentifier) + locusIdentifierHex := encoding.EncodeBytesToHexString(locusIdentifier[:]) + + locusObject, err := polygenicDiseases.GetPolygenicDiseaseLocusObject(diseaseName, locusIdentifierHex) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -1552,13 +1616,13 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi locusNameLabel := widget.NewLabel("Locus Name:") locusNameText := getBoldLabel(locusName) locusInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifier, currentPage) + setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifierHex, currentPage) }) locusNameRow := container.NewHBox(layout.NewSpacer(), locusNameLabel, locusNameText, locusInfoButton, layout.NewSpacer()) getGenomesLocusInfoGrid := func()(*fyne.Container, error){ - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisMapList) + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject) if (err != nil) { return nil, err } genomeNameLabel := getItalicLabelCentered("Genome Name") @@ -1569,9 +1633,9 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi riskWeightColumn := container.NewVBox(riskWeightLabel, widget.NewSeparator()) oddsRatioColumn := container.NewVBox(oddsRatioLabel, widget.NewSeparator()) - addGenomeRow := func(genomeName string, genomeIdentifier string, isACombinedGenome bool)error{ + addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ - genomeRiskWeightKnown, genomeRiskWeight, _, genomeOddsRatioKnown, _, genomeOddsRatioFormatted, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisMapList, diseaseName, locusIdentifier, genomeIdentifier) + genomeRiskWeightKnown, genomeRiskWeight, _, _, genomeOddsRatioKnown, _, genomeOddsRatioFormatted, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) if (err != nil) { return err } getGenomeRiskWeightText := func()string{ @@ -1649,7 +1713,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi genomeFound, _, timeGenomeWasExported, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier) if (err != nil) { return "", err } if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisMapList not found.") + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") } if (multipleGenomesExist == false){ @@ -1701,9 +1765,9 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi } -func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier string, analysisMapList []map[string]string, previousPage func()){ +func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, previousPage func()){ - currentPage := func(){setViewPersonGeneticAnalysisTraitsPage(window, personIdentifier, analysisMapList, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisTraitsPage(window, personIdentifier, analysisObject, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis - Traits") @@ -1713,13 +1777,13 @@ func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier getTraitsContainer := func()(*fyne.Container, error){ - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisMapList) + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject) if (err != nil){ return nil, err } // Outputs: - // -string: Main genome identifier (Is either the combined Only exclude conflicts genome or the only genome) + // -[16]byte: Main genome identifier (Is either the combined Only exclude conflicts genome or the only genome) // -error - getMainGenomeIdentifier := func()(string, error){ + getMainGenomeIdentifier := func()([16]byte, error){ if (multipleGenomesExist == true){ return onlyExcludeConflictsGenomeIdentifier, nil @@ -1762,8 +1826,8 @@ func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier continue } traitOutcomeNamesList := traitObject.OutcomesList - - _, anyTraitRuleTested, outcomeScoresMap, _, conflictExists, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(analysisMapList, traitName, mainGenomeIdentifier, multipleGenomesExist) + + _, anyTraitRuleTested, outcomeScoresMap, _, conflictExists, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(analysisObject, traitName, mainGenomeIdentifier) if (err != nil) { return nil, err } // We add all of the columns except for the trait outcomes column, which may be multiple rows high @@ -1772,7 +1836,7 @@ func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier conflictExistsLabel := getBoldLabelCentered(conflictExistsString) viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGeneticAnalysisTraitDetailsPage(window, personIdentifier, analysisMapList, traitName, currentPage) + setViewPersonGeneticAnalysisTraitDetailsPage(window, personIdentifier, analysisObject, traitName, currentPage) })) traitNameText := getBoldLabelCentered(traitName) @@ -1860,15 +1924,15 @@ func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier -func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIdentifier string, analysisMapList []map[string]string, traitName string, previousPage func()){ +func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, traitName string, previousPage func()){ - currentPage := func(){setViewPersonGeneticAnalysisTraitDetailsPage(window, personIdentifier, analysisMapList, traitName, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisTraitDetailsPage(window, personIdentifier, analysisObject, traitName, previousPage)} title := getPageTitleCentered("Viewing Genetic Analysis - " + traitName) backButton := getBackButtonCentered(previousPage) - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisMapList) + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -1900,7 +1964,13 @@ func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIden traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, traitInfoButton, layout.NewSpacer()) getGenomesContainer := func()(*fyne.Container, error){ + + traitRulesMap, err := traits.GetTraitRulesMap(traitName) + if (err != nil){ return nil, err } + totalNumberOfRules := len(traitRulesMap) + totalNumberOfRulesString := helpers.ConvertIntToString(totalNumberOfRules) + emptyLabelA := widget.NewLabel("") genomeNameLabel := getItalicLabelCentered("Genome Name") @@ -1918,7 +1988,7 @@ func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIden numberOfRulesTestedColumn := container.NewVBox(numberOfLabel, rulesTestedLabel, widget.NewSeparator()) viewRulesButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) - addGenomeRow := func(genomeName string, genomeIdentifier string, isACombinedGenome bool)error{ + addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ getGenomeNameCell := func()*fyne.Container{ if (isACombinedGenome == false){ @@ -1929,25 +1999,25 @@ func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIden viewHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setCombinedGenomesExplainerPage(window, currentPage) }) - + genomeNameLabel := getBoldLabel(genomeName) genomeNameCell := container.NewHBox(layout.NewSpacer(), viewHelpButton, genomeNameLabel, layout.NewSpacer()) - - return genomeNameCell + + return genomeNameCell } genomeNameCell := getGenomeNameCell() - - _, anyTraitRuleTested, outcomeScoresMap, numberOfRulesTested, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(analysisMapList, traitName, genomeIdentifier, multipleGenomesExist) + + _, anyTraitRuleTested, outcomeScoresMap, numberOfRulesTested, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(analysisObject, traitName, genomeIdentifier) if (err != nil) { return err } // We add all of the columns except for the trait rule column, which may be multiple rows high genomeNumberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested) - numberOfRulesTestedLabel := getBoldLabelCentered(genomeNumberOfRulesTestedString) + numberOfRulesTestedLabel := getBoldLabelCentered(genomeNumberOfRulesTestedString + "/" + totalNumberOfRulesString) viewRulesButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGenomeTraitRulesPage(window, analysisMapList, traitName, genomeIdentifier, genomeName, currentPage) + setViewPersonGenomeTraitRulesPage(window, analysisObject, traitName, genomeIdentifier, genomeName, currentPage) }) genomeNameColumn.Add(genomeNameCell) @@ -2018,7 +2088,7 @@ func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIden genomeFound, _, timeGenomeWasExported, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier) if (err != nil) { return "", err } if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisMapList not found.") + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") } if (multipleGenomesExist == false){ @@ -2071,11 +2141,11 @@ func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIden // Ths function provides a page to view the trait rules for a particular genome from a genetic analysis -func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisMapList []map[string]string, traitName string, genomeIdentifier string, genomeName string, previousPage func()){ +func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte, genomeName string, previousPage func()){ setLoadingScreen(window, "Loading Trait Rules", "Loading trait rules...") - currentPage := func(){setViewPersonGenomeTraitRulesPage(window, geneticAnalysisMapList, traitName, genomeIdentifier, genomeName, previousPage)} + currentPage := func(){setViewPersonGenomeTraitRulesPage(window, geneticAnalysisObject, traitName, genomeIdentifier, genomeName, previousPage)} title := getPageTitleCentered("View Trait Rules - " + traitName) @@ -2117,23 +2187,29 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisMapLis rulesList_RuleNotPassed := make([]string, 0) rulesList_Unknown := make([]string, 0) - for ruleIdentifier, _ := range traitRulesMap{ + for ruleIdentifierHex, _ := range traitRulesMap{ - ruleStatusIsKnown, genomePassesRule, _, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisMapList, traitName, ruleIdentifier, genomeIdentifier) + ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex) + if (err != nil){ + setErrorEncounteredPage(window, err, previousPage) + return + } + + ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (ruleStatusIsKnown == false){ - rulesList_Unknown = append(rulesList_Unknown, ruleIdentifier) + rulesList_Unknown = append(rulesList_Unknown, ruleIdentifierHex) continue } numberOfRulesTested += 1 if (genomePassesRule == true){ - rulesList_RulePassed = append(rulesList_RulePassed, ruleIdentifier) + rulesList_RulePassed = append(rulesList_RulePassed, ruleIdentifierHex) } else { - rulesList_RuleNotPassed = append(rulesList_RuleNotPassed, ruleIdentifier) + rulesList_RuleNotPassed = append(rulesList_RuleNotPassed, ruleIdentifierHex) } } @@ -2162,11 +2238,14 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisMapLis genomePassesRuleColumn := container.NewVBox(genomePassesRuleLabel, widget.NewSeparator()) viewButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) - addRuleRow := func(ruleIdentifier string)error{ + addRuleRow := func(ruleIdentifierHex string)error{ - ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifier) + ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifierHex) - ruleStatusIsKnown, genomePassesRule, _, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisMapList, traitName, ruleIdentifier, genomeIdentifier) + ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex) + if (err != nil){ return err } + + ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier) if (err != nil) { return err } getGenomePassesRuleText := func()string{ @@ -2188,14 +2267,14 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisMapLis // We do this because the rule effects column may be multiple rows tall viewRuleButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGeneticAnalysisTraitRuleDetailsPage(window, geneticAnalysisMapList, traitName, ruleIdentifier, currentPage) + setViewPersonGeneticAnalysisTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, currentPage) }) ruleIdentifierColumn.Add(ruleIdentifierLabel) genomePassesRuleColumn.Add(genomePassesRuleLabel) viewButtonsColumn.Add(viewRuleButton) - traitRuleObject, exists := traitRulesMap[ruleIdentifier] + traitRuleObject, exists := traitRulesMap[ruleIdentifierHex] if (exists == false){ return errors.New("Trait rule not found after being found already.") } @@ -2300,12 +2379,11 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisMapLis } - // This function provides a page to view the details of a specific trait rule from a person genetic analysis // The page will show the rule details for all of the person's genomes -func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneticAnalysisMapList []map[string]string, traitName string, ruleIdentifier string, previousPage func()){ +func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, previousPage func()){ - currentPage := func(){setViewPersonGeneticAnalysisTraitRuleDetailsPage(window, geneticAnalysisMapList, traitName, ruleIdentifier, previousPage)} + currentPage := func(){setViewPersonGeneticAnalysisTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, previousPage)} title := getPageTitleCentered("Trait Rule Details - " + traitName) @@ -2313,16 +2391,18 @@ func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneti description := getLabelCentered("Below is the rule result for the person's genomes.") + ruleIdentifierHex := encoding.EncodeBytesToHexString(ruleIdentifier[:]) + ruleIdentifierLabel := widget.NewLabel("Rule Identifier:") - ruleIdentifierText := getBoldLabel(ruleIdentifier) + ruleIdentifierText := getBoldLabel(ruleIdentifierHex) ruleInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewTraitRuleDetailsPage(window, traitName, ruleIdentifier, currentPage) + setViewTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage) }) ruleIdentifierRow := container.NewHBox(layout.NewSpacer(), ruleIdentifierLabel, ruleIdentifierText, ruleInfoButton, layout.NewSpacer()) getGenomesRuleInfoGrid := func()(*fyne.Container, error){ - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisMapList) + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject) if (err != nil) { return nil, err } genomeNameLabel := getItalicLabelCentered("Genome Name") @@ -2331,9 +2411,9 @@ func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneti genomeNameColumn := container.NewVBox(genomeNameLabel, widget.NewSeparator()) genomePassesRuleColumn := container.NewVBox(genomePassesRuleLabel, widget.NewSeparator()) - addGenomeRow := func(genomeName string, genomeIdentifier string, isACombinedGenome bool)error{ + addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ - genomeRuleStatusKnown, genomePassesRule, _, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisMapList, traitName, ruleIdentifier, genomeIdentifier) + genomeRuleStatusKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier) if (err != nil) { return err } getGenomePassesRuleText := func()string{ @@ -2396,7 +2476,7 @@ func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneti genomeFound, _, timeGenomeWasExported, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier) if (err != nil) { return "", err } if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisMapList not found.") + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") } if (multipleGenomesExist == false){ diff --git a/gui/viewProfileGui.go b/gui/viewProfileGui.go index f1d9093..4ec375b 100644 --- a/gui/viewProfileGui.go +++ b/gui/viewProfileGui.go @@ -2635,7 +2635,7 @@ func setViewMateProfilePage_MonogenicDiseases(window fyne.Window, userOrOffsprin offspringProbabilityOfHavingAVariantColumn := container.NewVBox(offspringProbabilityOfLabelB, havingVariantLabel, widget.NewSeparator()) offspringNumberOfVariantsTestedColumn := container.NewVBox(offspringNumberOfLabel, variantsTestedLabelB, widget.NewSeparator()) - myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisMapList, myGenomeIdentifier, iHaveMultipleGenomes, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { return nil, err } monogenicDiseaseObjectsList, err := monogenicDiseases.GetMonogenicDiseaseObjectsList() @@ -2699,9 +2699,9 @@ func setViewMateProfilePage_MonogenicDiseases(window fyne.Window, userOrOffsprin return false, 0, 0, nil } - probabilitiesKnown, _, _, probabilityOfPassingADiseaseVariant, _, numberOfVariantsTested, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(myAnalysisMapList, monogenicDiseaseName, myGenomeIdentifier, iHaveMultipleGenomes) + diseaseInfoIsKnown, _, probabilityOfPassingADiseaseVariant, _, numberOfVariantsTested, _, _, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(myAnalysisObject, monogenicDiseaseName, myGenomeIdentifier) if (err != nil) { return false, 0, 0, err } - if (probabilitiesKnown == false){ + if (diseaseInfoIsKnown == false){ return false, 0, 0, nil } @@ -2711,61 +2711,8 @@ func setViewMateProfilePage_MonogenicDiseases(window fyne.Window, userOrOffsprin myDiseaseInfoExists, myProbabilityOfPassingAVariant, myNumberOfVariantsTested, err := getMyDiseaseInfo() if (err != nil) { return nil, err } - //Outputs: - // -bool: Disease info known - // -int: Offspring probability of having disease (0-100) - // -bool: Probability offspring has variant is known - // -int: Probability offspring has variant (0-100) - // -int: Number of variants tested - // -error - getOffspringDiseaseInfo := func()(bool, int, bool, int, int, error){ - - if (userDiseaseInfoExists == false && myDiseaseInfoExists == false){ - return false, 0, false, 0, 0, nil - } - if (userDiseaseInfoExists == true && myDiseaseInfoExists == false){ - - if (diseaseIsDominantOrRecessive == "Dominant"){ - if (userProbabilityOfPassingAVariant == 100){ - // We know the offspring will have the disease - return true, 100, true, 100, userNumberOfVariantsTested, nil - } - return false, 0, false, 0, 0, nil - } - - if (userProbabilityOfPassingAVariant == 0){ - // We know the offspring will not have the disease - return true, 0, false, 0, userNumberOfVariantsTested, nil - } - return false, 0, false, 0, 0, nil - } - if (userDiseaseInfoExists == false && myDiseaseInfoExists == true){ - - if (diseaseIsDominantOrRecessive == "Dominant"){ - if (myProbabilityOfPassingAVariant == 100){ - // We know the offspring will have the disease - return true, 100, true, 100, myNumberOfVariantsTested, nil - } - return false, 0, false, 0, 0, nil - } - - if (myProbabilityOfPassingAVariant == 0){ - // We know the offspring will not have the disease - return true, 0, false, 0, myNumberOfVariantsTested, nil - } - return false, 0, false, 0, 0, nil - } - - probabilityOffspringHasDisease, probabilityOffspringHasVariant, err := createGeneticAnalysis.GetOffspringMonogenicDiseaseProbabilities(diseaseIsDominantOrRecessive, userProbabilityOfPassingAVariant, myProbabilityOfPassingAVariant) - if (err != nil) { return false, 0, false, 0, 0, err } - - offspringNumberOfVariantsTested := userNumberOfVariantsTested + myNumberOfVariantsTested - - return true, probabilityOffspringHasDisease, true, probabilityOffspringHasVariant, offspringNumberOfVariantsTested, nil - } - - offspringDiseaseInfoIsKnown, offspringProbabilityOfHavingDisease, offspringProbabilityOfHavingAVariantIsKnown, offspringProbabilityOfHavingAVariant, offspringNumberOfVariantsTested, err := getOffspringDiseaseInfo() - if (err != nil){ return nil, err } + probabilityOffspringHasDiseaseIsKnown, probabilityOffspringHasDisease, probabilityOffspringHasVariantIsKnown, probabilityOffspringHasVariant, err := createGeneticAnalysis.GetOffspringMonogenicDiseaseProbabilities(diseaseIsDominantOrRecessive, userDiseaseInfoExists, userProbabilityOfPassingAVariant, myDiseaseInfoExists, myProbabilityOfPassingAVariant) + if (err != nil) { return nil, err } getUserProbabilityOfPassingAVariantString := func()string{ if (userDiseaseInfoExists == false){ @@ -2794,41 +2741,37 @@ func setViewMateProfilePage_MonogenicDiseases(window fyne.Window, userOrOffsprin getOffspringProbabilityOfHavingDiseaseString := func()string{ - if (offspringDiseaseInfoIsKnown == false){ + if (probabilityOffspringHasDiseaseIsKnown == false){ result := translate("Unknown") return result } - offspringProbabilityOfHavingDiseaseString := helpers.ConvertIntToString(offspringProbabilityOfHavingDisease) + offspringProbabilityOfHavingDiseaseString := helpers.ConvertIntToString(probabilityOffspringHasDisease) resultFormatted := offspringProbabilityOfHavingDiseaseString + "%" return resultFormatted } offspringProbabilityOfHavingDiseaseString := getOffspringProbabilityOfHavingDiseaseString() - getOffspringProbabilityOfHavingAVariantString := func()string{ + getOffspringProbabilityOfHavingAVariantFormatted := func()string{ - if (offspringDiseaseInfoIsKnown == false || offspringProbabilityOfHavingAVariantIsKnown == false){ + if (probabilityOffspringHasVariantIsKnown == false){ result := translate("Unknown") return result } - offspringProbabilityOfHavingAVariantString := helpers.ConvertIntToString(offspringProbabilityOfHavingAVariant) + offspringProbabilityOfHavingAVariantString := helpers.ConvertIntToString(probabilityOffspringHasVariant) resultFormatted := offspringProbabilityOfHavingAVariantString + "%" return resultFormatted } - offspringProbabilityOfHavingAVariantString := getOffspringProbabilityOfHavingAVariantString() + offspringProbabilityOfHavingAVariantFormatted := getOffspringProbabilityOfHavingAVariantFormatted() - getOffspringNumberOfVariantsTestedString := func()string{ - totalNumberOfOffspringDiseaseVariantsString := helpers.ConvertIntToString(numberOfDiseaseVariants*2) - if (offspringDiseaseInfoIsKnown == false){ - result := "0/" + totalNumberOfOffspringDiseaseVariantsString - return result - } - offspringNumberOfVariantsTestedString := helpers.ConvertIntToString(offspringNumberOfVariantsTested) - result := offspringNumberOfVariantsTestedString + "/" + totalNumberOfOffspringDiseaseVariantsString - return result - } + totalNumberOfOffspringDiseaseVariantsString := helpers.ConvertIntToString(numberOfDiseaseVariants*2) + + offspringNumberOfVariantsTested := userNumberOfVariantsTested + myNumberOfVariantsTested + + offspringNumberOfVariantsTestedString := helpers.ConvertIntToString(offspringNumberOfVariantsTested) + offspringNumberOfVariantsTestedFormatted := offspringNumberOfVariantsTestedString + "/" + totalNumberOfOffspringDiseaseVariantsString viewDiseaseInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ setViewMonogenicDiseaseDetailsPage(window, monogenicDiseaseName, currentPage) @@ -2836,14 +2779,12 @@ func setViewMateProfilePage_MonogenicDiseases(window fyne.Window, userOrOffsprin diseaseNameLabel := getBoldLabelCentered(monogenicDiseaseName) - offspringNumberOfVariantsTestedString := getOffspringNumberOfVariantsTestedString() - userProbabilityOfPassingAVariantLabel := getBoldLabelCentered(userProbabilityOfPassingAVariantString) userNumberOfVariantsTestedLabel := getBoldLabelCentered(userNumberOfVariantsTestedString) offspringProbabilityOfHavingDiseaseLabel := getBoldLabelCentered(offspringProbabilityOfHavingDiseaseString) - offspringProbabilityOfHavingAVariantLabel := getBoldLabelCentered(offspringProbabilityOfHavingAVariantString) - offspringNumberOfVariantsTestedLabel := getBoldLabelCentered(offspringNumberOfVariantsTestedString) + offspringProbabilityOfHavingAVariantLabel := getBoldLabelCentered(offspringProbabilityOfHavingAVariantFormatted) + offspringNumberOfVariantsTestedLabel := getBoldLabelCentered(offspringNumberOfVariantsTestedFormatted) diseaseInfoButtonsColumn.Add(viewDiseaseInfoButton) diseaseNameColumn.Add(diseaseNameLabel) @@ -2959,7 +2900,7 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin offspringNumberOfLociTestedColumn := container.NewVBox(offspringNumberOfLabel, lociTestedLabelB, widget.NewSeparator()) viewDiseaseInfoButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) - myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisMapList, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { return nil, err } diseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList() @@ -2982,7 +2923,11 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin for _, locusObject := range diseaseLociList{ - locusIdentifier := locusObject.LocusIdentifier + locusIdentifierHex := locusObject.LocusIdentifier + + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil){ return nil, err } + locusRSID := locusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) @@ -3014,27 +2959,33 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin //Outputs: // -bool: My locus base pair exists - // -string: My locus base pair + // -string: My locus base 1 + // -string: My locus base 2 // -error - getMyLocusInfo := func()(bool, string, error){ + getMyLocusInfo := func()(bool, string, string, error){ if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ - return false, "", nil + return false, "", "", nil } - locusInfoKnown, _, locusBasePair, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myAnalysisMapList, diseaseName, locusIdentifier, myGenomeIdentifier) - if (err != nil){ return false, "", err } + locusInfoKnown, _, locusBase1, locusBase2, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, locusIdentifier, myGenomeIdentifier) + if (err != nil){ return false, "", "", err } if (locusInfoKnown == false){ - return false, "", nil + return false, "", "", nil } - return true, locusBasePair, nil + return true, locusBase1, locusBase2, nil } - myLocusBasePairExists, myLocusBasePair, err := getMyLocusInfo() + myLocusBasePairExists, myLocusBase1, myLocusBase2, err := getMyLocusInfo() if (err != nil) { return nil, err } if (userLocusBasePairExists == true && myLocusBasePairExists == true){ - offspringLocusRiskWeight, _, _, _, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBasePair, userLocusBasePair) + userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";") + if (semicolonFound == false){ + return nil, errors.New("Database contains profile containing invalid " + locusValueAttributeName + ": " + userLocusBasePair) + } + + offspringLocusRiskWeight, _, _, _, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBase1, myLocusBase2, userLocusBase1, userLocusBase2) if (err != nil) { return nil, err } offspringNumberOfLociTested += 1 @@ -3196,7 +3147,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName numberOfDiseaseLoci := len(diseaseLocusObjectsList) - myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisMapList, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -3242,19 +3193,23 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName //Outputs: // -bool: My locus Info is known - // -string: My locus base pair + // -string: My locus base 1 + // -string: My locus base 2 // -error - getMyLocusInfo := func(locusIdentifier string)(bool, string, error){ + getMyLocusInfo := func(locusIdentifierHex string)(bool, string, string, error){ if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ - return false, "", nil + return false, "", "", nil } - locusInfoKnown, _, locusBasePair, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myAnalysisMapList, diseaseName, locusIdentifier, myGenomeIdentifier) - if (err != nil){ return false, "", err } + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return false, "", "", err } + + locusInfoKnown, _, locusBase1, locusBase2, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, locusIdentifier, myGenomeIdentifier) + if (err != nil){ return false, "", "", err } if (locusInfoKnown == false){ - return false, "", nil + return false, "", "", nil } - return true, locusBasePair, nil + return true, locusBase1, locusBase2, nil } getNumberOfLociTested := func()(int, error){ @@ -3282,7 +3237,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName if (userOrOffspring == "Offspring") { - myLocusInfoKnown, _, err := getMyLocusInfo(locusIdentifier) + myLocusInfoKnown, _, _, err := getMyLocusInfo(locusIdentifier) if (err != nil) { return 0, err } if (myLocusInfoKnown == false){ continue @@ -3355,7 +3310,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName userLocusInfoIsKnown, userLocusRiskWeight, userLocusBasePair, userLocusOddsRatioKnown, userLocusOddsRatioFormatted, err := getUserLocusInfo(locusRSID, locusRiskWeightsMap, locusOddsRatiosMap) if (err != nil) { return nil, err } - myLocusInfoIsKnown, myLocusBasePair, err := getMyLocusInfo(locusIdentifier) + myLocusInfoIsKnown, myLocusBase1, myLocusBase2, err := getMyLocusInfo(locusIdentifier) if (err != nil) { return nil, err } getUserRiskWeightString := func()string{ @@ -3393,7 +3348,12 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName return false, 0, false, "", nil } - offspringLocusRiskWeight, offspringOddsRatioIsKnown, offspringOddsRatio, unknownOddsRatiosWeightSum, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBasePair, userLocusBasePair) + userLocusBase1, userLocusBase2, semicolonExists := strings.Cut(userLocusBasePair, ";") + if (semicolonExists == true){ + return false, 0, false, "", errors.New("Database corrupt: Contains profile with invalid " + locusName + " value: " + userLocusBasePair) + } + + offspringLocusRiskWeight, offspringOddsRatioIsKnown, offspringOddsRatio, unknownOddsRatiosWeightSum, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBase1, myLocusBase2, userLocusBase1, userLocusBase2) if (err != nil) { return false, 0, false, "", err } if (offspringOddsRatioIsKnown == false){ @@ -3557,7 +3517,7 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st offspringNumberOfRulesTestedColumn := container.NewVBox(numberOfLabelB, rulesTestedLabelB, widget.NewSeparator()) viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) - myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisMapList, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { return nil, err } traitObjectsList, err := traits.GetTraitObjectsList() @@ -3691,21 +3651,19 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st // -error getOffspringTraitOutcomeScoresMap := func()(bool, map[string]float64, int, error){ + if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ + // Without my genome person chosen, all offspring rules and outcome scores are unknown + return false, nil, 0, nil + } + + myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier) + if (err != nil) { return false, nil, 0, err } + offspringTraitOutcomeScoresMap := make(map[string]float64) offspringNumberOfRulesTested := 0 for _, traitRuleObject := range traitRulesList{ - if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ - // Without my genome person chosen, all offspring rules and outcome scores are unknown - return false, nil, 0, nil - } - - ruleIdentifier := traitRuleObject.RuleIdentifier - - _, _, myRuleLociBasePairsMap, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(myAnalysisMapList, traitName, ruleIdentifier, myGenomeIdentifier) - if (err != nil) { return false, nil, 0, err } - ruleLociList := traitRuleObject.LociList //Outputs: @@ -3718,7 +3676,6 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st for _, ruleLocusObject := range ruleLociList{ - locusIdentifier := ruleLocusObject.LocusIdentifier locusRSID := ruleLocusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) @@ -3732,15 +3689,23 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st return false, 0, nil } - myLocusBasePair, myLocusBasePairIsKnown := myRuleLociBasePairsMap[locusIdentifier] - if (myLocusBasePairIsKnown == false){ + userLocusBase1, userLocusBase2, semicolonExists := strings.Cut(userLocusBasePair, ";") + if (semicolonExists == false){ + return false, 0, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + ": " + userLocusBasePair) + } + + myLocusValue, myLocusValueIsKnown := myTraitLocusValuesMap[locusRSID] + if (myLocusValueIsKnown == false){ // We must know all rule loci base pairs to determine offspring probability of passing rule return false, 0, nil } + myLocusBase1 := myLocusValue.Base1Value + myLocusBase2 := myLocusValue.Base2Value + locusRequiredBasePairsList := ruleLocusObject.BasePairsList - - offspringProbabilityOfPassingRuleLocus, err := createGeneticAnalysis.GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, userLocusBasePair, myLocusBasePair) + + offspringProbabilityOfPassingRuleLocus, err := createGeneticAnalysis.GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, userLocusBase1, userLocusBase2, myLocusBase1, myLocusBase2) if (err != nil) { return false, 0, err } offspringProbabilityOfPassingRule *= offspringProbabilityOfPassingRuleLocus @@ -3754,7 +3719,7 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st continue } offspringNumberOfRulesTested += 1 - + ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap for traitOutcome, pointsEffect := range ruleOutcomePointsMap{ @@ -3807,7 +3772,7 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st if (exists == false){ return nil, errors.New("Outcome not found in userTraitOutcomeScoresMap.") } - + outcomeScoreString := helpers.ConvertIntToString(outcomeScore) outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString) @@ -3867,7 +3832,7 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st } } } - + traitNameColumn.Add(widget.NewSeparator()) userOutcomeScoresColumn.Add(widget.NewSeparator()) offspringOutcomeScoresColumn.Add(widget.NewSeparator()) @@ -3978,7 +3943,7 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use return true, true, nil } - myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisMapList, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return @@ -3995,14 +3960,13 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use return false, 0, nil } - _, _, myRuleLociBasePairsMap, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(myAnalysisMapList, traitName, ruleIdentifier, myGenomeIdentifier) + myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier) if (err != nil) { return false, 0, err } offspringProbabilityOfPassingRule := float64(1) for _, ruleLocusObject := range ruleLociList{ - locusIdentifier := ruleLocusObject.LocusIdentifier locusRSID := ruleLocusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) @@ -4016,15 +3980,23 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use return false, 0, nil } - myLocusBasePair, myLocusBasePairIsKnown := myRuleLociBasePairsMap[locusIdentifier] - if (myLocusBasePairIsKnown == false){ + userLocusBase1, userLocusBase2, semicolonExists := strings.Cut(userLocusBasePair, ";") + if (semicolonExists == false){ + return false, 0, errors.New("Database corrupt: Contains profile with invalid: " + userLocusValueAttributeName + ": " + userLocusBasePair) + } + + myLocusValue, myLocusValueIsKnown := myTraitLocusValuesMap[locusRSID] + if (myLocusValueIsKnown == false){ // We must know all rule loci base pairs to determine offspring probability of passing rule return false, 0, nil } + myLocusBase1 := myLocusValue.Base1Value + myLocusBase2 := myLocusValue.Base2Value + locusRequiredBasePairsList := ruleLocusObject.BasePairsList - offspringProbabilityOfPassingRuleLocus, err := createGeneticAnalysis.GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, userLocusBasePair, myLocusBasePair) + offspringProbabilityOfPassingRuleLocus, err := createGeneticAnalysis.GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, userLocusBase1, userLocusBase2, myLocusBase1, myLocusBase2) if (err != nil) { return false, 0, err } offspringProbabilityOfPassingRule *= offspringProbabilityOfPassingRuleLocus diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go index 5b13bdb..39ba36f 100644 --- a/internal/encoding/encoding.go +++ b/internal/encoding/encoding.go @@ -23,6 +23,34 @@ func DecodeHexStringToBytes(hexInput string)([]byte, error){ return decodedBytes, nil } +func DecodeHexStringTo3ByteArray(hexInput string)([3]byte, error){ + + decodedBytes, err := hex.DecodeString(hexInput) + if (err != nil) { return [3]byte{}, err } + + if (len(decodedBytes) != 3){ + return [3]byte{}, errors.New("DecodeHexStringTo3ByteArray called with invalid length hex string: " + hexInput) + } + + result := [3]byte(decodedBytes) + + return result, nil +} + +func DecodeHexStringTo16ByteArray(hexInput string)([16]byte, error){ + + decodedBytes, err := hex.DecodeString(hexInput) + if (err != nil) { return [16]byte{}, err } + + if (len(decodedBytes) != 16){ + return [16]byte{}, errors.New("DecodeHexStringTo16ByteArray called with invalid length hex string: " + hexInput) + } + + result := [16]byte(decodedBytes) + + return result, nil +} + func EncodeBytesToHexString(input []byte)string{ result := hex.EncodeToString(input) @@ -127,7 +155,6 @@ func EncodeMessagePackBytes(input interface{}) ([]byte, error) { return bytes, nil } - func DecodeRawMessagePackToString(data messagepack.RawMessage)(string, error){ var outputString string diff --git a/internal/genetics/companyAnalysis/23andMe.go b/internal/genetics/companyAnalysis/23andMe.go index 75023e0..66b62cb 100644 --- a/internal/genetics/companyAnalysis/23andMe.go +++ b/internal/genetics/companyAnalysis/23andMe.go @@ -440,66 +440,66 @@ func AddMissingParentsToAncestryCompositionMaps_23andMe(inputContinentPercentage // -map[string]float64: Region percentages map // -map[string]float64: Subregion percentages map // -error -func GetOffspringAncestryComposition_23andMe(personAAncestryCompositionAttribute string, personBAncestryCompositionAttribute string)(map[string]float64, map[string]float64, map[string]float64, error){ +func GetOffspringAncestryComposition_23andMe(person1AncestryCompositionAttribute string, person2AncestryCompositionAttribute string)(map[string]float64, map[string]float64, map[string]float64, error){ - personAAttributeIsValid, personAContinentPercentagesMap, personARegionPercentagesMap, personASubregionPercentagesMap, err := ReadAncestryCompositionAttribute_23andMe(true, personAAncestryCompositionAttribute) + person1AttributeIsValid, person1ContinentPercentagesMap, person1RegionPercentagesMap, person1SubregionPercentagesMap, err := ReadAncestryCompositionAttribute_23andMe(true, person1AncestryCompositionAttribute) if (err != nil) { return nil, nil, nil, err } - if (personAAttributeIsValid == false){ - return nil, nil, nil, errors.New("GetOffspringAncestryComposition_23andMe called with invalid person A ancestry composition attribute: " + personAAncestryCompositionAttribute) + if (person1AttributeIsValid == false){ + return nil, nil, nil, errors.New("GetOffspringAncestryComposition_23andMe called with invalid person A ancestry composition attribute: " + person1AncestryCompositionAttribute) } - personBAttributeIsValid, personBContinentPercentagesMap, personBRegionPercentagesMap, personBSubregionPercentagesMap, err := ReadAncestryCompositionAttribute_23andMe(true, personBAncestryCompositionAttribute) + person2AttributeIsValid, person2ContinentPercentagesMap, person2RegionPercentagesMap, person2SubregionPercentagesMap, err := ReadAncestryCompositionAttribute_23andMe(true, person2AncestryCompositionAttribute) if (err != nil) { return nil, nil, nil, err } - if (personBAttributeIsValid == false){ - return nil, nil, nil, errors.New("GetOffspringAncestryComposition_23andMe called with invalid person B ancestry composition attribute: " + personBAncestryCompositionAttribute) + if (person2AttributeIsValid == false){ + return nil, nil, nil, errors.New("GetOffspringAncestryComposition_23andMe called with invalid person B ancestry composition attribute: " + person2AncestryCompositionAttribute) } offspringContinentPercentagesMap := make(map[string]float64) - for continentName, continentPercentage := range personAContinentPercentagesMap{ + for continentName, continentPercentage := range person1ContinentPercentagesMap{ - personAPercentage := continentPercentage/2 + person1Percentage := continentPercentage/2 - offspringContinentPercentagesMap[continentName] = personAPercentage + offspringContinentPercentagesMap[continentName] = person1Percentage } - for continentName, continentPercentage := range personBContinentPercentagesMap{ + for continentName, continentPercentage := range person2ContinentPercentagesMap{ - personBPercentage := continentPercentage/2 + person2Percentage := continentPercentage/2 - offspringContinentPercentagesMap[continentName] += personBPercentage + offspringContinentPercentagesMap[continentName] += person2Percentage } offspringRegionPercentagesMap := make(map[string]float64) - for regionName, regionPercentage := range personARegionPercentagesMap{ + for regionName, regionPercentage := range person1RegionPercentagesMap{ - personAPercentage := regionPercentage/2 + person1Percentage := regionPercentage/2 - offspringRegionPercentagesMap[regionName] = personAPercentage + offspringRegionPercentagesMap[regionName] = person1Percentage } - for regionName, regionPercentage := range personBRegionPercentagesMap{ + for regionName, regionPercentage := range person2RegionPercentagesMap{ - personBPercentage := regionPercentage/2 + person2Percentage := regionPercentage/2 - offspringRegionPercentagesMap[regionName] += personBPercentage + offspringRegionPercentagesMap[regionName] += person2Percentage } offspringSubregionPercentagesMap := make(map[string]float64) - for subregionName, subregionPercentage := range personASubregionPercentagesMap{ + for subregionName, subregionPercentage := range person1SubregionPercentagesMap{ - personAPercentage := subregionPercentage/2 + person1Percentage := subregionPercentage/2 - offspringSubregionPercentagesMap[subregionName] = personAPercentage + offspringSubregionPercentagesMap[subregionName] = person1Percentage } - for subregionName, subregionPercentage := range personBSubregionPercentagesMap{ + for subregionName, subregionPercentage := range person2SubregionPercentagesMap{ - personBPercentage := subregionPercentage/2 + person2Percentage := subregionPercentage/2 - offspringSubregionPercentagesMap[subregionName] += personBPercentage + offspringSubregionPercentagesMap[subregionName] += person2Percentage } return offspringContinentPercentagesMap, offspringRegionPercentagesMap, offspringSubregionPercentagesMap, nil diff --git a/internal/genetics/createGeneticAnalysis/createGeneticAnalysis.go b/internal/genetics/createGeneticAnalysis/createGeneticAnalysis.go index d288cdb..f6d5b4d 100644 --- a/internal/genetics/createGeneticAnalysis/createGeneticAnalysis.go +++ b/internal/genetics/createGeneticAnalysis/createGeneticAnalysis.go @@ -8,25 +8,27 @@ package createGeneticAnalysis // TODO: Some of the probabilities produced by this package are wrong -// In this package, we are assuming that genetic recombination (the formation of the genetic sequences for the sperm/eggs) happens randomly for each allele locus +// In this package, we are assuming that genetic recombination (the formation of the genetic sequences for the sperm/eggs) +// happens randomly for each allele locus // In reality, the recombination break points occur less often, and larger portions of each chromosome remain intact. // This effects the estimates and probabilities for all of the generated analyses // In particular, the probability of passing a defective gene does not increase as much as this package currently -// estimates that it does, in the case of multiple defects existing in the same gene. +// estimates that it does, in the case of multiple defects existing in the same monongenic-disease-causing gene. // Also, based on my research, I believe that recombination break points are less likely to occur within genes, meaning they are more likely to occur at the gene boundaries (codons) // We need to remedy this problem and fix this package // Research gene linkage and recombination to understand more. // -// Fixing this problem may require us to only allow will-pass-a-variant probabilities to be calculated from phased loci. -// The phase of a loci becomes much more important in determining the will-pass-a-variant probability -// Having multiple variants within a gene might not increase the probability of passing a variant, assuming all of those variants were on the same chromosome +// The phase of a loci is actually relevant and important for determining the person-has-disease status and will-pass-a-variant probability +// Users who have multiple heterozygous single-base mutations on different locations of the same gene may have the disease, +// but we need their genome locations to be phased to be able to know +// Having multiple variants within a gene might not increase the probability of passing a variant, +// assuming all of those variants were on the same chromosome // Thus, we need phased loci to determine an accurate will-pass-a-variant probability -// We will still be able to determine will-pass-a-variant probabilities for users who only have 1 variant on 1 base, +// We will still be able to determine will-pass-a-variant probabilities for users who only have 1 mutation on 1 base in the entire gene, // regardless of if their loci phase is known or not. That probability is 50%. - -// TODO: We want to eventually use neural nets for both trait and polygenic disease analysis +// TODO: We want to eventually use neural nets for both trait and polygenic disease analysis (see geneticPrediction.go) // These will be trained on a set of genomes and will output a probability analysis for each trait/disease // This is only possible once we get access to the necessary training data // @@ -41,19 +43,6 @@ package createGeneticAnalysis // More offspring will take longer, but will yield a more accurate trait probability. // Seekia will show the the average trait result and a chart showing the trait results for all created offspring. - -// TODO: We should not add any passing-a-variant probabilities that are dependent on an unknown phase to user profiles -// For example, if the phase for any mutated bases are unknown, and this unknown phase effects the probability of -// the user passing a variant, that probability should not be shared on the user's profile. -// Seekia currently shares these probabilities on user profiles. -// Only probabilities which are fully known should be shared. Otherwise, the user is sharing -// information that is not fully accurate but is instead speculative. -// -// We must add this warning into the GUI for a user and couple genetic analyses too. -// Basically, we need to make it clear that some probabilities are based completely on random genetic chance, and others -// are based on our limited understanding of the user's genome (due to unphased data). - - // TODO: Add the ability to weight different genome files based on their reliability. // Some files are much more accurate because they record each location many times. @@ -63,15 +52,17 @@ import "seekia/resources/geneticReferences/monogenicDiseases" import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/resources/geneticReferences/traits" +import "seekia/internal/encoding" +import "seekia/internal/genetics/geneticAnalysis" import "seekia/internal/genetics/locusValue" import "seekia/internal/genetics/prepareRawGenomes" import "seekia/internal/helpers" import "errors" import "math" -import "encoding/json" import "strings" import "slices" +import "maps" func verifyBasePair(inputBasePair string)bool{ @@ -99,11 +90,9 @@ func verifyBasePair(inputBasePair string)bool{ return true } -//Inputs: -// -map[string]string: Genome Identifier -> Genome Raw data string //Outputs: // -bool: Process completed (it was not stopped manually before completion) -// -string: New Genetic analysis string +// -string: New Genetic analysis string (Encoded in MessagePack) // -error func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMetadata, updatePercentageCompleteFunction func(int)error, checkIfProcessIsStopped func()bool)(bool, string, error){ @@ -121,28 +110,16 @@ func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMe genomesWithMetadataList, allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(genomesList, prepareRawGenomesUpdatePercentageCompleteFunction) if (err != nil) { return false, "", err } - combinedGenomesExistString := helpers.ConvertBoolToYesOrNoString(multipleGenomesExist) - - metadataItem := map[string]string{ - "ItemType": "Metadata", - "AnalysisVersion": "1", - "AnalysisType": "Person", - "CombinedGenomesExist": combinedGenomesExistString, + newGeneticAnalysisObject := geneticAnalysis.PersonAnalysis{ + AnalysisVersion: 1, + CombinedGenomesExist: multipleGenomesExist, + AllRawGenomeIdentifiersList: allRawGenomeIdentifiersList, } if (multipleGenomesExist == true){ - metadataItem["OnlyExcludeConflictsGenomeIdentifier"] = onlyExcludeConflictsGenomeIdentifier - metadataItem["OnlyIncludeSharedGenomeIdentifier"] = onlyIncludeSharedGenomeIdentifier - - allRawGenomeIdentifiersListString := strings.Join(allRawGenomeIdentifiersList, "+") - metadataItem["AllRawGenomeIdentifiersList"] = allRawGenomeIdentifiersListString - - } else { - - genomeIdentifier := allRawGenomeIdentifiersList[0] - - metadataItem["GenomeIdentifier"] = genomeIdentifier + newGeneticAnalysisObject.OnlyExcludeConflictsGenomeIdentifier = onlyExcludeConflictsGenomeIdentifier + newGeneticAnalysisObject.OnlyIncludeSharedGenomeIdentifier = onlyIncludeSharedGenomeIdentifier } processIsStopped := checkIfProcessIsStopped() @@ -150,60 +127,61 @@ func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMe return false, "", nil } - geneticAnalysisMapList := []map[string]string{metadataItem} - monogenicDiseasesList, err := monogenicDiseases.GetMonogenicDiseaseObjectsList() if (err != nil) { return false, "", err } + // Map Structure: Disease Name -> PersonMonogenicDiseaseInfo + analysisMonogenicDiseasesMap := make(map[string]geneticAnalysis.PersonMonogenicDiseaseInfo) + for _, monogenicDiseaseObject := range monogenicDiseasesList{ diseaseName := monogenicDiseaseObject.DiseaseName - diseaseAnalysisMap, variantsMapList, err := getMonogenicDiseaseAnalysis(genomesWithMetadataList, monogenicDiseaseObject) + personDiseaseAnalysisObject, err := getPersonMonogenicDiseaseAnalysis(genomesWithMetadataList, monogenicDiseaseObject) if (err != nil) { return false, "", err } - diseaseAnalysisMap["ItemType"] = "MonogenicDisease" - diseaseAnalysisMap["DiseaseName"] = diseaseName - - geneticAnalysisMapList = append(geneticAnalysisMapList, diseaseAnalysisMap) - geneticAnalysisMapList = append(geneticAnalysisMapList, variantsMapList...) + analysisMonogenicDiseasesMap[diseaseName] = personDiseaseAnalysisObject } + newGeneticAnalysisObject.MonogenicDiseasesMap = analysisMonogenicDiseasesMap + polygenicDiseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList() if (err != nil) { return false, "", err } + // Map Structure: Disease Name -> PersonPolygenicDiseaseInfo + analysisPolygenicDiseasesMap := make(map[string]geneticAnalysis.PersonPolygenicDiseaseInfo) + for _, diseaseObject := range polygenicDiseaseObjectsList{ - diseaseAnalysisMap, diseaseLociMapList, err := getPolygenicDiseaseAnalysis(genomesWithMetadataList, diseaseObject) + personDiseaseAnalysisObject, err := getPersonPolygenicDiseaseAnalysis(genomesWithMetadataList, diseaseObject) if (err != nil) { return false, "", err } diseaseName := diseaseObject.DiseaseName - diseaseAnalysisMap["ItemType"] = "PolygenicDisease" - diseaseAnalysisMap["DiseaseName"] = diseaseName - - geneticAnalysisMapList = append(geneticAnalysisMapList, diseaseAnalysisMap) - geneticAnalysisMapList = append(geneticAnalysisMapList, diseaseLociMapList...) + analysisPolygenicDiseasesMap[diseaseName] = personDiseaseAnalysisObject } + newGeneticAnalysisObject.PolygenicDiseasesMap = analysisPolygenicDiseasesMap + traitObjectsList, err := traits.GetTraitObjectsList() if (err != nil) { return false, "", err } + // Map Structure: Trait Name -> PersonTraitInfo + analysisTraitsMap := make(map[string]geneticAnalysis.PersonTraitInfo) + for _, traitObject := range traitObjectsList{ - traitAnalysisMap, traitRulesMapList, err := getTraitAnalysis(genomesWithMetadataList, traitObject) + personTraitAnalysisObject, err := getPersonTraitAnalysis(genomesWithMetadataList, traitObject) if (err != nil) { return false, "", err } traitName := traitObject.TraitName - traitAnalysisMap["ItemType"] = "Trait" - traitAnalysisMap["TraitName"] = traitName - - geneticAnalysisMapList = append(geneticAnalysisMapList, traitAnalysisMap) - geneticAnalysisMapList = append(geneticAnalysisMapList, traitRulesMapList...) + analysisTraitsMap[traitName] = personTraitAnalysisObject } - analysisBytes, err := json.MarshalIndent(geneticAnalysisMapList, "", "\t") + newGeneticAnalysisObject.TraitsMap = analysisTraitsMap + + analysisBytes, err := encoding.EncodeMessagePackBytes(newGeneticAnalysisObject) if (err != nil) { return false, "", err } analysisString := string(analysisBytes) @@ -213,11 +191,11 @@ func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMe //Outputs: // -bool: Process completed (was not manually stopped mid-way) -// -string: Couple genetic analysis string +// -string: Couple genetic analysis string (encoded in MessagePack) // -error -func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenomeWithMetadata, personBGenomesList []prepareRawGenomes.RawGenomeWithMetadata, updatePercentageCompleteFunction func(int)error, checkIfProcessIsStopped func()bool)(bool, string, error){ +func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenomeWithMetadata, person2GenomesList []prepareRawGenomes.RawGenomeWithMetadata, updatePercentageCompleteFunction func(int)error, checkIfProcessIsStopped func()bool)(bool, string, error){ - personAPrepareRawGenomesUpdatePercentageCompleteFunction := func(newPercentage int)error{ + person1PrepareRawGenomesUpdatePercentageCompleteFunction := func(newPercentage int)error{ newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, newPercentage, 0, 100, 0, 25) if (err != nil){ return err } @@ -228,7 +206,7 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom return nil } - personAGenomesWithMetadataList, allPersonARawGenomeIdentifiersList, personAHasMultipleGenomes, personAOnlyExcludeConflictsGenomeIdentifier, personAOnlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(personAGenomesList, personAPrepareRawGenomesUpdatePercentageCompleteFunction) + person1GenomesWithMetadataList, allPerson1RawGenomeIdentifiersList, person1HasMultipleGenomes, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(person1GenomesList, person1PrepareRawGenomesUpdatePercentageCompleteFunction) if (err != nil) { return false, "", err } processIsStopped := checkIfProcessIsStopped() @@ -236,7 +214,7 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom return false, "", nil } - personBPrepareRawGenomesUpdatePercentageCompleteFunction := func(newPercentage int)error{ + person2PrepareRawGenomesUpdatePercentageCompleteFunction := func(newPercentage int)error{ newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, newPercentage, 0, 100, 25, 50) if (err != nil){ return err } @@ -247,7 +225,7 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom return nil } - personBGenomesWithMetadataList, allPersonBRawGenomeIdentifiersList, personBHasMultipleGenomes, personBOnlyExcludeConflictsGenomeIdentifier, personBOnlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(personBGenomesList, personBPrepareRawGenomesUpdatePercentageCompleteFunction) + person2GenomesWithMetadataList, allPerson2RawGenomeIdentifiersList, person2HasMultipleGenomes, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(person2GenomesList, person2PrepareRawGenomesUpdatePercentageCompleteFunction) if (err != nil) { return false, "", err } processIsStopped = checkIfProcessIsStopped() @@ -258,247 +236,141 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom // The analysis will analyze either 1 or 2 genome pairs // The gui will display the results from each pair //Outputs: - // -string: Pair 1 Person A Genome Identifier - // -string: Pair 1 Person B Genome Identifier + // -[16]byte: Pair 1 Person 1 Genome Identifier + // -[16]byte: Pair 1 Person 2 Genome Identifier // -bool: Second pair exists - // -string: Pair 2 Person A Genome Identifier - // -string: Pair 2 Person B Genome Identifier + // -[16]byte: Pair 2 Person 1 Genome Identifier + // -[16]byte: Pair 2 Person 2 Genome Identifier // -error - getGenomePairsToAnalyze := func()(string, string, bool, string, string, error){ + getGenomePairsToAnalyze := func()([16]byte, [16]byte, bool, [16]byte, [16]byte, error){ - if (personAHasMultipleGenomes == true && personBHasMultipleGenomes == true){ + if (person1HasMultipleGenomes == true && person2HasMultipleGenomes == true){ - return personAOnlyExcludeConflictsGenomeIdentifier, personBOnlyExcludeConflictsGenomeIdentifier, true, personAOnlyIncludeSharedGenomeIdentifier, personBOnlyIncludeSharedGenomeIdentifier, nil + return person1OnlyExcludeConflictsGenomeIdentifier, person2OnlyExcludeConflictsGenomeIdentifier, true, person1OnlyIncludeSharedGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, nil } - if (personAHasMultipleGenomes == true && personBHasMultipleGenomes == false){ + if (person1HasMultipleGenomes == true && person2HasMultipleGenomes == false){ - personBGenomeIdentifier := allPersonBRawGenomeIdentifiersList[0] + person2GenomeIdentifier := allPerson2RawGenomeIdentifiersList[0] - return personAOnlyExcludeConflictsGenomeIdentifier, personBGenomeIdentifier, true, personAOnlyIncludeSharedGenomeIdentifier, personBGenomeIdentifier, nil + return person1OnlyExcludeConflictsGenomeIdentifier, person2GenomeIdentifier, true, person1OnlyIncludeSharedGenomeIdentifier, person2GenomeIdentifier, nil } - if (personAHasMultipleGenomes == false && personBHasMultipleGenomes == true){ + if (person1HasMultipleGenomes == false && person2HasMultipleGenomes == true){ - personAGenomeIdentifier := allPersonARawGenomeIdentifiersList[0] + person1GenomeIdentifier := allPerson1RawGenomeIdentifiersList[0] - return personAGenomeIdentifier, personBOnlyExcludeConflictsGenomeIdentifier, true, personAGenomeIdentifier, personBOnlyIncludeSharedGenomeIdentifier, nil + return person1GenomeIdentifier, person2OnlyExcludeConflictsGenomeIdentifier, true, person1GenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, nil } - //personAHasMultipleGenomes == false && personBHasMultipleGenomes == false + //person1HasMultipleGenomes == false && person2HasMultipleGenomes == false - personAGenomeIdentifier := allPersonARawGenomeIdentifiersList[0] - personBGenomeIdentifier := allPersonBRawGenomeIdentifiersList[0] + person1GenomeIdentifier := allPerson1RawGenomeIdentifiersList[0] + person2GenomeIdentifier := allPerson2RawGenomeIdentifiersList[0] - return personAGenomeIdentifier, personBGenomeIdentifier, false, "", "", nil + return person1GenomeIdentifier, person2GenomeIdentifier, false, [16]byte{}, [16]byte{}, nil } - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, genomePair2Exists, pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier, err := getGenomePairsToAnalyze() + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, genomePair2Exists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, err := getGenomePairsToAnalyze() if (err != nil){ return false, "", err } - personAHasMultipleGenomesString := helpers.ConvertBoolToYesOrNoString(personAHasMultipleGenomes) - personBHasMultipleGenomesString := helpers.ConvertBoolToYesOrNoString(personBHasMultipleGenomes) - - secondPairExistsString := helpers.ConvertBoolToYesOrNoString(genomePair2Exists) - - metadataItem := map[string]string{ - "ItemType": "Metadata", - "AnalysisVersion": "1", - "AnalysisType": "Couple", - "PersonAHasMultipleGenomes": personAHasMultipleGenomesString, - "PersonBHasMultipleGenomes": personBHasMultipleGenomesString, - "Pair1PersonAGenomeIdentifier": pair1PersonAGenomeIdentifier, - "Pair1PersonBGenomeIdentifier": pair1PersonBGenomeIdentifier, - "SecondPairExists": secondPairExistsString, + newCoupleAnalysis := geneticAnalysis.CoupleAnalysis{ + AnalysisVersion: 1, + Pair1Person1GenomeIdentifier: pair1Person1GenomeIdentifier, + Pair1Person2GenomeIdentifier: pair1Person2GenomeIdentifier, + SecondPairExists: genomePair2Exists, + Person1HasMultipleGenomes: person1HasMultipleGenomes, + Person2HasMultipleGenomes: person2HasMultipleGenomes, } if (genomePair2Exists == true){ // At least 1 of the people have multiple genomes - metadataItem["Pair2PersonAGenomeIdentifier"] = pair2PersonAGenomeIdentifier - metadataItem["Pair2PersonBGenomeIdentifier"] = pair2PersonBGenomeIdentifier + newCoupleAnalysis.Pair2Person1GenomeIdentifier = pair2Person1GenomeIdentifier + newCoupleAnalysis.Pair2Person2GenomeIdentifier = pair2Person2GenomeIdentifier - if (personAHasMultipleGenomes == true){ - metadataItem["PersonAOnlyExcludeConflictsGenomeIdentifier"] = personAOnlyExcludeConflictsGenomeIdentifier - metadataItem["PersonAOnlyIncludeSharedGenomeIdentifier"] = personAOnlyIncludeSharedGenomeIdentifier + if (person1HasMultipleGenomes == true){ + newCoupleAnalysis.Person1OnlyExcludeConflictsGenomeIdentifier = person1OnlyExcludeConflictsGenomeIdentifier + newCoupleAnalysis.Person1OnlyIncludeSharedGenomeIdentifier = person1OnlyIncludeSharedGenomeIdentifier } - if (personBHasMultipleGenomes == true){ - metadataItem["PersonBOnlyExcludeConflictsGenomeIdentifier"] = personBOnlyExcludeConflictsGenomeIdentifier - metadataItem["PersonBOnlyIncludeSharedGenomeIdentifier"] = personBOnlyIncludeSharedGenomeIdentifier + if (person2HasMultipleGenomes == true){ + newCoupleAnalysis.Person2OnlyExcludeConflictsGenomeIdentifier = person2OnlyExcludeConflictsGenomeIdentifier + newCoupleAnalysis.Person2OnlyIncludeSharedGenomeIdentifier = person2OnlyIncludeSharedGenomeIdentifier } } - newAnalysisMapList := []map[string]string{metadataItem} - - //First we add monogenic disease probabilities + // We compute and add monogenic disease probabilities monogenicDiseasesList, err := monogenicDiseases.GetMonogenicDiseaseObjectsList() if (err != nil) { return false, "", err } + // Map Structure: Disease Name -> OffspringMonogenicDiseaseInfo + offspringMonogenicDiseasesMap := make(map[string]geneticAnalysis.OffspringMonogenicDiseaseInfo) + for _, diseaseObject := range monogenicDiseasesList{ diseaseName := diseaseObject.DiseaseName variantsList := diseaseObject.VariantsList diseaseIsDominantOrRecessive := diseaseObject.DominantOrRecessive - offspringDiseaseMap := map[string]string{ - "ItemType": "MonogenicDisease", - "DiseaseName": diseaseName, - } - - personADiseaseAnalysisMap, personAVariantsMapList, err := getMonogenicDiseaseAnalysis(personAGenomesWithMetadataList, diseaseObject) + person1DiseaseAnalysisObject, err := getPersonMonogenicDiseaseAnalysis(person1GenomesWithMetadataList, diseaseObject) if (err != nil) { return false, "", err } - personBDiseaseAnalysisMap, personBVariantsMapList, err := getMonogenicDiseaseAnalysis(personBGenomesWithMetadataList, diseaseObject) + person2DiseaseAnalysisObject, err := getPersonMonogenicDiseaseAnalysis(person2GenomesWithMetadataList, diseaseObject) if (err != nil) { return false, "", err } + // This map stores the number of variants tested in each person's genome + // Map Structure: Genome Identifier -> Number of variants tested + numberOfVariantsTestedMap := make(map[[16]byte]int) + + // This map stores the offspring disease probabilities for each genome pair. + // A genome pair is a concatenation of two genome identifiers + // If a map entry doesn't exist, the probabilities are unknown for that genome pair + // Map Structure: Genome Pair Identifier -> OffspringMonogenicDiseaseProbabilities + offspringMonogenicDiseaseInfoMap := make(map[[32]byte]geneticAnalysis.OffspringGenomePairMonogenicDiseaseInfo) + // This will calculate the probability of monogenic disease for the offspring from the two specified genomes - // It then adds the pair entry to the offspringDiseaseMap - addPairProbabilitiesToDiseaseMap := func(personAGenomeIdentifier string, personBGenomeIdentifier string)error{ + // It also calculates the probabilities for each monogenic disease variant for the offspring + // It then adds the genome pair disease information to the offspringMonogenicDiseaseInfoMap and numberOfVariantsTestedMap + addGenomePairInfoToDiseaseMaps := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{ //Outputs: // -bool: Probability is known // -int: Probability of passing a disease variant (value between 0 and 100) // -int: Number of variants tested - // -error - getPersonWillPassDiseaseVariantProbability := func(personDiseaseAnalysisMap map[string]string, genomeIdentifier string)(bool, int, int, error){ + getPersonWillPassDiseaseVariantProbability := func(personDiseaseAnalysisObject geneticAnalysis.PersonMonogenicDiseaseInfo, genomeIdentifier [16]byte)(bool, int, int){ - probabilityMapKey := genomeIdentifier + "_ProbabilityOfPassingADiseaseVariant" + personDiseaseInfoMap := personDiseaseAnalysisObject.MonogenicDiseaseInfoMap - genomeDiseasePercentageProbability, exists := personDiseaseAnalysisMap[probabilityMapKey] - if (exists == false) { - return false, 0, 0, errors.New("Malformed person analysis map list: Missing probability of passing a variant value") - } - if (genomeDiseasePercentageProbability == "Unknown"){ - return false, 0, 0, nil - } - - genomeDiseaseProbabilityInt, err := helpers.ConvertStringToInt(genomeDiseasePercentageProbability) - if (err != nil) { - return false, 0, 0, errors.New("Malformed person analysis map list: Invalid probability of passing a variant value: " + genomeDiseasePercentageProbability) - } - - numberOfTestedVariantsString, exists := personDiseaseAnalysisMap[genomeIdentifier + "_NumberOfVariantsTested"] + personGenomeDiseaseInfoObject, exists := personDiseaseInfoMap[genomeIdentifier] if (exists == false){ - return false, 0, 0, errors.New("Malformed person analysis map list: Probabilities map missing _NumberOfVariantsTested") + return false, 0, 0 } - numberOfTestedVariants, err := helpers.ConvertStringToInt(numberOfTestedVariantsString) - if (err != nil){ - return false, 0, 0, errors.New("Malformed person analysis map list: Contains invalid _NumberOfVariantsTested") - } + personGenomeProbabilityOfPassingADiseaseVariant := personGenomeDiseaseInfoObject.ProbabilityOfPassingADiseaseVariant - return true, genomeDiseaseProbabilityInt, numberOfTestedVariants, nil + personGenomeNumberOfVariantsTested := personGenomeDiseaseInfoObject.NumberOfVariantsTested + + return true, personGenomeProbabilityOfPassingADiseaseVariant, personGenomeNumberOfVariantsTested } - personAProbabilityIsKnown, personAWillPassVariantProbability, personANumberOfVariantsTested, err := getPersonWillPassDiseaseVariantProbability(personADiseaseAnalysisMap, personAGenomeIdentifier) + person1ProbabilityIsKnown, person1WillPassVariantProbability, person1NumberOfVariantsTested := getPersonWillPassDiseaseVariantProbability(person1DiseaseAnalysisObject, person1GenomeIdentifier) + + person2ProbabilityIsKnown, person2WillPassVariantProbability, person2NumberOfVariantsTested := getPersonWillPassDiseaseVariantProbability(person2DiseaseAnalysisObject, person2GenomeIdentifier) + + offspringHasDiseaseProbabilityIsKnown, percentageProbabilityOffspringHasDisease, offspringHasAVariantProbabilityIsKnown, percentageProbabilityOffspringHasVariant, err := GetOffspringMonogenicDiseaseProbabilities(diseaseIsDominantOrRecessive, person1ProbabilityIsKnown, person1WillPassVariantProbability, person2ProbabilityIsKnown, person2WillPassVariantProbability) if (err != nil) { return err } - personBProbabilityIsKnown, personBWillPassVariantProbability, personBNumberOfVariantsTested, err := getPersonWillPassDiseaseVariantProbability(personBDiseaseAnalysisMap, personBGenomeIdentifier) - if (err != nil) { return err } + // Now we calculate the probabilities for individual variants - personANumberOfVariantsTestedString := helpers.ConvertIntToString(personANumberOfVariantsTested) - personBNumberOfVariantsTestedString := helpers.ConvertIntToString(personBNumberOfVariantsTested) + offspringVariantsInfoMap := make(map[[3]byte]geneticAnalysis.OffspringMonogenicDiseaseVariantInfo) - offspringDiseaseMap[personAGenomeIdentifier + "_NumberOfVariantsTested"] = personANumberOfVariantsTestedString - offspringDiseaseMap[personBGenomeIdentifier + "_NumberOfVariantsTested"] = personBNumberOfVariantsTestedString + for _, variantObject := range variantsList{ - genomePairIdentifier := personAGenomeIdentifier + "+" + personBGenomeIdentifier + variantIdentifierHex := variantObject.VariantIdentifier - if (personAProbabilityIsKnown == false || personBProbabilityIsKnown == false){ - - offspringDiseaseMap[genomePairIdentifier + "_ProbabilityOffspringHasDisease"] = "Unknown" - offspringDiseaseMap[genomePairIdentifier + "_ProbabilityOffspringHasVariant"] = "Unknown" - - return nil - } - - percentageProbabilityOffspringHasDisease, percentageProbabilityOffspringHasVariant, err := GetOffspringMonogenicDiseaseProbabilities(diseaseIsDominantOrRecessive, personAWillPassVariantProbability, personBWillPassVariantProbability) - if (err != nil) { return err } - - probabilityOffspringHasDiseaseString := helpers.ConvertIntToString(percentageProbabilityOffspringHasDisease) - probabilityOffspringHasVariantString := helpers.ConvertIntToString(percentageProbabilityOffspringHasVariant) - - offspringDiseaseMap[genomePairIdentifier + "_ProbabilityOffspringHasDisease"] = probabilityOffspringHasDiseaseString - offspringDiseaseMap[genomePairIdentifier + "_ProbabilityOffspringHasVariant"] = probabilityOffspringHasVariantString - - return nil - } - - err = addPairProbabilitiesToDiseaseMap(pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier) - if (err != nil) { return false, "", err } - - if (genomePair2Exists == true){ - - err := addPairProbabilitiesToDiseaseMap(pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier) - if (err != nil) { return false, "", err } - } - - // Now we calculate the probabilities for individual variants - // We add a map for each variant - - offspringVariantsMapList := make([]map[string]string, 0, len(variantsList)) - - for _, variantObject := range variantsList{ - - variantIdentifier := variantObject.VariantIdentifier - - offspringVariantMap := map[string]string{ - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": variantIdentifier, - "DiseaseName": diseaseName, - } - - addPairProbabilitiesToVariantMap := func(personAGenomeIdentifier string, personBGenomeIdentifier string)error{ - - genomePairIdentifier := personAGenomeIdentifier + "+" + personBGenomeIdentifier - - //Outputs: - // -bool: Probability is known - // -float64: Probability that person will pass variant to offspring (between 0 and 1) - // -error - getPersonWillPassVariantProbability := func(personVariantsMapList []map[string]string, personGenomeIdentifier string)(bool, float64, error){ - - for _, personVariantMap := range personVariantsMapList{ - - itemType, exists := personVariantMap["ItemType"] - if (exists == false){ - return false, 0, errors.New("PersonVariantsMapList missing ItemType") - } - if (itemType != "MonogenicDiseaseVariant"){ - return false, 0, errors.New("Person variant map is malformed: Contains non-MonogenicDiseaseVariant itemType: " + itemType) - } - - currentVariantIdentifier, exists := personVariantMap["VariantIdentifier"] - if (exists == false){ - return false, 0, errors.New("Person variant map is malformed: Missing VariantIdentifier") - } - if (currentVariantIdentifier != variantIdentifier){ - continue - } - - personHasVariantInfo, exists := personVariantMap[personGenomeIdentifier + "_HasVariant"] - if (exists == false){ - return false, 0, errors.New("Person variant map is malformed: missing person genome _HasVariant value.") - } - - if (personHasVariantInfo == "Unknown"){ - return false, 0, nil - } - if (personHasVariantInfo == "Yes;Yes"){ - return true, 1, nil - } - if (personHasVariantInfo == "Yes;No" || personHasVariantInfo == "No;Yes"){ - return true, 0.5, nil - } - if (personHasVariantInfo == "No;No"){ - return true, 0, nil - } - return false, 0, errors.New("Person variant map is malformed: Contains invalid variant info: " + personHasVariantInfo) - } - - return false, 0, errors.New("Cannot find disease variant in personVariantsMapList: " + variantIdentifier) - } + variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex) + if (err != nil) { return err } // Outputs: // -bool: Probabilities are known @@ -511,23 +383,75 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom // -error getOffspringVariantProbabilities := func()(bool, int, int, int, int, int, int, error){ + //Outputs: + // -bool: Probability is known + // -float64: Probability that person will pass variant to offspring (between 0 and 1) + // -error + getPersonWillPassVariantProbability := func(personDiseaseAnalysisObject geneticAnalysis.PersonMonogenicDiseaseInfo, personGenomeIdentifier [16]byte)(bool, float64, error){ + + personDiseaseInfoMap := personDiseaseAnalysisObject.MonogenicDiseaseInfoMap + + personGenomeDiseaseInfoObject, exists := personDiseaseInfoMap[personGenomeIdentifier] + if (exists == false){ + return false, 0, nil + } + + personGenomeDiseaseVariantsMap := personGenomeDiseaseInfoObject.VariantsInfoMap + + personVariantInfoObject, exists := personGenomeDiseaseVariantsMap[variantIdentifier] + if (exists == false){ + // The genome does not have information about this variant + + return false, 0, nil + } + + + base1HasVariant := personVariantInfoObject.Base1HasVariant + base2HasVariant := personVariantInfoObject.Base2HasVariant + + if (base1HasVariant == true && base2HasVariant == true){ + return true, 1, nil + } + if (base1HasVariant == true && base2HasVariant == false){ + return true, 0.5, nil + } + if (base1HasVariant == false && base2HasVariant == true){ + return true, 0.5, nil + } + + // Neither base has a variant + + return true, 0, nil + } + + person1VariantProbabilityIsKnown, person1WillPassVariantProbability, err := getPersonWillPassVariantProbability(person1DiseaseAnalysisObject, person1GenomeIdentifier) + if (err != nil) { return false, 0, 0, 0, 0, 0, 0, err } + + person2VariantProbabilityIsKnown, person2WillPassVariantProbability, err := getPersonWillPassVariantProbability(person2DiseaseAnalysisObject, person2GenomeIdentifier) + if (err != nil) { return false, 0, 0, 0, 0, 0, 0, err } + + if (person1VariantProbabilityIsKnown == false && person2VariantProbabilityIsKnown == false){ + return false, 0, 0, 0, 0, 0, 0, nil + } + //Outputs: // -int: Percentage Probability of 0 mutations // -int: Percentage Probability of 1 mutation // -int: Percentage Probability of 2 mutations // -error - getOffspringProbabilities := func(personAWillPassVariantProbability float64, personBWillPassVariantProbability float64)(int, int, int, error){ - // This is the probability that neither will pass variant + getOffspringVariantProbabilities := func(person1WillPassVariantProbability float64, person2WillPassVariantProbability float64)(int, int, int, error){ + + // This is the probability that neither person will pass the variant // P = P(!A) * P(!B) - probabilityOf0Mutations := (1 - personAWillPassVariantProbability) * (1 - personBWillPassVariantProbability) + probabilityOf0Mutations := (1 - person1WillPassVariantProbability) * (1 - person2WillPassVariantProbability) - // This is the probability that either will pass variant, but not both + // This is the probability that either person will pass the variant, but not both // P(A XOR B) = P(A) + P(B) - (2 * P(A and B)) - probabilityOf1Mutation := personAWillPassVariantProbability + personBWillPassVariantProbability - (2 * personAWillPassVariantProbability * personBWillPassVariantProbability) + probabilityOf1Mutation := person1WillPassVariantProbability + person2WillPassVariantProbability - (2 * person1WillPassVariantProbability * person2WillPassVariantProbability) - // This is the probability that both will pass variant + // This is the probability that both people will pass the variant // P(A and B) = P(A) * P(B) - probabilityOf2Mutations := personAWillPassVariantProbability * personBWillPassVariantProbability + probabilityOf2Mutations := person1WillPassVariantProbability * person2WillPassVariantProbability percentageProbabilityOf0Mutations, err := helpers.FloorFloat64ToInt(probabilityOf0Mutations * 100) if (err != nil) { return 0, 0, 0, err } @@ -539,97 +463,103 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom return percentageProbabilityOf0Mutations, percentageProbabilityOf1Mutation, percentageProbabilityOf2Mutations, nil } - personAProbabilityIsKnown, personAWillPassVariantProbability, err := getPersonWillPassVariantProbability(personAVariantsMapList, personAGenomeIdentifier) + // Outputs: + // -float64: Best Case Person 1 will pass variant probability (0-1) + // -float64: Worst Case Person 1 will pass variant probability (0-1) + // -float64: Best Case Person 2 will pass variant probability (0-1) + // -float64: Worst Case Person 2 will pass variant probability (0-1) + getBestAndWorstCaseProbabilities := func()(float64, float64, float64, float64){ + + if (person1VariantProbabilityIsKnown == true && person2VariantProbabilityIsKnown == false){ + + return person1WillPassVariantProbability, person1WillPassVariantProbability, float64(0), float64(1) + } + if (person1VariantProbabilityIsKnown == false && person2VariantProbabilityIsKnown == true){ + return float64(0), float64(1), person2WillPassVariantProbability, person2WillPassVariantProbability + } + + // Both people's probabilities are known + return person1WillPassVariantProbability, person1WillPassVariantProbability, person2WillPassVariantProbability, person2WillPassVariantProbability + } + + bestCasePerson1WillPassVariantProbability, worstCasePerson1WillPassVariantProbability, bestCasePerson2WillPassVariantProbability, worstCasePerson2WillPassVariantProbability := getBestAndWorstCaseProbabilities() + + bestCase0MutationsProbability, bestCase1MutationProbability, bestCase2MutationsProbability, err := getOffspringVariantProbabilities(bestCasePerson1WillPassVariantProbability, bestCasePerson2WillPassVariantProbability) if (err != nil) { return false, 0, 0, 0, 0, 0, 0, err } - personBProbabilityIsKnown, personBWillPassVariantProbability, err := getPersonWillPassVariantProbability(personBVariantsMapList, personBGenomeIdentifier) - if (err != nil) { return false, 0, 0, 0, 0, 0, 0, err } - - if (personAProbabilityIsKnown == false && personBProbabilityIsKnown == false){ - return false, 0, 0, 0, 0, 0, 0, nil - } - - if (personAProbabilityIsKnown == true && personBProbabilityIsKnown == false){ - - bestCasePersonBWillPassVariantProbability := float64(0) - worstCasePersonBWillPassVariantProbability := float64(1) - - bestCase0MutationsProbability, bestCase1MutationProbability, bestCase2MutationsProbability, err := getOffspringProbabilities(personAWillPassVariantProbability, bestCasePersonBWillPassVariantProbability) - if (err != nil) { return false, 0, 0, 0, 0, 0, 0, err } - - worstCase0MutationsProbability, worstCase1MutationProbability, worstCase2MutationsProbability, err := getOffspringProbabilities(personAWillPassVariantProbability, worstCasePersonBWillPassVariantProbability) - if (err != nil) { return false, 0, 0, 0, 0, 0, 0, err } - - lowerBound1MutationProbability := min(bestCase1MutationProbability, worstCase1MutationProbability) - upperBound1MutationProbability := max(bestCase1MutationProbability, worstCase1MutationProbability) - - return true, worstCase0MutationsProbability, bestCase0MutationsProbability, lowerBound1MutationProbability, upperBound1MutationProbability, bestCase2MutationsProbability, worstCase2MutationsProbability, nil - } - - if (personAProbabilityIsKnown == false && personBProbabilityIsKnown == true){ - - bestCasePersonAWillPassVariantProbability := float64(0) - worstCasePersonAWillPassVariantProbability := float64(1) - - bestCase0MutationsProbability, bestCase1MutationProbability, bestCase2MutationsProbability, err := getOffspringProbabilities(bestCasePersonAWillPassVariantProbability, personBWillPassVariantProbability) - if (err != nil) { return false, 0, 0, 0, 0, 0, 0, err } - - worstCase0MutationsProbability, worstCase1MutationProbability, worstCase2MutationsProbability, err := getOffspringProbabilities(worstCasePersonAWillPassVariantProbability, personBWillPassVariantProbability) - if (err != nil) { return false, 0, 0, 0, 0, 0, 0, err } - - lowerBound1MutationProbability := min(bestCase1MutationProbability, worstCase1MutationProbability) - upperBound1MutationProbability := max(bestCase1MutationProbability, worstCase1MutationProbability) - - return true, worstCase0MutationsProbability, bestCase0MutationsProbability, lowerBound1MutationProbability, upperBound1MutationProbability, bestCase2MutationsProbability, worstCase2MutationsProbability, nil - } - - offspring0MutationsProbability, offspring1MutationProbability, offspring2MutationsProbability, err := getOffspringProbabilities(personAWillPassVariantProbability, personBWillPassVariantProbability) + worstCase0MutationsProbability, worstCase1MutationProbability, worstCase2MutationsProbability, err := getOffspringVariantProbabilities(worstCasePerson1WillPassVariantProbability, worstCasePerson2WillPassVariantProbability) if (err != nil) { return false, 0, 0, 0, 0, 0, 0, err } - return true, offspring0MutationsProbability, offspring0MutationsProbability, offspring1MutationProbability, offspring1MutationProbability, offspring2MutationsProbability, offspring2MutationsProbability, nil + // We have to figure out which 1-mutation-probability is lower + // The best case probabilities can actually result in a higher probability for 1 mutation + // than the worst case probabilities + // This is because in a worst-case-scenario, the probability of 1 mutation might be 0 because + // the probability of 2 mutations is 100 + // This is not needed for the 0 and 2 mutation probabilities because TODO + + lowerBound1MutationProbability := min(bestCase1MutationProbability, worstCase1MutationProbability) + upperBound1MutationProbability := max(bestCase1MutationProbability, worstCase1MutationProbability) + + return true, worstCase0MutationsProbability, bestCase0MutationsProbability, lowerBound1MutationProbability, upperBound1MutationProbability, bestCase2MutationsProbability, worstCase2MutationsProbability, nil } probabilitiesKnown, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, err := getOffspringVariantProbabilities() if (err != nil) { return err } - if (probabilitiesKnown == false){ - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf0MutationsLowerBound"] = "Unknown" - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf0MutationsUpperBound"] = "Unknown" - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf1MutationLowerBound"] = "Unknown" - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf1MutationUpperBound"] = "Unknown" - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf2MutationsLowerBound"] = "Unknown" - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf2MutationsUpperBound"] = "Unknown" - - return nil + continue } - probabilityOf0MutationsLowerBoundString := helpers.ConvertIntToString(probabilityOf0MutationsLowerBound) - probabilityOf0MutationsUpperBoundString := helpers.ConvertIntToString(probabilityOf0MutationsUpperBound) - probabilityOf1MutationLowerBoundString := helpers.ConvertIntToString(probabilityOf1MutationLowerBound) - probabilityOf1MutationUpperBoundString := helpers.ConvertIntToString(probabilityOf1MutationUpperBound) - probabilityOf2MutationsLowerBoundString := helpers.ConvertIntToString(probabilityOf2MutationsLowerBound) - probabilityOf2MutationsUpperBoundString := helpers.ConvertIntToString(probabilityOf2MutationsUpperBound) - - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf0MutationsLowerBound"] = probabilityOf0MutationsLowerBoundString - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf0MutationsUpperBound"] = probabilityOf0MutationsUpperBoundString - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf1MutationLowerBound"] = probabilityOf1MutationLowerBoundString - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf1MutationUpperBound"] = probabilityOf1MutationUpperBoundString - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf2MutationsLowerBound"] = probabilityOf2MutationsLowerBoundString - offspringVariantMap[genomePairIdentifier + "_ProbabilityOf2MutationsUpperBound"] = probabilityOf2MutationsUpperBoundString + newOffspringMonogenicDiseaseVariantInfoObject := geneticAnalysis.OffspringMonogenicDiseaseVariantInfo{ + ProbabilityOf0MutationsLowerBound: probabilityOf0MutationsLowerBound, + ProbabilityOf0MutationsUpperBound: probabilityOf0MutationsUpperBound, + + ProbabilityOf1MutationLowerBound: probabilityOf1MutationLowerBound, + ProbabilityOf1MutationUpperBound: probabilityOf1MutationUpperBound, + + ProbabilityOf2MutationsLowerBound: probabilityOf2MutationsLowerBound, + ProbabilityOf2MutationsUpperBound: probabilityOf2MutationsUpperBound, + } + + offspringVariantsInfoMap[variantIdentifier] = newOffspringMonogenicDiseaseVariantInfoObject + } + + if (person1ProbabilityIsKnown == false && person2ProbabilityIsKnown == false && len(offspringVariantsInfoMap) == 0){ + // We have no information about this genome pair's disease risk + // We don't add this genome pair's disease info to the diseaseInfoMap return nil } - err = addPairProbabilitiesToVariantMap(pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier) - if (err != nil) { return false, "", err } + numberOfVariantsTestedMap[person1GenomeIdentifier] = person1NumberOfVariantsTested + numberOfVariantsTestedMap[person2GenomeIdentifier] = person2NumberOfVariantsTested - if (genomePair2Exists == true){ - - err := addPairProbabilitiesToVariantMap(pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier) - if (err != nil) { return false, "", err } + newOffspringGenomePairMonogenicDiseaseInfoObject := geneticAnalysis.OffspringGenomePairMonogenicDiseaseInfo{ + ProbabilityOffspringHasDiseaseIsKnown: offspringHasDiseaseProbabilityIsKnown, + ProbabilityOffspringHasDisease: percentageProbabilityOffspringHasDisease, + ProbabilityOffspringHasVariantIsKnown: offspringHasAVariantProbabilityIsKnown, + ProbabilityOffspringHasVariant: percentageProbabilityOffspringHasVariant, + VariantsInfoMap: offspringVariantsInfoMap, } - offspringVariantsMapList = append(offspringVariantsMapList, offspringVariantMap) + genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier) + + offspringMonogenicDiseaseInfoMap[genomePairIdentifier] = newOffspringGenomePairMonogenicDiseaseInfoObject + + return nil + } + + err = addGenomePairInfoToDiseaseMaps(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) + if (err != nil) { return false, "", err } + + if (genomePair2Exists == true){ + + err := addGenomePairInfoToDiseaseMaps(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) + if (err != nil) { return false, "", err } + } + + newOffspringMonogenicDiseaseInfoObject := geneticAnalysis.OffspringMonogenicDiseaseInfo{ + NumberOfVariantsTestedMap: numberOfVariantsTestedMap, + MonogenicDiseaseInfoMap: offspringMonogenicDiseaseInfoMap, } // Now we check for conflicts in monogenic disease results @@ -638,111 +568,56 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom // This is different from a person having conflicts within their different genomes // Each genome pair only uses 1 genome from each person. - if (genomePair2Exists == true){ + if (len(offspringMonogenicDiseaseInfoMap) >= 2){ - // Conflicts are only possible if two genome pairs exist + // Conflicts are only possible if two genome pairs with information about this disease exist checkIfConflictExists := func()(bool, error){ - - genomePair1Identifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier - genomePair2Identifier := pair2PersonAGenomeIdentifier + "+" + pair2PersonBGenomeIdentifier + probabilityOffspringHasDiseaseIsKnown := false + probabilityOffspringHasDisease := 0 + probabilityOffspringHasVariantIsKnown := false + probabilityOffspringHasVariant := 0 - allGenomePairIdentifiersList := []string{genomePair1Identifier, genomePair2Identifier} + offspringVariantsInfoMap := make(map[[3]byte]geneticAnalysis.OffspringMonogenicDiseaseVariantInfo) - probabilityOffspringHasDisease := "" - probabilityOffspringHasVariant := "" + firstItemReached := false - for index, genomePairIdentifier := range allGenomePairIdentifiersList{ + for _, offspringMonogenicDiseaseInfoObject := range offspringMonogenicDiseaseInfoMap{ - currentProbabilityOffspringHasDisease, exists := offspringDiseaseMap[genomePairIdentifier + "_ProbabilityOffspringHasDisease"] - if (exists == false) { - return false, errors.New("Cannot find _ProbabilityOffspringHasDisease key when searching for conflicts.") - } - currentProbabilityOffspringHasVariant, exists := offspringDiseaseMap[genomePairIdentifier + "_ProbabilityOffspringHasVariant"] - if (exists == false) { - return false, errors.New("Cannot find _ProbabilityOffspringHasVariant key when searching for conflicts.") - } + currentProbabilityOffspringHasDiseaseIsKnown := offspringMonogenicDiseaseInfoObject.ProbabilityOffspringHasDiseaseIsKnown + currentProbabilityOffspringHasDisease := offspringMonogenicDiseaseInfoObject.ProbabilityOffspringHasDisease + currentProbabilityOffspringHasVariantIsKnown := offspringMonogenicDiseaseInfoObject.ProbabilityOffspringHasVariantIsKnown + currentProbabilityOffspringHasVariant := offspringMonogenicDiseaseInfoObject.ProbabilityOffspringHasVariant - if (index == 0){ + currentOffspringVariantsInfoMap := offspringMonogenicDiseaseInfoObject.VariantsInfoMap + + if (firstItemReached == false){ + probabilityOffspringHasDiseaseIsKnown = currentProbabilityOffspringHasDiseaseIsKnown probabilityOffspringHasDisease = currentProbabilityOffspringHasDisease + probabilityOffspringHasVariantIsKnown = currentProbabilityOffspringHasVariantIsKnown probabilityOffspringHasVariant = currentProbabilityOffspringHasVariant + + offspringVariantsInfoMap = currentOffspringVariantsInfoMap + + firstItemReached = true continue } + if (currentProbabilityOffspringHasDiseaseIsKnown != probabilityOffspringHasDiseaseIsKnown){ + return true, nil + } if (currentProbabilityOffspringHasDisease != probabilityOffspringHasDisease){ return true, nil } + if (currentProbabilityOffspringHasVariantIsKnown != probabilityOffspringHasVariantIsKnown){ + return true, nil + } if (currentProbabilityOffspringHasVariant != probabilityOffspringHasVariant){ return true, nil } - } - - for _, variantMap := range offspringVariantsMapList{ - - probabilityOf0MutationsLowerBound := "" - probabilityOf0MutationsUpperBound := "" - probabilityOf1MutationLowerBound := "" - probabilityOf1MutationUpperBound := "" - probabilityOf2MutationsLowerBound := "" - probabilityOf2MutationsUpperBound := "" - - for index, genomePairIdentifier := range allGenomePairIdentifiersList{ - - currentProbabilityOf0MutationsLowerBound, exists := variantMap[genomePairIdentifier + "_ProbabilityOf0MutationsLowerBound"] - if (exists == false){ - return false, errors.New("Cannot find _ProbabilityOf0MutationsLowerBound key when searching for conflicts.") - } - currentProbabilityOf0MutationsUpperBound, exists := variantMap[genomePairIdentifier + "_ProbabilityOf0MutationsUpperBound"] - if (exists == false){ - return false, errors.New("Cannot find _ProbabilityOf0MutationsUpperBound key when searching for conflicts.") - } - currentProbabilityOf1MutationLowerBound, exists := variantMap[genomePairIdentifier + "_ProbabilityOf1MutationLowerBound"] - if (exists == false){ - return false, errors.New("Cannot find _ProbabilityOf1MutationLowerBound key when searching for conflicts.") - } - currentProbabilityOf1MutationUpperBound, exists := variantMap[genomePairIdentifier + "_ProbabilityOf1MutationUpperBound"] - if (exists == false){ - return false, errors.New("Cannot find _ProbabilityOf1MutationUpperBound key when searching for conflicts.") - } - currentProbabilityOf2MutationsLowerBound, exists := variantMap[genomePairIdentifier + "_ProbabilityOf2MutationsLowerBound"] - if (exists == false){ - return false, errors.New("Cannot find _ProbabilityOf2MutationsLowerBound key when searching for conflicts.") - } - currentProbabilityOf2MutationsUpperBound, exists := variantMap[genomePairIdentifier + "_ProbabilityOf2MutationsUpperBound"] - if (exists == false){ - return false, errors.New("Cannot find _ProbabilityOf2MutationsUpperBound key when searching for conflicts.") - } - - if (index == 0){ - probabilityOf0MutationsLowerBound = currentProbabilityOf0MutationsLowerBound - probabilityOf0MutationsUpperBound = currentProbabilityOf0MutationsUpperBound - - probabilityOf1MutationLowerBound = currentProbabilityOf1MutationLowerBound - probabilityOf1MutationUpperBound = currentProbabilityOf1MutationUpperBound - - probabilityOf2MutationsLowerBound = currentProbabilityOf2MutationsLowerBound - probabilityOf2MutationsUpperBound = currentProbabilityOf2MutationsUpperBound - continue - } - - if (currentProbabilityOf0MutationsLowerBound != probabilityOf0MutationsLowerBound){ - return true, nil - } - if (currentProbabilityOf0MutationsUpperBound != probabilityOf0MutationsUpperBound){ - return true, nil - } - if (currentProbabilityOf1MutationLowerBound != probabilityOf1MutationLowerBound){ - return true, nil - } - if (currentProbabilityOf1MutationUpperBound != probabilityOf1MutationUpperBound){ - return true, nil - } - if (currentProbabilityOf2MutationsLowerBound != probabilityOf2MutationsLowerBound){ - return true, nil - } - if (currentProbabilityOf2MutationsUpperBound != probabilityOf2MutationsUpperBound){ - return true, nil - } + areEqual := maps.Equal(currentOffspringVariantsInfoMap, offspringVariantsInfoMap) + if (areEqual == false){ + return true, nil } } @@ -752,274 +627,202 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom conflictExists, err := checkIfConflictExists() if (err != nil) { return false, "", err } - conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) - - offspringDiseaseMap["ConflictExists"] = conflictExistsString + newOffspringMonogenicDiseaseInfoObject.ConflictExists = conflictExists } - newAnalysisMapList = append(newAnalysisMapList, offspringDiseaseMap) - newAnalysisMapList = append(newAnalysisMapList, offspringVariantsMapList...) + offspringMonogenicDiseasesMap[diseaseName] = newOffspringMonogenicDiseaseInfoObject } + newCoupleAnalysis.MonogenicDiseasesMap = offspringMonogenicDiseasesMap + // Step 2: Polygenic Diseases polygenicDiseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList() if (err != nil){ return false, "", err } + // Map Structure: Disease Name -> OffspringPolygenicDiseaseInfo + offspringPolygenicDiseasesMap := make(map[string]geneticAnalysis.OffspringPolygenicDiseaseInfo) + for _, diseaseObject := range polygenicDiseaseObjectsList{ diseaseName := diseaseObject.DiseaseName diseaseLociList := diseaseObject.LociList - _, personALociMapList, err := getPolygenicDiseaseAnalysis(personAGenomesWithMetadataList, diseaseObject) + person1DiseaseAnalysisObject, err := getPersonPolygenicDiseaseAnalysis(person1GenomesWithMetadataList, diseaseObject) if (err != nil) { return false, "", err } - _, personBLociMapList, err := getPolygenicDiseaseAnalysis(personBGenomesWithMetadataList, diseaseObject) + person2DiseaseAnalysisObject, err := getPersonPolygenicDiseaseAnalysis(person2GenomesWithMetadataList, diseaseObject) if (err != nil) { return false, "", err } - offspringLociMapList := make([]map[string]string, 0, len(diseaseLociList)) + // This map stores the polygenic disease info for each genome pair + // Map Structure: Genome Pair Identifier -> OffspringGenomePairPolygenicDiseaseInfo + offspringPolygenicDiseaseInfoMap := make(map[[32]byte]geneticAnalysis.OffspringGenomePairPolygenicDiseaseInfo) - for _, locusObject := range diseaseLociList{ + // This will calculate the disease info for the offspring from the two specified genomes to the diseases map + // It then adds the pair entry to the offspringPolygenicDiseaseInfoMap + addGenomePairDiseaseInfoToDiseaseMap := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{ - locusIdentifier := locusObject.LocusIdentifier - - locusRiskWeightsMap := locusObject.RiskWeightsMap - locusOddsRatiosMap := locusObject.OddsRatiosMap - - offspringLocusMap := map[string]string{ - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": locusIdentifier, - "DiseaseName": diseaseName, - } - - // This will calculate the average risk weight and probability change for the offspring from the two specified genomes - // It then adds the pair entry to the diseaseMap - addPairDiseaseLocusInfoToLocusMap := func(personAGenomeIdentifier string, personBGenomeIdentifier string)error{ - - getPersonGenomeLocusBasePair := func(personGenomeIdentifier string, personLociMapList []map[string]string)(bool, string, error){ - - for _, locusMap := range personLociMapList{ - - itemType, exists := locusMap["ItemType"] - if (exists == false){ - return false, "", errors.New("getPolygenicDiseaseAnalysis returning lociMapList with item missing ItemType") - } - if (itemType != "PolygenicDiseaseLocus"){ - return false, "", errors.New("getPolygenicDiseaseAnalysis returning lociMapList with non-PolygenicDiseaseLocus item: " + itemType) - } - currentLocusIdentifier, exists := locusMap["LocusIdentifier"] - if (exists == false){ - return false, "", errors.New("getPolygenicDiseaseAnalysis returning lociMapList with item missing LocusIdentifier") - } - - if (currentLocusIdentifier != locusIdentifier){ - continue - } - - locusBasePair, exists := locusMap[personGenomeIdentifier + "_LocusBasePair"] - if (exists == false){ - return false, "", errors.New("getPolygenicDiseaseAnalysis returning lociMapList with item missing _LocusBasePair") - } - - if (locusBasePair == "Unknown"){ - return false, "", nil - } - - return true, locusBasePair, nil - } - - return false, "", errors.New("getPolygenicDiseaseAnalysis returning lociMapList missing a locus: " + locusIdentifier) - } - - genomePairIdentifier := personAGenomeIdentifier + "+" + personBGenomeIdentifier - - personALocusBasePairKnown, personALocusBasePair, err := getPersonGenomeLocusBasePair(personAGenomeIdentifier, personALociMapList) - if (err != nil) { return err } - - personBLocusBasePairKnown, personBLocusBasePair, err := getPersonGenomeLocusBasePair(personBGenomeIdentifier, personBLociMapList) - if (err != nil) { return err } - - if (personALocusBasePairKnown == false || personBLocusBasePairKnown == false){ - - offspringLocusMap[genomePairIdentifier + "_OffspringRiskWeight"] = "Unknown" - offspringLocusMap[genomePairIdentifier + "_OffspringOddsRatio"] = "Unknown" - offspringLocusMap[genomePairIdentifier + "_OffspringUnknownOddsRatiosWeightSum"] = "Unknown" - - return nil - } - - offspringAverageRiskWeight, offspringOddsRatioKnown, offspringAverageOddsRatio, averageUnknownOddsRatiosWeightSum, err := GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, personALocusBasePair, personBLocusBasePair) - if (err != nil) { return err } - - averageRiskWeightString := helpers.ConvertIntToString(offspringAverageRiskWeight) - - offspringLocusMap[genomePairIdentifier + "_OffspringRiskWeight"] = averageRiskWeightString - - if (offspringOddsRatioKnown == false){ - - offspringLocusMap[genomePairIdentifier + "_OffspringOddsRatio"] = "Unknown" - - } else { - - averageOddsRatioString := helpers.ConvertFloat64ToString(offspringAverageOddsRatio) - - offspringLocusMap[genomePairIdentifier + "_OffspringOddsRatio"] = averageOddsRatioString - } - - averageUnknownOddsRatiosWeightSumString := helpers.ConvertIntToString(averageUnknownOddsRatiosWeightSum) - - offspringLocusMap[genomePairIdentifier + "_OffspringUnknownOddsRatiosWeightSum"] = averageUnknownOddsRatiosWeightSumString - - return nil - } - - err = addPairDiseaseLocusInfoToLocusMap(pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier) - if (err != nil) { return false, "", err } - - if (genomePair2Exists == true){ - - err := addPairDiseaseLocusInfoToLocusMap(pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier) - if (err != nil) { return false, "", err } - } - - offspringLociMapList = append(offspringLociMapList, offspringLocusMap) - } - - // Now we iterate through loci to determine genome pair disease info - - offspringPolygenicDiseaseMap := map[string]string{ - "ItemType": "PolygenicDisease", - "DiseaseName": diseaseName, - } - - polygenicDiseaseLociMap, err := polygenicDiseases.GetPolygenicDiseaseLociMap(diseaseName) - if (err != nil) { return false, "", err } - - addPairPolygenicDiseaseInfoToDiseaseMap := func(personAGenomeIdentifier string, personBGenomeIdentifier string)error{ - - genomePairIdentifier := personAGenomeIdentifier + "+" + personBGenomeIdentifier - - numberOfLociTested := 0 summedRiskWeights := 0 minimumPossibleRiskWeightSum := 0 maximumPossibleRiskWeightSum := 0 - for _, locusMap := range offspringLociMapList{ + // Map Structure: Locus Identifier -> OffspringPolygenicDiseaseLocusInfo + offspringDiseaseLociInfoMap := make(map[[3]byte]geneticAnalysis.OffspringPolygenicDiseaseLocusInfo) - locusIdentifier, exists := locusMap["LocusIdentifier"] - if (exists == false){ - return errors.New("offspringLociMapList item missing LocusIdentifier") + for _, locusObject := range diseaseLociList{ + + locusIdentifierHex := locusObject.LocusIdentifier + + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return err } + + locusRiskWeightsMap := locusObject.RiskWeightsMap + locusOddsRatiosMap := locusObject.OddsRatiosMap + + //Outputs: + // -bool: Locus value exists + // -string: Base1 Value + // -string: Base2 Value + // -error + getPersonGenomeLocusValues := func(personGenomeIdentifier [16]byte, personDiseaseAnalysisObject geneticAnalysis.PersonPolygenicDiseaseInfo)(bool, string, string, error){ + + personPolygenicDiseaseInfoMap := personDiseaseAnalysisObject.PolygenicDiseaseInfoMap + + personGenomeDiseaseInfoObject, exists := personPolygenicDiseaseInfoMap[personGenomeIdentifier] + if (exists == false){ + // This person's genome has no information about loci related to this disease + return false, "", "", nil + } + + personGenomeLociMap := personGenomeDiseaseInfoObject.LociInfoMap + + personGenomeLocusInfoObject, exists := personGenomeLociMap[locusIdentifier] + if (exists == false){ + // This person's genome doesn't have information about this locus + return false, "", "", nil + } + + locusBase1 := personGenomeLocusInfoObject.LocusBase1 + locusBase2 := personGenomeLocusInfoObject.LocusBase2 + + return true, locusBase1, locusBase2, nil } - locusObject, exists := polygenicDiseaseLociMap[locusIdentifier] - if (exists == false){ - return errors.New("offspringLociMapList contains locus not found in allDiseaseLociObjectsMap") + person1LocusBasePairKnown, person1LocusBase1, person1LocusBase2, err := getPersonGenomeLocusValues(person1GenomeIdentifier, person1DiseaseAnalysisObject) + if (err != nil) { return err } + if (person1LocusBasePairKnown == false){ + // Offspring's disease info for this locus on this genome pair is unknown + continue } + person2LocusBasePairKnown, person2LocusBase1, person2LocusBase2, err := getPersonGenomeLocusValues(person2GenomeIdentifier, person2DiseaseAnalysisObject) + if (err != nil) { return err } + if (person2LocusBasePairKnown == false){ + // Offspring's disease info for this locus on this genome pair is unknown + continue + } + + offspringAverageRiskWeight, offspringOddsRatioIsKnown, offspringAverageOddsRatio, averageUnknownOddsRatiosWeightSum, err := GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, person1LocusBase1, person1LocusBase2, person2LocusBase1, person2LocusBase2) + if (err != nil) { return err } + + newOffspringPolygenicDiseaseLocusInfo := geneticAnalysis.OffspringPolygenicDiseaseLocusInfo{ + OffspringRiskWeight: offspringAverageRiskWeight, + OffspringUnknownOddsRatiosWeightSum: averageUnknownOddsRatiosWeightSum, + } + + if (offspringOddsRatioIsKnown == true){ + + newOffspringPolygenicDiseaseLocusInfo.OffspringOddsRatioIsKnown = true + newOffspringPolygenicDiseaseLocusInfo.OffspringOddsRatio = offspringAverageOddsRatio + } + + offspringDiseaseLociInfoMap[locusIdentifier] = newOffspringPolygenicDiseaseLocusInfo + + // Now we add risk weight info for this locus + locusMinimumRiskWeight := locusObject.MinimumRiskWeight locusMaximumRiskWeight := locusObject.MaximumRiskWeight minimumPossibleRiskWeightSum += locusMinimumRiskWeight maximumPossibleRiskWeightSum += locusMaximumRiskWeight - locusRiskWeight, exists := locusMap[genomePairIdentifier + "_OffspringRiskWeight"] - if (exists == false){ - return errors.New("offspringLociMapList contains locusMap missing _OffspringRiskWeight") - } - - if (locusRiskWeight == "Unknown"){ - continue - } - numberOfLociTested += 1 - - locusRiskWeightInt, err := helpers.ConvertStringToInt(locusRiskWeight) - if (err != nil){ - return errors.New("offspringLociMapList contains locusMap with invalid _OffspringRiskWeight: " + locusRiskWeight) - } - - summedRiskWeights += locusRiskWeightInt + summedRiskWeights += offspringAverageRiskWeight } + numberOfLociTested := len(offspringDiseaseLociInfoMap) + if (numberOfLociTested == 0){ - - // No loci were tested - offspringPolygenicDiseaseMap[genomePairIdentifier + "_NumberOfLociTested"] = "0" - offspringPolygenicDiseaseMap[genomePairIdentifier + "_OffspringRiskScore"] = "Unknown" - + // We have no information about this genome pair's disease risk + // We don't add this genome pair's disease info to the diseaseInfoMap return nil } - numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) - offspringPolygenicDiseaseMap[genomePairIdentifier + "_NumberOfLociTested"] = numberOfLociTestedString - - pairDiseaseRiskScore, err := helpers.ScaleNumberProportionally(true, summedRiskWeights, minimumPossibleRiskWeightSum, maximumPossibleRiskWeightSum, 0, 10) + genomePairDiseaseRiskScore, err := helpers.ScaleNumberProportionally(true, summedRiskWeights, minimumPossibleRiskWeightSum, maximumPossibleRiskWeightSum, 0, 10) if (err != nil) { return err } - pairDiseaseRiskScoreString := helpers.ConvertIntToString(pairDiseaseRiskScore) + newOffspringGenomePairPolygenicDiseaseInfo := geneticAnalysis.OffspringGenomePairPolygenicDiseaseInfo{ - offspringPolygenicDiseaseMap[genomePairIdentifier + "_OffspringRiskScore"] = pairDiseaseRiskScoreString + NumberOfLociTested: numberOfLociTested, + OffspringRiskScore: genomePairDiseaseRiskScore, + LociInfoMap: offspringDiseaseLociInfoMap, + } + + genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier) + + offspringPolygenicDiseaseInfoMap[genomePairIdentifier] = newOffspringGenomePairPolygenicDiseaseInfo return nil } - addPairPolygenicDiseaseInfoToDiseaseMap(pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier) + err = addGenomePairDiseaseInfoToDiseaseMap(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) if (err != nil) { return false, "", err } if (genomePair2Exists == true){ - err := addPairPolygenicDiseaseInfoToDiseaseMap(pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier) + err := addGenomePairDiseaseInfoToDiseaseMap(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) if (err != nil) { return false, "", err } } - if (genomePair2Exists == true){ + newOffspringPolygenicDiseaseInfoObject := geneticAnalysis.OffspringPolygenicDiseaseInfo{ + PolygenicDiseaseInfoMap: offspringPolygenicDiseaseInfoMap, + } - // We check for conflicts - // Conflicts are only possible if two genome pairs exist + if (len(offspringPolygenicDiseaseInfoMap) >= 2){ + + // We check for conflicts + // Conflicts are only possible if two genome pairs with disease info for this disease exist checkIfConflictExists := func()(bool, error){ - genomePair1Identifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier + numberOfLociTested := 0 + offspringRiskScore := 0 + offspringLociInfoMap := make(map[[3]byte]geneticAnalysis.OffspringPolygenicDiseaseLocusInfo) - genomePair2Identifier := pair2PersonAGenomeIdentifier + "+" + pair2PersonBGenomeIdentifier + firstItemReached := false - allGenomePairIdentifiersList := []string{genomePair1Identifier, genomePair2Identifier} + for _, genomePairDiseaseInfoObject := range offspringPolygenicDiseaseInfoMap{ - for _, locusMap := range offspringLociMapList{ + genomePairNumberOfLociTested := genomePairDiseaseInfoObject.NumberOfLociTested + genomePairOffspringRiskScore := genomePairDiseaseInfoObject.OffspringRiskScore + genomePairLociInfoMap := genomePairDiseaseInfoObject.LociInfoMap - offspringRiskWeight := "" - offspringOddsRatio := "" - offspringUnknownOddsRatiosWeightSum := "" - - for index, genomePairIdentifier := range allGenomePairIdentifiersList{ - - currentOffspringRiskWeight, exists := locusMap[genomePairIdentifier + "_OffspringRiskWeight"] - if (exists == false){ - return false, errors.New("Cannot find _OffspringRiskWeight key when searching for conflicts.") - } - currentOffspringOddsRatio, exists := locusMap[genomePairIdentifier + "_OffspringOddsRatio"] - if (exists == false){ - return false, errors.New("Cannot find _OffspringOddsRatio key when searching for conflicts.") - } - currentOffspringUnknownOddsRatiosWeightSum, exists := locusMap[genomePairIdentifier + "_OffspringUnknownOddsRatiosWeightSum"] - if (exists == false){ - return false, errors.New("Cannot find _OffspringUnknownOddsRatiosWeightSum key when searching for conflicts.") - } - - if (index == 0){ - offspringRiskWeight = currentOffspringRiskWeight - offspringOddsRatio = currentOffspringOddsRatio - offspringUnknownOddsRatiosWeightSum = currentOffspringUnknownOddsRatiosWeightSum - continue - } - if (currentOffspringRiskWeight != offspringRiskWeight){ - return true, nil - } - if (currentOffspringOddsRatio != offspringOddsRatio){ - return true, nil - } - if (currentOffspringUnknownOddsRatiosWeightSum != offspringUnknownOddsRatiosWeightSum){ - return true, nil - } + if (firstItemReached == false){ + numberOfLociTested = genomePairNumberOfLociTested + offspringRiskScore = genomePairOffspringRiskScore + offspringLociInfoMap = genomePairLociInfoMap + firstItemReached = true + continue + } + if (numberOfLociTested != genomePairNumberOfLociTested){ + return true, nil + } + if (offspringRiskScore != genomePairOffspringRiskScore){ + return true, nil + } + areEqual := maps.Equal(offspringLociInfoMap, genomePairLociInfoMap) + if (areEqual == false){ + // A conflict exists + return true, nil } } @@ -1029,219 +832,150 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom conflictExists, err := checkIfConflictExists() if (err != nil) { return false, "", err } - conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) - - offspringPolygenicDiseaseMap["ConflictExists"] = conflictExistsString + newOffspringPolygenicDiseaseInfoObject.ConflictExists = conflictExists } - newAnalysisMapList = append(newAnalysisMapList, offspringPolygenicDiseaseMap) - newAnalysisMapList = append(newAnalysisMapList, offspringLociMapList...) + offspringPolygenicDiseasesMap[diseaseName] = newOffspringPolygenicDiseaseInfoObject } + newCoupleAnalysis.PolygenicDiseasesMap = offspringPolygenicDiseasesMap + // Step 3: Traits traitObjectsList, err := traits.GetTraitObjectsList() if (err != nil) { return false, "", err } + // Map Structure: Trait Name -> Trait Info Object + offspringTraitsMap := make(map[string]geneticAnalysis.OffspringTraitInfo) + for _, traitObject := range traitObjectsList{ traitName := traitObject.TraitName traitRulesList := traitObject.RulesList - _, personATraitRulesMapList, err := getTraitAnalysis(personAGenomesWithMetadataList, traitObject) + person1TraitAnalysisObject, err := getPersonTraitAnalysis(person1GenomesWithMetadataList, traitObject) if (err != nil) { return false, "", err } - _, personBTraitRulesMapList, err := getTraitAnalysis(personBGenomesWithMetadataList, traitObject) + person2TraitAnalysisObject, err := getPersonTraitAnalysis(person2GenomesWithMetadataList, traitObject) if (err != nil) { return false, "", err } - offspringRulesMapList := make([]map[string]string, 0, len(traitRulesList)) + // This map stores the trait info for each genome pair + // Map Structure: Genome Pair Identifier -> OffspringGenomePairTraitInfo + offspringTraitInfoMap := make(map[[32]byte]geneticAnalysis.OffspringGenomePairTraitInfo) - for _, ruleObject := range traitRulesList{ + // This will add the offspring trait information for the provided genome pair to the offspringTraitInfoMap + addGenomePairTraitInfoToOffspringMap := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{ - ruleIdentifier := ruleObject.RuleIdentifier + // Map Structure: Outcome Name -> Outcome Score + // Example: "Intolerant" -> 2.5 + offspringAverageOutcomeScoresMap := make(map[string]float64) - // This is a list that describes the locus rsids and their values that must be fulfilled to pass the rule - ruleLocusObjectsList := ruleObject.LociList + // Map Structure: Rule Identifier -> Offspring Probability Of Passing Rule + // The value stores the probability that the offspring will pass the rule + // This is a number between 0-100% + offspringProbabilityOfPassingRulesMap := make(map[[3]byte]int) - offspringRuleMap := map[string]string{ - "ItemType": "TraitRule", - "RuleIdentifier": ruleIdentifier, - "TraitName": traitName, - } + // We iterate through rules to determine genome pair trait info - // This will calculate the number of offspring that will pass the rule for the offspring from the two specified genomes - // It then adds the pair entry to the ruleMap - addPairTraitRuleInfoToRuleMap := func(personAGenomeIdentifier string, personBGenomeIdentifier string)error{ + for _, ruleObject := range traitRulesList{ + + ruleIdentifierHex := ruleObject.RuleIdentifier + + ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex) + if (err != nil) { return err } + + // This is a list that describes the locus rsids and their values that must be fulfilled to pass the rule + ruleLocusObjectsList := ruleObject.LociList //Outputs: - // -bool: All rule loci are known - // -map[string]string: RSID -> Locus base pair value + // -bool: Any rule loci are known + // -map[int64]locusValue.LocusValue: rsID -> Locus base pair value // -error - getPersonGenomeRuleLociValuesMap := func(personGenomeIdentifier string, personTraitRulesMapList []map[string]string)(bool, map[string]string, error){ + getPersonGenomeTraitLociValuesMap := func(personGenomeIdentifier [16]byte, personTraitAnalysisObject geneticAnalysis.PersonTraitInfo)(bool, map[int64]locusValue.LocusValue, error){ - for _, ruleMap := range personTraitRulesMapList{ + personTraitInfoMap := personTraitAnalysisObject.TraitInfoMap - itemType, exists := ruleMap["ItemType"] - if (exists == false){ - return false, nil, errors.New("getTraitAnalysis returning rulesMapList with item missing ItemType") - } - if (itemType != "TraitRule"){ - return false, nil, errors.New("getTraitAnalysis returning rulesMapList with non-TraitRule item: " + itemType) - } - currentRuleIdentifier, exists := ruleMap["RuleIdentifier"] - if (exists == false){ - return false, nil, errors.New("getTraitAnalysis returning rulesMapList with item missing RuleIdentifier") - } - - if (currentRuleIdentifier != ruleIdentifier){ - continue - } - - // Map structure: Locus identifier -> Locus base pair value - personTraitLociBasePairsMap := make(map[string]string) - - for _, locusObject := range ruleLocusObjectsList{ - - locusIdentifier := locusObject.LocusIdentifier - - personLocusBasePair, exists := ruleMap[personGenomeIdentifier + "_RuleLocusBasePair_" + locusIdentifier] - if (exists == false){ - return false, nil, errors.New("_LocusBasePair_ value not found in person trait analysis ruleMap") - } - - if (personLocusBasePair == "Unknown"){ - // Not all locus values are known, thus, we cannot determine if the genome passes the rule - return false, nil, nil - } - - personTraitLociBasePairsMap[locusIdentifier] = personLocusBasePair - } - - return true, personTraitLociBasePairsMap, nil + personGenomeTraitInfoObject, exists := personTraitInfoMap[personGenomeIdentifier] + if (exists == false){ + // This person has no genome values for any loci for this trait + return false, nil, nil } - return false, nil, errors.New("getTraitAnalysis returning rulesMapList missing a rule: " + ruleIdentifier) + personLocusValuesMap := personGenomeTraitInfoObject.LocusValuesMap + + return true, personLocusValuesMap, nil } - allPersonALociKnown, personAGenomeRuleLociValuesMap, err := getPersonGenomeRuleLociValuesMap(personAGenomeIdentifier, personATraitRulesMapList) + anyPerson1LociKnown, person1GenomeTraitLociValuesMap, err := getPersonGenomeTraitLociValuesMap(person1GenomeIdentifier, person1TraitAnalysisObject) if (err != nil) { return err } - - allPersonBLociKnown, personBGenomeRuleLociValuesMap, err := getPersonGenomeRuleLociValuesMap(personBGenomeIdentifier, personBTraitRulesMapList) - if (err != nil) { return err } - - genomePairIdentifier := personAGenomeIdentifier + "+" + personBGenomeIdentifier - - if (allPersonALociKnown == false || allPersonBLociKnown == false){ - + if (anyPerson1LociKnown == false){ // We only know how many of the 4 prospective offspring pass the rule if all loci are known for both people's genomes - - offspringRuleMap[genomePairIdentifier + "_OffspringProbabilityOfPassingRule"] = "Unknown" return nil } - // This is a probability between 0 and 1 - offspringProbabilityOfPassingRule := float64(1) + anyPerson2LociKnown, person2GenomeTraitLociValuesMap, err := getPersonGenomeTraitLociValuesMap(person2GenomeIdentifier, person2TraitAnalysisObject) + if (err != nil) { return err } + if (anyPerson2LociKnown == false){ + // We only know how many of the 4 prospective offspring pass the rule if all loci are known for both people's genomes + return nil + } - for _, ruleLocusObject := range ruleLocusObjectsList{ + getOffspringProbabilityOfPassingRule := func()(bool, int, error){ - locusIdentifier := ruleLocusObject.LocusIdentifier + // This is a probability between 0 and 1 + offspringProbabilityOfPassingRule := float64(1) - personALocusBasePair, exists := personAGenomeRuleLociValuesMap[locusIdentifier] - if (exists == false){ - return errors.New("personAGenomeRuleLociValuesMap missing locusIdentifier") + for _, ruleLocusObject := range ruleLocusObjectsList{ + + locusRSID := ruleLocusObject.LocusRSID + + person1LocusValue, exists := person1GenomeTraitLociValuesMap[locusRSID] + if (exists == false){ + // We don't know the locus value for this rule for this person + // Thus, we cannot calculate the probabilityOfPassingRule for the offspring + return false, 0, nil + } + + person2LocusValue, exists := person2GenomeTraitLociValuesMap[locusRSID] + if (exists == false){ + // We don't know the locus value for this rule for this person + // Thus, we cannot calculate the probabilityOfPassingRule for the offspring + return false, 0, nil + } + + locusRequiredBasePairsList := ruleLocusObject.BasePairsList + + person1LocusBase1Value := person1LocusValue.Base1Value + person1LocusBase2Value := person1LocusValue.Base2Value + + person2LocusBase1Value := person2LocusValue.Base1Value + person2LocusBase2Value := person2LocusValue.Base2Value + + offspringProbabilityOfPassingRuleLocus, err := GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, person1LocusBase1Value, person1LocusBase2Value, person2LocusBase1Value, person2LocusBase2Value) + if (err != nil) { return false, 0, err } + + offspringProbabilityOfPassingRule *= offspringProbabilityOfPassingRuleLocus } - personBLocusBasePair, exists := personBGenomeRuleLociValuesMap[locusIdentifier] - if (exists == false){ - return errors.New("personBGenomeRuleLociValuesMap missing locusIdentifier") - } + offspringPercentageProbabilityOfPassingRule := offspringProbabilityOfPassingRule * 100 - locusRequiredBasePairsList := ruleLocusObject.BasePairsList + probabilityRounded, err := helpers.FloorFloat64ToInt(offspringPercentageProbabilityOfPassingRule) + if (err != nil) { return false, 0, err } - offspringProbabilityOfPassingRuleLocus, err := GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, personALocusBasePair, personBLocusBasePair) - if (err != nil) { return err } - - offspringProbabilityOfPassingRule *= offspringProbabilityOfPassingRuleLocus + return true, probabilityRounded, nil } - offspringPercentageProbabilityOfPassingRule := offspringProbabilityOfPassingRule * 100 - - offspringPercentageProbabilityOfPassingRuleString := helpers.ConvertFloat64ToStringRounded(offspringPercentageProbabilityOfPassingRule, 0) - - offspringRuleMap[genomePairIdentifier + "_OffspringProbabilityOfPassingRule"] = offspringPercentageProbabilityOfPassingRuleString - - return nil - } - - err = addPairTraitRuleInfoToRuleMap(pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier) - if (err != nil) { return false, "", err } - - if (genomePair2Exists == true){ - - err := addPairTraitRuleInfoToRuleMap(pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier) - if (err != nil) { return false, "", err } - } - - offspringRulesMapList = append(offspringRulesMapList, offspringRuleMap) - } - - // Now we iterate through rules to determine genome pair trait info - - offspringTraitMap := map[string]string{ - "ItemType": "Trait", - "TraitName": traitName, - } - - traitRulesMap, err := traits.GetTraitRulesMap(traitName) - if (err != nil) { return false, "", err } - - addPairTraitInfoToTraitMap := func(personAGenomeIdentifier string, personBGenomeIdentifier string)error{ - - genomePairIdentifier := personAGenomeIdentifier + "+" + personBGenomeIdentifier - - if (len(traitRulesList) == 0){ - // This trait is not yet able to be analyzed. - // This feature will be added soon - // These traits will use neural networks instead of rules - offspringTraitMap[genomePairIdentifier + "_NumberOfRulesTested"] = "0" - return nil - } - - numberOfRulesTested := 0 - - // Map Structure: Outcome -> Average score from all rules - averageOutcomeScoresMap := make(map[string]float64) - - for _, ruleMap := range offspringRulesMapList{ - - ruleIdentifier, exists := ruleMap["RuleIdentifier"] - if (exists == false){ - return errors.New("offspringRulesMapList item missing RuleIdentifier") - } - - offspringPercentageProbabilityOfPassingRule, exists := ruleMap[genomePairIdentifier + "_OffspringProbabilityOfPassingRule"] - if (exists == false){ - return errors.New("offspringRulesMapList contains ruleMap missing _OffspringProbabilityOfPassingRule") - } - - if (offspringPercentageProbabilityOfPassingRule == "Unknown"){ + ruleProbabilityIsKnown, offspringPercentageProbabilityOfPassingRule, err := getOffspringProbabilityOfPassingRule() + if (err != nil) { return err } + if (ruleProbabilityIsKnown == false){ + // We continue to the next rule continue } - numberOfRulesTested += 1 - - offspringPercentageProbabilityOfPassingRuleFloat64, err := helpers.ConvertStringToFloat64(offspringPercentageProbabilityOfPassingRule) - if (err != nil){ - return errors.New("offspringRulesMapList contains ruleMap with invalid _OffspringProbabilityOfPassingRule: " + offspringPercentageProbabilityOfPassingRule) - } + offspringProbabilityOfPassingRulesMap[ruleIdentifier] = offspringPercentageProbabilityOfPassingRule // This is the 0 - 1 probability value - offspringProbabilityOfPassingRule := offspringPercentageProbabilityOfPassingRuleFloat64/100 - - ruleObject, exists := traitRulesMap[ruleIdentifier] - if (exists == false){ - return errors.New("offspringRulesMapList contains rule not found in traitRulesMap: " + ruleIdentifier) - } + offspringProbabilityOfPassingRule := float64(offspringPercentageProbabilityOfPassingRule)/100 ruleOutcomePointsMap := ruleObject.OutcomePointsMap @@ -1249,83 +983,92 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom pointsToAdd := float64(outcomePointsEffect) * offspringProbabilityOfPassingRule - averageOutcomeScoresMap[outcomeName] += pointsToAdd + offspringAverageOutcomeScoresMap[outcomeName] += pointsToAdd } } - numberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested) - offspringTraitMap[genomePairIdentifier + "_NumberOfRulesTested"] = numberOfRulesTestedString + numberOfRulesTested := len(offspringProbabilityOfPassingRulesMap) if (numberOfRulesTested == 0){ + // No rules were tested for this trait + // We will not add anything to the trait info map for this genome pair return nil } traitOutcomesList := traitObject.OutcomesList + // We add a 0 outcome for outcomes without any points + for _, outcomeName := range traitOutcomesList{ - getOffspringAverageOutcomeScore := func()float64{ - - averageOutcomeScore, exists := averageOutcomeScoresMap[outcomeName] - if (exists == false){ - // No rules effected this outcome. - return 0 - } - return averageOutcomeScore + _, exists := offspringAverageOutcomeScoresMap[outcomeName] + if (exists == false){ + // No rules effected this outcome. + offspringAverageOutcomeScoresMap[outcomeName] = 0 } - - offspringAverageOutcomeScore := getOffspringAverageOutcomeScore() - - offspringAverageOutcomeScoreString := helpers.ConvertFloat64ToStringRounded(offspringAverageOutcomeScore, 2) - - offspringTraitMap[genomePairIdentifier + "_OffspringAverageOutcomeScore_" + outcomeName] = offspringAverageOutcomeScoreString } + newOffspringGenomePairTraitInfoObject := geneticAnalysis.OffspringGenomePairTraitInfo{ + NumberOfRulesTested: numberOfRulesTested, + OffspringAverageOutcomeScoresMap: offspringAverageOutcomeScoresMap, + ProbabilityOfPassingRulesMap: offspringProbabilityOfPassingRulesMap, + } + + genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier) + + offspringTraitInfoMap[genomePairIdentifier] = newOffspringGenomePairTraitInfoObject + return nil } - addPairTraitInfoToTraitMap(pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier) + err = addGenomePairTraitInfoToOffspringMap(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) if (err != nil) { return false, "", err } if (genomePair2Exists == true){ - err := addPairTraitInfoToTraitMap(pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier) + err := addGenomePairTraitInfoToOffspringMap(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) if (err != nil) { return false, "", err } } - if (genomePair2Exists == true){ + newOffspringTraitInfoObject := geneticAnalysis.OffspringTraitInfo{ + TraitInfoMap: offspringTraitInfoMap, + } + + if (len(offspringTraitInfoMap) >= 2){ // We check for conflicts - // Conflicts are only possible if two genome pairs exist + // Conflicts are only possible if two genome pairs exist with information about the trait checkIfConflictExists := func()(bool, error){ - genomePair1Identifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier + // We check for conflicts between each genome pair's outcome scores and trait rules maps - genomePair2Identifier := pair2PersonAGenomeIdentifier + "+" + pair2PersonBGenomeIdentifier + offspringAverageOutcomeScoresMap := make(map[string]float64) + offspringProbabilityOfPassingRulesMap := make(map[[3]byte]int) - allGenomePairIdentifiersList := []string{genomePair1Identifier, genomePair2Identifier} + firstItemReached := false - for _, ruleMap := range offspringRulesMapList{ + for _, genomePairTraitInfoObject := range offspringTraitInfoMap{ - offspringProbabilityOfPassingRule := "" + currentOffspringAverageOutcomeScoresMap := genomePairTraitInfoObject.OffspringAverageOutcomeScoresMap + currentProbabilityOfPassingRulesMap := genomePairTraitInfoObject.ProbabilityOfPassingRulesMap - for index, genomePairIdentifier := range allGenomePairIdentifiersList{ + if (firstItemReached == false){ + offspringAverageOutcomeScoresMap = currentOffspringAverageOutcomeScoresMap + offspringProbabilityOfPassingRulesMap = currentProbabilityOfPassingRulesMap - currentOffspringProbabilityOfPassingRule, exists := ruleMap[genomePairIdentifier + "_OffspringProbabilityOfPassingRule"] - if (exists == false){ - return false, errors.New("Cannot find _OffspringProbabilityOfPassingRule key when searching for conflicts.") - } + firstItemReached = true + continue + } - if (index == 0){ - - offspringProbabilityOfPassingRule = currentOffspringProbabilityOfPassingRule - continue - } - if (currentOffspringProbabilityOfPassingRule != offspringProbabilityOfPassingRule){ - return true, nil - } + areEqual := maps.Equal(offspringAverageOutcomeScoresMap, currentOffspringAverageOutcomeScoresMap) + if (areEqual == false){ + return true, nil + } + areEqual = maps.Equal(offspringProbabilityOfPassingRulesMap, currentProbabilityOfPassingRulesMap) + if (areEqual == false){ + return true, nil } } @@ -1335,16 +1078,15 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom conflictExists, err := checkIfConflictExists() if (err != nil) { return false, "", err } - conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) - - offspringTraitMap["ConflictExists"] = conflictExistsString + newOffspringTraitInfoObject.ConflictExists = conflictExists } - newAnalysisMapList = append(newAnalysisMapList, offspringTraitMap) - newAnalysisMapList = append(newAnalysisMapList, offspringRulesMapList...) + offspringTraitsMap[traitName] = newOffspringTraitInfoObject } - analysisBytes, err := json.MarshalIndent(newAnalysisMapList, "", "\t") + newCoupleAnalysis.TraitsMap = offspringTraitsMap + + analysisBytes, err := encoding.EncodeMessagePackBytes(newCoupleAnalysis) if (err != nil) { return false, "", err } analysisString := string(analysisBytes) @@ -1354,32 +1096,72 @@ func CreateCoupleGeneticAnalysis(personAGenomesList []prepareRawGenomes.RawGenom // We also use this function when calculating offspring probabilities between users in viewProfileGui.go //Outputs: +// -bool: Probability offspring has disease is known // -int: Percentage probability offspring has disease (0-100) +// -bool: Probability offspring has variant is known // -int: Percentage probability offspring has variant (0-100) // -error -func GetOffspringMonogenicDiseaseProbabilities(dominantOrRecessive string, personAWillPassVariantPercentageProbability int, personBWillPassVariantPercentageProbability int)(int, int, error){ +func GetOffspringMonogenicDiseaseProbabilities(dominantOrRecessive string, person1ProbabilityIsKnown bool, person1WillPassVariantPercentageProbability int, person2ProbabilityIsKnown bool, person2WillPassVariantPercentageProbability int)(bool, int, bool, int, error){ if (dominantOrRecessive != "Dominant" && dominantOrRecessive != "Recessive"){ - return 0, 0, errors.New("GetOffspringMonogenicDiseaseProbabilities called with invalid dominantOrRecessive: " + dominantOrRecessive) + return false, 0, false, 0, errors.New("GetOffspringMonogenicDiseaseProbabilities called with invalid dominantOrRecessive: " + dominantOrRecessive) + } + if (person1ProbabilityIsKnown == false && person2ProbabilityIsKnown == false){ + return false, 0, false, 0, nil } - personAWillPassVariantProbability := float64(personAWillPassVariantPercentageProbability)/100 - personBWillPassVariantProbability := float64(personBWillPassVariantPercentageProbability)/100 + if (person1ProbabilityIsKnown == true){ + if (person1WillPassVariantPercentageProbability < 0 || person1WillPassVariantPercentageProbability > 100){ + return false, 0, false, 0, errors.New("GetOffspringMonogenicDiseaseProbabilities called with invalid person1WillPassVariantProbability") + } + } + if (person2ProbabilityIsKnown == true){ + if (person2WillPassVariantPercentageProbability < 0 || person2WillPassVariantPercentageProbability > 100){ + return false, 0, false, 0, errors.New("GetOffspringMonogenicDiseaseProbabilities called with invalid person2WillPassVariantProbability") + } + } + if (person1ProbabilityIsKnown == false || person2ProbabilityIsKnown == false){ + // Only 1 of the person's probabilities are known - if (personAWillPassVariantProbability < 0 || personAWillPassVariantProbability > 1){ - return 0, 0, errors.New("GetOffspringMonogenicDiseaseProbabilities called with invalid personAWillPassVariantProbability") + getPersonWhoHasVariantProbabilityOfPassingIt := func()int{ + if (person1ProbabilityIsKnown == true){ + return person1WillPassVariantPercentageProbability + } + return person2WillPassVariantPercentageProbability + } + + personWhoHasVariantProbabilityOfPassingIt := getPersonWhoHasVariantProbabilityOfPassingIt() + + if (personWhoHasVariantProbabilityOfPassingIt == 100){ + if (dominantOrRecessive == "Dominant"){ + // We know the offspring will have the disease and will have a variant + return true, 100, true, 100, nil + } + //dominantOrRecessive == "Recessive" + // We don't know if the offspring will have the disease, but we know they will have a variant + return false, 0, true, 100, nil + } + if (personWhoHasVariantProbabilityOfPassingIt == 0){ + + if (dominantOrRecessive == "Recessive"){ + // We know the offspring will not have the disease, but we don't know if they will have a variant + return true, 0, false, 0, nil + } + } + + // We don't know the probabilities that the offspring will have the disease or if they will have a variant + return false, 0, false, 0, nil } - if (personBWillPassVariantProbability < 0 || personBWillPassVariantProbability > 1){ - return 0, 0, errors.New("GetOffspringMonogenicDiseaseProbabilities called with invalid personBWillPassVariantProbability") - } + person1WillPassVariantProbability := float64(person1WillPassVariantPercentageProbability)/100 + person2WillPassVariantProbability := float64(person2WillPassVariantPercentageProbability)/100 // The probability offspring has a variant = the probability that either parent passes a variant (inclusive or) // We find the probability of the offspring having a monogenic disease variant as follows: // P(A U B) = P(A) + P(B) - P(A ∩ B) - // (Probability of person A passing a variant) + (Probability of person B passing a variant) - (Probability of offspring having disease) + // (Probability of person 1 passing a variant) + (Probability of person 2 passing a variant) - (Probability of offspring having disease) // A person with a variant may have the disease, or just be a carrier. - probabilityOffspringHasVariant := personAWillPassVariantProbability + personBWillPassVariantProbability - (personAWillPassVariantProbability * personBWillPassVariantProbability) + probabilityOffspringHasVariant := person1WillPassVariantProbability + person2WillPassVariantProbability - (person1WillPassVariantProbability * person2WillPassVariantProbability) if (dominantOrRecessive == "Dominant"){ @@ -1387,13 +1169,13 @@ func GetOffspringMonogenicDiseaseProbabilities(dominantOrRecessive string, perso percentageProbabilityOffspringHasVariant := int(probabilityOffspringHasVariant * 100) - return percentageProbabilityOffspringHasVariant, percentageProbabilityOffspringHasVariant, nil + return true, percentageProbabilityOffspringHasVariant, true, percentageProbabilityOffspringHasVariant, nil } // We find the probability of the offspring having the mongenic disease as follows: // P(A and B) = P(A) * P(B) - // (Probability of person A Passing a variant) * (Probability of person B passing a variant) - probabilityOffspringHasDisease := personAWillPassVariantProbability * personBWillPassVariantProbability + // (Probability of person 1 Passing a variant) * (Probability of person 2 passing a variant) + probabilityOffspringHasDisease := person1WillPassVariantProbability * person2WillPassVariantProbability percentageProbabilityOffspringHasDisease := probabilityOffspringHasDisease * 100 percentageProbabilityOffspringHasVariant := probabilityOffspringHasVariant * 100 @@ -1405,7 +1187,7 @@ func GetOffspringMonogenicDiseaseProbabilities(dominantOrRecessive string, perso percentageProbabilityOffspringHasDiseaseInt := int(percentageProbabilityOffspringHasDisease) percentageProbabilityOffspringHasVariantInt := int(percentageProbabilityOffspringHasVariant) - return percentageProbabilityOffspringHasDiseaseInt, percentageProbabilityOffspringHasVariantInt, nil + return true, percentageProbabilityOffspringHasDiseaseInt, true, percentageProbabilityOffspringHasVariantInt, nil } //Outputs: @@ -1414,25 +1196,16 @@ func GetOffspringMonogenicDiseaseProbabilities(dominantOrRecessive string, perso // -float64: Offspring average odds ratio // -int: Offspring unknown odds ratios weight sum // -error -func GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap map[string]int, locusOddsRatiosMap map[string]float64, personALocusBasePair string, personBLocusBasePair string)(int, bool, float64, int, error){ - - personABase1, personABase2, delimiterFound := strings.Cut(personALocusBasePair, ";") - if (delimiterFound == false){ - return 0, false, 0, 0, errors.New("GetOffspringPolygenicDiseaseLocusInfo called with invalid person A locus base pair: " + personALocusBasePair) - } - personBBase1, personBBase2, delimiterFound := strings.Cut(personBLocusBasePair, ";") - if (delimiterFound == false){ - return 0, false, 0, 0, errors.New("GetOffspringPolygenicDiseaseLocusInfo called with invalid person B locus base pair: " + personBLocusBasePair) - } +func GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap map[string]int, locusOddsRatiosMap map[string]float64, person1LocusBase1 string, person1LocusBase2 string, person2LocusBase1 string, person2LocusBase2 string)(int, bool, float64, int, error){ // We create the 4 options for the offspring's bases at this locus - offspringBasePairOutcomeA := personABase1 + ";" + personBBase1 - offspringBasePairOutcomeB := personABase2 + ";" + personBBase2 - offspringBasePairOutcomeC := personABase1 + ";" + personBBase2 - offspringBasePairOutcomeD := personABase2 + ";" + personBBase1 + offspringBasePairOutcome1 := person1LocusBase1 + ";" + person2LocusBase1 + offspringBasePairOutcome2 := person1LocusBase2 + ";" + person2LocusBase2 + offspringBasePairOutcome3 := person1LocusBase1 + ";" + person2LocusBase2 + offspringBasePairOutcome4 := person1LocusBase2 + ";" + person2LocusBase1 - baseOutcomesList := []string{offspringBasePairOutcomeA, offspringBasePairOutcomeB, offspringBasePairOutcomeC, offspringBasePairOutcomeD} + baseOutcomesList := []string{offspringBasePairOutcome1, offspringBasePairOutcome2, offspringBasePairOutcome3, offspringBasePairOutcome4} summedRiskWeight := 0 @@ -1453,6 +1226,7 @@ func GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap map[string]int, l if (exists == false){ // We do not know the risk weight for this base pair // We treat this as a 0 risk for both weight and odds ratio + // No effect on risk is represented as an odds ratio of 1 summedOddsRatios += 1 numberOfSummedOddsRatios += 1 @@ -1499,25 +1273,16 @@ func GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap map[string]int, l //Outputs: // -float64: Probability of offspring passing rule (0-1) // -error -func GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList []string, personALocusBasePair string, personBLocusBasePair string)(float64, error){ - - personABase1, personABase2, delimiterFound := strings.Cut(personALocusBasePair, ";") - if (delimiterFound == false){ - return 0, errors.New("GetOffspringTraitRuleLocusInfo called with invalid personA locus base pair: " + personALocusBasePair) - } - personBBase1, personBBase2, delimiterFound := strings.Cut(personBLocusBasePair, ";") - if (delimiterFound == false){ - return 0, errors.New("GetOffspringTraitRuleLocusInfo called with invalid personB locus base pair: " + personBLocusBasePair) - } +func GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList []string, person1LocusBase1 string, person1LocusBase2 string, person2LocusBase1 string, person2LocusBase2 string)(float64, error){ // We create the 4 options for the offspring's bases at this locus - offspringBasePairOutcomeA := personABase1 + ";" + personBBase1 - offspringBasePairOutcomeB := personABase2 + ";" + personBBase2 - offspringBasePairOutcomeC := personABase1 + ";" + personBBase2 - offspringBasePairOutcomeD := personABase2 + ";" + personBBase1 + offspringBasePairOutcome1 := person1LocusBase1 + ";" + person2LocusBase1 + offspringBasePairOutcome2 := person1LocusBase2 + ";" + person2LocusBase2 + offspringBasePairOutcome3 := person1LocusBase1 + ";" + person2LocusBase2 + offspringBasePairOutcome4 := person1LocusBase2 + ";" + person2LocusBase1 - baseOutcomesList := []string{offspringBasePairOutcomeA, offspringBasePairOutcomeB, offspringBasePairOutcomeC, offspringBasePairOutcomeD} + baseOutcomesList := []string{offspringBasePairOutcome1, offspringBasePairOutcome2, offspringBasePairOutcome3, offspringBasePairOutcome4} numberOfOffspringOutcomesWhomPassRuleLocus := 0 @@ -1547,7 +1312,7 @@ func GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList []string, personA // -bool: Valid base pair value found // -string: Base 1 Value (Nucleotide base for the SNP) // -string: Base 2 Value (Nucleotide base for the SNP) -// -bool: Base pairs are phased +// -bool: Locus base pair is phased // -error func getGenomeLocusBasePair(inputGenomeMap map[int64]locusValue.LocusValue, locusRSID int64)(bool, string, string, bool, error){ @@ -1595,63 +1360,122 @@ func getGenomeLocusBasePair(inputGenomeMap map[int64]locusValue.LocusValue, locu } - //Outputs: -// -map[string]string: Monogenic disease analysis map (contains probabilities for each genomeIdentifier and number of tested variants) -// -[]map[string]string: Variants map list (contains variant info for each genomeIdentifier) +// -geneticAnalysis.PersonMonogenicDiseaseInfo: Monogenic disease analysis object // -error -func getMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, diseaseObject monogenicDiseases.MonogenicDisease)(map[string]string, []map[string]string, error){ +func getPersonMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, diseaseObject monogenicDiseases.MonogenicDisease)(geneticAnalysis.PersonMonogenicDiseaseInfo, error){ + + emptyDiseaseInfoObject := geneticAnalysis.PersonMonogenicDiseaseInfo{} - diseaseName := diseaseObject.DiseaseName dominantOrRecessive := diseaseObject.DominantOrRecessive variantsList := diseaseObject.VariantsList // We use this map to keep track of which RSIDs corresponds to each variant + // We also use it to have a map of all variants for the disease // Map Structure: Variant Identifier -> []rsID - variantRSIDsMap := make(map[string][]int64) + variantRSIDsMap := make(map[[3]byte][]int64) - variantsMapList := make([]map[string]string, 0, len(variantsList)) + // This map stores all rsIDs for this monogenic disease + // These are locations in the disease's gene which, if mutated, are known to cause the disease + // We use this map to avoid duplicate rsIDs, because one rsID can have multiple variants which belong to it + // We also store all alias rsIDs in this map + allRSIDsMap := make(map[int64]struct{}) for _, variantObject := range variantsList{ - variantIdentifier := variantObject.VariantIdentifier + variantIdentifierHex := variantObject.VariantIdentifier - // Map Structure: - // -GenomeIdentifier -> ("Yes"/"No" + ";" + "Yes"/"No") or "Unknown" - variantMap := map[string]string{ - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": variantIdentifier, - "DiseaseName": diseaseName, - } + variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex) + if (err != nil) { return emptyDiseaseInfoObject, err } variantRSID := variantObject.VariantRSID - variantDefectiveBase := variantObject.DefectiveBase variantRSIDsList := []int64{variantRSID} // We add aliases to variantRSIDsList anyAliasesExist, rsidAliasesList, err := locusMetadata.GetRSIDAliases(variantRSID) - if (err != nil) { return nil, nil, err } + if (err != nil) { return emptyDiseaseInfoObject, err } if (anyAliasesExist == true){ variantRSIDsList = append(variantRSIDsList, rsidAliasesList...) } variantRSIDsMap[variantIdentifier] = variantRSIDsList - for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{ + for _, rsID := range variantRSIDsList{ + allRSIDsMap[rsID] = struct{}{} + } + } - genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier - genomeMap := genomeWithMetadataObject.GenomeMap + // Now we create a new map without any rsID aliases + // Each rsID in this map represents a unique locus on the genome + // Each rsID may have aliases, but they are not included in this map + allUniqueRSIDsMap := make(map[int64]struct{}) + + for rsID, _ := range allRSIDsMap{ + + anyAliasesExist, rsidAliasesList, err := locusMetadata.GetRSIDAliases(rsID) + if (err != nil) { return emptyDiseaseInfoObject, err } + if (anyAliasesExist == false){ + allUniqueRSIDsMap[rsID] = struct{}{} + continue + } + + // We see if we already added an alias of this rsID to the map + + checkIfAliasAlreadyExists := func()bool{ + + for _, rsIDAlias := range rsidAliasesList{ + _, exists := allUniqueRSIDsMap[rsIDAlias] + if (exists == true){ + return true + } + } + return false + } + + aliasAlreadyExists := checkIfAliasAlreadyExists() + if (aliasAlreadyExists == true){ + // We already added this alias + continue + } + allUniqueRSIDsMap[rsID] = struct{}{} + } + + // Map Structure: Genome Identifier -> PersonGenomeMonogenicDiseaseInfo + monogenicDiseaseInfoMap := make(map[[16]byte]geneticAnalysis.PersonGenomeMonogenicDiseaseInfo) + + for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{ + + genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier + genomeMap := genomeWithMetadataObject.GenomeMap + + // This stores all variant info for this genome + // Map Structure: Variant Identifier -> PersonGenomeMonogenicDiseaseVariantInfo + variantsInfoMap := make(map[[3]byte]geneticAnalysis.PersonGenomeMonogenicDiseaseVariantInfo) + + for _, variantObject := range variantsList{ + + variantIdentifierHex := variantObject.VariantIdentifier + + variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex) + if (err != nil) { return emptyDiseaseInfoObject, err } + + variantRSID := variantObject.VariantRSID basePairValueFound, base1Value, base2Value, locusIsPhased, err := getGenomeLocusBasePair(genomeMap, variantRSID) - if (err != nil) { return nil, nil, err } + if (err != nil) { return emptyDiseaseInfoObject, err } if (basePairValueFound == false){ - variantMap[genomeIdentifier + "_HasVariant"] = "Unknown" + // This genome does not contain info for this variant + // We skip it continue } + // This genome has at least 1 variant + + variantDefectiveBase := variantObject.DefectiveBase + getBaseIsVariantMutationBool := func(inputBase string)bool{ if (inputBase == variantDefectiveBase){ @@ -1666,152 +1490,122 @@ func getMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenome base1IsDefective := getBaseIsVariantMutationBool(base1Value) base2IsDefective := getBaseIsVariantMutationBool(base2Value) - base1IsDefectiveString := helpers.ConvertBoolToYesOrNoString(base1IsDefective) - base2IsDefectiveString := helpers.ConvertBoolToYesOrNoString(base2IsDefective) + newDiseaseVariantInfoObject := geneticAnalysis.PersonGenomeMonogenicDiseaseVariantInfo{ + Base1HasVariant: base1IsDefective, + Base2HasVariant: base2IsDefective, + LocusIsPhased: locusIsPhased, + } - genomeHasVariantMapValue := base1IsDefectiveString + ";" + base2IsDefectiveString - locusIsPhasedString := helpers.ConvertBoolToYesOrNoString(locusIsPhased) - - variantMap[genomeIdentifier + "_HasVariant"] = genomeHasVariantMapValue - variantMap[genomeIdentifier + "_LocusIsPhased"] = locusIsPhasedString + variantsInfoMap[variantIdentifier] = newDiseaseVariantInfoObject //TODO: Add LocusIsPhased to readGeneticAnalysis package } - variantsMapList = append(variantsMapList, variantMap) - } + // We are done adding variant information for the genome + // Now we determine probability that user will pass a disease variant to offspring, and if the user has the disease - // Now we determine probability that user will pass disease to offspring, and probability that user has disease - // We compute this for each genome + numberOfVariantsTested := len(variantsInfoMap) - diseaseAnalysisMap := make(map[string]string) + if (numberOfVariantsTested == 0){ + // We don't know anything about this genome's disease risk for this disease + // We won't add any information to the map + continue + } - // We will use this list when checking for conflicts - allGenomeIdentifiersList := make([]string, 0, len(inputGenomesWithMetadataList)) + // This stores the number of loci that were tested + // Each locus can have multiple potential variants + numberOfLociTested := 0 - for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{ + // This stores the number of tested loci that are phased + // A higher number means that the results are more potentially more accurate + // It is only more accurate if multiple heterozygous variants on seperate loci exist. + numberOfPhasedLoci := 0 - genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier + for rsID, _ := range allUniqueRSIDsMap{ - allGenomeIdentifiersList = append(allGenomeIdentifiersList, genomeIdentifier) - - numberOfVariantsTested := 0 - - for _, variantMap := range variantsMapList{ - - genomeHasVariantValue, exists := variantMap[genomeIdentifier + "_HasVariant"] - if (exists == false){ - return nil, nil, errors.New("variantMap malformed: Map missing a _HasVariant for genome") + locusValueExists, _, _, locusIsPhased, err := getGenomeLocusBasePair(genomeMap, rsID) + if (err != nil) { return emptyDiseaseInfoObject, err } + if (locusValueExists == false){ + continue } - if (genomeHasVariantValue != "Unknown"){ - numberOfVariantsTested += 1 + numberOfLociTested += 1 + + if (locusIsPhased == true){ + numberOfPhasedLoci += 1 } } - numberOfVariantsTestedString := helpers.ConvertIntToString(numberOfVariantsTested) - - diseaseAnalysisMap[genomeIdentifier + "_NumberOfVariantsTested"] = numberOfVariantsTestedString - // Outputs: - // -bool: Probability is known (will be false if the genome has no tested variants) - // -float64: Probability Person has disease - // -float64: Probability Person will pass a defect (variant) to offspring + // -bool: Person has disease + // -float64: Probability Person will pass a defect (variant) to offspring (0-1) // -error - getPersonDiseaseInfo := func()(bool, float64, float64, error){ - - anyProbabilityKnown := false + getPersonDiseaseInfo := func()(bool, float64, error){ // These variables are used to count the number of defective variants that exist on each chromosome numberOfVariants_Chromosome1 := 0 numberOfVariants_Chromosome2 := 0 numberOfVariants_UnknownChromosome := 0 - // We use this map to keep track of how many mutations exist for each rsid - // This allows us to know if 2 different variant mutations exist for a single RSID + // We use this map to keep track of how many mutations exist for each rsID + // This allows us to know if 2 different variant mutations exist for a single rsID // For example, base1 is a different deleterious mutation than base2 // If this ever happens, we know that the user has the disease, // because both copies of the gene locus are defective. rsidMutationsMap := make(map[int64]int) - for _, variantMap := range variantsMapList{ + for variantIdentifier, variantInfoObject := range variantsInfoMap{ - personHasVariantStatus, exists := variantMap[genomeIdentifier + "_HasVariant"] - if (exists == false){ - return false, 0, 0, errors.New("variantMap malformed: Map missing a _HasVariant for genome.") - } + locusIsPhasedStatus := variantInfoObject.LocusIsPhased - if (personHasVariantStatus == "Unknown"){ - // We don't know if the genome has the variant. Skip to next variant - continue - } + base1HasVariant := variantInfoObject.Base1HasVariant + base2HasVariant := variantInfoObject.Base2HasVariant - anyProbabilityKnown = true - - locusIsPhasedStatus, exists := variantMap[genomeIdentifier + "_LocusIsPhased"] - if (exists == false){ - return false, 0, 0, errors.New("variantMap malformed: Map missing _LocusIsPhased for genome.") - } - - base1HasVariant, base2HasVariant, foundSemicolon := strings.Cut(personHasVariantStatus, ";") - if (foundSemicolon == false){ - return false, 0, 0, errors.New("variantMap is malformed: Contains invalid personHasVariantValue: " + personHasVariantStatus) - } - - if (base1HasVariant == "No" && base2HasVariant == "No"){ + if (base1HasVariant == false && base2HasVariant == false){ // Neither chromosome contains the variant mutation. continue } - if (base1HasVariant == "Yes" && base2HasVariant == "Yes"){ + if (base1HasVariant == true && base2HasVariant == true){ // Both chromosomes contain the same variant mutation. // Person has the disease. // Person will definitely pass disease variant to offspring. - return true, 1, 1, nil + return true, 1, nil } // We know that this variant exists on 1 of the bases, but not both. - variantIdentifier, exists := variantMap["VariantIdentifier"] - if (exists == false){ - return false, 0, 0, errors.New("VariantMap missing VariantIdentifier.") - } - variantRSIDsList, exists := variantRSIDsMap[variantIdentifier] if (exists == false){ - return false, 0, 0, errors.New("variantRSIDMap missing variantIdentifier.") + return false, 0, errors.New("variantRSIDsMap missing variantIdentifier.") } - for _, rsid := range variantRSIDsList{ - rsidMutationsMap[rsid] += 1 + for _, rsID := range variantRSIDsList{ + rsidMutationsMap[rsID] += 1 } - if (locusIsPhasedStatus == "Yes"){ + if (locusIsPhasedStatus == true){ - if (base1HasVariant == "Yes"){ + if (base1HasVariant == true){ numberOfVariants_Chromosome1 += 1 } - if (base2HasVariant == "Yes"){ + if (base2HasVariant == true){ numberOfVariants_Chromosome2 += 1 } } else { - if (base1HasVariant == "Yes" || base2HasVariant == "Yes"){ + if (base1HasVariant == true || base2HasVariant == true){ numberOfVariants_UnknownChromosome += 1 } } } - if (anyProbabilityKnown == false){ - return false, 0, 0, nil - } - totalNumberOfVariants := numberOfVariants_Chromosome1 + numberOfVariants_Chromosome2 + numberOfVariants_UnknownChromosome if (totalNumberOfVariants == 0){ // Person does not have any disease variants. // They do not have the disease, and have no chance of passing a disease variant - return true, 0, 0, nil + return false, 0, nil } // Now we check to see if there are any loci which have 2 different variants, one for each base @@ -1821,7 +1615,7 @@ func getMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenome if (numberOfMutations >= 2){ // Person has 2 mutations on the same location // They must have the disease, and will definitely pass a variant to their offspring - return true, 1, 1, nil + return true, 1, nil } } @@ -1829,12 +1623,12 @@ func getMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenome // All variant mutations are heterozygous, meaning the other chromosome base is healthy // Probability is expressed as a float between 0 - 1 - getProbabilityPersonHasDisease := func()float64{ + getPersonHasDiseaseBool := func()bool{ if (dominantOrRecessive == "Dominant"){ // Only 1 variant is needed for the person to have the disease // We know they have at least 1 variant - return 1 + return true } // dominantOrRecessive == "Recessive" @@ -1843,63 +1637,48 @@ func getMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenome // There is only 1 variant in total. // This single variant cannot exist on both chromosomes. // The person does not have the disease - return 0 + return false } if (numberOfVariants_Chromosome1 >= 1 && numberOfVariants_Chromosome2 >= 1){ // We know there is at least 1 variant mutation on each chromosome // Therefore, the person has the disease - return 1 + return true } if (numberOfVariants_UnknownChromosome == 0){ // We know that variants do not exist on both chromosomes, only on 1. // Thus, the person does not have the disease - return 0 + return false } if (numberOfVariants_Chromosome1 == 0 && numberOfVariants_Chromosome2 == 0){ // All variants have an unknown phase, and we know there are multiple of them. - // The probability the person does not have the disease is the probability that all mutations are on the same chromosome - // We calculate the probability that all of the mutations are on the same chromosome - // If they are all on the same chromosome, the person does not have the disease - // If at least 1 variant exists on both chromosomes, the person has the disease + // The person does not have the disease if all mutations are on the same chromosome + // The person does have the disease if at least 1 mutation exists on each chromosome + // Either way, we don't know enough to say if the person has the disease + // We will report that they do not, because their genome does not conclusively say that they do + // This is why phased genomes are useful and provide a more accurate reading + // TODO: Explain this to the user in the GUI + // We must explain that unphased genomes will not detect disease sometimes - // We calculate the probability that all variants are all on the same chromosome - // Probability of n variants existing on the same chromosome = 1/(2^n) - // P(X) = Probability all variants existing on chromosome 1 = 1/(2^n) - // P(Y) = Probability all variants existing on chromosome 2 = 1/(2^n) - // P(A U B) = P(A) + P(B) (If A and B are mutually exclusive) - // P(X) and P(Y) are mutually exclusive - // Probability of n variants existing on either chromosome 1 or 2 = P(X) + P(Y) - // Probability person has disease = !P(X U Y) - - probabilityAllVariantsAreOnOneChromosome := 1/(math.Pow(2, float64(numberOfVariants_UnknownChromosome))) - - probabilityPersonHasDisease := 1 - (probabilityAllVariantsAreOnOneChromosome * 2) - - return probabilityPersonHasDisease + return false } // We know that there is at least 1 variant whose phase is known // We know that there are no variants whose phase is known which exist on both chromosomes // We know there are at least some variants whose phase is unknown - // The probability that the person has the disease is - // the probability that the unknown-phase variants exist on the same chromosome as the ones which we know exist do - // This probability is 50% for each unknown-phase variant + // The person has the disease if the unknown-phase variants exist on the same chromosome as the ones which we know exist do + // This is the same as the last if statement, we report false even though they may actually have the disease - probabilityAllVariantsAreOnOneChromosome := 1/(math.Pow(2, float64(numberOfVariants_UnknownChromosome))) - - probabilityPersonHasDisease := 1 - probabilityAllVariantsAreOnOneChromosome - - return probabilityPersonHasDisease + return false } - probabilityPersonHasDisease := getProbabilityPersonHasDisease() + personHasDiseaseBool := getPersonHasDiseaseBool() // We know all variants are heterozygous @@ -1908,104 +1687,136 @@ func getMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenome probabilityPersonWillPassAnyVariant := 1 - (1/(math.Pow(2, float64(totalNumberOfVariants)))) - return true, probabilityPersonHasDisease, probabilityPersonWillPassAnyVariant, nil + return personHasDiseaseBool, probabilityPersonWillPassAnyVariant, nil } - probabilityKnown, probabilityPersonHasDisease, probabilityPersonWillPassAnyVariant, err := getPersonDiseaseInfo() - if (err != nil) { return nil, nil, err } - if (probabilityKnown == false){ - diseaseAnalysisMap[genomeIdentifier + "_ProbabilityOfHavingDisease"] = "Unknown" - diseaseAnalysisMap[genomeIdentifier + "_ProbabilityOfPassingADiseaseVariant"] = "Unknown" - continue + personHasDisease, probabilityPersonWillPassAnyVariant, err := getPersonDiseaseInfo() + if (err != nil) { return emptyDiseaseInfoObject, err } + + percentageProbabilityPersonWillPassADiseaseVariant := int(probabilityPersonWillPassAnyVariant * 100) + + diseaseAnalysisObject := geneticAnalysis.PersonGenomeMonogenicDiseaseInfo{ + PersonHasDisease: personHasDisease, + NumberOfVariantsTested: numberOfVariantsTested, + NumberOfLociTested: numberOfLociTested, + NumberOfPhasedLoci: numberOfPhasedLoci, + ProbabilityOfPassingADiseaseVariant: percentageProbabilityPersonWillPassADiseaseVariant, + VariantsInfoMap: variantsInfoMap, } - percentageProbabilityPersonHasDisease := probabilityPersonHasDisease * 100 - percentageProbabilityPersonWillPassADiseaseVariant := probabilityPersonWillPassAnyVariant * 100 - - probabilityOfHavingDiseaseString := helpers.ConvertFloat64ToStringRounded(percentageProbabilityPersonHasDisease, 0) - probabilityOfPassingADiseaseVariantString := helpers.ConvertFloat64ToStringRounded(percentageProbabilityPersonWillPassADiseaseVariant, 0) - - diseaseAnalysisMap[genomeIdentifier + "_ProbabilityOfHavingDisease"] = probabilityOfHavingDiseaseString - diseaseAnalysisMap[genomeIdentifier + "_ProbabilityOfPassingADiseaseVariant"] = probabilityOfPassingADiseaseVariantString + monogenicDiseaseInfoMap[genomeIdentifier] = diseaseAnalysisObject } - if (len(allGenomeIdentifiersList) == 1){ - // We do not need to check for conflicts + personMonogenicDiseaseInfoObject := geneticAnalysis.PersonMonogenicDiseaseInfo{ + MonogenicDiseaseInfoMap: monogenicDiseaseInfoMap, + } + + if (len(monogenicDiseaseInfoMap) <= 1){ + // We do not need to check for conflicts, there is only <=1 genome with disease information // Nothing left to do. Analysis is complete. - return diseaseAnalysisMap, variantsMapList, nil + return personMonogenicDiseaseInfoObject, nil } // We check for conflicts getConflictExistsBool := func()(bool, error){ - // We start with disease analysis map + firstItemReached := false - probabilityOfHavingDisease := "" - probabilityOfPassingAVariant := "" + personHasDisease := false + probabilityOfPassingAVariant := 0 - for index, genomeIdentifier := range allGenomeIdentifiersList{ + for _, currentGenomeDiseaseAnalysisObject := range monogenicDiseaseInfoMap{ - currentProbabilityOfHavingDisease, exists := diseaseAnalysisMap[genomeIdentifier + "_ProbabilityOfHavingDisease"] - if (exists == false){ - return false, errors.New("Cannot create analysis: diseaseAnalysisMap missing _ProbabilityOfHavingDisease") - } + currentGenomePersonHasDisease := currentGenomeDiseaseAnalysisObject.PersonHasDisease + currentGenomeProbabilityOfPassingAVariant := currentGenomeDiseaseAnalysisObject.ProbabilityOfPassingADiseaseVariant - currentProbabilityOfPassingAVariant, exists := diseaseAnalysisMap[genomeIdentifier + "_ProbabilityOfPassingADiseaseVariant"] - if (exists == false){ - return false, errors.New("Cannot create analysis: diseaseAnalysisMap missing _ProbabilityOfPassingADiseaseVariant") - } - - if (index == 0){ - probabilityOfHavingDisease = currentProbabilityOfHavingDisease - probabilityOfPassingAVariant = currentProbabilityOfPassingAVariant + if (firstItemReached == false){ + personHasDisease = currentGenomePersonHasDisease + probabilityOfPassingAVariant = currentGenomeProbabilityOfPassingAVariant + firstItemReached = true continue } - if (currentProbabilityOfHavingDisease != probabilityOfHavingDisease){ + if (currentGenomePersonHasDisease != personHasDisease){ return true, nil } - if (currentProbabilityOfPassingAVariant != probabilityOfPassingAVariant){ + if (currentGenomeProbabilityOfPassingAVariant != probabilityOfPassingAVariant){ return true, nil } } // Now we test variants for conflicts + // We are only doing this to see if there are variants which one genome has and another doesn't + // For example, the analysis results say that you have a 50% chance of passing a variant for both genomes, but + // they have detected a different variant for each genome. + // This means that your real risk of passing a variant may actually be higher, and you are more likely to have the disease too - for _, variantMap := range variantsMapList{ + for variantIdentifier, _ := range variantRSIDsMap{ - // Each variant value is either "Yes;No", "No;Yes", "No;No", or "Yes;Yes" + // Each variant base pair is either true/false, true/true, false/false, false/true - // Two different genomes have "Yes;No" and "No;Yes", it will not count as a conflict - // If the locus is unphased, then there is no difference between "Yes;No" and "No;Yes" + // Two different genomes have true/false and false/true, it will not count as a conflict + // If the locus is unphased, then there is no difference between true/false and false/true // If the locus is phased, then this flip is only meaningful if it effects the probability of disease/passing a variant // We already checked those probabilities for conflicts earlier // Therefore, any flip is not considered a conflict + // We only care about conflicts where 1 genome says you have a variant and the other says you don't, or + // one says you have only 1 mutation and the other says you have 2 at that location - genomeHasVariantStatus := "" + firstItemReached := false - for index, genomeIdentifier := range allGenomeIdentifiersList{ + base1HasVariant := false + base2HasVariant := false - currentGenomeHasVariantStatus, exists := variantMap[genomeIdentifier + "_HasVariant"] - if (exists == false){ - return false, errors.New("variantMap missing genomeIdentifier key when checking for conflicts") + for _, currentGenomeDiseaseAnalysisObject := range monogenicDiseaseInfoMap{ + + // Outputs: + // -bool: Bases are known + // -bool: Base1 Has Variant + // -bool: Base2 Has Variant + getGenomeBasesInfo := func()(bool, bool, bool){ + + variantsInfoMap := currentGenomeDiseaseAnalysisObject.VariantsInfoMap + + variantInfoObject, exists := variantsInfoMap[variantIdentifier] + if (exists == false){ + return false, false, false + } + + currentBase1HasVariant := variantInfoObject.Base1HasVariant + currentBase2HasVariant := variantInfoObject.Base2HasVariant + + return true, currentBase1HasVariant, currentBase2HasVariant } - if (index == 0){ - genomeHasVariantStatus = currentGenomeHasVariantStatus + basesAreKnown, currentBase1HasVariant, currentBase2HasVariant := getGenomeBasesInfo() + if (basesAreKnown == false){ + if (firstItemReached == true){ + // A previous genome has information for this variant, and the current one does not + return true, nil + } + continue + } + + if (firstItemReached == false){ + base1HasVariant = currentBase1HasVariant + base2HasVariant = currentBase2HasVariant + firstItemReached = true continue } - if (currentGenomeHasVariantStatus != genomeHasVariantStatus){ - - if (currentGenomeHasVariantStatus == "Yes;No" && genomeHasVariantStatus == "No;Yes"){ - continue - } - if (currentGenomeHasVariantStatus == "No;Yes" && genomeHasVariantStatus == "Yes;No"){ - continue - } - return true, nil + if (base1HasVariant == currentBase1HasVariant && base2HasVariant == currentBase2HasVariant){ + // No conflict exists + continue } + if (base1HasVariant == currentBase2HasVariant && base2HasVariant == currentBase1HasVariant){ + // We don't count this as a conflict + continue + } + + // A conflict exists + return true, nil } } @@ -2013,222 +1824,217 @@ func getMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenome } conflictExists, err := getConflictExistsBool() - if (err != nil) { return nil, nil, err } + if (err != nil) { return emptyDiseaseInfoObject, err } - conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) + personMonogenicDiseaseInfoObject.ConflictExists = conflictExists - diseaseAnalysisMap["ConflictExists"] = conflictExistsString - - return diseaseAnalysisMap, variantsMapList, nil + return personMonogenicDiseaseInfoObject, nil } - //Outputs: -// -map[string]string: Polygenic Disease analysis map (contains weight/probability for each genomeIdentifier and number of loci tested) -// -[]map[string]string: Loci map list (contains locus info for each genomeIdentifier) +// -geneticAnalysis.PersonPolygenicDiseaseInfo // -error -func getPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, diseaseObject polygenicDiseases.PolygenicDisease)(map[string]string, []map[string]string, error){ +func getPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, diseaseObject polygenicDiseases.PolygenicDisease)(geneticAnalysis.PersonPolygenicDiseaseInfo, error){ + + // We use this when returning errors + emptyDiseaseInfoObject := geneticAnalysis.PersonPolygenicDiseaseInfo{} - diseaseName := diseaseObject.DiseaseName diseaseLociList := diseaseObject.LociList - lociMapList := make([]map[string]string, 0, len(diseaseLociList)) + // This map stores the polygenic disease for each of the person's genomes + // Map Structure: Genome Identifier -> PersonGenomePolygenicDiseaseInfo + personPolygenicDiseaseInfoMap := make(map[[16]byte]geneticAnalysis.PersonGenomePolygenicDiseaseInfo) - // Map Structure: Locus identifier -> Disease Locus object - locusObjectsMap := make(map[string]polygenicDiseases.DiseaseLocus) - - for _, locusObject := range diseaseLociList{ - - locusIdentifier := locusObject.LocusIdentifier - - locusObjectsMap[locusIdentifier] = locusObject - - locusMap := map[string]string{ - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": locusIdentifier, - "DiseaseName": diseaseName, - } - - locusRSID := locusObject.LocusRSID - locusRiskWeightsMap := locusObject.RiskWeightsMap - locusOddsRatiosMap := locusObject.OddsRatiosMap - - for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{ - - genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier - genomeMap := genomeWithMetadataObject.GenomeMap - - //Outputs: - // -bool: Disease locus info is known - // -string: Locus Bases - // -int: Genome disease locus risk weight - // -bool: Genome disease locus odds ratio known - // -float64: Genome disease locus odds ratio - // -error - getGenomeDiseaseLocusInfo := func()(bool, string, int, bool, float64, error){ - - basePairValueFound, base1Value, base2Value, _, err := getGenomeLocusBasePair(genomeMap, locusRSID) - if (err != nil) { return false, "", 0, false, 0, err } - if (basePairValueFound == false){ - return false, "", 0, false, 0, nil - } - - locusBasePair := base1Value + ";" + base2Value - - riskWeight, exists := locusRiskWeightsMap[locusBasePair] - if (exists == false){ - // This is an unknown base combination - // We will treat it as a 0 risk weight - return true, locusBasePair, 0, true, 1, nil - } - - if (riskWeight == 0){ - return true, locusBasePair, 0, true, 1, nil - } - - oddsRatio, exists := locusOddsRatiosMap[locusBasePair] - if (exists == false){ - return true, locusBasePair, 0, false, 0, nil - } - - return true, locusBasePair, riskWeight, true, oddsRatio, nil - } - - diseaseLocusInfoKnown, locusBasePair, locusRiskWeight, locusOddsRatioKnown, locusOddsRatio, err := getGenomeDiseaseLocusInfo() - if (err != nil) { return nil, nil, err } - - if (diseaseLocusInfoKnown == false){ - - locusMap[genomeIdentifier + "_LocusBasePair"] = "Unknown" - locusMap[genomeIdentifier + "_RiskWeight"] = "Unknown" - locusMap[genomeIdentifier + "_OddsRatio"] = "Unknown" - } else { - - locusMap[genomeIdentifier + "_LocusBasePair"] = locusBasePair - - riskWeightString := helpers.ConvertIntToString(locusRiskWeight) - - locusMap[genomeIdentifier + "_RiskWeight"] = riskWeightString - - if (locusOddsRatioKnown == false){ - locusMap[genomeIdentifier + "_OddsRatio"] = "Unknown" - } else { - locusOddsRatioString := helpers.ConvertFloat64ToString(locusOddsRatio) - locusMap[genomeIdentifier + "_OddsRatio"] = locusOddsRatioString - } - } - } - - lociMapList = append(lociMapList, locusMap) - } - - // Now we construct polygenic disease probability map for each genome - - diseaseAnalysisMap := make(map[string]string) - - // We use this list to check for conflicts later - allGenomeIdentifiersList := make([]string, 0, len(inputGenomesWithMetadataList)) + // We construct polygenic disease probability info for each genome for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{ genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier + genomeMap := genomeWithMetadataObject.GenomeMap - allGenomeIdentifiersList = append(allGenomeIdentifiersList, genomeIdentifier) - - numberOfLociTested := 0 + // Map Structure: Locus Identifier -> PersonGenomePolygenicDiseaseLocusInfo + genomeLociInfoMap := make(map[[3]byte]geneticAnalysis.PersonGenomePolygenicDiseaseLocusInfo) minimumPossibleRiskWeightSum := 0 maximumPossibleRiskWeightSum := 0 summedDiseaseRiskWeight := 0 - - for _, locusMap := range lociMapList{ - genomeRiskWeight, exists := locusMap[genomeIdentifier + "_RiskWeight"] - if (exists == false){ - return nil, nil, errors.New("locusMap malformed: Map missing a genomeIdentifier riskWeight") - } - - if (genomeRiskWeight == "Unknown"){ - // The genome does not have this locus - // We cannot test for risk weight + for _, locusObject := range diseaseLociList{ + + locusIdentifierHex := locusObject.LocusIdentifier + + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return emptyDiseaseInfoObject, err } + + locusRSID := locusObject.LocusRSID + locusRiskWeightsMap := locusObject.RiskWeightsMap + locusOddsRatiosMap := locusObject.OddsRatiosMap + locusMinimumWeight := locusObject.MinimumRiskWeight + locusMaximumWeight := locusObject.MaximumRiskWeight + + basePairValueFound, locusBase1Value, locusBase2Value, _, err := getGenomeLocusBasePair(genomeMap, locusRSID) + if (err != nil) { return emptyDiseaseInfoObject, err } + if (basePairValueFound == false){ continue } - numberOfLociTested += 1 + //Outputs: + // -int: Genome disease locus risk weight + // -bool: Genome disease locus odds ratio known + // -float64: Genome disease locus odds ratio + // -error + getGenomeDiseaseLocusRiskInfo := func()(int, bool, float64, error){ - locusIdentifier, exists := locusMap["LocusIdentifier"] - if (exists == false) { - return nil, nil, errors.New("locusMap malformed: Map missing LocusIdentifier") + locusBasePairJoined := locusBase1Value + ";" + locusBase2Value + + riskWeight, exists := locusRiskWeightsMap[locusBasePairJoined] + if (exists == false){ + // This is an unknown base combination + // We will treat it as a 0 risk weight + return 0, true, 1, nil + } + + if (riskWeight == 0){ + return 0, true, 1, nil + } + + oddsRatio, exists := locusOddsRatiosMap[locusBasePairJoined] + if (exists == false){ + return riskWeight, false, 0, nil + } + + return riskWeight, true, oddsRatio, nil } - locusObject, exists := locusObjectsMap[locusIdentifier] - if (exists == false){ - return nil, nil, errors.New("LocusObjectsMap missing locus: " + locusIdentifier) + locusRiskWeight, locusOddsRatioIsKnown, locusOddsRatio, err := getGenomeDiseaseLocusRiskInfo() + if (err != nil) { return emptyDiseaseInfoObject, err } + + newLocusInfoObject := geneticAnalysis.PersonGenomePolygenicDiseaseLocusInfo{ + LocusBase1: locusBase1Value, + LocusBase2: locusBase2Value, + RiskWeight: locusRiskWeight, + OddsRatioIsKnown: locusOddsRatioIsKnown, } - locusMinimumWeight := locusObject.MinimumRiskWeight - locusMaximumWeight := locusObject.MaximumRiskWeight + if (locusOddsRatioIsKnown == true){ + newLocusInfoObject.OddsRatio = locusOddsRatio + } + + genomeLociInfoMap[locusIdentifier] = newLocusInfoObject minimumPossibleRiskWeightSum += locusMinimumWeight maximumPossibleRiskWeightSum += locusMaximumWeight - genomeRiskWeightInt, err := helpers.ConvertStringToInt(genomeRiskWeight) - if (err != nil) { - return nil, nil, errors.New("Locus map malformed: Contains invalid _RiskWeight: " + genomeRiskWeight) - } - - summedDiseaseRiskWeight += genomeRiskWeightInt + summedDiseaseRiskWeight += locusRiskWeight } - numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) - - diseaseAnalysisMap[genomeIdentifier + "_NumberOfLociTested"] = numberOfLociTestedString + numberOfLociTested := len(genomeLociInfoMap) if (numberOfLociTested == 0){ - diseaseAnalysisMap[genomeIdentifier + "_RiskScore"] = "Unknown" - + // We have no information about this disease for this genome continue } diseaseRiskScore, err := helpers.ScaleNumberProportionally(true, summedDiseaseRiskWeight, minimumPossibleRiskWeightSum, maximumPossibleRiskWeightSum, 0, 10) - if (err != nil) { return nil, nil, err } + if (err != nil) { return emptyDiseaseInfoObject, err } - diseaseRiskScoreString := helpers.ConvertIntToString(diseaseRiskScore) + newDiseaseInfoObject := geneticAnalysis.PersonGenomePolygenicDiseaseInfo{ + NumberOfLociTested: numberOfLociTested, + RiskScore: diseaseRiskScore, + LociInfoMap: genomeLociInfoMap, + } - diseaseAnalysisMap[genomeIdentifier + "_RiskScore"] = diseaseRiskScoreString + personPolygenicDiseaseInfoMap[genomeIdentifier] = newDiseaseInfoObject } - if (len(allGenomeIdentifiersList) == 1){ - // We do not need to check for conflicts + newPersonPolygenicDiseaseInfoObject := geneticAnalysis.PersonPolygenicDiseaseInfo{ + PolygenicDiseaseInfoMap: personPolygenicDiseaseInfoMap, + } + + if (len(personPolygenicDiseaseInfoMap) <= 1){ + // We do not need to check for conflicts, there is only <=1 genome with disease information // Nothing left to do. Analysis is complete. - return diseaseAnalysisMap, lociMapList, nil + return newPersonPolygenicDiseaseInfoObject, nil } - // We check for conflicts + // We check for conflicts between the different genome's results getConflictExistsBool := func()(bool, error){ - for _, locusMap := range lociMapList{ + // First we check to see if any of the genomes have different risk scores or NumberOfLociTested - locusBasePair := "" + genomeRiskScore := 0 + genomeNumberOfLociTested := 0 - for index, genomeIdentifier := range allGenomeIdentifiersList{ + firstItemReached := false - currentLocusBasePair, exists := locusMap[genomeIdentifier + "_LocusBasePair"] + for _, personGenomeDiseaseInfoObject := range personPolygenicDiseaseInfoMap{ + + currentGenomeRiskScore := personGenomeDiseaseInfoObject.RiskScore + currentGenomeNumberOfLociTested := personGenomeDiseaseInfoObject.NumberOfLociTested + + if (firstItemReached == false){ + genomeRiskScore = currentGenomeRiskScore + genomeNumberOfLociTested = currentGenomeNumberOfLociTested + firstItemReached = true + continue + } + + if (genomeRiskScore != currentGenomeRiskScore){ + return true, nil + } + if (genomeNumberOfLociTested != currentGenomeNumberOfLociTested){ + return true, nil + } + } + + // Now we check for conflicts between the different locus values + // We consider a conflict any time the same locus has different weights/odds ratios + // We don't care if the loci have different base pair values, so long as those base pairs have the same risk weights/odds ratios + + for _, locusObject := range diseaseLociList{ + + locusIdentifierHex := locusObject.LocusIdentifier + + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return false, err } + + locusRiskWeight := 0 + locusOddsRatio := float64(0) + + firstItemReached := false + + for _, personGenomeDiseaseInfoObject := range personPolygenicDiseaseInfoMap{ + + genomeLociInfoMap := personGenomeDiseaseInfoObject.LociInfoMap + + genomeLocusObject, exists := genomeLociInfoMap[locusIdentifier] if (exists == false){ - return false, errors.New("Cannot find _LocusBasePair key when searching for conflicts") - } - - if (index == 0){ - - locusBasePair = currentLocusBasePair + if (firstItemReached == true){ + // A previous genome has information for this locus, and the current one does not + return true, nil + } continue } - if (currentLocusBasePair != locusBasePair){ - return true, nil + + genomeLocusRiskWeight := genomeLocusObject.RiskWeight + genomeLocusOddsRatio := genomeLocusObject.OddsRatio + + if (firstItemReached == false){ + locusRiskWeight = genomeLocusRiskWeight + locusOddsRatio = genomeLocusOddsRatio + firstItemReached = true + continue } + if (locusRiskWeight == genomeLocusRiskWeight && locusOddsRatio == genomeLocusOddsRatio){ + // No conflict exists for this locus on the genomes we have already checked + continue + } + + // Conflict exists + return true, nil } } @@ -2236,192 +2042,132 @@ func getPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRawGenome } conflictExists, err := getConflictExistsBool() - if (err != nil) { return nil, nil, err } + if (err != nil) { return emptyDiseaseInfoObject, err } - conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) + newPersonPolygenicDiseaseInfoObject.ConflictExists = conflictExists - diseaseAnalysisMap["ConflictExists"] = conflictExistsString - - return diseaseAnalysisMap, lociMapList, nil + return newPersonPolygenicDiseaseInfoObject, nil } //Outputs: -// -map[string]string: Trait analysis map (contains weight/probability for each genomeIdentifier and number of rules tested) -// -[]map[string]string: Loci map list (contains locus info for each genomeIdentifier) +// -geneticAnalysis.PersonTraitInfo: Trait analysis object // -error -func getTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, traitObject traits.Trait)(map[string]string, []map[string]string, error){ +func getPersonTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, traitObject traits.Trait)(geneticAnalysis.PersonTraitInfo, error){ + + // We use this when returning errors + emptyPersonTraitInfo := geneticAnalysis.PersonTraitInfo{} - traitName := traitObject.TraitName traitLociList := traitObject.LociList traitRulesList := traitObject.RulesList - // We first add loci values to trait analysis map - - traitAnalysisMap := make(map[string]string) - - // We use this list to check for conflicts later - allGenomeIdentifiersList := make([]string, 0, len(inputGenomesWithMetadataList)) + // Map Structure: Genome Identifier -> PersonGenomeTraitInfo + newPersonTraitInfoMap := make(map[[16]byte]geneticAnalysis.PersonGenomeTraitInfo) for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{ genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier - - allGenomeIdentifiersList = append(allGenomeIdentifiersList, genomeIdentifier) - genomeMap := genomeWithMetadataObject.GenomeMap + // This map contains the locus values for the genome + // If an locus's entry doesn't exist, its value is unknown + // Map Structure: Locus rsID -> Locus Value + genomeLocusValuesMap := make(map[int64]locusValue.LocusValue) + for _, locusRSID := range traitLociList{ - locusRSIDString := helpers.ConvertInt64ToString(locusRSID) - locusBasePairKnown, locusBase1, locusBase2, locusIsPhased, err := getGenomeLocusBasePair(genomeMap, locusRSID) - if (err != nil) { return nil, nil, err } + if (err != nil) { return emptyPersonTraitInfo, err } if (locusBasePairKnown == false){ - - traitAnalysisMap[genomeIdentifier + "_LocusValue_rs" + locusRSIDString] = "Unknown" - } else { - - locusBasePair := locusBase1 + ";" + locusBase2 - - locusIsPhasedString := helpers.ConvertBoolToYesOrNoString(locusIsPhased) - - traitAnalysisMap[genomeIdentifier + "_LocusValue_rs" + locusRSIDString] = locusBasePair - traitAnalysisMap[genomeIdentifier + "_LocusIsPhased_rs" + locusRSIDString] = locusIsPhasedString - } - } - } - - // This map stores the TraitRule maps - rulesMapList := make([]map[string]string, 0, len(traitRulesList)) - - // Map Structure: Rule identifier -> Rule object - ruleObjectsMap := make(map[string]traits.TraitRule) - - if (len(traitRulesList) != 0){ - - // This trait contains at least 1 rule - - for _, ruleObject := range traitRulesList{ - - ruleIdentifier := ruleObject.RuleIdentifier - - ruleObjectsMap[ruleIdentifier] = ruleObject - - ruleMap := map[string]string{ - "ItemType": "TraitRule", - "RuleIdentifier": ruleIdentifier, - "TraitName": traitName, + continue } - ruleLociList := ruleObject.LociList - - for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{ - - genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier - genomeMap := genomeWithMetadataObject.GenomeMap - - // We add locus base pairs to ruleMap - // We also check to see if genome passes all rule loci - // We only consider a rule Known if the genome either passes all loci, or fails to pass 1 locus - - allRuleLociKnown := true - anyLocusFailureKnown := false //This is true if at least 1 locus is known to not pass - - for _, locusObject := range ruleLociList{ - - locusIdentifier := locusObject.LocusIdentifier - locusRSID := locusObject.LocusRSID - - locusBasePairKnown, locusBase1, locusBase2, _, err := getGenomeLocusBasePair(genomeMap, locusRSID) - if (err != nil) { return nil, nil, err } - if (locusBasePairKnown == false){ - - ruleMap[genomeIdentifier + "_RuleLocusBasePair_" + locusIdentifier] = "Unknown" - - // The genome has failed to pass a single rule locus, thus, the rule is not passed - // We still continue so we can add all locus base pairs to the ruleMap - allRuleLociKnown = false - continue - } - - locusBasePair := locusBase1 + ";" + locusBase2 - - ruleMap[genomeIdentifier + "_RuleLocusBasePair_" + locusIdentifier] = locusBasePair - - if (anyLocusFailureKnown == true){ - // We don't have to check if the rule locus is passed, because we already know the rule is not passed - continue - } - - locusBasePairsList := locusObject.BasePairsList - - genomePassesRuleLocus := slices.Contains(locusBasePairsList, locusBasePair) - if (genomePassesRuleLocus == false){ - // We know the rule is not passed - // We still continue so we can add all locus base pairs to the ruleMap - anyLocusFailureKnown = true - continue - } - } - - if (anyLocusFailureKnown == true){ - - ruleMap[genomeIdentifier + "_PassesRule"] = "No" - - } else if (allRuleLociKnown == false){ - - ruleMap[genomeIdentifier + "_PassesRule"] = "Unknown" - } else { - - ruleMap[genomeIdentifier + "_PassesRule"] = "Yes" - } + newLocusValue := locusValue.LocusValue{ + LocusIsPhased: locusIsPhased, + Base1Value: locusBase1, + Base2Value: locusBase2, } - rulesMapList = append(rulesMapList, ruleMap) + genomeLocusValuesMap[locusRSID] = newLocusValue } - // Now we construct trait outcome scores map for each genome + // This map contains the trait outcome scores for the genome + // Map Structure: Outcome Name -> Score + // Example: "Intolerant" -> 5 + traitOutcomeScoresMap := make(map[string]int) - traitOutcomesList := traitObject.OutcomesList + // Map Structure: Rule Identifier -> Genome Passes rule (true if the genome passes the rule) + personPassesRulesMap := make(map[[3]byte]bool) - for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{ + if (len(traitRulesList) != 0){ - genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier + // At least 1 rule exists for this trait - // A rule is considered tested if at least 1 locus within the rule is known to fail, or all loci within the rule pass - numberOfRulesTested := 0 + for _, ruleObject := range traitRulesList{ - // Map Structure: Trait outcome -> Number of points - traitOutcomeScoresMap := make(map[string]int) + ruleIdentifierHex := ruleObject.RuleIdentifier - for _, ruleMap := range rulesMapList{ + ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex) + if (err != nil) { return emptyPersonTraitInfo, err } - genomePassesRule, exists := ruleMap[genomeIdentifier + "_PassesRule"] - if (exists == false){ - return nil, nil, errors.New("ruleMap malformed: Map missing a genomeIdentifier _PassesRule") + ruleLociList := ruleObject.LociList + + // Outputs: + // -bool: Genome passes rule is known + // -bool: Genome passes rule + // -error + getGenomePassesRuleBool := func()(bool, bool, error){ + + // We check to see if genome passes all rule loci + // We consider a rule Known if the genome either passes all loci, or fails to pass 1 locus + // We consider a rule Unknown if any loci are unknown, and all loci which are known pass the rule + + anyLocusIsUnknown := false + + for _, locusObject := range ruleLociList{ + + locusRSID := locusObject.LocusRSID + + locusBasePairKnown, locusBase1, locusBase2, _, err := getGenomeLocusBasePair(genomeMap, locusRSID) + if (err != nil) { return false, false, err } + if (locusBasePairKnown == false){ + anyLocusIsUnknown = true + continue + } + + locusBasePairJoined := locusBase1 + ";" + locusBase2 + + locusBasePairsList := locusObject.BasePairsList + + genomePassesRuleLocus := slices.Contains(locusBasePairsList, locusBasePairJoined) + if (genomePassesRuleLocus == false){ + // The genome has failed to pass a single rule locus, thus, the rule is not passed + return true, false, nil + } + } + + if (anyLocusIsUnknown == true){ + // The rule is not passed, but it's status is unknown + // There were no rules which were known not to pass + return false, false, nil + } + + // All rules were passed + + return true, true, nil } - if (genomePassesRule == "Unknown"){ + genomePassesRuleIsKnown, genomePassesRule, err := getGenomePassesRuleBool() + if (err != nil) { return emptyPersonTraitInfo, err } + if (genomePassesRuleIsKnown == false){ continue } - numberOfRulesTested += 1 + personPassesRulesMap[ruleIdentifier] = genomePassesRule - if (genomePassesRule == "No"){ - continue - } - - ruleIdentifier, exists := ruleMap["RuleIdentifier"] - if (exists == false) { - return nil, nil, errors.New("ruleMap malformed: Map missing RuleIdentifier") - } - - ruleObject, exists := ruleObjectsMap[ruleIdentifier] - if (exists == false){ - return nil, nil, errors.New("RuleObjectsMap missing rule: " + ruleIdentifier) - } + // The rule has been passed by this genome + // We add the outcome points for the rule to the traitOutcomeScoresMap ruleOutcomePointsMap := ruleObject.OutcomePointsMap @@ -2430,47 +2176,40 @@ func getTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWit traitOutcomeScoresMap[traitOutcome] += pointsChange } } + } - numberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested) + traitOutcomesList := traitObject.OutcomesList - traitAnalysisMap[genomeIdentifier + "_NumberOfRulesTested"] = numberOfRulesTestedString + // We add all outcomes for which there were no points - if (numberOfRulesTested == 0){ - // No rules were tested. The number of points in each outcome are all unknown. - continue - } + for _, traitOutcome := range traitOutcomesList{ - // We add all outcomes for which there were no points - - for _, traitOutcome := range traitOutcomesList{ - - _, exists := traitOutcomeScoresMap[traitOutcome] - if (exists == false){ - traitOutcomeScoresMap[traitOutcome] = 0 - } - } - - for traitOutcome, outcomeScore := range traitOutcomeScoresMap{ - - outcomeScoreString := helpers.ConvertIntToString(outcomeScore) - - traitAnalysisMap[genomeIdentifier + "_OutcomeScore_" + traitOutcome] = outcomeScoreString + _, exists := traitOutcomeScoresMap[traitOutcome] + if (exists == false){ + traitOutcomeScoresMap[traitOutcome] = 0 } } - } else { - // No rules exist for this trait + numberOfRulesTested := len(personPassesRulesMap) - for _, genomeIdentifier := range allGenomeIdentifiersList{ - - traitAnalysisMap[genomeIdentifier + "_NumberOfRulesTested"] = "0" + newPersonGenomeTraitInfo := geneticAnalysis.PersonGenomeTraitInfo{ + NumberOfRulesTested: numberOfRulesTested, + LocusValuesMap: genomeLocusValuesMap, + OutcomeScoresMap: traitOutcomeScoresMap, + GenomePassesRulesMap: personPassesRulesMap, } + + newPersonTraitInfoMap[genomeIdentifier] = newPersonGenomeTraitInfo } - if (len(allGenomeIdentifiersList) == 1){ - // We do not need to check for conflicts + newPersonTraitInfoObject := geneticAnalysis.PersonTraitInfo{ + TraitInfoMap: newPersonTraitInfoMap, + } + + if (len(newPersonTraitInfoMap) <= 1){ + // We do not need to check for conflicts, there is only <=1 genome with trait information // Nothing left to do. Analysis is complete. - return traitAnalysisMap, rulesMapList, nil + return newPersonTraitInfoObject, nil } // We check for conflicts @@ -2479,27 +2218,39 @@ func getTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWit //TODO: Check for locus value conflicts once locus values are used in neural network prediction. - // We only have to check each rule result, because they will determine the overall person results + if (len(traitRulesList) == 0){ + return false, nil + } - for _, ruleMap := range rulesMapList{ + // We check to see if the outcome scores are the same for all genomes + // We also check each rule result - passesRule := "" + firstItemReached := false - for index, genomeIdentifier := range allGenomeIdentifiersList{ + outcomeScoresMap := make(map[string]int) + passesRulesMap := make(map[[3]byte]bool) - currentPassesRule, exists := ruleMap[genomeIdentifier + "_PassesRule"] - if (exists == false){ - return false, errors.New("Cannot find _PassesRule key when searching for conflicts") - } + for _, genomeTraitInfoObject := range newPersonTraitInfoMap{ - if (index == 0){ + currentGenomeOutcomeScoresMap := genomeTraitInfoObject.OutcomeScoresMap + currentGenomePassesRulesMap := genomeTraitInfoObject.GenomePassesRulesMap - passesRule = currentPassesRule - continue - } - if (currentPassesRule != passesRule){ - return true, nil - } + if (firstItemReached == false){ + outcomeScoresMap = currentGenomeOutcomeScoresMap + passesRulesMap = currentGenomePassesRulesMap + firstItemReached = true + continue + } + + areEqual := maps.Equal(currentGenomeOutcomeScoresMap, outcomeScoresMap) + if (areEqual == false){ + // A conflict exists + return true, nil + } + areEqual = maps.Equal(currentGenomePassesRulesMap, passesRulesMap) + if (areEqual == false){ + // A conflict exists + return true, nil } } @@ -2507,13 +2258,11 @@ func getTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWit } conflictExists, err := getConflictExistsBool() - if (err != nil) { return nil, nil, err } + if (err != nil) { return emptyPersonTraitInfo, err } - conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) + newPersonTraitInfoObject.ConflictExists = conflictExists - traitAnalysisMap["ConflictExists"] = conflictExistsString - - return traitAnalysisMap, rulesMapList, nil + return newPersonTraitInfoObject, nil } diff --git a/internal/genetics/createGeneticAnalysis/createGeneticAnalysis_test.go b/internal/genetics/createGeneticAnalysis/createGeneticAnalysis_test.go index c68e7a2..7410e2d 100644 --- a/internal/genetics/createGeneticAnalysis/createGeneticAnalysis_test.go +++ b/internal/genetics/createGeneticAnalysis/createGeneticAnalysis_test.go @@ -27,9 +27,9 @@ func TestCreatePersonGeneticAnalysis_SingleGenome(t *testing.T){ polygenicDiseases.InitializePolygenicDiseaseVariables() traits.InitializeTraitVariables() - genomeIdentifier, err := helpers.GetNewRandomHexString(16) + genomeIdentifier, err := helpers.GetNewRandom16ByteArray() if (err != nil) { - t.Fatalf("Failed to get random hex string: " + err.Error()) + t.Fatalf("Failed to get random 16 byte array: " + err.Error()) } fakeRawGenome, _, _, _, err := createRawGenomes.CreateFakeRawGenome_AncestryDNA() @@ -63,12 +63,12 @@ func TestCreatePersonGeneticAnalysis_SingleGenome(t *testing.T){ t.Fatalf("Failed to create person genetic analysis: Process did not complete.") } - personGeneticAnalysisMapList, err := readGeneticAnalysis.ReadGeneticAnalysisString(personGeneticAnalysis) + personGeneticAnalysisObject, err := readGeneticAnalysis.ReadPersonGeneticAnalysisString(personGeneticAnalysis) if (err != nil){ t.Fatalf("Failed to read person genetic analysis string: " + err.Error()) } - err = readGeneticAnalysis.ReadPersonGeneticAnalysisForTests(personGeneticAnalysisMapList) + err = readGeneticAnalysis.VerifyPersonGeneticAnalysis(personGeneticAnalysisObject) if (err != nil){ t.Fatalf("Failed to read person genetic analysis: " + err.Error()) } @@ -92,9 +92,9 @@ func TestCreatePersonGeneticAnalysis_MultipleGenomes(t *testing.T){ for i:=0; i < numberOfGenomesToAdd; i++{ - genomeIdentifier, err := helpers.GetNewRandomHexString(16) + genomeIdentifier, err := helpers.GetNewRandom16ByteArray() if (err != nil) { - t.Fatalf("Failed to get random hex string: " + err.Error()) + t.Fatalf("Failed to get random 16 byte array: " + err.Error()) } getFakeRawGenome := func()(string, error){ @@ -149,12 +149,12 @@ func TestCreatePersonGeneticAnalysis_MultipleGenomes(t *testing.T){ t.Fatalf("Failed to create person genetic analysis: Process did not complete.") } - personGeneticAnalysisMapList, err := readGeneticAnalysis.ReadGeneticAnalysisString(personGeneticAnalysis) + personGeneticAnalysisObject, err := readGeneticAnalysis.ReadPersonGeneticAnalysisString(personGeneticAnalysis) if (err != nil){ t.Fatalf("Failed to read person genetic analysis string: " + err.Error()) } - err = readGeneticAnalysis.ReadPersonGeneticAnalysisForTests(personGeneticAnalysisMapList) + err = readGeneticAnalysis.VerifyPersonGeneticAnalysis(personGeneticAnalysisObject) if (err != nil){ t.Fatalf("Failed to read person genetic analysis: " + err.Error()) } @@ -174,9 +174,9 @@ func TestCreateCoupleGeneticAnalysis_SingleGenomes(t *testing.T){ getPersonGenomesList := func()([]prepareRawGenomes.RawGenomeWithMetadata, error){ - genomeIdentifier, err := helpers.GetNewRandomHexString(16) + genomeIdentifier, err := helpers.GetNewRandom16ByteArray() if (err != nil) { - return nil, errors.New("Failed to get random hex string: " + err.Error()) + return nil, errors.New("Failed to get random 16 byte array: " + err.Error()) } fakeRawGenome, _, _, _, err := createRawGenomes.CreateFakeRawGenome_AncestryDNA() @@ -197,12 +197,12 @@ func TestCreateCoupleGeneticAnalysis_SingleGenomes(t *testing.T){ return personGenomesList, nil } - personAGenomesList, err := getPersonGenomesList() + person1GenomesList, err := getPersonGenomesList() if (err != nil){ t.Fatalf("getPersonGenomesList failed: " + err.Error()) } - personBGenomesList, err := getPersonGenomesList() + person2GenomesList, err := getPersonGenomesList() if (err != nil){ t.Fatalf("getPersonGenomesList failed: " + err.Error()) } @@ -215,7 +215,7 @@ func TestCreateCoupleGeneticAnalysis_SingleGenomes(t *testing.T){ return false } - processCompleted, coupleGeneticAnalysis, err := createGeneticAnalysis.CreateCoupleGeneticAnalysis(personAGenomesList, personBGenomesList, updateProgressFunction, checkIfProcessIsStoppedFunction) + processCompleted, coupleGeneticAnalysis, err := createGeneticAnalysis.CreateCoupleGeneticAnalysis(person1GenomesList, person2GenomesList, updateProgressFunction, checkIfProcessIsStoppedFunction) if (err != nil){ t.Fatalf("Failed to create couple genetic analysis: " + err.Error()) } @@ -223,12 +223,12 @@ func TestCreateCoupleGeneticAnalysis_SingleGenomes(t *testing.T){ t.Fatalf("Failed to create couple genetic analysis: Process did not complete.") } - coupleGeneticAnalysisMapList, err := readGeneticAnalysis.ReadGeneticAnalysisString(coupleGeneticAnalysis) + coupleGeneticAnalysisObject, err := readGeneticAnalysis.ReadCoupleGeneticAnalysisString(coupleGeneticAnalysis) if (err != nil){ t.Fatalf("Failed to read couple genetic analysis string: " + err.Error()) } - err = readGeneticAnalysis.ReadCoupleGeneticAnalysisForTests(coupleGeneticAnalysisMapList) + err = readGeneticAnalysis.VerifyCoupleGeneticAnalysis(coupleGeneticAnalysisObject) if (err != nil){ t.Fatalf("Failed to read couple genetic analysis: " + err.Error()) } @@ -249,9 +249,9 @@ func TestCreateCoupleGeneticAnalysis_SingleAndMultipleGenomes(t *testing.T){ getPersonGenomesList := func(addSecondGenome bool)([]prepareRawGenomes.RawGenomeWithMetadata, error){ - genomeIdentifier1, err := helpers.GetNewRandomHexString(16) + genomeIdentifier1, err := helpers.GetNewRandom16ByteArray() if (err != nil) { - return nil, errors.New("Failed to get random hex string: " + err.Error()) + return nil, errors.New("Failed to get random 16 byte array: " + err.Error()) } fakeRawGenome1, _, _, _, err := createRawGenomes.CreateFakeRawGenome_AncestryDNA() @@ -271,9 +271,9 @@ func TestCreateCoupleGeneticAnalysis_SingleAndMultipleGenomes(t *testing.T){ if (addSecondGenome == true){ - genomeIdentifier2, err := helpers.GetNewRandomHexString(16) + genomeIdentifier2, err := helpers.GetNewRandom16ByteArray() if (err != nil) { - return nil, errors.New("Failed to get random hex string: " + err.Error()) + return nil, errors.New("Failed to get random 16 byte array: " + err.Error()) } fakeRawGenome2, _, _, _, err := createRawGenomes.CreateFakeRawGenome_23andMe() @@ -295,12 +295,12 @@ func TestCreateCoupleGeneticAnalysis_SingleAndMultipleGenomes(t *testing.T){ return genomesList, nil } - personAGenomesList, err := getPersonGenomesList(false) + person1GenomesList, err := getPersonGenomesList(false) if (err != nil){ t.Fatalf("getPersonGenomesList failed: " + err.Error()) } - personBGenomesList, err := getPersonGenomesList(true) + person2GenomesList, err := getPersonGenomesList(true) if (err != nil){ t.Fatalf("getPersonGenomesList failed: " + err.Error()) } @@ -313,7 +313,7 @@ func TestCreateCoupleGeneticAnalysis_SingleAndMultipleGenomes(t *testing.T){ return false } - processCompleted, coupleGeneticAnalysis, err := createGeneticAnalysis.CreateCoupleGeneticAnalysis(personAGenomesList, personBGenomesList, updateProgressFunction, checkIfProcessIsStoppedFunction) + processCompleted, coupleGeneticAnalysis, err := createGeneticAnalysis.CreateCoupleGeneticAnalysis(person1GenomesList, person2GenomesList, updateProgressFunction, checkIfProcessIsStoppedFunction) if (err != nil){ t.Fatalf("Failed to create couple genetic analysis: " + err.Error()) } @@ -321,12 +321,12 @@ func TestCreateCoupleGeneticAnalysis_SingleAndMultipleGenomes(t *testing.T){ t.Fatalf("Failed to create couple genetic analysis: Process did not complete.") } - coupleGeneticAnalysisMapList, err := readGeneticAnalysis.ReadGeneticAnalysisString(coupleGeneticAnalysis) + coupleGeneticAnalysisObject, err := readGeneticAnalysis.ReadCoupleGeneticAnalysisString(coupleGeneticAnalysis) if (err != nil){ t.Fatalf("Failed to read couple genetic analysis string: " + err.Error()) } - err = readGeneticAnalysis.ReadCoupleGeneticAnalysisForTests(coupleGeneticAnalysisMapList) + err = readGeneticAnalysis.VerifyCoupleGeneticAnalysis(coupleGeneticAnalysisObject) if (err != nil){ t.Fatalf("Failed to read couple genetic analysis: " + err.Error()) } @@ -347,9 +347,9 @@ func TestCreateCoupleGeneticAnalysis_MultipleGenomes(t *testing.T){ getPersonGenomesList := func()([]prepareRawGenomes.RawGenomeWithMetadata, error){ - genomeIdentifier1, err := helpers.GetNewRandomHexString(16) + genomeIdentifier1, err := helpers.GetNewRandom16ByteArray() if (err != nil) { - return nil, errors.New("Failed to get random hex string: " + err.Error()) + return nil, errors.New("Failed to get random 16 byte array: " + err.Error()) } fakeRawGenome1, _, _, _, err := createRawGenomes.CreateFakeRawGenome_AncestryDNA() @@ -365,9 +365,9 @@ func TestCreateCoupleGeneticAnalysis_MultipleGenomes(t *testing.T){ return nil, errors.New("CreateRawGenomeWithMetadataObject failed: Genome is not valid.") } - genomeIdentifier2, err := helpers.GetNewRandomHexString(16) + genomeIdentifier2, err := helpers.GetNewRandom16ByteArray() if (err != nil) { - return nil, errors.New("Failed to get random hex string: " + err.Error()) + return nil, errors.New("Failed to get random 16 byte array: " + err.Error()) } fakeRawGenome2, _, _, _, err := createRawGenomes.CreateFakeRawGenome_23andMe() @@ -388,12 +388,12 @@ func TestCreateCoupleGeneticAnalysis_MultipleGenomes(t *testing.T){ return genomesList, nil } - personAGenomesList, err := getPersonGenomesList() + person1GenomesList, err := getPersonGenomesList() if (err != nil){ t.Fatalf("getPersonGenomesList failed: " + err.Error()) } - personBGenomesList, err := getPersonGenomesList() + person2GenomesList, err := getPersonGenomesList() if (err != nil){ t.Fatalf("getPersonGenomesList failed: " + err.Error()) } @@ -406,7 +406,7 @@ func TestCreateCoupleGeneticAnalysis_MultipleGenomes(t *testing.T){ return false } - processCompleted, coupleGeneticAnalysis, err := createGeneticAnalysis.CreateCoupleGeneticAnalysis(personAGenomesList, personBGenomesList, updateProgressFunction, checkIfProcessIsStoppedFunction) + processCompleted, coupleGeneticAnalysis, err := createGeneticAnalysis.CreateCoupleGeneticAnalysis(person1GenomesList, person2GenomesList, updateProgressFunction, checkIfProcessIsStoppedFunction) if (err != nil){ t.Fatalf("Failed to create couple genetic analysis: " + err.Error()) } @@ -414,12 +414,12 @@ func TestCreateCoupleGeneticAnalysis_MultipleGenomes(t *testing.T){ t.Fatalf("Failed to create couple genetic analysis: Process did not complete.") } - coupleGeneticAnalysisMapList, err := readGeneticAnalysis.ReadGeneticAnalysisString(coupleGeneticAnalysis) + coupleGeneticAnalysisObject, err := readGeneticAnalysis.ReadCoupleGeneticAnalysisString(coupleGeneticAnalysis) if (err != nil){ t.Fatalf("Failed to read couple genetic analysis string: " + err.Error()) } - err = readGeneticAnalysis.ReadCoupleGeneticAnalysisForTests(coupleGeneticAnalysisMapList) + err = readGeneticAnalysis.VerifyCoupleGeneticAnalysis(coupleGeneticAnalysisObject) if (err != nil){ t.Fatalf("Failed to read couple genetic analysis: " + err.Error()) } diff --git a/internal/genetics/geneticAnalysis/geneticAnalysis.go b/internal/genetics/geneticAnalysis/geneticAnalysis.go new file mode 100644 index 0000000..fde620f --- /dev/null +++ b/internal/genetics/geneticAnalysis/geneticAnalysis.go @@ -0,0 +1,318 @@ + +// geneticAnalysis implements the geneticAnalysis objects +// There are 2 geneticAnalysis types: Person and Couple + +package geneticAnalysis + +import "seekia/internal/genetics/locusValue" + + +type PersonAnalysis struct{ + + AnalysisVersion int + + // This is a list of each raw genome identifier (not including combined genomes) + AllRawGenomeIdentifiersList [][16]byte + + // This is true if there is more than 1 raw genome + CombinedGenomesExist bool + + // These are the identifiers for the combined genomes + // These only exist if CombinedGenomesExist == true + OnlyIncludeSharedGenomeIdentifier [16]byte + OnlyExcludeConflictsGenomeIdentifier [16]byte + + // Map Structure: Disease Name -> PersonMonogenicDiseaseInfo + MonogenicDiseasesMap map[string]PersonMonogenicDiseaseInfo + + // Map Structure: Disease Name -> PersonPolygenicDiseaseInfo + PolygenicDiseasesMap map[string]PersonPolygenicDiseaseInfo + + // Map Structure: Trait Name -> Trait Info Object + TraitsMap map[string]PersonTraitInfo +} + + +type PersonMonogenicDiseaseInfo struct{ + + // This map gives information about each genome's monogenic disease risk + // If no map entries exist, then no disease info is known + // Map Structure: Genome Identifier -> PersonGenomeMonogenicDiseaseInfo + MonogenicDiseaseInfoMap map[[16]byte]PersonGenomeMonogenicDiseaseInfo + + // This is true if there are multiple genomes and the results for each genome differ + ConflictExists bool +} + +type PersonGenomeMonogenicDiseaseInfo struct{ + + // This describes if the person has the disease + PersonHasDisease bool + + // This describes the number of variants that were tested for this disease + NumberOfVariantsTested int + + // This describes the number of loci that were tested for this disease + // 1 locus can have multiple potential variants + NumberOfLociTested int + + // This describes the number of loci which are phased + // This number will always be <= NumberOfLociTested + NumberOfPhasedLoci int + + // This describes the probability that the person will pass a disease variant + // It is a value that represents a percentage between 0-100 + ProbabilityOfPassingADiseaseVariant int + + // This map contains info about all tested monogenic disease variants for the genome + // If the map does not contain an item for a disease variant, then the genome does not contain information for that variant + // Map Structure: Variant Identifier -> PersonGenomeMonogenicDiseaseVariantInfo + VariantsInfoMap map[[3]byte]PersonGenomeMonogenicDiseaseVariantInfo +} + +type PersonGenomeMonogenicDiseaseVariantInfo struct{ + + // These bools describe if each base has the variant at the variant's locus + Base1HasVariant bool + Base2HasVariant bool + + // This bool describes if the bases are phased or not + // If they are not phased, then Base1/2 are the same, the number carries no meaning + // If they are phased, then Base1 is inherited from the father? and Base2 was inherited from the mother? + LocusIsPhased bool +} + +type PersonPolygenicDiseaseInfo struct{ + + // If no map entries exist, then no disease info is known + // Map Structure: Genome Identifier -> PersonGenomePolygenicDiseaseInfo + PolygenicDiseaseInfoMap map[[16]byte]PersonGenomePolygenicDiseaseInfo + + // This is true if there are multiple genomes and the results from each genome differ + ConflictExists bool +} + +type PersonGenomePolygenicDiseaseInfo struct{ + + // This describes the number of loci tested for this disease + // This should be len(LociInfoList) + NumberOfLociTested int + + // This is total risk score for this disease for the person's genome + // This is a number between 1-10 + RiskScore int + + // This map contains info about all tested polygenic disease loci for this genome + // If a locus does not exist in the map, its values are unknown + // Map Structure: Locus Identifier -> PersonGenomePolygenicDiseaseLocusInfo + LociInfoMap map[[3]byte]PersonGenomePolygenicDiseaseLocusInfo +} + +type PersonGenomePolygenicDiseaseLocusInfo struct{ + + // The person's genome locus base pair value for this variant's locus + // Example: "G", "C" + LocusBase1 string + LocusBase2 string + + // This is the risk weight that this person's genome has for this variant + // A higher risk weight means more risk of getting the disease + RiskWeight int + + // This is valse if the odds ratio is not known + OddsRatioIsKnown bool + + // This is the person's genome odds ratio value for this variant's locus + // A ratio >1 means their risk is increased, a ratio <1 means their risk is decreased + OddsRatio float64 +} + + +type PersonTraitInfo struct{ + + // This map contains the person's trait info for each genome + // If no map entries exist, then no trait info is known + // Map Structure: Genome Identifier -> PersonGenomeTraitInfo + TraitInfoMap map[[16]byte]PersonGenomeTraitInfo + + // This is true if there are multiple genomes and the results from each genome differ + ConflictExists bool +} + +type PersonGenomeTraitInfo struct{ + + // This should be len(RulesList) + NumberOfRulesTested int + + // This map contains the locus values for the genome + // If an locus's entry doesn't exist, its value is unknown + // Map Structure: Locus rsID -> Locus Value + LocusValuesMap map[int64]locusValue.LocusValue + + // This map contains the outcome scores for the genome + // Map Structure: Outcome Name -> Score + // Example: "Intolerant" -> 5 + OutcomeScoresMap map[string]int + + // Map Structure: Rule Identifier -> Genome Passes rule (true if the genome passes the rule) + GenomePassesRulesMap map[[3]byte]bool +} + + +type CoupleAnalysis struct{ + + AnalysisVersion int + + Pair1Person1GenomeIdentifier [16]byte + Pair1Person2GenomeIdentifier [16]byte + + // This is only true if at least 1 person has more than 1 genome + SecondPairExists bool + + // These are empty unless SecondPairExists == true + Pair2Person1GenomeIdentifier [16]byte + Pair2Person2GenomeIdentifier [16]byte + + Person1HasMultipleGenomes bool + Person2HasMultipleGenomes bool + + // These are empty unless Person1HasMultipleGenomes == true + Person1OnlyExcludeConflictsGenomeIdentifier [16]byte + Person1OnlyIncludeSharedGenomeIdentifier [16]byte + + // These are empty unless Person2HasMultipleGenomes == true + Person2OnlyExcludeConflictsGenomeIdentifier [16]byte + Person2OnlyIncludeSharedGenomeIdentifier [16]byte + + // Map Structure: Disease Name -> OffspringMonogenicDiseaseInfo + MonogenicDiseasesMap map[string]OffspringMonogenicDiseaseInfo + + // Map Structure: Disease Name -> OffspringPolygenicDiseaseInfo + PolygenicDiseasesMap map[string]OffspringPolygenicDiseaseInfo + + // Map Structure: Trait Name -> Trait Info Object + TraitsMap map[string]OffspringTraitInfo +} + + +type OffspringMonogenicDiseaseInfo struct{ + + // This map stores the number of variants tested in each person's genome + // Map Structure: Genome Identifier -> Number of variants tested + NumberOfVariantsTestedMap map[[16]byte]int + + // This map stores the offspring disease probabilities for each genome pair. + // A genome pair is a concatenation of two genome identifiers + // If a map entry doesn't exist, the probabilities are unknown for that genome pair + // Map Structure: Genome Pair Identifier -> OffspringGenomePairMonogenicDiseaseInfo + MonogenicDiseaseInfoMap map[[32]byte]OffspringGenomePairMonogenicDiseaseInfo + + // This is true if there is more than 1 genome pair and the results from each genome pair differ + ConflictExists bool +} + + +type OffspringGenomePairMonogenicDiseaseInfo struct{ + + // At least 1 variant's information is needed from either person to include the diseaseInfo object in the MonogenicDiseaseInfoMap + + ProbabilityOffspringHasDiseaseIsKnown bool + + // This is the probability that the offspring will have the disease + // Is a number between 0-100% + ProbabilityOffspringHasDisease int + + ProbabilityOffspringHasVariantIsKnown bool + + // This is the probability that the offspring will have a variant + // Is a number between 0-100% + ProbabilityOffspringHasVariant int + + // Map Structure: Variant Identifier -> OffspringMonogenicDiseaseVariantInfo + VariantsInfoMap map[[3]byte]OffspringMonogenicDiseaseVariantInfo +} + +type OffspringMonogenicDiseaseVariantInfo struct{ + + // These are all numbers between 0-100% + ProbabilityOf0MutationsLowerBound int + ProbabilityOf0MutationsUpperBound int + + ProbabilityOf1MutationLowerBound int + ProbabilityOf1MutationUpperBound int + + ProbabilityOf2MutationsLowerBound int + ProbabilityOf2MutationsUpperBound int +} + + +type OffspringPolygenicDiseaseInfo struct{ + + // This map stores the polygenic disease info for each genome pair + // Map Structure: Genome Pair Identifier -> OffspringGenomePairPolygenicDiseaseInfo + PolygenicDiseaseInfoMap map[[32]byte]OffspringGenomePairPolygenicDiseaseInfo + + // This is true if there is more than 1 genome pair and the results from each genome pair differ + ConflictExists bool +} + + +type OffspringGenomePairPolygenicDiseaseInfo struct{ + + // This should be len(DiseaseLociList) + NumberOfLociTested int + + // A number between 1-10 representing the offspring's risk + // 1 == lowest risk, 10 == highest risk + OffspringRiskScore int + + // A map of the offspring's locus information + // Map Structure: Locus Identifier -> OffspringPolygenicDiseaseLocusInfo + LociInfoMap map[[3]byte]OffspringPolygenicDiseaseLocusInfo +} + + +type OffspringPolygenicDiseaseLocusInfo struct{ + + // This is the offspring's risk weight for this locus value + // A higher weight means a higher risk of the disease + OffspringRiskWeight int + + OffspringOddsRatioIsKnown bool + + // This value represent's the offspring's average odds ratio for the disease locus + // A value <1 denotes a lesser risk, a value >1 denotes an increased risk + OffspringOddsRatio float64 + + // This is the sum of weights for the loci which have no odds ratios + // We do this to understand what effect those loci are having on the odds ratio + // If the sum is <0, we say the ratio is probably lower + // If the sum is >0, we say the ratio is probably higher + OffspringUnknownOddsRatiosWeightSum int +} + + +type OffspringTraitInfo struct{ + + // This map stores the trait info for each genome pair + // Map Structure: Genome Pair Identifier -> OffspringGenomePairTraitInfo + TraitInfoMap map[[32]byte]OffspringGenomePairTraitInfo + + ConflictExists bool +} + +type OffspringGenomePairTraitInfo struct{ + + // This should be len(TraitRulesList) + NumberOfRulesTested int + + // Map Structure: Outcome Name -> Outcome Score + // Example: "Intolerant" -> 2.5 + OffspringAverageOutcomeScoresMap map[string]float64 + + // Map Structure: Rule Identifier -> Offspring Probability Of Passing Rule + // The value stores the probability that the offspring will pass the rule + // This is a number between 0-100% + ProbabilityOfPassingRulesMap map[[3]byte]int +} + diff --git a/internal/genetics/myAnalyses/myAnalyses.go b/internal/genetics/myAnalyses/myAnalyses.go index af0176c..098ae26 100644 --- a/internal/genetics/myAnalyses/myAnalyses.go +++ b/internal/genetics/myAnalyses/myAnalyses.go @@ -4,12 +4,14 @@ package myAnalyses -import "seekia/internal/helpers" import "seekia/internal/appMemory" import "seekia/internal/appValues" +import "seekia/internal/encoding" +import "seekia/internal/helpers" import "seekia/internal/localFilesystem" import "seekia/internal/myDatastores/myMapList" import "seekia/internal/genetics/createGeneticAnalysis" +import "seekia/internal/genetics/geneticAnalysis" import "seekia/internal/genetics/prepareRawGenomes" import "seekia/internal/genetics/readGeneticAnalysis" import "seekia/internal/genetics/myGenomes" @@ -84,14 +86,14 @@ func DeleteAllAnalysesForPerson(personIdentifier string)error{ if (err != nil) { return err } mapToDelete = map[string]string{ - "PersonAIdentifier": personIdentifier, + "Person1Identifier": personIdentifier, } err = myCoupleAnalysesMapListDatastore.DeleteMapListItems(mapToDelete) if (err != nil) { return err } mapToDelete = map[string]string{ - "PersonBIdentifier": personIdentifier, + "Person2Identifier": personIdentifier, } err = myCoupleAnalysesMapListDatastore.DeleteMapListItems(mapToDelete) @@ -100,17 +102,17 @@ func DeleteAllAnalysesForPerson(personIdentifier string)error{ return nil } -func DeleteAllAnalysesForCouple(inputPersonAIdentifier string, inputPersonBIdentifier string)error{ +func DeleteAllAnalysesForCouple(inputPerson1Identifier string, inputPerson2Identifier string)error{ updatingMyAnalysesMutex.Lock() defer updatingMyAnalysesMutex.Unlock() - personAIdentifier, personBIdentifier, err := GetPeopleIdentifiersSortedForCouple(inputPersonAIdentifier, inputPersonBIdentifier) + person1Identifier, person2Identifier, err := GetPeopleIdentifiersSortedForCouple(inputPerson1Identifier, inputPerson2Identifier) if (err != nil) { return err } mapToDelete := map[string]string{ - "PersonAIdentifier": personAIdentifier, - "PersonBIdentifier": personBIdentifier, + "Person1Identifier": person1Identifier, + "Person2Identifier": person2Identifier, } err = myCoupleAnalysesMapListDatastore.DeleteMapListItems(mapToDelete) @@ -120,18 +122,18 @@ func DeleteAllAnalysesForCouple(inputPersonAIdentifier string, inputPersonBIdent } // This function is used to sort the person identifiers, so that each pair of identifiers will only map to a single couple -// PersonA and PersonB will always follow this order within a couple analysis +// Person1 and Person2 will always follow this order within a couple analysis // The sorting method has no significance -func GetPeopleIdentifiersSortedForCouple(personAIdentifier string, personBIdentifier string)(string, string, error){ +func GetPeopleIdentifiersSortedForCouple(person1Identifier string, person2Identifier string)(string, string, error){ - if (personAIdentifier == personBIdentifier){ - return "", "", errors.New("GetPeopleIdentifiersSortedForCouple called with identical person identifiers: " + personAIdentifier) + if (person1Identifier == person2Identifier){ + return "", "", errors.New("GetPeopleIdentifiersSortedForCouple called with identical person identifiers: " + person1Identifier) } - if (personAIdentifier < personBIdentifier){ - return personAIdentifier, personBIdentifier, nil + if (person1Identifier < person2Identifier){ + return person1Identifier, person2Identifier, nil } - return personBIdentifier, personAIdentifier, nil + return person2Identifier, person1Identifier, nil } @@ -139,12 +141,12 @@ func GetPeopleIdentifiersSortedForCouple(personAIdentifier string, personBIdenti // -bool: Any analysis exists // -string: Newest analysis identifier // -int64: Time newest analysis was performed -// -[]string: List of genomes analyzed +// -[][16]byte: List of genomes analyzed // -bool: Newer analysis version available // -This is not the same as new genomes being available. New version can be for the same genomes. // -To fully determine if the analysis is up to date, we must check if new (or less) genomes are available to analyse for the person // -error -func GetPersonNewestGeneticAnalysisInfo(personIdentifier string)(bool, string, int64, []string, bool, error){ +func GetPersonNewestGeneticAnalysisInfo(personIdentifier string)(bool, string, int64, [][16]byte, bool, error){ lookupMap := map[string]string{ "PersonIdentifier": personIdentifier, @@ -162,7 +164,7 @@ func GetPersonNewestGeneticAnalysisInfo(personIdentifier string)(bool, string, i newestAnalysisFound := false newestAnalysisCreatedTime := int64(0) newestAnalysisIdentifier := "" - newestAnalysisAnalyzedGenomesList := []string{} + newestAnalysisAnalyzedGenomesList := make([][16]byte, 0) newerAnalysisVersionAvailable := false for _, analysisMap := range analysisItemsMapList{ @@ -204,6 +206,18 @@ func GetPersonNewestGeneticAnalysisInfo(personIdentifier string)(bool, string, i analyzedGenomesList := strings.Split(analyzedGenomes, "+") + analyzedGenomesArrayList := make([][16]byte, 0) + + for _, genomeIdentifierHex := range analyzedGenomesList{ + + genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex) + if (err != nil){ + return false, "", 0, nil, false, errors.New("Malformed myAnalysesMapList: Item contains invalid AnalyzedGenomes: " + analyzedGenomes) + } + + analyzedGenomesArrayList = append(analyzedGenomesArrayList, genomeIdentifier) + } + analysisVersion, exists := analysisMap["AnalysisVersion"] if (exists == false) { return false, "", 0, nil, false, errors.New("Malformed myAnalysesMapList: Item missing AnalysisVersion") @@ -224,7 +238,7 @@ func GetPersonNewestGeneticAnalysisInfo(personIdentifier string)(bool, string, i newestAnalysisCreatedTime = timeCreatedInt64 newestAnalysisIdentifier = analysisIdentifier - newestAnalysisAnalyzedGenomesList = analyzedGenomesList + newestAnalysisAnalyzedGenomesList = analyzedGenomesArrayList } if (newestAnalysisFound == false){ @@ -238,20 +252,20 @@ func GetPersonNewestGeneticAnalysisInfo(personIdentifier string)(bool, string, i // -bool: Analysis exists // -string: Newest analysis identifier // -int64: Time newest analysis was performed -// -[]string: Person A list of genomes analyzed -// -[]string: Person B list of genomes analyzed +// -[][16]byte: Person A list of genomes analyzed +// -[][16]byte: Person B list of genomes analyzed // -bool: Newer analysis version available // -This is not the same as new genomes being available. New version can be for the same genomes. // -To fully determine if the analysis is up to date, we must check if new (or less) genomes are available to analyse for the person // -error -func GetCoupleNewestGeneticAnalysisInfo(inputPersonAIdentifier string, inputPersonBIdentifier string)(bool, string, int64, []string, []string, bool, error){ +func GetCoupleNewestGeneticAnalysisInfo(inputPerson1Identifier string, inputPerson2Identifier string)(bool, string, int64, [][16]byte, [][16]byte, bool, error){ - personAIdentifier, personBIdentifier, err := GetPeopleIdentifiersSortedForCouple(inputPersonAIdentifier, inputPersonBIdentifier) + person1Identifier, person2Identifier, err := GetPeopleIdentifiersSortedForCouple(inputPerson1Identifier, inputPerson2Identifier) if (err != nil) { return false, "", 0, nil, nil, false, err } lookupMap := map[string]string{ - "PersonAIdentifier": personAIdentifier, - "PersonBIdentifier": personBIdentifier, + "Person1Identifier": person1Identifier, + "Person2Identifier": person2Identifier, } anyItemsFound, analysisItemsMapList, err := myCoupleAnalysesMapListDatastore.GetMapListItems(lookupMap) @@ -266,28 +280,28 @@ func GetCoupleNewestGeneticAnalysisInfo(inputPersonAIdentifier string, inputPers newestAnalysisFound := false newestAnalysisCreatedTime := int64(0) newestAnalysisIdentifier := "" - newestAnalysisPersonAAnalyzedGenomesList := []string{} - newestAnalysisPersonBAnalyzedGenomesList := []string{} + newestAnalysisPerson1AnalyzedGenomesList := [][16]byte{} + newestAnalysisPerson2AnalyzedGenomesList := [][16]byte{} newerAnalysisVersionAvailable := false for _, analysisMap := range analysisItemsMapList{ - currentPersonAIdentifier, exists := analysisMap["PersonAIdentifier"] + currentPerson1Identifier, exists := analysisMap["Person1Identifier"] if (exists == false) { - return false, "", 0, nil, nil, false, errors.New("Malformed myAnalysesMapList: Item missing PersonAIdentifier") + return false, "", 0, nil, nil, false, errors.New("Malformed myAnalysesMapList: Item missing Person1Identifier") } - if (personAIdentifier != currentPersonAIdentifier){ - return false, "", 0, nil, nil, false, errors.New("GetMapListItems returning map with different personAIdentifier.") + if (person1Identifier != currentPerson1Identifier){ + return false, "", 0, nil, nil, false, errors.New("GetMapListItems returning map with different person1Identifier.") } - currentPersonBIdentifier, exists := analysisMap["PersonBIdentifier"] + currentPerson2Identifier, exists := analysisMap["Person2Identifier"] if (exists == false) { - return false, "", 0, nil, nil, false, errors.New("Malformed myAnalysesMapList: Item missing PersonBIdentifier") + return false, "", 0, nil, nil, false, errors.New("Malformed myAnalysesMapList: Item missing Person2Identifier") } - if (personBIdentifier != currentPersonBIdentifier){ - return false, "", 0, nil, nil, false, errors.New("GetMapListItems returning map with different personBIdentifier.") + if (person2Identifier != currentPerson2Identifier){ + return false, "", 0, nil, nil, false, errors.New("GetMapListItems returning map with different person2Identifier.") } timeCreated, exists := analysisMap["TimeCreated"] @@ -310,19 +324,41 @@ func GetCoupleNewestGeneticAnalysisInfo(inputPersonAIdentifier string, inputPers return false, "", 0, nil, nil, false, errors.New("Malformed myAnalysesMapList: Item missing AnalysisIdentifier") } - personAAnalyzedGenomes, exists := analysisMap["PersonAAnalyzedGenomes"] + person1AnalyzedGenomes, exists := analysisMap["Person1AnalyzedGenomes"] if (exists == false) { - return false, "", 0, nil, nil, false, errors.New("Malformed myAnalysesMapList: Item missing PersonAAnalyzedGenomes") + return false, "", 0, nil, nil, false, errors.New("Malformed myAnalysesMapList: Item missing Person1AnalyzedGenomes") } - personAAnalyzedGenomesList := strings.Split(personAAnalyzedGenomes, "+") - - personBAnalyzedGenomes, exists := analysisMap["PersonBAnalyzedGenomes"] + person2AnalyzedGenomes, exists := analysisMap["Person2AnalyzedGenomes"] if (exists == false) { - return false, "", 0, nil, nil, false, errors.New("Malformed myAnalysesMapList: Item missing PersonBAnalyzedGenomes") + return false, "", 0, nil, nil, false, errors.New("Malformed myAnalysesMapList: Item missing Person2AnalyzedGenomes") } - personBAnalyzedGenomesList := strings.Split(personBAnalyzedGenomes, "+") + person1AnalyzedGenomesList := strings.Split(person1AnalyzedGenomes, "+") + person2AnalyzedGenomesList := strings.Split(person2AnalyzedGenomes, "+") + + person1AnalyzedGenomesArrayList := make([][16]byte, 0) + person2AnalyzedGenomesArrayList := make([][16]byte, 0) + + for _, genomeIdentifierHex := range person1AnalyzedGenomesList{ + + genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex) + if (err != nil){ + return false, "", 0, nil, nil, false, errors.New("Malformed myCoupleAnalysesMapListDatastore: Item contains invalid Person1AnalyzedGenomes: " + person1AnalyzedGenomes) + } + + person1AnalyzedGenomesArrayList = append(person1AnalyzedGenomesArrayList, genomeIdentifier) + } + + for _, genomeIdentifierHex := range person2AnalyzedGenomesList{ + + genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex) + if (err != nil){ + return false, "", 0, nil, nil, false, errors.New("Malformed myCoupleAnalysesMapListDatastore: Item contains invalid Person2AnalyzedGenomes: " + person1AnalyzedGenomes) + } + + person2AnalyzedGenomesArrayList = append(person2AnalyzedGenomesArrayList, genomeIdentifier) + } analysisVersion, exists := analysisMap["AnalysisVersion"] if (exists == false) { @@ -344,54 +380,83 @@ func GetCoupleNewestGeneticAnalysisInfo(inputPersonAIdentifier string, inputPers newestAnalysisCreatedTime = timeCreatedInt64 newestAnalysisIdentifier = analysisIdentifier - newestAnalysisPersonAAnalyzedGenomesList = personAAnalyzedGenomesList - newestAnalysisPersonBAnalyzedGenomesList = personBAnalyzedGenomesList + newestAnalysisPerson1AnalyzedGenomesList = person1AnalyzedGenomesArrayList + newestAnalysisPerson2AnalyzedGenomesList = person2AnalyzedGenomesArrayList } if (newestAnalysisFound == false){ return false, "", 0, nil, nil, false, errors.New("GetMapListItems not returning any items when anyItemsFound == true.") } - // We have to return the personA/PersonB analyzed genomes list in the same order that they came in + // We have to return the person1/Person2 analyzed genomes list in the same order that they came in - if (inputPersonAIdentifier == personAIdentifier){ + if (inputPerson1Identifier == person1Identifier){ // No swapping happened. - return true, newestAnalysisIdentifier, newestAnalysisCreatedTime, newestAnalysisPersonAAnalyzedGenomesList, newestAnalysisPersonBAnalyzedGenomesList, newerAnalysisVersionAvailable, nil + return true, newestAnalysisIdentifier, newestAnalysisCreatedTime, newestAnalysisPerson1AnalyzedGenomesList, newestAnalysisPerson2AnalyzedGenomesList, newerAnalysisVersionAvailable, nil } - // We swap the personA/PersonB analyzed genomes lists + // We swap the person1/Person2 analyzed genomes lists - return true, newestAnalysisIdentifier, newestAnalysisCreatedTime, newestAnalysisPersonBAnalyzedGenomesList, newestAnalysisPersonAAnalyzedGenomesList, newerAnalysisVersionAvailable, nil + return true, newestAnalysisIdentifier, newestAnalysisCreatedTime, newestAnalysisPerson2AnalyzedGenomesList, newestAnalysisPerson1AnalyzedGenomesList, newerAnalysisVersionAvailable, nil } // This function should only be used to retrieve an existing analysis -// It can be used for person and couple genetic analyses //Outputs: // -bool: Analysis found -// -[]map[string]string: Analysis map list +// -geneticAnalysis.PersonAnalysis Analysis object // -error -func GetGeneticAnalysis(analysisIdentifier string)(bool, []map[string]string, error){ +func GetPersonGeneticAnalysis(analysisIdentifier string)(bool, geneticAnalysis.PersonAnalysis, error){ + + analysisFound, analysisFileString, err := getGeneticAnalysisFileString(analysisIdentifier) + if (err != nil) { return false, geneticAnalysis.PersonAnalysis{}, err } + if (analysisFound == false){ + return false, geneticAnalysis.PersonAnalysis{}, nil + } + + analysisObject, err := readGeneticAnalysis.ReadPersonGeneticAnalysisString(analysisFileString) + if (err != nil) { return false, geneticAnalysis.PersonAnalysis{}, err } + + return true, analysisObject, nil +} + +// This function should only be used to retrieve an existing analysis +//Outputs: +// -bool: Analysis found +// -geneticAnalysis.CoupleAnalysis Analysis object +// -error +func GetCoupleGeneticAnalysis(analysisIdentifier string)(bool, geneticAnalysis.CoupleAnalysis, error){ + + analysisFound, analysisFileString, err := getGeneticAnalysisFileString(analysisIdentifier) + if (err != nil) { return false, geneticAnalysis.CoupleAnalysis{}, err } + if (analysisFound == false){ + return false, geneticAnalysis.CoupleAnalysis{}, nil + } + + analysisObject, err := readGeneticAnalysis.ReadCoupleGeneticAnalysisString(analysisFileString) + if (err != nil) { return false, geneticAnalysis.CoupleAnalysis{}, err } + + return true, analysisObject, nil +} + +func getGeneticAnalysisFileString(analysisIdentifier string)(bool, string, error){ userDirectory, err := localFilesystem.GetAppUserFolderPath() - if (err != nil) { return false, nil, err } + if (err != nil) { return false, "", err } - analysisFileName := analysisIdentifier + ".json" + analysisFileName := analysisIdentifier + ".messagepack" analysisFilePath := filepath.Join(userDirectory, "MyAnalyses", analysisFileName) fileExists, fileBytes, err := localFilesystem.GetFileContents(analysisFilePath) - if (err != nil) { return false, nil, err } + if (err != nil) { return false, "", err } if (fileExists == false){ - return false, nil, nil + return false, "", nil } fileString := string(fileBytes) - - analysisMapList, err := readGeneticAnalysis.ReadGeneticAnalysisString(fileString) - if (err != nil) { return false, nil, err } - return true, analysisMapList, nil + return true, fileString, nil } // This map keeps track of current person analyses being generated @@ -401,7 +466,7 @@ var personAnalysisProcessesMap map[string]string = make(map[string]string) var personAnalysisProcessesMapMutex sync.RWMutex // This map keeps track of current couple analyses being generated -// Map structure: PersonAIdentifier + "+" + PersonBIdentifier -> Process identifier +// Map structure: Person1Identifier + "+" + Person2Identifier -> Process identifier var coupleAnalysisProcessesMap map[string]string = make(map[string]string) var coupleAnalysisProcessesMapMutex sync.RWMutex @@ -429,12 +494,12 @@ func GetPersonGeneticAnalysisProcessIdentifier(personIdentifier string)(bool, st // -bool: Any process found (The process may be complete) // -string: Process identifier // -error -func GetCoupleGeneticAnalysisProcessIdentifier(inputPersonAIdentifier string, inputPersonBIdentifier string)(bool, string, error){ +func GetCoupleGeneticAnalysisProcessIdentifier(inputPerson1Identifier string, inputPerson2Identifier string)(bool, string, error){ - personAIdentifier, personBIdentifier, err := GetPeopleIdentifiersSortedForCouple(inputPersonAIdentifier, inputPersonBIdentifier) + person1Identifier, person2Identifier, err := GetPeopleIdentifiersSortedForCouple(inputPerson1Identifier, inputPerson2Identifier) if (err != nil) { return false, "", err } - coupleIdentifier := personAIdentifier + "+" + personBIdentifier + coupleIdentifier := person1Identifier + "+" + person2Identifier coupleAnalysisProcessesMapMutex.RLock() processIdentifier, exists := coupleAnalysisProcessesMap[coupleIdentifier] @@ -576,22 +641,25 @@ func StartCreateNewPersonGeneticAnalysis(personIdentifier string)(string, error) return errors.New("GetAllPersonGenomesMapList returning genome for different person.") } - genomeIdentifier, exists := genomeMap["GenomeIdentifier"] + genomeIdentifierHex, exists := genomeMap["GenomeIdentifier"] if (exists == false) { return errors.New("PersonGenomesMapList malformed: Item missing GenomeIdentifier") } + genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex) + if (err != nil) { return err } + genomeRawDataString, err := myGenomes.GetGenomeRawDataString(genomeIdentifier) if (err != nil) { return err } rawGenomeIsValid, rawGenomeWithMetadata, err := prepareRawGenomes.CreateRawGenomeWithMetadataObject(genomeIdentifier, genomeRawDataString) if (err != nil) { return err } if (rawGenomeIsValid == false){ - return errors.New("myGenomes contains invalid rawGenomeDataString: " + genomeIdentifier) + return errors.New("myGenomes contains invalid rawGenomeDataString: " + genomeIdentifierHex) } genomesList = append(genomesList, rawGenomeWithMetadata) - genomeIdentifiersList = append(genomeIdentifiersList, genomeIdentifier) + genomeIdentifiersList = append(genomeIdentifiersList, genomeIdentifierHex) } analysisUpdatePercentageCompleteFunction := func(inputProgress int)error{ @@ -642,7 +710,7 @@ func StartCreateNewPersonGeneticAnalysis(personIdentifier string)(string, error) myAnalysesFolderPath := filepath.Join(userDirectory, "MyAnalyses") - analysisFileName := analysisIdentifier + ".json" + analysisFileName := analysisIdentifier + ".messagepack" err = localFilesystem.CreateOrOverwriteFile([]byte(newGeneticAnalysisString), myAnalysesFolderPath, analysisFileName) if (err != nil) { return err } @@ -677,12 +745,12 @@ func StartCreateNewPersonGeneticAnalysis(personIdentifier string)(string, error) //Outputs: // -string: New process identifier // -error -func StartCreateNewCoupleGeneticAnalysis(inputPersonAIdentifier string, inputPersonBIdentifier string)(string, error){ +func StartCreateNewCoupleGeneticAnalysis(inputPerson1Identifier string, inputPerson2Identifier string)(string, error){ - personAIdentifier, personBIdentifier, err := GetPeopleIdentifiersSortedForCouple(inputPersonAIdentifier, inputPersonBIdentifier) + person1Identifier, person2Identifier, err := GetPeopleIdentifiersSortedForCouple(inputPerson1Identifier, inputPerson2Identifier) if (err != nil) { return "", err } - coupleIdentifier := personAIdentifier + "+" + personBIdentifier + coupleIdentifier := person1Identifier + "+" + person2Identifier coupleAnalysisProcessesMapMutex.Lock() existingProcessIdentifier, exists := coupleAnalysisProcessesMap[coupleIdentifier] @@ -732,11 +800,11 @@ func StartCreateNewCoupleGeneticAnalysis(inputPersonAIdentifier string, inputPer return false } - // We need both personA and personB to have an analysis before performing the couple analysis + // We need both person1 and person2 to have an analysis before performing the couple analysis // We will see if an analysis is already running for each person // We will either start a new analysis for each person or monitor the existing one until it is done - personIdentifiersList := []string{personAIdentifier, personBIdentifier} + personIdentifiersList := []string{person1Identifier, person2Identifier} for index, personIdentifier := range personIdentifiersList{ @@ -777,7 +845,7 @@ func StartCreateNewCoupleGeneticAnalysis(inputPersonAIdentifier string, inputPer return true, nil } - allPersonRawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(personAIdentifier) + allPersonRawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(person1Identifier) if (err != nil) { return false, err } genomesAreIdentical := helpers.CheckIfTwoListsContainIdenticalItems(allPersonRawGenomeIdentifiersList, newestPersonAnalysisListOfGenomesAnalyzed) @@ -873,33 +941,36 @@ func StartCreateNewCoupleGeneticAnalysis(inputPersonAIdentifier string, inputPer return nil, nil, errors.New("GetAllPersonGenomesMapList returning genome for different person.") } - genomeIdentifier, exists := genomeMap["GenomeIdentifier"] + genomeIdentifierHex, exists := genomeMap["GenomeIdentifier"] if (exists == false) { return nil, nil, errors.New("PersonGenomesMapList malformed: Item missing GenomeIdentifier") } + genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex) + if (err != nil) { return nil, nil, err } + genomeRawDataString, err := myGenomes.GetGenomeRawDataString(genomeIdentifier) if (err != nil) { return nil, nil, err } rawGenomeIsValid, rawGenomeWithMetadata, err := prepareRawGenomes.CreateRawGenomeWithMetadataObject(genomeIdentifier, genomeRawDataString) if (err != nil) { return nil, nil, err } if (rawGenomeIsValid == false){ - return nil, nil, errors.New("myGenomes contains invalid rawGenomeDataString: " + genomeIdentifier) + return nil, nil, errors.New("myGenomes contains invalid rawGenomeDataString: " + genomeIdentifierHex) } genomesList = append(genomesList, rawGenomeWithMetadata) - genomeIdentifiersList = append(genomeIdentifiersList, genomeIdentifier) + genomeIdentifiersList = append(genomeIdentifiersList, genomeIdentifierHex) } return genomesList, genomeIdentifiersList, nil } - personAGenomesList, personARawGenomeIdentifiersList, err := getPersonGenomesList(personAIdentifier) + person1GenomesList, person1RawGenomeIdentifiersList, err := getPersonGenomesList(person1Identifier) if (err != nil) { return err } updatePercentageCompleteFunction(70) - personBGenomesList, personBRawGenomeIdentifiersList, err := getPersonGenomesList(personBIdentifier) + person2GenomesList, person2RawGenomeIdentifiersList, err := getPersonGenomesList(person2Identifier) if (err != nil) { return err } updatePercentageCompleteFunction(74) @@ -915,15 +986,15 @@ func StartCreateNewCoupleGeneticAnalysis(inputPersonAIdentifier string, inputPer return nil } - processCompleted, newGeneticAnalysisString, err := createGeneticAnalysis.CreateCoupleGeneticAnalysis(personAGenomesList, personBGenomesList, updateCoupleAnalysisPercentageCompleteFunction, checkIfProcessIsStopped) + processCompleted, newGeneticAnalysisString, err := createGeneticAnalysis.CreateCoupleGeneticAnalysis(person1GenomesList, person2GenomesList, updateCoupleAnalysisPercentageCompleteFunction, checkIfProcessIsStopped) if (err != nil) { return err } if (processCompleted == false){ // User stopped the analysis mid-way return nil } - personAAnalyzedGenomesListString := strings.Join(personARawGenomeIdentifiersList, "+") - personBAnalyzedGenomesListString := strings.Join(personBRawGenomeIdentifiersList, "+") + person1AnalyzedGenomesListString := strings.Join(person1RawGenomeIdentifiersList, "+") + person2AnalyzedGenomesListString := strings.Join(person2RawGenomeIdentifiersList, "+") analysisIdentifier, err := helpers.GetNewRandomHexString(17) if (err != nil) { return err } @@ -935,12 +1006,12 @@ func StartCreateNewCoupleGeneticAnalysis(inputPersonAIdentifier string, inputPer currentAnalysisVersionString := helpers.ConvertIntToString(currentAnalysisVersion) newAnalysisMap := map[string]string{ - "PersonAIdentifier": personAIdentifier, - "PersonBIdentifier": personBIdentifier, + "Person1Identifier": person1Identifier, + "Person2Identifier": person2Identifier, "AnalysisIdentifier": analysisIdentifier, "TimeCreated": currentTimeString, - "PersonAAnalyzedGenomes": personAAnalyzedGenomesListString, - "PersonBAnalyzedGenomes": personBAnalyzedGenomesListString, + "Person1AnalyzedGenomes": person1AnalyzedGenomesListString, + "Person2AnalyzedGenomes": person2AnalyzedGenomesListString, "AnalysisVersion": currentAnalysisVersionString, } @@ -955,7 +1026,7 @@ func StartCreateNewCoupleGeneticAnalysis(inputPersonAIdentifier string, inputPer myAnalysesFolderPath := filepath.Join(userDirectory, "MyAnalyses") - analysisFileName := analysisIdentifier + ".json" + analysisFileName := analysisIdentifier + ".messagepack" err = localFilesystem.CreateOrOverwriteFile([]byte(newGeneticAnalysisString), myAnalysesFolderPath, analysisFileName) if (err != nil) { return err } diff --git a/internal/genetics/myChosenAnalysis/myChosenAnalysis.go b/internal/genetics/myChosenAnalysis/myChosenAnalysis.go index 5e97e9c..a71e7dc 100644 --- a/internal/genetics/myChosenAnalysis/myChosenAnalysis.go +++ b/internal/genetics/myChosenAnalysis/myChosenAnalysis.go @@ -8,11 +8,10 @@ package myChosenAnalysis +import "seekia/internal/genetics/geneticAnalysis" import "seekia/internal/genetics/myAnalyses" import "seekia/internal/genetics/myPeople" import "seekia/internal/genetics/readGeneticAnalysis" -import "seekia/internal/helpers" - import "seekia/internal/profiles/myLocalProfiles" import "errors" @@ -27,12 +26,12 @@ var myCacheChosenGeneticAnalysisMutex sync.RWMutex var myCacheChosenGeneticAnalysisIdentifier string // We use this variable to store the analysis in memory -// This prevents us from having to read and unmarshal the json file each time we want to retrieve the analysis +// This prevents us from having to read and unmarshal the messagepack file each time we want to retrieve the analysis // TODO: Read attributes from the analysis into maps for faster retrieval -var myCacheChosenGeneticAnalysis []map[string]string +var myCacheChosenGeneticAnalysis geneticAnalysis.PersonAnalysis // These variables store metadata about the cache genetic analysis -var myCacheChosenGeneticAnalysis_GenomeIdentifierToUse string +var myCacheChosenGeneticAnalysis_GenomeIdentifierToUse [16]byte // This variable tells us if the current cache chosen genetic analysis contains multiple genomes var myCacheChosenGeneticAnalysis_MultipleGenomesExist bool @@ -41,31 +40,35 @@ var myCacheChosenGeneticAnalysis_MultipleGenomesExist bool // The user must choose a person to link to their mate profile/identity // This genetic analysis is not shared on the person's profile publicly. // Portions of the analysis may be shared if the user opts in. +// The analysis returned by this function is not a copy, so fields must not be edited //Outputs: // -bool: Person identifier chosen // -bool: Any genomes exist // -bool: Person analysis is ready -// -[]map[string]string: Genetic analysis map list -// -string: Genome identifier to use +// -geneticAnalysis.PersonAnalysis: My Chosen Genetic analysis +// -[16]byte: Genome identifier to use // -bool: Multiple genomes exist // -error -func GetMyChosenMateGeneticAnalysis()(bool, bool, bool, []map[string]string, string, bool, error){ +func GetMyChosenMateGeneticAnalysis()(bool, bool, bool, geneticAnalysis.PersonAnalysis, [16]byte, bool, error){ + + // We use this when returning errors + emptyPersonAnalysis := geneticAnalysis.PersonAnalysis{} // We check if the user has added a genome person, and add their genome info if necessary myGenomePersonIdentifierExists, myGenomePersonIdentifier, err := myLocalProfiles.GetProfileData("Mate", "GenomePersonIdentifier") - if (err != nil){ return false, false, false, nil, "", false, err } + if (err != nil){ return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err } if (myGenomePersonIdentifierExists == false){ - return false, false, false, nil, "", false, nil + return false, false, false, emptyPersonAnalysis, [16]byte{}, false, nil } anyGenomesExist, personAnalysisIsReady, personAnalysisIdentifier, err := myPeople.CheckIfPersonAnalysisIsReady(myGenomePersonIdentifier) - if (err != nil){ return false, false, false, nil, "", false, err } + if (err != nil){ return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err } if (anyGenomesExist == false){ - return true, false, false, nil, "", false, nil + return true, false, false, emptyPersonAnalysis, [16]byte{}, false, nil } if (personAnalysisIsReady == false){ - return true, true, false, nil, "", false, nil + return true, true, false, emptyPersonAnalysis, [16]byte{}, false, nil } myCacheChosenGeneticAnalysisMutex.RLock() @@ -78,7 +81,8 @@ func GetMyChosenMateGeneticAnalysis()(bool, bool, bool, []map[string]string, str myCacheChosenGeneticAnalysisMutex.RLock() - myCacheChosenGeneticAnalysisCopy := helpers.DeepCopyStringToStringMapList(myCacheChosenGeneticAnalysis) + // This is a shallow copy, but it is all we need, because we never directly edit attributes of the returned object + myCacheChosenGeneticAnalysisCopy := myCacheChosenGeneticAnalysis myCacheChosenGeneticAnalysis_GenomeIdentifierToUseCopy := myCacheChosenGeneticAnalysis_GenomeIdentifierToUse myCacheChosenGeneticAnalysis_MultipleGenomesExistCopy := myCacheChosenGeneticAnalysis_MultipleGenomesExist @@ -89,18 +93,18 @@ func GetMyChosenMateGeneticAnalysis()(bool, bool, bool, []map[string]string, str } // The analysis and its metadata have not been read into the cache yet - // We will retrieve it from its .json file + // We will retrieve it from its .messagepack file - myAnalysisFound, myGeneticAnalysisMapList, err := myAnalyses.GetGeneticAnalysis(personAnalysisIdentifier) - if (err != nil){ return false, false, false, nil, "", false, err } + myAnalysisFound, myGeneticAnalysisObject, err := myAnalyses.GetPersonGeneticAnalysis(personAnalysisIdentifier) + if (err != nil){ return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err } if (myAnalysisFound == false){ - return false, false, false, nil, "", false, errors.New("CheckIfPersonAnalysisIsReady returning missing genetic analysis.") + return false, false, false, emptyPersonAnalysis, [16]byte{}, false, errors.New("CheckIfPersonAnalysisIsReady returning missing genetic analysis.") } - allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisMapList) - if (err != nil) { return false, false, false, nil, "", false, err } + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject) + if (err != nil) { return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err } - getGenomeIdentifierToUse := func()(string, error){ + getGenomeIdentifierToUse := func()([16]byte, error){ if (multipleGenomesExist == false){ @@ -111,7 +115,7 @@ func GetMyChosenMateGeneticAnalysis()(bool, bool, bool, []map[string]string, str } currentCombinedGenomeToUse, err := GetMyCombinedGenomeToUse() - if (err != nil){ return "", err } + if (err != nil){ return [16]byte{}, err } if (currentCombinedGenomeToUse == "Only Exclude Conflicts"){ return onlyExcludeConflictsGenomeIdentifier, nil @@ -121,16 +125,16 @@ func GetMyChosenMateGeneticAnalysis()(bool, bool, bool, []map[string]string, str } genomeIdentifierToUse, err := getGenomeIdentifierToUse() - if (err != nil) { return false, false, false, nil, "", false, err } + if (err != nil) { return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err } myCacheChosenGeneticAnalysisMutex.Lock() myCacheChosenGeneticAnalysisIdentifier = personAnalysisIdentifier - myCacheChosenGeneticAnalysis = myGeneticAnalysisMapList + myCacheChosenGeneticAnalysis = myGeneticAnalysisObject myCacheChosenGeneticAnalysis_GenomeIdentifierToUse = genomeIdentifierToUse myCacheChosenGeneticAnalysis_MultipleGenomesExist = multipleGenomesExist - myChosenGeneticAnalysisCopy := helpers.DeepCopyStringToStringMapList(myGeneticAnalysisMapList) + myChosenGeneticAnalysisCopy := myGeneticAnalysisObject myCacheChosenGeneticAnalysisMutex.Unlock() @@ -170,8 +174,8 @@ func SetMyCombinedGenomeToUse(combinedGenomeToUse string)error{ myCacheChosenGeneticAnalysisMutex.Lock() myCacheChosenGeneticAnalysisIdentifier = "" - myCacheChosenGeneticAnalysis = nil - myCacheChosenGeneticAnalysis_GenomeIdentifierToUse = "" + myCacheChosenGeneticAnalysis = geneticAnalysis.PersonAnalysis{} + myCacheChosenGeneticAnalysis_GenomeIdentifierToUse = [16]byte{} myCacheChosenGeneticAnalysis_MultipleGenomesExist = false myCacheChosenGeneticAnalysisMutex.Unlock() diff --git a/internal/genetics/myCouples/myCouples.go b/internal/genetics/myCouples/myCouples.go index ad2d981..30aa299 100644 --- a/internal/genetics/myCouples/myCouples.go +++ b/internal/genetics/myCouples/myCouples.go @@ -31,8 +31,8 @@ func InitializeMyGenomeCouplesDatastore()error{ //Outputs: // -[]map[string]string -// -PersonAIdentifier -> Person A Identifier -// -PersonBIdentifier -> Person B Identifier +// -Person1Identifier -> Person 1 Identifier +// -Person2Identifier -> Person 2 Identifier // -error func GetMyGenomeCouplesMapList()([]map[string]string, error){ @@ -45,9 +45,9 @@ func GetMyGenomeCouplesMapList()([]map[string]string, error){ //Outputs: // -bool: Couple already exists // -error -func AddCouple(inputPersonAIdentifier string, inputPersonBIdentifier string)(bool, error){ - - personAIdentifier, personBIdentifier, err := getPeopleIdentifiersSortedForCouple(inputPersonAIdentifier, inputPersonBIdentifier) +func AddCouple(inputPerson1Identifier string, inputPerson2Identifier string)(bool, error){ + + person1Identifier, person2Identifier, err := getPeopleIdentifiersSortedForCouple(inputPerson1Identifier, inputPerson2Identifier) if (err != nil) { return false, err } updatingMyCouplesMutex.Lock() @@ -58,17 +58,17 @@ func AddCouple(inputPersonAIdentifier string, inputPersonBIdentifier string)(boo for _, coupleMap := range couplesMapList{ - currentPersonAIdentifier, exists := coupleMap["PersonAIdentifier"] + currentPerson1Identifier, exists := coupleMap["Person1Identifier"] if (exists == false){ - return false, errors.New("MyGenomeCouplesMapList malformed: Contains entry missing PersonAIdentifier") + return false, errors.New("MyGenomeCouplesMapList malformed: Contains entry missing Person1Identifier") } - currentPersonBIdentifier, exists := coupleMap["PersonBIdentifier"] + currentPerson2Identifier, exists := coupleMap["Person2Identifier"] if (exists == false){ - return false, errors.New("MyGenomeCouplesMapList malformed: Contains entry missing PersonBIdentifier") + return false, errors.New("MyGenomeCouplesMapList malformed: Contains entry missing Person2Identifier") } - if (currentPersonAIdentifier == personAIdentifier && currentPersonBIdentifier == personBIdentifier){ + if (currentPerson1Identifier == person1Identifier && currentPerson2Identifier == person2Identifier){ return true, nil } } @@ -76,8 +76,8 @@ func AddCouple(inputPersonAIdentifier string, inputPersonBIdentifier string)(boo // Couple does not exist. We add it. newPersonMap := map[string]string{ - "PersonAIdentifier": personAIdentifier, - "PersonBIdentifier": personBIdentifier, + "Person1Identifier": person1Identifier, + "Person2Identifier": person2Identifier, } newCouplesMapList := append(couplesMapList, newPersonMap) @@ -88,23 +88,23 @@ func AddCouple(inputPersonAIdentifier string, inputPersonBIdentifier string)(boo return false, nil } -func DeleteCouple(inputPersonAIdentifier string, inputPersonBIdentifier string)error{ +func DeleteCouple(inputPerson1Identifier string, inputPerson2Identifier string)error{ - personAIdentifier, personBIdentifier, err := getPeopleIdentifiersSortedForCouple(inputPersonAIdentifier, inputPersonBIdentifier) + person1Identifier, person2Identifier, err := getPeopleIdentifiersSortedForCouple(inputPerson1Identifier, inputPerson2Identifier) if (err != nil) { return err } updatingMyCouplesMutex.Lock() defer updatingMyCouplesMutex.Unlock() mapToDelete := map[string]string{ - "PersonAIdentifier": personAIdentifier, - "PersonBIdentifier": personBIdentifier, + "Person1Identifier": person1Identifier, + "Person2Identifier": person2Identifier, } err = myGenomeCouplesMapListDatastore.DeleteMapListItems(mapToDelete) if (err != nil) { return err } - err = myAnalyses.DeleteAllAnalysesForCouple(personAIdentifier, personBIdentifier) + err = myAnalyses.DeleteAllAnalysesForCouple(person1Identifier, person2Identifier) if (err != nil) { return err } return nil @@ -115,18 +115,18 @@ func DeleteAllCouplesForPerson(personIdentifier string)error{ updatingMyCouplesMutex.Lock() defer updatingMyCouplesMutex.Unlock() - mapToDeleteA := map[string]string{ - "PersonAIdentifier": personIdentifier, + mapToDelete1 := map[string]string{ + "Person1Identifier": personIdentifier, } - err := myGenomeCouplesMapListDatastore.DeleteMapListItems(mapToDeleteA) + err := myGenomeCouplesMapListDatastore.DeleteMapListItems(mapToDelete1) if (err != nil) { return err } - mapToDeleteB := map[string]string{ - "PersonBIdentifier": personIdentifier, + mapToDelete2 := map[string]string{ + "Person2Identifier": personIdentifier, } - err = myGenomeCouplesMapListDatastore.DeleteMapListItems(mapToDeleteB) + err = myGenomeCouplesMapListDatastore.DeleteMapListItems(mapToDelete2) if (err != nil) { return err } return nil @@ -137,24 +137,24 @@ func GetNumberOfCouplesForPerson(personIdentifier string)(int, error){ totalCouples := 0 - lookupMapA := map[string]string{ - "PersonAIdentifier": personIdentifier, + lookupMap1 := map[string]string{ + "Person1Identifier": personIdentifier, } - anyItemsFoundA, matchingItemsListA, err := myGenomeCouplesMapListDatastore.GetMapListItems(lookupMapA) + anyItemsFound1, matchingItemsList1, err := myGenomeCouplesMapListDatastore.GetMapListItems(lookupMap1) if (err != nil){ return 0, err } - if (anyItemsFoundA == true){ - totalCouples += len(matchingItemsListA) + if (anyItemsFound1 == true){ + totalCouples += len(matchingItemsList1) } - lookupMapB := map[string]string{ - "PersonBIdentifier": personIdentifier, + lookupMap2 := map[string]string{ + "Person2Identifier": personIdentifier, } - anyItemsFoundB, matchingItemsListB, err := myGenomeCouplesMapListDatastore.GetMapListItems(lookupMapB) + anyItemsFound2, matchingItemsList2, err := myGenomeCouplesMapListDatastore.GetMapListItems(lookupMap2) if (err != nil){ return 0, err } - if (anyItemsFoundB == true){ - totalCouples += len(matchingItemsListB) + if (anyItemsFound2 == true){ + totalCouples += len(matchingItemsList2) } return totalCouples, nil @@ -163,16 +163,16 @@ func GetNumberOfCouplesForPerson(personIdentifier string)(int, error){ // This function is used to sort the person identifiers, so that each pair of identifiers will always map to the same couple when added // The sorting method has no significance -func getPeopleIdentifiersSortedForCouple(personAIdentifier string, personBIdentifier string)(string, string, error){ +func getPeopleIdentifiersSortedForCouple(person1Identifier string, person2Identifier string)(string, string, error){ - if (personAIdentifier == personBIdentifier){ - return "", "", errors.New("getPeopleIdentifiersSortedForCouple called with identical person identifiers: " + personAIdentifier) + if (person1Identifier == person2Identifier){ + return "", "", errors.New("getPeopleIdentifiersSortedForCouple called with identical person identifiers: " + person1Identifier) } - if (personAIdentifier < personBIdentifier){ - return personAIdentifier, personBIdentifier, nil + if (person1Identifier < person2Identifier){ + return person1Identifier, person2Identifier, nil } - return personBIdentifier, personAIdentifier, nil + return person2Identifier, person1Identifier, nil } diff --git a/internal/genetics/myGenomes/myGenomes.go b/internal/genetics/myGenomes/myGenomes.go index 2684684..1b77a2a 100644 --- a/internal/genetics/myGenomes/myGenomes.go +++ b/internal/genetics/myGenomes/myGenomes.go @@ -4,11 +4,12 @@ package myGenomes -import "seekia/internal/myDatastores/myMapList" -import "seekia/internal/genetics/readRawGenomes" -import "seekia/internal/localFilesystem" -import "seekia/internal/helpers" import "seekia/internal/cryptography/blake3" +import "seekia/internal/encoding" +import "seekia/internal/genetics/readRawGenomes" +import "seekia/internal/helpers" +import "seekia/internal/localFilesystem" +import "seekia/internal/myDatastores/myMapList" import "path/filepath" import "time" @@ -61,7 +62,7 @@ func InitializeMyGenomeDatastore()error{ // -SNPCount -> Number of readable SNPs in file // -CompanyName -> Company name ("23andMe", "AncestryDNA") // -ImportVersion -> Import version for the company from which the metadata was retrieved -// -FileHash -> 256 bits Blake3 hash of the genome file +// -FileHash -> 256 bits Blake3 hash of the genome file, encoded in Hex // -error func GetMyRawGenomesMetadataMapList()([]map[string]string, error){ @@ -152,18 +153,15 @@ func AddRawGenome(personIdentifier string, rawGenomeString string)(bool, bool, e return true, false, nil } -func DeleteMyRawGenome(genomeIdentifier string)error{ - - isValid := helpers.VerifyHexString(16, genomeIdentifier) - if (isValid == false){ - return errors.New("DeleteMyRawGenome called with invalid genomeIdentifier: " + genomeIdentifier) - } +func DeleteMyRawGenome(genomeIdentifier [16]byte)error{ updatingMyGenomesMutex.Lock() defer updatingMyGenomesMutex.Unlock() + genomeIdentifierHex := encoding.EncodeBytesToHexString(genomeIdentifier[:]) + mapToDelete := map[string]string{ - "GenomeIdentifier": genomeIdentifier, + "GenomeIdentifier": genomeIdentifierHex, } err := myGenomesMapListDatastore.DeleteMapListItems(mapToDelete) @@ -172,7 +170,7 @@ func DeleteMyRawGenome(genomeIdentifier string)error{ userDirectory, err := localFilesystem.GetAppUserFolderPath() if (err != nil) { return err } - genomeFileName := genomeIdentifier + ".txt" + genomeFileName := genomeIdentifierHex + ".txt" genomeFilePath := filepath.Join(userDirectory, "MyGenomes", genomeFileName) @@ -193,15 +191,17 @@ func DeleteMyRawGenome(genomeIdentifier string)error{ // -int: Import version // -string: FileHash // -error -func GetMyRawGenomeMetadata(genomeIdentifier string)(bool, string, int64, int64, bool, int64, string, int, string, error){ +func GetMyRawGenomeMetadata(genomeIdentifier [16]byte)(bool, string, int64, int64, bool, int64, string, int, string, error){ - if (genomeIdentifier == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" || genomeIdentifier == "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"){ + genomeIdentifierHex := encoding.EncodeBytesToHexString(genomeIdentifier[:]) + + if (genomeIdentifierHex == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" || genomeIdentifierHex == "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"){ // These are the genome identifiers we use for example reports // These are used to show the user what a genetic analysis would look like getPersonIdentifier := func()string{ - if (genomeIdentifier == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"){ + if (genomeIdentifierHex == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"){ return "111111111111111111111111111111" } @@ -214,7 +214,7 @@ func GetMyRawGenomeMetadata(genomeIdentifier string)(bool, string, int64, int64, } lookupMap := map[string]string{ - "GenomeIdentifier": genomeIdentifier, + "GenomeIdentifier": genomeIdentifierHex, } anyItemFound, foundItemsMapList, err := myGenomesMapListDatastore.GetMapListItems(lookupMap) @@ -225,7 +225,7 @@ func GetMyRawGenomeMetadata(genomeIdentifier string)(bool, string, int64, int64, return false, "", 0, 0, false, 0, "", 0, "", errors.New("Malformed myGenomesMapList: Contains multiple entries for same GenomeIdentifier") } genomeMap := foundItemsMapList[0] - + personIdentifier, exists := genomeMap["PersonIdentifier"] if (exists == false){ return false, "", 0, 0, false, 0, "", 0, "", errors.New("Malformed myGenomesMapList: Item missing PersonIdentifier") @@ -290,11 +290,13 @@ func GetMyRawGenomeMetadata(genomeIdentifier string)(bool, string, int64, int64, } // This function is used to refresh the genome metadata when a new import version is available -func RefreshRawGenomeMetadata(genomeIdentifier string)error{ +func RefreshRawGenomeMetadata(genomeIdentifier [16]byte)error{ updatingMyGenomesMutex.Lock() defer updatingMyGenomesMutex.Unlock() + genomeIdentifierHex := encoding.EncodeBytesToHexString(genomeIdentifier[:]) + myGenomesMapList, err := myGenomesMapListDatastore.GetMapList() if (err != nil) { return err } @@ -307,7 +309,7 @@ func RefreshRawGenomeMetadata(genomeIdentifier string)error{ return errors.New("myGenomesMapList item is malformed: item missing GenomeIdentifier.") } - if (currentGenomeIdentifier != genomeIdentifier){ + if (currentGenomeIdentifier != genomeIdentifierHex){ continue } if (foundGenome == true){ @@ -355,12 +357,14 @@ func RefreshRawGenomeMetadata(genomeIdentifier string)error{ //Outputs: // -string: Genome raw data string // -error -func GetGenomeRawDataString(genomeIdentifier string)(string, error){ +func GetGenomeRawDataString(genomeIdentifier [16]byte)(string, error){ + + genomeIdentifierHex := encoding.EncodeBytesToHexString(genomeIdentifier[:]) userDirectory, err := localFilesystem.GetAppUserFolderPath() if (err != nil) { return "", err } - genomeFileName := genomeIdentifier + ".txt" + genomeFileName := genomeIdentifierHex + ".txt" genomeFilePath := filepath.Join(userDirectory, "MyGenomes", genomeFileName) @@ -392,20 +396,25 @@ func GetAllPersonGenomesMapList(personIdentifier string)([]map[string]string, er } // This will not include any calculated genome identifiers, which only exist within analyses -func GetAllPersonRawGenomeIdentifiersList(personIdentifier string)([]string, error){ +func GetAllPersonRawGenomeIdentifiersList(personIdentifier string)([][16]byte, error){ allPersonGenomesMapList, err := GetAllPersonGenomesMapList(personIdentifier) if (err != nil) { return nil, err } - personGenomeIdentifiersList := make([]string, 0, len(allPersonGenomesMapList)) + personGenomeIdentifiersList := make([][16]byte, 0, len(allPersonGenomesMapList)) for _, genomeMap := range allPersonGenomesMapList{ - genomeIdentifier, exists := genomeMap["GenomeIdentifier"] + genomeIdentifierHex, exists := genomeMap["GenomeIdentifier"] if (exists == false){ return nil, errors.New("Malformed myGenomesMapList: Item missing GenomeIdentifier") } + genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex) + if (err != nil){ + return nil, errors.New("Malformed myGenomesMapList: Item contains invalid GenomeIdentifier: " + genomeIdentifierHex) + } + personGenomeIdentifiersList = append(personGenomeIdentifiersList, genomeIdentifier) } diff --git a/internal/genetics/myPeople/myPeople.go b/internal/genetics/myPeople/myPeople.go index 985828e..d601eb0 100644 --- a/internal/genetics/myPeople/myPeople.go +++ b/internal/genetics/myPeople/myPeople.go @@ -186,9 +186,9 @@ func GetPersonInfo(personIdentifier string)(bool, string, int64, string, error){ getPersonName := func()string{ if (personIdentifier == "111111111111111111111111111111"){ - return "Person A" + return "Person 1" } - return "Person B" + return "Person 2" } personName := getPersonName() diff --git a/internal/genetics/prepareRawGenomes/prepareRawGenomes.go b/internal/genetics/prepareRawGenomes/prepareRawGenomes.go index f612131..f32127f 100644 --- a/internal/genetics/prepareRawGenomes/prepareRawGenomes.go +++ b/internal/genetics/prepareRawGenomes/prepareRawGenomes.go @@ -18,7 +18,7 @@ import "strings" type RawGenomeWithMetadata struct{ - GenomeIdentifier string + GenomeIdentifier [16]byte GenomeIsPhased bool @@ -31,8 +31,7 @@ type GenomeWithMetadata struct{ // "Single"/"OnlyExcludeConflicts"/"OnlyIncludeShared" GenomeType string - // A 16 byte hex-encoded identifier - GenomeIdentifier string + GenomeIdentifier [16]byte // Map Structure: RSID -> Locus base pair value object GenomeMap map[int64]locusValue.LocusValue @@ -43,12 +42,7 @@ type GenomeWithMetadata struct{ // -bool: Raw genome string is valid // -RawGenomeWithMetadata // -error (returns non-nil if called with invalid genomeIdentifier) -func CreateRawGenomeWithMetadataObject(genomeIdentifier string, rawGenomeString string)(bool, RawGenomeWithMetadata, error){ - - isValid := helpers.VerifyHexString(16, genomeIdentifier) - if (isValid == false){ - return false, RawGenomeWithMetadata{}, errors.New("CreateRawGenomeWithMetadataObject called with invalid genomeIdentifier: " + genomeIdentifier) - } +func CreateRawGenomeWithMetadataObject(genomeIdentifier [16]byte, rawGenomeString string)(bool, RawGenomeWithMetadata, error){ rawDataReader := strings.NewReader(rawGenomeString) @@ -75,32 +69,32 @@ func CreateRawGenomeWithMetadataObject(genomeIdentifier string, rawGenomeString // -func(int)error: Update Percentage Complete Function //Outputs: // -[]GenomeWithMetadata: Genomes with metadata list -// -[]string: All raw genome identifiers list (not including combined genomes) +// -[][16]byte: All raw genome identifiers list (not including combined genomes) // -bool: Combined genomes exist -// -string: Only exclude conflicts genome identifier -// -string: Only include shared genome identifier +// -[16]byte: Only exclude conflicts genome identifier +// -[16]byte: Only include shared genome identifier // -error -func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWithMetadata, updatePercentageCompleteFunction func(int)error)([]GenomeWithMetadata, []string, bool, string, string, error){ +func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWithMetadata, updatePercentageCompleteFunction func(int)error)([]GenomeWithMetadata, [][16]byte, bool, [16]byte, [16]byte, error){ // The reading of genomes will take up the first 20% of the percentage range // The creation of multiple genomes will take up the last 80% of the percentage range // Structure: A list of genomes. - // Each map stores a genome from a company or a combined genome. + // Each map stores a genome from a company or a combined genome. genomesWithMetadataList := make([]GenomeWithMetadata, 0) numberOfGenomesRead := 0 totalNumberOfGenomesToRead := len(inputGenomesList) - allRawGenomeIdentifiersList := make([]string, 0) + allRawGenomeIdentifiersList := make([][16]byte, 0) for _, rawGenomeWithMetadataObject := range inputGenomesList{ newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, numberOfGenomesRead, 0, totalNumberOfGenomesToRead, 0, 20) - if (err != nil) { return nil, nil, false, "", "", err } + if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err } err = updatePercentageCompleteFunction(newPercentageCompletion) - if (err != nil) { return nil, nil, false, "", "", err } + if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err } genomeIdentifier := rawGenomeWithMetadataObject.GenomeIdentifier genomeIsPhased := rawGenomeWithMetadataObject.GenomeIsPhased @@ -150,7 +144,7 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi } aliasExists, rsidAliasesList, err := locusMetadata.GetRSIDAliases(rsID) - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } if (aliasExists == false){ continue } @@ -209,11 +203,11 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi containsDuplicates, _ := helpers.CheckIfListContainsDuplicates(allRawGenomeIdentifiersList) if (containsDuplicates == true){ - return nil, nil, false, "", "", errors.New("GetGenomesWithMetadataListFromRawGenomesList called with inputGenomesList containing duplicate genomeIdentifiers.") + return nil, nil, false, [16]byte{}, [16]byte{}, errors.New("GetGenomesWithMetadataListFromRawGenomesList called with inputGenomesList containing duplicate genomeIdentifiers.") } err := updatePercentageCompleteFunction(20) - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } if (len(genomesWithMetadataList) == 1){ @@ -221,9 +215,9 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi // No genome combining is needed. err = updatePercentageCompleteFunction(100) - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } - return genomesWithMetadataList, allRawGenomeIdentifiersList, false, "", "", nil + return genomesWithMetadataList, allRawGenomeIdentifiersList, false, [16]byte{}, [16]byte{}, nil } // Now we create the shared genomes @@ -241,10 +235,10 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi for index, genomeWithMetadataObject := range genomesWithMetadataList{ newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, index, 0, finalIndex, 20, 50) - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } err = updatePercentageCompleteFunction(newPercentageCompletion) - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } genomeMap := genomeWithMetadataObject.GenomeMap @@ -266,10 +260,10 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi for rsID, _ := range allRSIDsMap{ newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, index, 0, finalIndex, 50, 100) - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } err = updatePercentageCompleteFunction(newPercentageCompletion) - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } index += 1 @@ -280,7 +274,7 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi } anyAliasesExist, rsidAliasesList, err := locusMetadata.GetRSIDAliases(rsID) - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } if (anyAliasesExist == true){ for _, rsidAlias := range rsidAliasesList{ @@ -466,7 +460,7 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi } locusBase1, locusBase2, phaseIsKnown_OnlyExcludeConflicts, phaseIsKnown_OnlyIncludeShared, err := getLocusBasePair() - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } // Now we add to the combined genome maps // The OnlyExcludeConflicts will only omit when there is a tie @@ -493,21 +487,19 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi } } - onlyExcludeConflictsGenomeIdentifier, err := helpers.GetNewRandomHexString(16) - if (err != nil) { return nil, nil, false, "", "", err } + onlyExcludeConflictsGenomeIdentifier, err := helpers.GetNewRandom16ByteArray() + if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err } onlyExcludeConflictsGenomeWithMetadataObject := GenomeWithMetadata{ - GenomeType: "OnlyExcludeConflicts", GenomeIdentifier: onlyExcludeConflictsGenomeIdentifier, GenomeMap: onlyExcludeConflictsGenomeMap, } - onlyIncludeSharedGenomeIdentifier, err := helpers.GetNewRandomHexString(16) - if (err != nil) { return nil, nil, false, "", "", err } + onlyIncludeSharedGenomeIdentifier, err := helpers.GetNewRandom16ByteArray() + if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err } onlyIncludeSharedGenomeWithMetadataObject := GenomeWithMetadata{ - GenomeType: "OnlyIncludeShared", GenomeIdentifier: onlyIncludeSharedGenomeIdentifier, GenomeMap: onlyIncludeSharedGenomeMap, @@ -516,7 +508,7 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi genomesWithMetadataList = append(genomesWithMetadataList, onlyExcludeConflictsGenomeWithMetadataObject, onlyIncludeSharedGenomeWithMetadataObject) err = updatePercentageCompleteFunction(100) - if (err != nil){ return nil, nil, false, "", "", err } + if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err } return genomesWithMetadataList, allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, nil } diff --git a/internal/genetics/readGeneticAnalysis/readGeneticAnalysis.go b/internal/genetics/readGeneticAnalysis/readGeneticAnalysis.go index 747b919..4636042 100644 --- a/internal/genetics/readGeneticAnalysis/readGeneticAnalysis.go +++ b/internal/genetics/readGeneticAnalysis/readGeneticAnalysis.go @@ -7,230 +7,138 @@ import "seekia/resources/geneticReferences/monogenicDiseases" import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/resources/geneticReferences/traits" - +import "seekia/internal/encoding" +import "seekia/internal/genetics/geneticAnalysis" +import "seekia/internal/genetics/locusValue" import "seekia/internal/helpers" -import "encoding/json" -import "strings" import "errors" -// This works for person and couple analyses -func ReadGeneticAnalysisString(inputAnalysisString string)([]map[string]string, error){ + +// This converts a person genetic analysis MessagePack to a PersonAnalysis object +func ReadPersonGeneticAnalysisString(inputAnalysisString string)(geneticAnalysis.PersonAnalysis, error){ inputAnalysisBytes := []byte(inputAnalysisString) - var newAnalysisMapList []map[string]string + var newAnalysisObject geneticAnalysis.PersonAnalysis - err := json.Unmarshal(inputAnalysisBytes, &newAnalysisMapList) - if (err != nil) { return nil, err } + err := encoding.DecodeMessagePackBytes(false, inputAnalysisBytes, &newAnalysisObject) + if (err != nil) { return geneticAnalysis.PersonAnalysis{}, err } - return newAnalysisMapList, nil + return newAnalysisObject, nil } +// This converts a couple genetic analysis MessagePack to a CoupleAnalysis object +func ReadCoupleGeneticAnalysisString(inputAnalysisString string)(geneticAnalysis.CoupleAnalysis, error){ + + inputAnalysisBytes := []byte(inputAnalysisString) + + var newAnalysisObject geneticAnalysis.CoupleAnalysis + + err := encoding.DecodeMessagePackBytes(false, inputAnalysisBytes, &newAnalysisObject) + if (err != nil) { return geneticAnalysis.CoupleAnalysis{}, err } + + return newAnalysisObject, nil +} + + //Outputs: -// -[]string: All raw genome identifiers list (excluding combined genomes) +// -[][16]byte: All raw genome identifiers list (excluding combined genomes) // -bool: Multiple genomes exist -// -string: OnlyExcludeConflicts GenomeIdentifier -// -string: OnlyIncludeShared GenomeIdentifier +// -[16]byte: OnlyExcludeConflicts GenomeIdentifier +// -[16]byte: OnlyIncludeShared GenomeIdentifier // -error -func GetMetadataFromPersonGeneticAnalysis(inputGeneticAnalysisMapList []map[string]string)([]string, bool, string, string, error){ +func GetMetadataFromPersonGeneticAnalysis(inputGeneticAnalysis geneticAnalysis.PersonAnalysis)([][16]byte, bool, [16]byte, [16]byte, error){ - for _, element := range inputGeneticAnalysisMapList{ - - itemType, exists := element["ItemType"] - if (exists == false){ - return nil, false, "", "", errors.New("Malformed genetic analysis map list: Item missing itemType") - } - if (itemType != "Metadata"){ - continue - } - - analysisVersion, exists := element["AnalysisVersion"] - if (exists == false){ - return nil, false, "", "", errors.New("Cannot read analysis: Metadata missing analysisVersion") - } - - if (analysisVersion != "1"){ - // This analysis must have been created by a newer version of Seekia - // We cannot read it - return nil, false, "", "", errors.New("Cannot read analysis: Is a newer analysis version.") - } - - analysisType, exists := element["AnalysisType"] - if (exists == false || analysisType != "Person"){ - return nil, false, "", "", errors.New("Trying to get metadata from non-person analysis.") - } - - combinedGenomesExist, exists := element["CombinedGenomesExist"] - if (exists == false){ - return nil, false, "", "", errors.New("Malformed analysis map list: Metadata missing CombinedGenomesExist") - } - if (combinedGenomesExist == "No"){ - - mainGenomeIdentifier, exists := element["GenomeIdentifier"] - if (exists == false){ - return nil, false, "", "", errors.New("Malformed analysis map list: Single-genome analysis Metadata missing GenomeIdentifier") - } - - allRawGenomeIdentifiersList := []string{mainGenomeIdentifier} - - return allRawGenomeIdentifiersList, false, "", "", nil - } - - onlyExcludeConflictsGenomeIdentifier, exists := element["OnlyExcludeConflictsGenomeIdentifier"] - if (exists == false){ - return nil, false, "", "", errors.New("Malformed analysis map list: Metadata missing OnlyExcludeConflictsGenomeIdentifier") - } - onlyIncludeSharedGenomeIdentifier, exists := element["OnlyIncludeSharedGenomeIdentifier"] - if (exists == false){ - return nil, false, "", "", errors.New("Malformed genetic analysis map list: Metadata missing OnlyIncludeSharedGenomeIdentifier") - } - - allRawGenomeIdentifiersListString, exists := element["AllRawGenomeIdentifiersList"] - if (exists == false){ - return nil, false, "", "", errors.New("Malformed analysis map list: Multi-genome Metadata missing AllRawGenomeIdentifiersList") - } - - allRawGenomeIdentifiersList := strings.Split(allRawGenomeIdentifiersListString, "+") - - if (len(allRawGenomeIdentifiersList) < 2){ - return nil, false, "", "", errors.New("Malformed analysis map list: Multi-genome Metadata contains AllRawGenomeIdentifiersList with less than two identifiers") - } - - return allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, nil + analysisVersion := inputGeneticAnalysis.AnalysisVersion + if (analysisVersion != 1){ + // This analysis must have been created by a newer version of Seekia + // We cannot read it + return nil, false, [16]byte{}, [16]byte{}, errors.New("Cannot read analysis: Is a newer analysis version.") } - return nil, false, "", "", errors.New("GetMetadataFromPersonGeneticAnalysis called with malformed person analysis map list: missing Metadata item.") + allRawGenomeIdentifiersList := inputGeneticAnalysis.AllRawGenomeIdentifiersList + + combinedGenomesExist := inputGeneticAnalysis.CombinedGenomesExist + if (combinedGenomesExist == false){ + return allRawGenomeIdentifiersList, false, [16]byte{}, [16]byte{}, nil + } + + onlyExcludeConflictsGenomeIdentifier := inputGeneticAnalysis.OnlyExcludeConflictsGenomeIdentifier + onlyIncludeSharedGenomeIdentifier := inputGeneticAnalysis.OnlyIncludeSharedGenomeIdentifier + + return allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, nil } //Outputs: -// -string: Pair 1 Person A Genome Identifier -// -string: Pair 1 Person B Genome Identifier +// -[16]byte: Pair 1 Person 1 Genome Identifier +// -[16]byte: Pair 1 Person 2 Genome Identifier // -bool: Second Genome pair exists -// -string: Pair 2 Person A Genome Identifier -// -string: Pair 2 Person B Genome Identifier -// -bool: Person A Has Multiple Genomes -// -string: Person A OnlyExcludeConflicts Genome Identifier -// -string: Person A OnlyIncludeShared Genome Identifier -// -bool: Person B Has Multiple Genomes -// -string: Person B OnlyExcludeConflicts Genome Identifier -// -string: Person B OnlyIncludeShared Genome Identifier +// -[16]byte: Pair 2 Person 1 Genome Identifier +// -[16]byte: Pair 2 Person 2 Genome Identifier +// -bool: Person 1 Has Multiple Genomes +// -[16]byte: Person 1 OnlyExcludeConflicts Genome Identifier +// -[16]byte: Person 1 OnlyIncludeShared Genome Identifier +// -bool: Person 2 Has Multiple Genomes +// -[16]byte: Person 2 OnlyExcludeConflicts Genome Identifier +// -[16]byte: Person 2 OnlyIncludeShared Genome Identifier // -error -func GetMetadataFromCoupleGeneticAnalysis(inputGeneticAnalysisMapList []map[string]string)(string, string, bool, string, string, bool, string, string, bool, string, string, error){ +func GetMetadataFromCoupleGeneticAnalysis(inputGeneticAnalysis geneticAnalysis.CoupleAnalysis)([16]byte, [16]byte, bool, [16]byte, [16]byte, bool, [16]byte, [16]byte, bool, [16]byte, [16]byte, error){ - for _, element := range inputGeneticAnalysisMapList{ - - itemType, exists := element["ItemType"] - if (exists == false){ - return "", "", false, "", "", false, "", "", false, "", "", errors.New("Malformed genetic analysis map list: Item missing itemType") - } - if (itemType != "Metadata"){ - continue - } - - analysisVersion, exists := element["AnalysisVersion"] - if (exists == false){ - return "", "", false, "", "", false, "", "", false, "", "", errors.New("Cannot read analysis: Metadata missing analysisVersion") - } - - if (analysisVersion != "1"){ - // This analysis must have been created by a newer version of Seekia - // We cannot read it - return "", "", false, "", "", false, "", "", false, "", "", errors.New("Cannot read analysis: Is a newer analysis version.") - } - - analysisType, exists := element["AnalysisType"] - if (exists == false || analysisType != "Couple"){ - return "", "", false, "", "", false, "", "", false, "", "", errors.New("Trying to get metadata from non-couple analysis.") - } - - pair1PersonAGenomeIdentifier, exists := element["Pair1PersonAGenomeIdentifier"] - if (exists == false){ - return "", "", false, "", "", false, "", "", false, "", "", errors.New("Malformed couple analysis: Metadata missing Pair1PersonAGenomeIdentifier") - } - - pair1PersonBGenomeIdentifier, exists := element["Pair1PersonBGenomeIdentifier"] - if (exists == false){ - return "", "", false, "", "", false, "", "", false, "", "", errors.New("Malformed couple analysis: Metadata missing Pair1PersonBGenomeIdentifier") - } - - secondPairExists, exists := element["SecondPairExists"] - if (exists == false){ - return "", "", false, "", "", false, "", "", false, "", "", errors.New("Malformed analysis map list: Metadata missing CombinedGenomesExist") - } - if (secondPairExists == "No"){ - - return pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, false, "", "", false, "", "", false, "", "", nil - } - - pair2PersonAGenomeIdentifier, exists := element["Pair2PersonAGenomeIdentifier"] - if (exists == false){ - return "", "", false, "", "", false, "", "", false, "", "", errors.New("Malformed couple analysis map list: Metadata missing Pair2PersonAGenomeIdentifier") - } - - pair2PersonBGenomeIdentifier, exists := element["Pair2PersonBGenomeIdentifier"] - if (exists == false){ - return "", "", false, "", "", false, "", "", false, "", "", errors.New("Malformed couple analysis map list: Metadata missing Pair2PersonBGenomeIdentifier") - } - - getPersonAMultipleGenomesInfo := func()(bool, string, string, error){ - - personAHasMultipleGenomes, exists := element["PersonAHasMultipleGenomes"] - if (exists == false){ - return false, "", "", errors.New("Couple analysis malformed: Missing PersonAHasMultipleGenomes") - } - - if (personAHasMultipleGenomes == "No"){ - return false, "", "", nil - } - - personAOnlyExcludeConflictsGenomeIdentifier, exists := element["PersonAOnlyExcludeConflictsGenomeIdentifier"] - if (exists == false) { - return false, "", "", errors.New("Couple analysis malformed: Missing PersonAOnlyExcludeConflictsGenomeIdentifier") - } - - personAOnlyIncludeSharedGenomeIdentifier, exists := element["PersonAOnlyIncludeSharedGenomeIdentifier"] - if (exists == false) { - return false, "", "", errors.New("Couple analysis malformed: Missing PersonAOnlyIncludeSharedGenomeIdentifier") - } - - return true, personAOnlyExcludeConflictsGenomeIdentifier, personAOnlyIncludeSharedGenomeIdentifier, nil - } - - getPersonBMultipleGenomesInfo := func()(bool, string, string, error){ - - personBHasMultipleGenomes, exists := element["PersonBHasMultipleGenomes"] - if (exists == false){ - return false, "", "", errors.New("Couple analysis malformed: Missing PersonBHasMultipleGenomes") - } - - if (personBHasMultipleGenomes == "No"){ - return false, "", "", nil - } - - personBOnlyExcludeConflictsGenomeIdentifier, exists := element["PersonBOnlyExcludeConflictsGenomeIdentifier"] - if (exists == false) { - return false, "", "", errors.New("Couple analysis malformed: Missing PersonBOnlyExcludeConflictsGenomeIdentifier") - } - - personBOnlyIncludeSharedGenomeIdentifier, exists := element["PersonBOnlyIncludeSharedGenomeIdentifier"] - if (exists == false) { - return false, "", "", errors.New("Couple analysis malformed: Missing PersonBOnlyIncludeSharedGenomeIdentifier") - } - - return true, personBOnlyExcludeConflictsGenomeIdentifier, personBOnlyIncludeSharedGenomeIdentifier, nil - } - - personAHasMultipleGenomes, personAOnlyExcludeConflictsGenomeIdentifier, personAOnlyIncludeSharedGenomeIdentifier, err := getPersonAMultipleGenomesInfo() - if (err != nil) { return "", "", false, "", "", false, "", "", false, "", "", err } - - personBHasMultipleGenomes, personBOnlyExcludeConflictsGenomeIdentifier, personBOnlyIncludeSharedGenomeIdentifier, err := getPersonBMultipleGenomesInfo() - if (err != nil) { return "", "", false, "", "", false, "", "", false, "", "", err } - - return pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, true, pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier, personAHasMultipleGenomes, personAOnlyExcludeConflictsGenomeIdentifier, personAOnlyIncludeSharedGenomeIdentifier, personBHasMultipleGenomes, personBOnlyExcludeConflictsGenomeIdentifier, personBOnlyIncludeSharedGenomeIdentifier, nil + analysisVersion := inputGeneticAnalysis.AnalysisVersion + if (analysisVersion != 1){ + // This analysis must have been created by a newer version of Seekia + // We cannot read it + return [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, errors.New("Cannot read analysis: Is a newer analysis version.") } - return "", "", false, "", "", false, "", "", false, "", "", errors.New("GetMetadataFromCoupleGeneticAnalysis called with malformed couple analysis map list: missing Metadata item.") + pair1Person1GenomeIdentifier := inputGeneticAnalysis.Pair1Person1GenomeIdentifier + pair1Person2GenomeIdentifier := inputGeneticAnalysis.Pair1Person2GenomeIdentifier + + secondPairExists := inputGeneticAnalysis.SecondPairExists + if (secondPairExists == false){ + + return pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, nil + } + + pair2Person1GenomeIdentifier := inputGeneticAnalysis.Pair2Person1GenomeIdentifier + pair2Person2GenomeIdentifier := inputGeneticAnalysis.Pair2Person2GenomeIdentifier + + getPerson1MultipleGenomesInfo := func()(bool, [16]byte, [16]byte, error){ + + person1HasMultipleGenomes := inputGeneticAnalysis.Person1HasMultipleGenomes + if (person1HasMultipleGenomes == false){ + return false, [16]byte{}, [16]byte{}, nil + } + + person1OnlyExcludeConflictsGenomeIdentifier := inputGeneticAnalysis.Person1OnlyExcludeConflictsGenomeIdentifier + + person1OnlyIncludeSharedGenomeIdentifier := inputGeneticAnalysis.Person1OnlyIncludeSharedGenomeIdentifier + + return true, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, nil + } + + getPerson2MultipleGenomesInfo := func()(bool, [16]byte, [16]byte, error){ + + person2HasMultipleGenomes := inputGeneticAnalysis.Person2HasMultipleGenomes + if (person2HasMultipleGenomes == false){ + return false, [16]byte{}, [16]byte{}, nil + } + + person2OnlyExcludeConflictsGenomeIdentifier := inputGeneticAnalysis.Person2OnlyExcludeConflictsGenomeIdentifier + + person2OnlyIncludeSharedGenomeIdentifier := inputGeneticAnalysis.Person2OnlyIncludeSharedGenomeIdentifier + + return true, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, nil + } + + person1HasMultipleGenomes, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, err := getPerson1MultipleGenomesInfo() + if (err != nil) { return [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, err } + + person2HasMultipleGenomes, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, err := getPerson2MultipleGenomesInfo() + if (err != nil) { return [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, err } + + return pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, true, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, person1HasMultipleGenomes, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, person2HasMultipleGenomes, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, nil } @@ -239,29 +147,29 @@ func GetMetadataFromCoupleGeneticAnalysis(inputGeneticAnalysisMapList []map[stri // It returns the equivalent genome identifier from the person analysis // This is needed for combined genomes, because their identifiers are generated randomly for each analysis //Inputs: -// -bool: Is person A -// -[]map[string]string: Person A Analysis Map List -// -[]map[string]string: Person B Analysis Map List -// -[]map[string]string: Couple Analysis Map List -// -string: Input Genome Identifier (Should be from Couple identifier) +// -bool: Is person 1 +// -geneticAnalysis.PersonAnalysis: Person 1 Analysis +// -geneticAnalysis.PersonAnalysis: Person 2 Analysis +// -geneticAnalysis.CoupleAnalysis: Couple Analysis +// -[16]byte: Input Genome Identifier (Should be from Couple identifier) //Outputs: -// -string: Genome identifier from person analysis +// -[16]byte: Genome identifier from person analysis // -bool: Person analysis has multiple genomes // -bool: Genome is a combined genome // -string: Genome combined type ("Only Exclude Conflicts"/"Only Include Shared") // -error -func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPersonA bool, personAAnalysisMapList []map[string]string, personBAnalysisMapList []map[string]string, coupleAnalysisMapList []map[string]string, inputGenomeIdentifier string)(string, bool, bool, string, error){ +func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1 bool, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, inputGenomeIdentifier [16]byte)([16]byte, bool, bool, string, error){ // We need to figure out which genome identifier the current genome identifier corresponds to within the person analysis // If the genome is not a combined genome, then the genome identifier should be identical between each analysis // If it is a combined genome, we need to determine which genome it corresponds to - _, _, secondGenomePairExists, _, _, personAHasMultipleGenomes, personAOnlyExcludeConflictsGenomeIdentifier, personAOnlyIncludeSharedGenomeIdentifier, personBHasMultipleGenomes, personBOnlyExcludeConflictsGenomeIdentifier, personBOnlyIncludeSharedGenomeIdentifier, err := GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) - if (err != nil) { return "", false, false, "", err } + _, _, secondGenomePairExists, _, _, person1HasMultipleGenomes, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, person2HasMultipleGenomes, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, err := GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) + if (err != nil) { return [16]byte{}, false, false, "", err } - if (isPersonA == true){ + if (isPerson1 == true){ - if (personAHasMultipleGenomes == false){ + if (person1HasMultipleGenomes == false){ // This person does not have multiple genomes. The genome identifier is the same between both analyses return inputGenomeIdentifier, false, false, "", nil } @@ -270,24 +178,24 @@ func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPersonA bool, return inputGenomeIdentifier, true, false, "", nil } - _, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(personAAnalysisMapList) - if (err != nil) { return "", false, false, "", err } + _, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(person1AnalysisObject) + if (err != nil) { return [16]byte{}, false, false, "", err } if (multipleGenomesExist == false){ - return "", false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.") + return [16]byte{}, false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.") } - if (inputGenomeIdentifier == personAOnlyExcludeConflictsGenomeIdentifier){ + if (inputGenomeIdentifier == person1OnlyExcludeConflictsGenomeIdentifier){ return onlyExcludeConflictsGenomeIdentifier, true, true, "Only Exclude Conflicts", nil } - if (inputGenomeIdentifier == personAOnlyIncludeSharedGenomeIdentifier){ + if (inputGenomeIdentifier == person1OnlyIncludeSharedGenomeIdentifier){ return onlyIncludeSharedGenomeIdentifier, true, true, "Only Include Shared", nil } - return "", false, false, "", errors.New("Combined genome identifier from couple analysis does not correspond to either combined genome from person analysis.") + return [16]byte{}, false, false, "", errors.New("Combined genome identifier from couple analysis does not correspond to either combined genome from person analysis.") } - // isPersonA == false + // isPerson1 == false - if (personBHasMultipleGenomes == false){ + if (person2HasMultipleGenomes == false){ // This person does not have multiple genomes. The genome identifier is the same between both analyses return inputGenomeIdentifier, false, false, "", nil } @@ -296,262 +204,171 @@ func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPersonA bool, return inputGenomeIdentifier, true, false, "", nil } - _, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(personBAnalysisMapList) - if (err != nil) { return "", false, false, "", err } + _, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(person2AnalysisObject) + if (err != nil) { return [16]byte{}, false, false, "", err } if (multipleGenomesExist == false){ - return "", false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.") + return [16]byte{}, false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.") } - if (inputGenomeIdentifier == personBOnlyExcludeConflictsGenomeIdentifier){ + if (inputGenomeIdentifier == person2OnlyExcludeConflictsGenomeIdentifier){ return onlyExcludeConflictsGenomeIdentifier, true, true, "Only Exclude Conflicts", nil } - if (inputGenomeIdentifier == personBOnlyIncludeSharedGenomeIdentifier){ + if (inputGenomeIdentifier == person2OnlyIncludeSharedGenomeIdentifier){ return onlyIncludeSharedGenomeIdentifier, true, true, "Only Include Shared", nil } - return "", false, false, "", errors.New("Combined genome identifier from couple analysis does not correspond to either combined genome from person analysis.") + return [16]byte{}, false, false, "", errors.New("Combined genome identifier from couple analysis does not correspond to either combined genome from person analysis.") } //Outputs: -// -bool: Probabilities known -// -int: Probability of having disease -// -string: Probability of having disease formatted (with % suffix) +// -bool: Disease info known +// -bool: Person has disease // -int: Probability of passing a disease variant // -string: Probability of passing a disease variant formatted (with % suffix) // -int: Number of variants tested -// -bool: Conflict exists +// -int: Number of loci tested +// -int: Number of phased loci +// -bool: Conflict exists // -error -func GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisMapList []map[string]string, diseaseName string, genomeIdentifier string, personHasMultipleGenomes bool)(bool, int, string, int, string, int, bool, error){ +func GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, bool, int, string, int, int, int, bool, error){ - for _, element := range personAnalysisMapList{ + personMonogenicDiseasesMap := personAnalysisObject.MonogenicDiseasesMap - itemType, exists := element["ItemType"] - if (exists == false) { - return false, 0, "", 0, "", 0, false, errors.New("Malformed analysisMapList: Item missing itemType") - } - - if (itemType != "MonogenicDisease"){ - continue - } - - currentDiseaseName, exists := element["DiseaseName"] - if (exists == false){ - return false, 0, "", 0, "", 0, false, errors.New("Malformed analysisMapList: Monogenic Disease item missing DiseaseName") - } - - if (currentDiseaseName != diseaseName){ - continue - } - - numberOfVariantsTested, exists := element[genomeIdentifier + "_NumberOfVariantsTested"] - if (exists == false){ - return false, 0, "", 0, "", 0, false, errors.New("Malformed person analysis: Monogenic Disease item missing _NumberOfVariantsTested") - } - - numberOfVariantsTestedInt, err := helpers.ConvertStringToInt(numberOfVariantsTested) - if (err != nil){ - return false, 0, "", 0, "", 0, false, errors.New("Malformed person analysis: Monogenic Disease item contains invalid _NumberOfVariantsTested: " + numberOfVariantsTested) - } - - getConflictExistsBool := func()(bool, error){ - - if (personHasMultipleGenomes == false){ - return false, nil - } - - conflictExists, exists := element["ConflictExists"] - if (exists == false) { - return false, errors.New("Malformed analysisMapList: Missing ConflictExists") - } - - if (conflictExists == "Yes"){ - return true, nil - } - return false, nil - } - - conflictExistsBool, err := getConflictExistsBool() - if (err != nil) { return false, 0, "", 0, "", 0, false, err } - - probabilityOfHavingDisease, exists := element[genomeIdentifier + "_ProbabilityOfHavingDisease"] - if (exists == false) { - return false, 0, "", 0, "", 0, false, errors.New("Malformed analysisMapList: Monogenic Disease map missing _ProbabilityOfHavingDisease") - } - - if (probabilityOfHavingDisease == "Unknown"){ - return false, 0, "", 0, "", numberOfVariantsTestedInt, conflictExistsBool, nil - } - - probabilityOfHavingDiseaseInt, err := helpers.ConvertStringToInt(probabilityOfHavingDisease) - if (err != nil){ - return false, 0, "", 0, "", 0, false, errors.New("Malformed person analysis: Monogenic Disease map contains invalid _ProbabilityOfHavingDisease: " + probabilityOfHavingDisease) - } - - probabilityOfHavingDiseaseFormatted := probabilityOfHavingDisease + "%" - - probabilityOfPassingAVariant, exists := element[genomeIdentifier + "_ProbabilityOfPassingADiseaseVariant"] - if (exists == false) { - return false, 0, "", 0, "", 0, false, errors.New("Malformed analysisMapList: Monogenic Disease map missing _ProbabilityOfPassingADiseaseVariant") - } - - probabilityOfPassingAVariantInt, err := helpers.ConvertStringToInt(probabilityOfPassingAVariant) - if (err != nil){ - return false, 0, "", 0, "", 0, false, errors.New("Malformed person analysis: Monogenic Disease map contains invalid _ProbabilityOfPassingADiseaseVariant: " + probabilityOfPassingAVariant) - } - - probabilityOfPassingAVariantFormatted := probabilityOfPassingAVariant + "%" - - return true, probabilityOfHavingDiseaseInt, probabilityOfHavingDiseaseFormatted, probabilityOfPassingAVariantInt, probabilityOfPassingAVariantFormatted, numberOfVariantsTestedInt, conflictExistsBool, nil + personMonogenicDiseaseInfo, exists := personMonogenicDiseasesMap[diseaseName] + if (exists == false){ + return false, false, 0, "", 0, 0, 0, false, nil } - return false, 0, "", 0, "", 0, false, errors.New("GetPersonMonogenicDiseaseInfoFromGeneticAnalysis failed: Cannot find monogenicDisease item in person analysis. Disease name: " + diseaseName) + personMonogenicDiseaseInfoMap := personMonogenicDiseaseInfo.MonogenicDiseaseInfoMap + + genomeMonogenicDiseaseInfo, exists := personMonogenicDiseaseInfoMap[genomeIdentifier] + if (exists == false){ + return false, false, 0, "", 0, 0, 0, false, nil + } + + conflictExists := personMonogenicDiseaseInfo.ConflictExists + + personHasDisease := genomeMonogenicDiseaseInfo.PersonHasDisease + numberOfVariantsTested := genomeMonogenicDiseaseInfo.NumberOfVariantsTested + numberOfLociTested := genomeMonogenicDiseaseInfo.NumberOfLociTested + numberOfPhasedLoci := genomeMonogenicDiseaseInfo.NumberOfPhasedLoci + probabilityOfPassingAVariant := genomeMonogenicDiseaseInfo.ProbabilityOfPassingADiseaseVariant + + probabilityOfPassingAVariantString := helpers.ConvertIntToString(probabilityOfPassingAVariant) + + probabilityOfPassingAVariantFormatted := probabilityOfPassingAVariantString + "%" + + return true, personHasDisease, probabilityOfPassingAVariant, probabilityOfPassingAVariantFormatted, numberOfVariantsTested, numberOfLociTested, numberOfPhasedLoci, conflictExists, nil } + //Outputs: -// -bool: Probabilities known +// -bool: Probability Offspring Has Disease Is Known // -int: Percentage Probability of offspring having disease // -string: Probability of offspring having disease formatted (with % suffix) +// -bool: Probability Offspring Has Variant Is Known // -int: Percentage probability of offspring having a disease variant // -string: Percentage probability of offspring having a disease variant formatted (with % suffix) // -bool: Conflict exists between genome pairs // -error -func GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisMapList []map[string]string, diseaseName string, genomePairIdentifier string, secondGenomePairExists bool)(bool, int, string, int, string, bool, error){ +func GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, string, bool, int, string, bool, error){ - for _, element := range coupleAnalysisMapList{ + offspringMonogenicDiseasesMap := coupleAnalysisObject.MonogenicDiseasesMap - itemType, exists := element["ItemType"] - if (exists == false) { - return false, 0, "", 0, "", false, errors.New("Malformed couple analysis: Item missing itemType") - } - - if (itemType != "MonogenicDisease"){ - continue - } - - currentDiseaseName, exists := element["DiseaseName"] - if (exists == false){ - return false, 0, "", 0, "", false, errors.New("Malformed couple analysis: Monogenic Disease item missing DiseaseName") - } - - if (diseaseName != currentDiseaseName){ - continue - } - - getConflictExistsBool := func()(bool, error){ - - if (secondGenomePairExists == false){ - return false, nil - } - - conflictExists, exists := element["ConflictExists"] - if (exists == false) { - return false, errors.New("Malformed couple analysis: Monogenic disease map missing ConflictExists") - } - - if (conflictExists == "Yes"){ - return true, nil - } - - return false, nil - } - - conflictExistsBool, err := getConflictExistsBool() - if (err != nil) { return false, 0, "", 0, "", false, err } - - probabilityOfOffspringHavingDisease, exists := element[genomePairIdentifier + "_ProbabilityOffspringHasDisease"] - if (exists == false) { - return false, 0, "", 0, "", false, errors.New("Malformed couple analysis: Monogenic Disease map missing _ProbabilityOffspringHasDisease") - } - - if (probabilityOfOffspringHavingDisease == "Unknown"){ - return false, 0, "", 0, "", conflictExistsBool, nil - } - - probabilityOfOffspringHavingDiseaseInt, err := helpers.ConvertStringToInt(probabilityOfOffspringHavingDisease) - if (err != nil){ - return false, 0, "", 0, "", false, errors.New("Malformed couple analysis: Monogenic Disease map contains invalid _ProbabilityOffspringHasDisease: " + probabilityOfOffspringHavingDisease) - } - - probabilityOfOffspringHavingDiseaseFormatted := probabilityOfOffspringHavingDisease + "%" - - probabilityOfOffspringHavingVariant, exists := element[genomePairIdentifier + "_ProbabilityOffspringHasVariant"] - if (exists == false) { - return false, 0, "", 0, "", false, errors.New("Malformed analysisMapList: Monogenic Disease map missing _ProbabilityOffspringHasVariant") - } - - probabilityOfOffspringHavingVariantInt, err := helpers.ConvertStringToInt(probabilityOfOffspringHavingVariant) - if (err != nil){ - return false, 0, "", 0, "", false, errors.New("Malformed analysisMapList: Monogenic Disease map missing _ProbabilityOffspringHasVariant") - } - - probabilityOfOffspringHavingVariantFormatted := probabilityOfOffspringHavingVariant + "%" - - return true, probabilityOfOffspringHavingDiseaseInt, probabilityOfOffspringHavingDiseaseFormatted, probabilityOfOffspringHavingVariantInt, probabilityOfOffspringHavingVariantFormatted, conflictExistsBool, nil + offspringMonogenicDiseaseInfoObject, exists := offspringMonogenicDiseasesMap[diseaseName] + if (exists == false){ + return false, 0, "", false, 0, "", false, nil } - return false, 0, "", 0, "", false, errors.New("GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis called with invalid analysis: Cannot find disease info in couple analysis: " + diseaseName) + monogenicDiseaseInfoMap := offspringMonogenicDiseaseInfoObject.MonogenicDiseaseInfoMap + + offspringGenomePairMonogenicDiseaseInfo, exists := monogenicDiseaseInfoMap[genomePairIdentifier] + if (exists == false){ + return false, 0, "", false, 0, "", false, nil + } + + conflictExists := offspringMonogenicDiseaseInfoObject.ConflictExists + + probabilityOffspringHasDiseaseIsKnown := offspringGenomePairMonogenicDiseaseInfo.ProbabilityOffspringHasDiseaseIsKnown + probabilityOffspringHasDisease := offspringGenomePairMonogenicDiseaseInfo.ProbabilityOffspringHasDisease + probabilityOffspringHasVariantIsKnown := offspringGenomePairMonogenicDiseaseInfo.ProbabilityOffspringHasVariantIsKnown + probabilityOffspringHasVariant := offspringGenomePairMonogenicDiseaseInfo.ProbabilityOffspringHasVariant + + getProbabilityOffspringHasDiseaseFormatted := func()string{ + if (probabilityOffspringHasDiseaseIsKnown == false){ + return "" + } + + probabilityOffspringHasDiseaseString := helpers.ConvertIntToString(probabilityOffspringHasDisease) + + probabilityOffspringHasDiseaseFormatted := probabilityOffspringHasDiseaseString + "%" + + return probabilityOffspringHasDiseaseFormatted + } + + probabilityOffspringHasDiseaseFormatted := getProbabilityOffspringHasDiseaseFormatted() + + getProbabilityOffspringHasVariantFormatted := func()string{ + if (probabilityOffspringHasVariantIsKnown == false){ + return "" + } + + probabilityOffspringHasVariantString := helpers.ConvertIntToString(probabilityOffspringHasVariant) + + probabilityOffspringHasVariantFormatted := probabilityOffspringHasVariantString + "%" + + return probabilityOffspringHasVariantFormatted + } + + probabilityOffspringHasVariantFormatted := getProbabilityOffspringHasVariantFormatted() + + return probabilityOffspringHasDiseaseIsKnown, probabilityOffspringHasDisease, probabilityOffspringHasDiseaseFormatted, probabilityOffspringHasVariantIsKnown, probabilityOffspringHasVariant, probabilityOffspringHasVariantFormatted, conflictExists, nil } + //Outputs: -// -bool: Number of mutations known +// -bool: Number of mutations is known // -int: Number of mutations // -error -func GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(personAnalysisMapList []map[string]string, diseaseName string, variantIdentifier string, genomeIdentifier string)(bool, int, error){ +func GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, variantIdentifier [3]byte, genomeIdentifier [16]byte)(bool, int, error){ - for _, element := range personAnalysisMapList{ + personMonogenicDiseasesMap := personAnalysisObject.MonogenicDiseasesMap - itemType, exists := element["ItemType"] - if (exists == false) { - return false, 0, errors.New("Malformed person analysis: Item missing itemType") - } - - if (itemType != "MonogenicDiseaseVariant"){ - continue - } - - currentDiseaseName, exists := element["DiseaseName"] - if (exists == false){ - return false, 0, errors.New("Malformed person analysis: Monogenic Disease item missing DiseaseName") - } - - if (currentDiseaseName != diseaseName){ - continue - } - - currentVariantIdentifier, exists := element["VariantIdentifier"] - if (exists == false) { - return false, 0, errors.New("Malformed person analysis: MonogenicDiseaseVariant item missing VariantIdentifier") - } - - if (currentVariantIdentifier != variantIdentifier){ - continue - } - - genomeHasVariantValue, exists := element[genomeIdentifier + "_HasVariant"] - if (exists == false){ - return false, 0, errors.New("Malformed person analysis: MonogenicDiseaseVariant item missing _HasVariant key for genome") - } - - if (genomeHasVariantValue == "Unknown"){ - return false, 0, nil - } - if (genomeHasVariantValue == "No;No"){ - return true, 0, nil - } - if (genomeHasVariantValue == "Yes;No" || genomeHasVariantValue == "No;Yes"){ - return true, 1, nil - } - if (genomeHasVariantValue == "Yes;Yes"){ - return true, 2, nil - } - - return false, 0, errors.New("Malformed person analysis: Invalid genomeHasVariantValue: " + genomeHasVariantValue) + personMonogenicDiseaseInfo, exists := personMonogenicDiseasesMap[diseaseName] + if (exists == false){ + return false, 0, nil } - return false, 0, errors.New("GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis failed: Info not found.") -} + personMonogenicDiseaseInfoMap := personMonogenicDiseaseInfo.MonogenicDiseaseInfoMap + genomeMonogenicDiseaseInfo, exists := personMonogenicDiseaseInfoMap[genomeIdentifier] + if (exists == false){ + return false, 0, nil + } + + variantsInfoMap := genomeMonogenicDiseaseInfo.VariantsInfoMap + + variantInfoObject, exists := variantsInfoMap[variantIdentifier] + if (exists == false){ + // The genome's variant info is unknown for this variant + return false, 0, nil + } + + base1HasVariant := variantInfoObject.Base1HasVariant + base2HasVariant := variantInfoObject.Base2HasVariant + + numberOfMutations := 0 + + if (base1HasVariant == true){ + numberOfMutations += 1 + } + if (base2HasVariant == true){ + numberOfMutations += 1 + } + + return true, numberOfMutations, nil +} //Outputs: // -bool: Offspring Probabilities Known @@ -565,402 +382,227 @@ func GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(personAnalysisMapLi // -int: Upper Bound Percentage probability of 2 mutations // -string: Percentage probability of 2 mutations formatted (has % suffix and - between non-identical bounds) // -error -func GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisMapList []map[string]string, diseaseName string, variantIdentifier string, genomePairIdentifier string)(bool, int, int, string, int, int, string, int, int, string, error){ +func GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, variantIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, int, string, int, int, string, int, int, string, error){ - for _, element := range coupleAnalysisMapList{ + coupleMonogenicDiseasesMap := coupleAnalysisObject.MonogenicDiseasesMap - itemType, exists := element["ItemType"] - if (exists == false) { - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed coupleAnalysisMapList: Item missing itemType") - } - - if (itemType != "MonogenicDiseaseVariant"){ - continue - } - currentDiseaseName, exists := element["DiseaseName"] - if (exists == false) { - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed couple Analysis: MonogenicDiseaseVariant item missing DiseaseName") - } - - if (currentDiseaseName != diseaseName){ - continue - } - - currentVariantIdentifier, exists := element["VariantIdentifier"] - if (exists == false) { - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed couple analysis: MonogenicDiseaseVariant item missing VariantIdentifier") - } - - if (currentVariantIdentifier != variantIdentifier){ - continue - } - - probabilityOf0MutationsLowerBound, exists := element[genomePairIdentifier + "_ProbabilityOf0MutationsLowerBound"] - if (exists == false){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed analysisMapList: MonogenicDiseaseVariant item missing _ProbabilityOf0MutationsLowerBound key") - } - if (probabilityOf0MutationsLowerBound == "Unknown"){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", nil - } - - probabilityOf0MutationsUpperBound, exists := element[genomePairIdentifier + "_ProbabilityOf0MutationsUpperBound"] - if (exists == false){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed analysisMapList: MonogenicDiseaseVariant item missing _ProbabilityOf0MutationsUpperBound") - } - - probabilityOf1MutationLowerBound, exists := element[genomePairIdentifier + "_ProbabilityOf1MutationLowerBound"] - if (exists == false){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed analysisMapList: MonogenicDiseaseVariant item missing _ProbabilityOf1MutationLowerBound key") - } - - probabilityOf1MutationUpperBound, exists := element[genomePairIdentifier + "_ProbabilityOf1MutationUpperBound"] - if (exists == false){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed analysisMapList: MonogenicDiseaseVariant item missing _ProbabilityOf1MutationUpperBound") - } - - probabilityOf2MutationsLowerBound, exists := element[genomePairIdentifier + "_ProbabilityOf2MutationsLowerBound"] - if (exists == false){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed analysisMapList: MonogenicDiseaseVariant item missing _ProbabilityOf2MutationsLowerBound key") - } - - probabilityOf2MutationsUpperBound, exists := element[genomePairIdentifier + "_ProbabilityOf2MutationsUpperBound"] - if (exists == false){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed analysisMapList: MonogenicDiseaseVariant item missing _ProbabilityOf2MutationsUpperBound") - } - - probabilityOf0MutationsLowerBoundInt, err := helpers.ConvertStringToInt(probabilityOf0MutationsLowerBound) - if (err != nil){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed couple analysis: MonogenicDiseaseVariant contains invalid _ProbabilityOf0MutationsLowerBound: " + probabilityOf0MutationsLowerBound) - } - - probabilityOf0MutationsUpperBoundInt, err := helpers.ConvertStringToInt(probabilityOf0MutationsUpperBound) - if (err != nil){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed couple analysis: MonogenicDiseaseVariant contains invalid _ProbabilityOf0MutationsUpperBound: " + probabilityOf0MutationsUpperBound) - } - - probabilityOf1MutationLowerBoundInt, err := helpers.ConvertStringToInt(probabilityOf1MutationLowerBound) - if (err != nil){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed couple analysis: MonogenicDiseaseVariant contains invalid _ProbabilityOf1MutationLowerBound: " + probabilityOf1MutationLowerBound) - } - - probabilityOf1MutationUpperBoundInt, err := helpers.ConvertStringToInt(probabilityOf1MutationUpperBound) - if (err != nil){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed couple analysis: MonogenicDiseaseVariant contains invalid _ProbabilityOf1MutationUpperBound: " + probabilityOf1MutationUpperBound) - } - - probabilityOf2MutationsLowerBoundInt, err := helpers.ConvertStringToInt(probabilityOf2MutationsLowerBound) - if (err != nil){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed couple analysis: MonogenicDiseaseVariant contains invalid _ProbabilityOf2MutationsLowerBound: " + probabilityOf2MutationsLowerBound) - } - - probabilityOf2MutationsUpperBoundInt, err := helpers.ConvertStringToInt(probabilityOf2MutationsUpperBound) - if (err != nil){ - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("Malformed couple analysis: MonogenicDiseaseVariant contains invalid _ProbabilityOf2MutationsUpperBound: " + probabilityOf2MutationsUpperBound) - } - - getOffspringProbabilityOf0MutationsFormatted := func()string{ - - probabilityOf0MutationsLowerBoundFormatted := probabilityOf0MutationsLowerBound + "%" - - if (probabilityOf0MutationsLowerBoundInt == probabilityOf0MutationsUpperBoundInt){ - return probabilityOf0MutationsLowerBoundFormatted - } - - probabilityOf0MutationsUpperBoundFormatted := probabilityOf0MutationsUpperBound + "%" - - formattedResult := probabilityOf0MutationsLowerBoundFormatted + " - " + probabilityOf0MutationsUpperBoundFormatted - - return formattedResult - } - - probabilityOf0MutationsFormatted := getOffspringProbabilityOf0MutationsFormatted() - - getOffspringProbabilityOf1MutationFormatted := func()string{ - - probabilityOf1MutationLowerBoundFormatted := probabilityOf1MutationLowerBound + "%" - - if (probabilityOf1MutationLowerBoundInt == probabilityOf1MutationUpperBoundInt){ - return probabilityOf1MutationLowerBoundFormatted - } - - probabilityOf1MutationUpperBoundFormatted := probabilityOf1MutationUpperBound + "%" - - formattedResult := probabilityOf1MutationLowerBoundFormatted + " - " + probabilityOf1MutationUpperBoundFormatted - - return formattedResult - } - - probabilityOf1MutationFormatted := getOffspringProbabilityOf1MutationFormatted() - - getOffspringProbabilityOf2MutationsFormatted := func()string{ - - probabilityOf2MutationsLowerBoundFormatted := probabilityOf2MutationsLowerBound + "%" - - if (probabilityOf2MutationsLowerBoundInt == probabilityOf2MutationsUpperBoundInt){ - return probabilityOf2MutationsLowerBoundFormatted - } - - probabilityOf2MutationsUpperBoundFormatted := probabilityOf2MutationsUpperBound + "%" - - formattedResult := probabilityOf2MutationsLowerBoundFormatted + " - " + probabilityOf2MutationsUpperBoundFormatted - - return formattedResult - } - - probabilityOf2MutationsFormatted := getOffspringProbabilityOf2MutationsFormatted() - - return true, probabilityOf0MutationsLowerBoundInt, probabilityOf0MutationsUpperBoundInt, probabilityOf0MutationsFormatted, probabilityOf1MutationLowerBoundInt, probabilityOf1MutationUpperBoundInt, probabilityOf1MutationFormatted, probabilityOf2MutationsLowerBoundInt, probabilityOf2MutationsUpperBoundInt, probabilityOf2MutationsFormatted, nil + coupleMonogenicDiseaseInfo, exists := coupleMonogenicDiseasesMap[diseaseName] + if (exists == false){ + return false, 0, 0, "", 0, 0, "", 0, 0, "", nil } - return false, 0, 0, "", 0, 0, "", 0, 0, "", errors.New("GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis failed: Info not found.") + coupleMonogenicDiseaseInfoMap := coupleMonogenicDiseaseInfo.MonogenicDiseaseInfoMap + + genomePairMonogenicDiseaseInfo, exists := coupleMonogenicDiseaseInfoMap[genomePairIdentifier] + if (exists == false){ + return false, 0, 0, "", 0, 0, "", 0, 0, "", nil + } + + genomePairVariantsInfoMap := genomePairMonogenicDiseaseInfo.VariantsInfoMap + + variantInfoObject, exists := genomePairVariantsInfoMap[variantIdentifier] + if (exists == false){ + return false, 0, 0, "", 0, 0, "", 0, 0, "", nil + } + + probabilityOf0MutationsLowerBound := variantInfoObject.ProbabilityOf0MutationsLowerBound + probabilityOf0MutationsUpperBound := variantInfoObject.ProbabilityOf0MutationsUpperBound + + probabilityOf1MutationLowerBound := variantInfoObject.ProbabilityOf1MutationLowerBound + probabilityOf1MutationUpperBound := variantInfoObject.ProbabilityOf1MutationUpperBound + + probabilityOf2MutationsLowerBound := variantInfoObject.ProbabilityOf2MutationsLowerBound + probabilityOf2MutationsUpperBound := variantInfoObject.ProbabilityOf2MutationsUpperBound + + getOffspringProbabilityOf0MutationsFormatted := func()string{ + + probabilityOf0MutationsLowerBoundString := helpers.ConvertIntToString(probabilityOf0MutationsLowerBound) + + probabilityOf0MutationsLowerBoundFormatted := probabilityOf0MutationsLowerBoundString + "%" + + if (probabilityOf0MutationsLowerBound == probabilityOf0MutationsUpperBound){ + return probabilityOf0MutationsLowerBoundFormatted + } + + probabilityOf0MutationsUpperBoundString := helpers.ConvertIntToString(probabilityOf0MutationsUpperBound) + + probabilityOf0MutationsUpperBoundFormatted := probabilityOf0MutationsUpperBoundString + "%" + + formattedResult := probabilityOf0MutationsLowerBoundFormatted + " - " + probabilityOf0MutationsUpperBoundFormatted + + return formattedResult + } + + probabilityOf0MutationsFormatted := getOffspringProbabilityOf0MutationsFormatted() + + getOffspringProbabilityOf1MutationFormatted := func()string{ + + probabilityOf1MutationLowerBoundString := helpers.ConvertIntToString(probabilityOf1MutationLowerBound) + + probabilityOf1MutationLowerBoundFormatted := probabilityOf1MutationLowerBoundString + "%" + + if (probabilityOf1MutationLowerBound == probabilityOf1MutationUpperBound){ + return probabilityOf1MutationLowerBoundFormatted + } + + probabilityOf1MutationUpperBoundString := helpers.ConvertIntToString(probabilityOf1MutationUpperBound) + + probabilityOf1MutationUpperBoundFormatted := probabilityOf1MutationUpperBoundString + "%" + + formattedResult := probabilityOf1MutationLowerBoundFormatted + " - " + probabilityOf1MutationUpperBoundFormatted + + return formattedResult + } + + probabilityOf1MutationFormatted := getOffspringProbabilityOf1MutationFormatted() + + getOffspringProbabilityOf2MutationsFormatted := func()string{ + + probabilityOf2MutationsLowerBoundString := helpers.ConvertIntToString(probabilityOf2MutationsLowerBound) + + probabilityOf2MutationsLowerBoundFormatted := probabilityOf2MutationsLowerBoundString + "%" + + if (probabilityOf2MutationsLowerBound == probabilityOf2MutationsUpperBound){ + return probabilityOf2MutationsLowerBoundFormatted + } + + probabilityOf2MutationsUpperBoundString := helpers.ConvertIntToString(probabilityOf2MutationsUpperBound) + + probabilityOf2MutationsUpperBoundFormatted := probabilityOf2MutationsUpperBoundString + "%" + + formattedResult := probabilityOf2MutationsLowerBoundFormatted + " - " + probabilityOf2MutationsUpperBoundFormatted + + return formattedResult + } + + probabilityOf2MutationsFormatted := getOffspringProbabilityOf2MutationsFormatted() + + return true, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, probabilityOf0MutationsFormatted, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, probabilityOf1MutationFormatted, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, probabilityOf2MutationsFormatted, nil } //Outputs: // -bool: Polygenic Disease Risk Score known // -int: Disease risk score -// -string: Disease risk score formatted (contains "/10" suffix) +// -string: Disease risk score formatted (has "/10" suffix) // -int: Number of loci tested // -bool: Conflict exists // -error -func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisMapList []map[string]string, diseaseName string, genomeIdentifier string, personHasMultipleGenomes bool)(bool, int, string, int, bool, error){ +func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, int, string, int, bool, error){ - for _, element := range personAnalysisMapList{ + personPolygenicDiseasesMap := personAnalysisObject.PolygenicDiseasesMap - analysisItemType, exists := element["ItemType"] - if (exists == false) { - return false, 0, "", 0, false, errors.New("Malformed person analysis: Item missing ItemType") - } - - if (analysisItemType != "PolygenicDisease"){ - continue - } - - itemDiseaseName, exists := element["DiseaseName"] - if (exists == false) { - return false, 0, "", 0, false, errors.New("Malformed person analysis: PolygenicDisease item missing DiseaseName") - } - - if (itemDiseaseName != diseaseName){ - continue - } - - numberOfLociTested, exists := element[genomeIdentifier + "_NumberOfLociTested"] - if (exists == false){ - return false, 0, "", 0, false, errors.New("Malformed person analysis: PolygenicDisease item missing _NumberOfLociTested") - } - - numberOfLociTestedInt, err := helpers.ConvertStringToInt(numberOfLociTested) - if (err != nil){ - return false, 0, "", 0, false, errors.New("Malformed person analysis: PolygenicDisease item contains invalid _NumberOfLociTested: " + numberOfLociTested) - } - - getConflictExistsBool := func()(bool, error){ - - if (personHasMultipleGenomes == false){ - return false, nil - } - - conflictExists, exists := element["ConflictExists"] - if (exists == false) { - return false, errors.New("Malformed analysisMapList: PolygenicDisease analysis missing ConflictExists") - } - - if (conflictExists == "Yes"){ - return true, nil - } - - return false, nil - } - - conflictExistsBool, err := getConflictExistsBool() - if (err != nil) { return false, 0, "", 0, false, err } - - if (numberOfLociTestedInt == 0){ - return false, 0, "", numberOfLociTestedInt, conflictExistsBool, nil - } - - personRiskScore, exists := element[genomeIdentifier + "_RiskScore"] - if (exists == false){ - return false, 0, "", 0, false, errors.New("Person analysis missing _RiskScore key.") - } - - personRiskScoreInt, err := helpers.ConvertStringToInt(personRiskScore) - if (err != nil) { - return false, 0, "", 0, false, errors.New("Person analysis contains invalid _RiskScore: " + personRiskScore) - } - - personRiskScoreFormatted := personRiskScore + "/10" - - return true, personRiskScoreInt, personRiskScoreFormatted, numberOfLociTestedInt, conflictExistsBool, nil + personPolygenicDiseaseInfo, exists := personPolygenicDiseasesMap[diseaseName] + if (exists == false){ + return false, 0, "", 0, false, nil } - return false, 0, "", 0, false, errors.New("GetPersonPolygenicDiseaseInfoFromGeneticAnalysis failed: Disease info not found.") + personPolygenicDiseaseInfoMap := personPolygenicDiseaseInfo.PolygenicDiseaseInfoMap + + genomePolygenicDiseaseInfo, exists := personPolygenicDiseaseInfoMap[genomeIdentifier] + if (exists == false){ + return false, 0, "", 0, false, nil + } + + conflictExists := personPolygenicDiseaseInfo.ConflictExists + + personDiseaseRiskScore := genomePolygenicDiseaseInfo.RiskScore + + numberOfLociTested := genomePolygenicDiseaseInfo.NumberOfLociTested + + personDiseaseRiskScoreString := helpers.ConvertIntToString(personDiseaseRiskScore) + + personDiseaseRiskScoreFormatted := personDiseaseRiskScoreString + "/10" + + return true, personDiseaseRiskScore, personDiseaseRiskScoreFormatted, numberOfLociTested, conflictExists, nil } //Outputs: -// -bool: Disease Risk Score known +// -bool: Offspring Disease Risk Score known // -int: Disease risk score -// -string: Disease risk score formatted (contains "/10" suffix) +// -string: Disease risk score formatted (has "/10" suffix) // -int: Number of loci tested // -bool: Conflict exists // -error -func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisMapList []map[string]string, diseaseName string, genomePairIdentifier string, secondGenomePairExists bool)(bool, int, string, int, bool, error){ +func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, string, int, bool, error){ - for _, element := range coupleAnalysisMapList{ - - analysisItemType, exists := element["ItemType"] - if (exists == false) { - return false, 0, "", 0, false, errors.New("Malformed couple analysis: Item missing ItemType") - } + couplePolygenicDiseasesMap := coupleAnalysisObject.PolygenicDiseasesMap - if (analysisItemType != "PolygenicDisease"){ - continue - } - - itemDiseaseName, exists := element["DiseaseName"] - if (exists == false) { - return false, 0, "", 0, false, errors.New("Malformed couple analysis: PolygenicDisease item missing DiseaseName") - } - - if (itemDiseaseName != diseaseName){ - continue - } - - numberOfLociTested, exists := element[genomePairIdentifier + "_NumberOfLociTested"] - if (exists == false){ - return false, 0, "", 0, false, errors.New("Malformed couple analysis: PolygenicDisease item missing _NumberOfLociTested") - } - - numberOfLociTestedInt, err := helpers.ConvertStringToInt(numberOfLociTested) - if (err != nil){ - return false, 0, "", 0, false, errors.New("Malformed couple analysis: PolygenicDisease item contains invalid _NumberOfLociTested: " + numberOfLociTested) - } - - getConflictExistsBool := func()(bool, error){ - - if (secondGenomePairExists == false){ - return false, nil - } - - conflictExists, exists := element["ConflictExists"] - if (exists == false) { - return false, errors.New("Malformed analysisMapList: PolygenicDisease analysis missing ConflictExists") - } - - if (conflictExists == "Yes"){ - return true, nil - } - - return false, nil - } - - conflictExistsBool, err := getConflictExistsBool() - if (err != nil) { return false, 0, "", 0, false, err } - - offspringRiskScore, exists := element[genomePairIdentifier + "_OffspringRiskScore"] - if (exists == false){ - return false, 0, "", 0, false, errors.New("Couple analysis missing _RiskScore key.") - } - - if (offspringRiskScore == "Unknown"){ - return false, 0, "", numberOfLociTestedInt, conflictExistsBool, nil - } - - offspringRiskScoreInt, err := helpers.ConvertStringToInt(offspringRiskScore) - if (err != nil) { - return false, 0, "", 0, false, errors.New("Couple analysis contains invalid _RiskScore: " + offspringRiskScore) - } - - offspringRiskScoreFormatted := offspringRiskScore + "/10" - - return true, offspringRiskScoreInt, offspringRiskScoreFormatted, numberOfLociTestedInt, conflictExistsBool, nil + couplePolygenicDiseaseInfo, exists := couplePolygenicDiseasesMap[diseaseName] + if (exists == false){ + return false, 0, "", 0, false, nil } - return false, 0, "", 0, false, errors.New("GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis failed: Cannot find disease info for disease: " + diseaseName) + polygenicDiseaseInfoMap := couplePolygenicDiseaseInfo.PolygenicDiseaseInfoMap + + genomePairPolygenicDiseaseInfo, exists := polygenicDiseaseInfoMap[genomePairIdentifier] + if (exists == false){ + return false, 0, "", 0, false, nil + } + + conflictExists := couplePolygenicDiseaseInfo.ConflictExists + + numberOfLociTested := genomePairPolygenicDiseaseInfo.NumberOfLociTested + + offspringRiskScore := genomePairPolygenicDiseaseInfo.OffspringRiskScore + + offspringRiskScoreString := helpers.ConvertIntToString(offspringRiskScore) + + offspringRiskScoreFormatted := offspringRiskScoreString + "/10" + + return true, offspringRiskScore, offspringRiskScoreFormatted, numberOfLociTested, conflictExists, nil } //Outputs: // -bool: Risk Weight and base pair known // -int: Locus risk weight -// -string: Locus base pair +// -string: Locus base 1 +// -string: Locus base 2 // -bool: Locus odds ratio known // -float64: Locus odds ratio // -string: Locus odds ratio formatted (with x suffix) // -error -func GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalyisMapList []map[string]string, diseaseName string, locusIdentifier string, genomeIdentifier string)(bool, int, string, bool, float64, string, error){ +func GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalyisObject geneticAnalysis.PersonAnalysis, diseaseName string, locusIdentifier [3]byte, genomeIdentifier [16]byte)(bool, int, string, string, bool, float64, string, error){ - for _, element := range personAnalyisMapList{ + personPolygenicDiseasesMap := personAnalyisObject.PolygenicDiseasesMap - itemType, exists := element["ItemType"] - if (exists == false) { - return false, 0, "", false, 0, "", errors.New("Malformed person analysis: Item missing itemType") - } - - if (itemType != "PolygenicDiseaseLocus"){ - continue - } - - currentDiseaseName, exists := element["DiseaseName"] - if (exists == false){ - return false, 0, "", false, 0, "", errors.New("Malformed person analysis: PolygenicDiseaseLocus item missing DiseaseName") - } - - if (currentDiseaseName != diseaseName){ - continue - } - - currentLocusIdentifier, exists := element["LocusIdentifier"] - if (exists == false) { - return false, 0, "", false, 0, "", errors.New("Malformed person analysis: PolygenicDiseaseLocus item missing LocusIdentifier") - } - - if (currentLocusIdentifier != locusIdentifier){ - continue - } - - genomeLocusRiskWeight, exists := element[genomeIdentifier + "_RiskWeight"] - if (exists == false){ - return false, 0, "", false, 0, "", errors.New("Malformed person analysis: PolygenicDiseaseLocus item missing _RiskWeight") - } - - if (genomeLocusRiskWeight == "Unknown"){ - return false, 0, "", false, 0, "", nil - } - - locusBasePair, exists := element[genomeIdentifier + "_LocusBasePair"] - if (exists == false){ - return false, 0, "", false, 0, "", errors.New("Malformed person analysis: PolygenicDiseaseLocus item missing LocusBasePair") - } - - genomeLocusRiskWeightInt, err := helpers.ConvertStringToInt(genomeLocusRiskWeight) - if (err != nil){ - return false, 0, "", false, 0, "", errors.New("Malformed person analysis: PolygenicDiseaseLocus item contains invalid _RiskWeight: " + genomeLocusRiskWeight) - } - - genomeLocusOddsRatio, exists := element[genomeIdentifier + "_OddsRatio"] - if (exists == false){ - return false, 0, "", false, 0, "", errors.New("Malformed person analysis: PolygenicDiseaseLocus item missing _OddsRatio") - } - - if (genomeLocusOddsRatio == "Unknown"){ - return true, genomeLocusRiskWeightInt, locusBasePair, false, 0, "", nil - } - - locusOddsRatioFloat64, err := helpers.ConvertStringToFloat64(genomeLocusOddsRatio) - if (err != nil){ - return false, 0, "", false, 0, "", errors.New("Malformed person analysis: PolygenicDiseaseLocus item contains invalid _OddsRatio: " + genomeLocusOddsRatio) - } - - genomeLocusOddsRatioString := helpers.ConvertFloat64ToStringRounded(locusOddsRatioFloat64, 2) - - locusOddsRatioFormatted := genomeLocusOddsRatioString + "x" - - return true, genomeLocusRiskWeightInt, locusBasePair, true, locusOddsRatioFloat64, locusOddsRatioFormatted, nil + personPolygenicDiseaseMap, exists := personPolygenicDiseasesMap[diseaseName] + if (exists == false){ + return false, 0, "", "", false, 0, "", nil } - return false, 0, "", false, 0, "", errors.New("GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis failed: Locus info not found.") + personPolygenicDiseaseInfoMap := personPolygenicDiseaseMap.PolygenicDiseaseInfoMap + + personGenomePolygenicDiseaseInfo, exists := personPolygenicDiseaseInfoMap[genomeIdentifier] + if (exists == false){ + return false, 0, "", "", false, 0, "", nil + } + + genomeLociInfoMap := personGenomePolygenicDiseaseInfo.LociInfoMap + + locusInfoObject, exists := genomeLociInfoMap[locusIdentifier] + if (exists == false){ + return false, 0, "", "", false, 0, "", nil + } + + locusRiskWeight := locusInfoObject.RiskWeight + + locusBase1 := locusInfoObject.LocusBase1 + locusBase2 := locusInfoObject.LocusBase2 + + locusOddsRatioIsKnown := locusInfoObject.OddsRatioIsKnown + if (locusOddsRatioIsKnown == false){ + return true, locusRiskWeight, locusBase1, locusBase2, false, 0, "", nil + } + + locusOddsRatio := locusInfoObject.OddsRatio + + genomeLocusOddsRatioString := helpers.ConvertFloat64ToStringRounded(locusOddsRatio, 2) + + locusOddsRatioFormatted := genomeLocusOddsRatioString + "x" + + return true, locusRiskWeight, locusBase1, locusBase2, true, locusOddsRatio, locusOddsRatioFormatted, nil } //Outputs: @@ -970,238 +612,99 @@ func GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalyisMapList // -float64: Offspring odds ratio // -string: Offspring odds ratio formatted (with + and < from unknownFactors weight sum and x suffix) // -error -func GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisMapList []map[string]string, diseaseName string, locusIdentifier string, genomePairIdentifier string)(bool, int, bool, float64, string, error){ +func GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, locusIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, bool, float64, string, error){ - for _, element := range coupleAnalysisMapList{ + offspringPolygenicDiseasesMap := coupleAnalysisObject.PolygenicDiseasesMap - itemType, exists := element["ItemType"] - if (exists == false) { - return false, 0, false, 0, "", errors.New("Malformed couple analysis: Item missing itemType") - } - - if (itemType != "PolygenicDiseaseLocus"){ - continue - } - currentDiseaseName, exists := element["DiseaseName"] - if (exists == false) { - return false, 0, false, 0, "", errors.New("Malformed couple analysis: PolygenicDiseaseLocus item missing DiseaseName") - } - - if (currentDiseaseName != diseaseName){ - continue - } - - currentLocusIdentifier, exists := element["LocusIdentifier"] - if (exists == false) { - return false, 0, false, 0, "", errors.New("Malformed couple analysis: PolygenicDiseaseLocus item missing LocusIdentifier") - } - - if (currentLocusIdentifier != locusIdentifier){ - continue - } - - offspringRiskWeight, exists := element[genomePairIdentifier + "_OffspringRiskWeight"] - if (exists == false){ - return false, 0, false, 0, "", errors.New("Malformed couple analysis: PolygenicDiseaseLocus item missing _OffspringRiskWeight key") - } - - if (offspringRiskWeight == "Unknown"){ - return false, 0, false, 0, "", nil - } - - offspringRiskWeightInt, err := helpers.ConvertStringToInt(offspringRiskWeight) - if (err != nil){ - return false, 0, false, 0, "", errors.New("Malformed couple analysis: PolygenicDiseaseLocus item contains invalid _OffspringRiskWeight key: " + offspringRiskWeight) - } - - offspringOddsRatio, exists := element[genomePairIdentifier + "_OffspringOddsRatio"] - if (exists == false){ - return false, 0, false, 0, "", errors.New("Malformed couple analysis: PolygenicDiseaseLocus item missing _OffspringOddsRatio key") - } - if (offspringOddsRatio == "Unknown"){ - return true, offspringRiskWeightInt, false, 0, "", nil - } - - offspringOddsRatioFloat64, err := helpers.ConvertStringToFloat64(offspringOddsRatio) - if (err != nil){ - return false, 0, false, 0, "", errors.New("Malformed couple analysis: Contains invalid _OffspringOddsRatio: " + offspringOddsRatio) - } - - offspringUnknownOddsRatiosWeightSum, exists := element[genomePairIdentifier + "_OffspringUnknownOddsRatiosWeightSum"] - if (exists == false){ - return false, 0, false, 0, "", errors.New("Malformed couple analysis: PolygenicDiseaseLocus item missing _OffspringUnknownOddsRatiosWeightSum key") - } - - offspringUnknownOddsRatiosWeightSumInt, err := helpers.ConvertStringToInt(offspringUnknownOddsRatiosWeightSum) - if (err != nil){ - return false, 0, false, 0, "", errors.New("Malformed couple analysis: PolygenicDiseaseLocus item contains invalid _OffspringUnknownOddsRatiosWeightSum key: " + offspringUnknownOddsRatiosWeightSum) - } - - getOddsRatioFormatted := func()string{ - - if (offspringUnknownOddsRatiosWeightSumInt == 0){ - result := offspringOddsRatio + "x" - return result - } - if (offspringUnknownOddsRatiosWeightSumInt < 0){ - result := "<" + offspringOddsRatio + "x" - return result - } - // offspringUnknownOddsRatiosWeightSumInt > 0 - result := offspringOddsRatio + "x+" - return result - } - - oddsRatioFormatted := getOddsRatioFormatted() - - return true, offspringRiskWeightInt, true, offspringOddsRatioFloat64, oddsRatioFormatted, nil + offspringPolygenicDiseaseInfo, exists := offspringPolygenicDiseasesMap[diseaseName] + if (exists == false){ + return false, 0, false, 0, "", nil } - return false, 0, false, 0, "", errors.New("Malformed couple analysis: Polygenic Disease locus not found.") -} + offspringPolygenicDiseaseMap := offspringPolygenicDiseaseInfo.PolygenicDiseaseInfoMap -type LocusValue struct{ + genomePairPolygenicDiseaseInfo, exists := offspringPolygenicDiseaseMap[genomePairIdentifier] + if (exists == false){ + return false, 0, false, 0, "", nil + } - Base1 string - Base2 string + genomePairLociInfoMap := genomePairPolygenicDiseaseInfo.LociInfoMap - // Are the bases ordered or in random order? - LocusIsPhased bool + locusInfoObject, exists := genomePairLociInfoMap[locusIdentifier] + if (exists == false){ + return false, 0, false, 0, "", nil + } + + offspringRiskWeight := locusInfoObject.OffspringRiskWeight + offspringOddsRatioIsKnown := locusInfoObject.OffspringOddsRatioIsKnown + + if (offspringOddsRatioIsKnown == false){ + return true, offspringRiskWeight, false, 0, "", nil + } + + offspringOddsRatio := locusInfoObject.OffspringOddsRatio + + getOddsRatioFormatted := func()string{ + + offspringUnknownOddsRatiosWeightSum := locusInfoObject.OffspringUnknownOddsRatiosWeightSum + + offspringOddsRatioString := helpers.ConvertFloat64ToStringRounded(offspringOddsRatio, 2) + + if (offspringUnknownOddsRatiosWeightSum == 0){ + result := offspringOddsRatioString + "x" + + return result + } + if (offspringUnknownOddsRatiosWeightSum < 0){ + result := "<" + offspringOddsRatioString + "x" + + return result + } + // offspringUnknownOddsRatiosWeightSum > 0 + result := offspringOddsRatioString + "x+" + + return result + } + + oddsRatioFormatted := getOddsRatioFormatted() + + return true, offspringRiskWeight, true, offspringOddsRatio, oddsRatioFormatted, nil } //Outputs: -// -map[int64]LocusValue (rsID -> Base pair) (missing rsIDs represent unknown values) +// -map[int64]locusValue.LocusValue (rsID -> Base pair) (missing rsIDs represent unknown values) // -bool: Any Trait Rule known/tested -// -map[string]int: Trait outcomes scores map (outcome -> Number of points) +// -map[string]int: Trait outcomes scores map (Outcome -> Number of points) // -int: Number of rules tested // -bool: Conflict exists // -error -func GetPersonTraitInfoFromGeneticAnalysis(personAnalysisMapList []map[string]string, traitName string, genomeIdentifier string, personHasMultipleGenomes bool)(map[int64]LocusValue, bool, map[string]int, int, bool, error){ +func GetPersonTraitInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte)(map[int64]locusValue.LocusValue, bool, map[string]int, int, bool, error){ - for _, element := range personAnalysisMapList{ + personTraitsMap := personAnalysisObject.TraitsMap - analysisItemType, exists := element["ItemType"] - if (exists == false) { - return nil, false, nil, 0, false, errors.New("Malformed person analysis: Item missing ItemType") - } - - if (analysisItemType != "Trait"){ - continue - } - - itemTraitName, exists := element["TraitName"] - if (exists == false) { - return nil, false, nil, 0, false, errors.New("Malformed person analysis: Trait item missing TraitName") - } - - if (itemTraitName != traitName){ - continue - } - - traitObject, err := traits.GetTraitObject(traitName) - if (err != nil) { return nil, false, nil, 0, false, err } - - traitLociList := traitObject.LociList - - // Map Structure: rsID -> Locus Value - locusValuesMap := make(map[int64]LocusValue) - - for _, rsID := range traitLociList{ - - rsidString := helpers.ConvertInt64ToString(rsID) - - locusValue, exists := element[genomeIdentifier + "_LocusValue_rs" + rsidString] - if (exists == false){ - return nil, false, nil, 0, false, errors.New("Malformed person analysis: missing trait locus value for locus: " + rsidString) - } - - if (locusValue == "Unknown"){ - // No value exists for this locus - continue - } - - locusIsPhasedString, exists := element[genomeIdentifier + "_LocusIsPhased_rs" + rsidString] - if (exists == false){ - return nil, false, nil, 0, false, errors.New("Malformed person analysis: Contains trait locus value which is missing locusIsPhased information.") - } - - locusIsPhased, err := helpers.ConvertYesOrNoStringToBool(locusIsPhasedString) - if (err != nil){ - return nil, false, nil, 0, false, errors.New("Malformed person analysis: Contains invalid trait locusIsPhased rsID value: " + locusIsPhasedString) - } - - locusBase1, locusBase2, semicolonExists := strings.Cut(locusValue, ";") - if (semicolonExists == false){ - return nil, false, nil, 0, false, errors.New("Malformed person analysis: Contains invalid trait locus value: " + locusValue) - } - - locusValueObject := LocusValue{ - Base1: locusBase1, - Base2: locusBase2, - LocusIsPhased: locusIsPhased, - } - - locusValuesMap[rsID] = locusValueObject - } - - numberOfRulesTestedString, exists := element[genomeIdentifier + "_NumberOfRulesTested"] - if (exists == false){ - return nil, false, nil, 0, false, errors.New("Malformed person analysis: Trait item missing _NumberOfRulesTested") - } - - numberOfRulesTested, err := helpers.ConvertStringToInt(numberOfRulesTestedString) - if (err != nil){ - return nil, false, nil, 0, false, errors.New("Malformed person analysis: Trait item contains invalid _NumberOfRulesTested: " + numberOfRulesTestedString) - } - - getConflictExistsBool := func()(bool, error){ - - if (personHasMultipleGenomes == false){ - return false, nil - } - - conflictExists, exists := element["ConflictExists"] - if (exists == false) { - return false, errors.New("Malformed analysisMapList: Trait analysis missing ConflictExists") - } - - if (conflictExists == "Yes"){ - return true, nil - } - - return false, nil - } - - conflictExistsBool, err := getConflictExistsBool() - if (err != nil) { return nil, false, nil, 0, false, err } - - if (numberOfRulesTested == 0){ - return locusValuesMap, false, nil, 0, conflictExistsBool, nil - } - - traitOutcomesList := traitObject.OutcomesList - - traitOutcomeScoresMap := make(map[string]int) - - for _, traitOutcome := range traitOutcomesList{ - - outcomeScoreString, exists := element[genomeIdentifier + "_OutcomeScore_" + traitOutcome] - if (exists == false){ - return nil, false, nil, 0, false, errors.New("Person analysis missing _OutcomePoints_" + traitOutcome + " key.") - } - - outcomeScoreInt, err := helpers.ConvertStringToInt(outcomeScoreString) - if (err != nil){ - return nil, false, nil, 0, false, errors.New("Person analysis contains invalid _OutcomeScore_ value: " + outcomeScoreString) - } - - traitOutcomeScoresMap[traitOutcome] = outcomeScoreInt - } - - return locusValuesMap, true, traitOutcomeScoresMap, numberOfRulesTested, conflictExistsBool, nil + personTraitInfoObject, exists := personTraitsMap[traitName] + if (exists == false){ + emptyMap := make(map[int64]locusValue.LocusValue) + return emptyMap, false, nil, 0, false, nil } - return nil, false, nil, 0, false, errors.New("GetPersonTraitInfoFromGeneticAnalysis failed: Cannot find trait info for trait: " + traitName) + personTraitInfoMap := personTraitInfoObject.TraitInfoMap + + personGenomeTraitInfoObject, exists := personTraitInfoMap[genomeIdentifier] + if (exists == false){ + emptyMap := make(map[int64]locusValue.LocusValue) + return emptyMap, false, nil, 0, false, nil + } + + conflictExists := personTraitInfoObject.ConflictExists + + genomeNumberOfRulesTested := personGenomeTraitInfoObject.NumberOfRulesTested + + genomeLocusValuesMap := personGenomeTraitInfoObject.LocusValuesMap + + genomeOutcomeScoresMap := personGenomeTraitInfoObject.OutcomeScoresMap + + return genomeLocusValuesMap, true, genomeOutcomeScoresMap, genomeNumberOfRulesTested, conflictExists, nil } @@ -1212,166 +715,59 @@ func GetPersonTraitInfoFromGeneticAnalysis(personAnalysisMapList []map[string]st // -int: Number of rules tested // -bool: Conflict exists // -error -func GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisMapList []map[string]string, traitName string, genomePairIdentifier string, secondGenomePairExists bool)(bool, map[string]float64, int, bool, error){ +func GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte)(bool, map[string]float64, int, bool, error){ - for _, element := range coupleAnalysisMapList{ - - analysisItemType, exists := element["ItemType"] - if (exists == false) { - return false, nil, 0, false, errors.New("Malformed couple analysis: Item missing ItemType") - } + offspringTraitsMap := coupleAnalysisObject.TraitsMap - if (analysisItemType != "Trait"){ - continue - } - - itemTraitName, exists := element["TraitName"] - if (exists == false) { - return false, nil, 0, false, errors.New("Malformed couple analysis: Trait item missing TraitName") - } - - if (itemTraitName != traitName){ - continue - } - - numberOfRulesTestedString, exists := element[genomePairIdentifier + "_NumberOfRulesTested"] - if (exists == false){ - return false, nil, 0, false, errors.New("Malformed couple analysis: Trait item missing _NumberOfRulesTested") - } - - numberOfRulesTested, err := helpers.ConvertStringToInt(numberOfRulesTestedString) - if (err != nil){ - return false, nil, 0, false, errors.New("Malformed couple analysis: Trait item contains invalid _NumberOfRulesTested: " + numberOfRulesTestedString) - } - - getConflictExistsBool := func()(bool, error){ - - if (secondGenomePairExists == false){ - return false, nil - } - - conflictExists, exists := element["ConflictExists"] - if (exists == false) { - return false, errors.New("Malformed analysisMapList: Trait analysis missing ConflictExists") - } - - if (conflictExists == "Yes"){ - return true, nil - } - - return false, nil - } - - conflictExistsBool, err := getConflictExistsBool() - if (err != nil) { return false, nil, 0, false, err } - - if (numberOfRulesTested == 0){ - return false, nil, 0, conflictExistsBool, nil - } - - traitObject, err := traits.GetTraitObject(traitName) - if (err != nil) { return false, nil, 0, false, err } - - traitOutcomesList := traitObject.OutcomesList - - traitOutcomeScoresMap := make(map[string]float64) - - for _, traitOutcome := range traitOutcomesList{ - - averageOutcomeScoreString, exists := element[genomePairIdentifier + "_OffspringAverageOutcomeScore_" + traitOutcome] - if (exists == false){ - return false, nil, 0, false, errors.New("Couple analysis missing _OutcomePoints_" + traitOutcome + " key.") - } - - averageOutcomeScoreFloat64, err := helpers.ConvertStringToFloat64(averageOutcomeScoreString) - if (err != nil){ - return false, nil, 0, false, errors.New("Couple analysis contains invalid _OutcomeScore_ value: " + averageOutcomeScoreString) - } - - traitOutcomeScoresMap[traitOutcome] = averageOutcomeScoreFloat64 - } - - return true, traitOutcomeScoresMap, numberOfRulesTested, conflictExistsBool, nil + traitInfoObject, exists := offspringTraitsMap[traitName] + if (exists == false){ + return false, nil, 0, false, nil } - return false, nil, 0, false, errors.New("GetOffspringTraitInfoFromGeneticAnalysis failed: Cannot find trait info for trait: " + traitName) + traitInfoMap := traitInfoObject.TraitInfoMap + + genomePairTraitInfoObject, exists := traitInfoMap[genomePairIdentifier] + if (exists == false){ + return false, nil, 0, false, nil + } + + conflictExists := traitInfoObject.ConflictExists + + numberOfRulesTested := genomePairTraitInfoObject.NumberOfRulesTested + offspringAverageOutcomeScoresMap := genomePairTraitInfoObject.OffspringAverageOutcomeScoresMap + + return true, offspringAverageOutcomeScoresMap, numberOfRulesTested, conflictExists, nil } + //Outputs: -// -bool: Rule status known (we know if the rule is passed or not) +// -bool: Rule status is known (we know if the rule is passed or not) // -bool: Genome passes rule -// -map[string]string: Rule locus identifier -> genome base pair // -error -func GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalyisMapList []map[string]string, traitName string, ruleIdentifier string, genomeIdentifier string)(bool, bool, map[string]string, error){ +func GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, genomeIdentifier [16]byte)(bool, bool, error){ - for _, element := range personAnalyisMapList{ + personTraitsMap := personAnalysisObject.TraitsMap - itemType, exists := element["ItemType"] - if (exists == false) { - return false, false, nil, errors.New("Malformed person analysis: Item missing itemType") - } - - if (itemType != "TraitRule"){ - continue - } - - currentTraitName, exists := element["TraitName"] - if (exists == false){ - return false, false, nil, errors.New("Malformed person analysis: Trait item missing TraitName") - } - - if (currentTraitName != traitName){ - continue - } - - currentRuleIdentifier, exists := element["RuleIdentifier"] - if (exists == false) { - return false, false, nil, errors.New("Malformed person analysis: TraitRule item missing RuleIdentifier") - } - - if (currentRuleIdentifier != ruleIdentifier){ - continue - } - - genomePassesRuleString, exists := element[genomeIdentifier + "_PassesRule"] - if (exists == false){ - return false, false, nil, errors.New("Malformed person analysis: TraitRule item missing _PassesRule") - } - - // Map Structure: Locus Identifier -> Locus base pair - ruleLocusBasePairsMap := make(map[string]string) - - locusKeysPrefix := genomeIdentifier + "_RuleLocusBasePair_" - - for key, value := range element{ - - isLocusValueEntry := strings.HasPrefix(key, locusKeysPrefix) - if (isLocusValueEntry == false){ - continue - } - - locusIdentifier := strings.TrimPrefix(key, locusKeysPrefix) - - if (value == "Unknown"){ - continue - } - - ruleLocusBasePairsMap[locusIdentifier] = value - } - - if (genomePassesRuleString == "Unknown"){ - return false, false, ruleLocusBasePairsMap, nil - } - - genomePassesRuleBool, err := helpers.ConvertYesOrNoStringToBool(genomePassesRuleString) - if (err != nil){ - return false, false, nil, errors.New("Malformed person analysis: TraitRule item contains invalid _PassesRule: " + genomePassesRuleString) - } - - return true, genomePassesRuleBool, ruleLocusBasePairsMap, nil + traitInfoObject, exists := personTraitsMap[traitName] + if (exists == false){ + return false, false, nil } - return false, false, nil, errors.New("GetPersonTraitRuleInfoFromGeneticAnalysis failed: Trait rule info not found.") + personTraitInfoMap := traitInfoObject.TraitInfoMap + + genomeTraitInfoObject, exists := personTraitInfoMap[genomeIdentifier] + if (exists == false){ + return false, false, nil + } + + genomePassesRulesMap := genomeTraitInfoObject.GenomePassesRulesMap + + genomePassesRule, statusIsKnown := genomePassesRulesMap[ruleIdentifier] + if (statusIsKnown == false){ + return false, false, nil + } + + return true, genomePassesRule, nil } @@ -1380,63 +776,42 @@ func GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalyisMapList []map[string // -int: Offspring probability of passing rule (0 - 100) // -string: Offspring probability of passing rule formatted (with % suffix) // -error -func GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisMapList []map[string]string, traitName string, ruleIdentifier string, genomePairIdentifier string)(bool, int, string, error){ +func GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, string, error){ - for _, element := range coupleAnalysisMapList{ + offspringTraitsMap := coupleAnalysisObject.TraitsMap - itemType, exists := element["ItemType"] - if (exists == false) { - return false, 0, "", errors.New("Malformed couple analysis: Item missing itemType") - } - - if (itemType != "TraitRule"){ - continue - } - currentTraitName, exists := element["TraitName"] - if (exists == false) { - return false, 0, "", errors.New("Malformed couple analysis: TraitRule item missing TraitName") - } - - if (currentTraitName != traitName){ - continue - } - - currentRuleIdentifier, exists := element["RuleIdentifier"] - if (exists == false) { - return false, 0, "", errors.New("Malformed couple analysis: TraitRule item missing RuleIdentifier") - } - - if (currentRuleIdentifier != ruleIdentifier){ - continue - } - - offspringProbabilityOfPassingRule, exists := element[genomePairIdentifier + "_OffspringProbabilityOfPassingRule"] - if (exists == false){ - return false, 0, "", errors.New("Malformed couple analysis: TraitRule item missing _OffspringProbabilityOfPassingRule key") - } - - if (offspringProbabilityOfPassingRule == "Unknown"){ - return false, 0, "", nil - } - - offspringProbabilityOfPassingRuleInt, err := helpers.ConvertStringToInt(offspringProbabilityOfPassingRule) - if (err != nil) { - return false, 0, "", errors.New("Malformed couple analysis: TraitRule item contains invalid _OffspringProbabilityOfPassingRule: " + offspringProbabilityOfPassingRule) - } - - offspringProbabilityOfPassingRuleFormatted := offspringProbabilityOfPassingRule + "%" - - return true, offspringProbabilityOfPassingRuleInt, offspringProbabilityOfPassingRuleFormatted, nil + offspringTraitInfo, exists := offspringTraitsMap[traitName] + if (exists == false){ + return false, 0, "", nil } - return false, 0, "", errors.New("GetOffspringTraitRuleInfoFromGeneticAnalysis failed: Trait rule info not found.") + offspringTraitInfoMap := offspringTraitInfo.TraitInfoMap + + offspringTraitInfoObject, exists := offspringTraitInfoMap[genomePairIdentifier] + if (exists == false){ + return false, 0, "", nil + } + + offspringProbabilityOfPassingRulesMap := offspringTraitInfoObject.ProbabilityOfPassingRulesMap + + offspringProbabilityOfPassingRule, exists := offspringProbabilityOfPassingRulesMap[ruleIdentifier] + if (exists == false){ + return false, 0, "", nil + } + + offspringProbabilityOfPassingRuleString := helpers.ConvertIntToString(offspringProbabilityOfPassingRule) + + offspringProbabilityOfPassingRuleFormatted := offspringProbabilityOfPassingRuleString + "%" + + return true, offspringProbabilityOfPassingRule, offspringProbabilityOfPassingRuleFormatted, nil } // We use this function to verify a person genetic analysis is well formed -func ReadPersonGeneticAnalysisForTests(personAnalysisMapList []map[string]string)error{ +//TODO: Perform sanity checks on data +func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis)error{ - allRawGenomeIdentifiersList, personHasMultipleGenomes, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(personAnalysisMapList) + allRawGenomeIdentifiersList, personHasMultipleGenomes, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(personAnalysisObject) if (err != nil) { return err } allGenomeIdentifiersList := allRawGenomeIdentifiersList @@ -1453,7 +828,7 @@ func ReadPersonGeneticAnalysisForTests(personAnalysisMapList []map[string]string for _, genomeIdentifier := range allGenomeIdentifiersList{ - _, _, _, _, _, _, _, err := GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisMapList, diseaseName, genomeIdentifier, personHasMultipleGenomes) + _, _, _, _, _, _, _, _, err := GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier) if (err != nil) { return err } } @@ -1461,11 +836,14 @@ func ReadPersonGeneticAnalysisForTests(personAnalysisMapList []map[string]string for _, diseaseVariantObject := range diseaseVariantObjectsList{ - variantIdentifier := diseaseVariantObject.VariantIdentifier + variantIdentifierHex := diseaseVariantObject.VariantIdentifier + + variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex) + if (err != nil) { return err } for _, genomeIdentifier := range allGenomeIdentifiersList{ - _, _, err := GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(personAnalysisMapList, diseaseName, variantIdentifier, genomeIdentifier) + _, _, err := GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, variantIdentifier, genomeIdentifier) if (err != nil) { return err } } } @@ -1480,7 +858,7 @@ func ReadPersonGeneticAnalysisForTests(personAnalysisMapList []map[string]string for _, genomeIdentifier := range allGenomeIdentifiersList{ - _, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisMapList, diseaseName, genomeIdentifier, personHasMultipleGenomes) + _, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier) if (err != nil) { return err } } @@ -1488,11 +866,14 @@ func ReadPersonGeneticAnalysisForTests(personAnalysisMapList []map[string]string for _, diseaseLocusObject := range diseaseLocusObjectsList{ - locusIdentifier := diseaseLocusObject.LocusIdentifier + locusIdentifierHex := diseaseLocusObject.LocusIdentifier + + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return err } for _, genomeIdentifier := range allGenomeIdentifiersList{ - _, _, _, _, _, _, err := GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalysisMapList, diseaseName, locusIdentifier, genomeIdentifier) + _, _, _, _, _, _, _, err := GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) if (err != nil) { return err } } } @@ -1507,7 +888,7 @@ func ReadPersonGeneticAnalysisForTests(personAnalysisMapList []map[string]string for _, genomeIdentifier := range allGenomeIdentifiersList{ - _, _, _, _, _, err := GetPersonTraitInfoFromGeneticAnalysis(personAnalysisMapList, traitName, genomeIdentifier, personHasMultipleGenomes) + _, _, _, _, _, err := GetPersonTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier) if (err != nil) { return err } } @@ -1515,11 +896,14 @@ func ReadPersonGeneticAnalysisForTests(personAnalysisMapList []map[string]string for _, traitRuleObject := range traitRulesList{ - ruleIdentifier := traitRuleObject.RuleIdentifier + ruleIdentifierHex := traitRuleObject.RuleIdentifier + + ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex) + if (err != nil) { return err } for _, genomeIdentifier := range allGenomeIdentifiersList{ - _, _, _, err := GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalysisMapList, traitName, ruleIdentifier, genomeIdentifier) + _, _, err := GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalysisObject, traitName, ruleIdentifier, genomeIdentifier) if (err != nil) { return err } } } @@ -1529,19 +913,20 @@ func ReadPersonGeneticAnalysisForTests(personAnalysisMapList []map[string]string } -// We use this function to verify a person genetic analysis is well formed -func ReadCoupleGeneticAnalysisForTests(coupleAnalysisMapList []map[string]string)error{ +// We use this function to verify a couple genetic analysis is well formed +//TODO: Perform sanity checks on data +func VerifyCoupleGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis)error{ - pair1PersonAGenomeIdentifier, pair1PersonBGenomeIdentifier, secondGenomePairExists, pair2PersonAGenomeIdentifier, pair2PersonBGenomeIdentifier, _, _, _, _, _, _, err := GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisMapList) + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil) { return err } - pair1GenomeIdentifier := pair1PersonAGenomeIdentifier + "+" + pair1PersonBGenomeIdentifier + pair1GenomeIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) - allGenomePairIdentifiersList := []string{pair1GenomeIdentifier} + allGenomePairIdentifiersList := [][32]byte{pair1GenomeIdentifier} if (secondGenomePairExists == true){ - pair2GenomeIdentifier := pair2PersonAGenomeIdentifier + "+" + pair2PersonBGenomeIdentifier + pair2GenomeIdentifier := helpers.JoinTwo16ByteArrays(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) allGenomePairIdentifiersList = append(allGenomePairIdentifiersList, pair2GenomeIdentifier) } @@ -1555,7 +940,7 @@ func ReadCoupleGeneticAnalysisForTests(coupleAnalysisMapList []map[string]string for _, genomePairIdentifier := range allGenomePairIdentifiersList{ - _, _, _, _, _, _, err = GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, genomePairIdentifier, secondGenomePairExists) + _, _, _, _, _, _, _, err = GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) if (err != nil) { return err } } @@ -1563,11 +948,14 @@ func ReadCoupleGeneticAnalysisForTests(coupleAnalysisMapList []map[string]string for _, diseaseVariantObject := range diseaseVariantObjectsList{ - variantIdentifier := diseaseVariantObject.VariantIdentifier + variantIdentifierHex := diseaseVariantObject.VariantIdentifier + + variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex) + if (err != nil) { return err } for _, genomePairIdentifier := range allGenomePairIdentifiersList{ - _, _, _, _, _, _, _, _, _, _, err := GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, variantIdentifier, genomePairIdentifier) + _, _, _, _, _, _, _, _, _, _, err := GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, variantIdentifier, genomePairIdentifier) if (err != nil) { return err } } } @@ -1582,7 +970,7 @@ func ReadCoupleGeneticAnalysisForTests(coupleAnalysisMapList []map[string]string for _, genomePairIdentifier := range allGenomePairIdentifiersList{ - _, _, _, _, _, err := GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, genomePairIdentifier, secondGenomePairExists) + _, _, _, _, _, err := GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) if (err != nil) { return err } } @@ -1590,11 +978,14 @@ func ReadCoupleGeneticAnalysisForTests(coupleAnalysisMapList []map[string]string for _, diseaseLocusObject := range diseaseLocusObjectsList{ - locusIdentifier := diseaseLocusObject.LocusIdentifier + locusIdentifierHex := diseaseLocusObject.LocusIdentifier + + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return err } for _, genomePairIdentifier := range allGenomePairIdentifiersList{ - _, _, _, _, _, err := GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisMapList, diseaseName, locusIdentifier, genomePairIdentifier) + _, _, _, _, _, err := GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, locusIdentifier, genomePairIdentifier) if (err != nil) { return err } } } @@ -1609,7 +1000,7 @@ func ReadCoupleGeneticAnalysisForTests(coupleAnalysisMapList []map[string]string for _, genomePairIdentifier := range allGenomePairIdentifiersList{ - _, _, _, _, err := GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisMapList, traitName, genomePairIdentifier, secondGenomePairExists) + _, _, _, _, err := GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier) if (err != nil) { return err } } @@ -1617,11 +1008,14 @@ func ReadCoupleGeneticAnalysisForTests(coupleAnalysisMapList []map[string]string for _, traitRuleObject := range traitRulesList{ - ruleIdentifier := traitRuleObject.RuleIdentifier + ruleIdentifierHex := traitRuleObject.RuleIdentifier + + ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex) + if (err != nil) { return err } for _, genomePairIdentifier := range allGenomePairIdentifiersList{ - _, _, _, err := GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisMapList, traitName, ruleIdentifier, genomePairIdentifier) + _, _, _, err := GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier) if (err != nil) { return err } } } diff --git a/internal/genetics/sampleAnalyses/SampleCoupleAnalysis.json b/internal/genetics/sampleAnalyses/SampleCoupleAnalysis.json deleted file mode 100644 index 97ceca1..0000000 --- a/internal/genetics/sampleAnalyses/SampleCoupleAnalysis.json +++ /dev/null @@ -1,712 +0,0 @@ -[ - { - "AnalysisType": "Couple", - "AnalysisVersion": "1", - "ItemType": "Metadata", - "Pair1PersonAGenomeIdentifier": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "Pair1PersonBGenomeIdentifier": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "PersonAHasMultipleGenomes": "No", - "PersonBHasMultipleGenomes": "No", - "SecondPairExists": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDisease", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOffspringHasDisease": "25", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOffspringHasVariant": "75", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfVariantsTested": "26", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfVariantsTested": "26" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "36965d", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "50", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "50", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "50", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "50", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "5706b0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "01a2a0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "bd4106", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "4d6f38", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "c6135a", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "50", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "50", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "50", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "50", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "88a7f4", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "058a4d", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "2a4ddf", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "e1bcfb", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "c28795", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "f1965c", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "3420c1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "25d0b4", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "139ab2", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "f7a12e", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "deb2e2", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "884cf0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "b9fad1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "f2448d", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "09b96f", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "2f8651", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "46efe1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "528406", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "e8e8fc", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "e60633", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "d72d30", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "a3b068", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "770086", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "a00f11", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "79d73b", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Sickle Cell Anemia", - "ItemType": "MonogenicDisease", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOffspringHasDisease": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOffspringHasVariant": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfVariantsTested": "1", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfVariantsTested": "1" - }, - { - "DiseaseName": "Sickle Cell Anemia", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "50e857", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsLowerBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf0MutationsUpperBound": "100", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf1MutationUpperBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsLowerBound": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOf2MutationsUpperBound": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDisease", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfLociTested": "24", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskScore": "1" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d7891c", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.14000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "41c164", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "f3a097", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "0.50000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d4626f", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.07000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "84aaa4", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "c8de7a", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.50000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d30087", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.07000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "cafa72", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "8f671c", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.14000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "b3e49a", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "8b0b02", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "25cafc", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "34c7e5", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "60ce27", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "328cdf", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "849bc7", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "5af5e3", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "c354fa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "eedc23", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.02500", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "2ee027", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "fc4bab", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "f8b225", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "4a072c", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "070f24", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d08516", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "Unknown" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "047b84", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringOddsRatio": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringRiskWeight": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringUnknownOddsRatiosWeightSum": "Unknown" - }, - { - "ItemType": "Trait", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "5", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringAverageOutcomeScore_Intolerant": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringAverageOutcomeScore_Tolerant": "2.50" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "f4e02c", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "0" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "cc3df0", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "100" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "8170ee", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "0" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "52425f", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "50" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "4b5c35", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "50" - }, - { - "ItemType": "Trait", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "9", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringAverageOutcomeScore_Curly": "1.50", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringAverageOutcomeScore_Straight": "3" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "fde405", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "50" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "6bd1da", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "50" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "32e377", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "0" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "34e6d2", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "50" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "cf6cb5", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "50" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "2ba65b", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "0" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "ae3274", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "50" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "a546bf", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "50" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "b8dc0a", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OffspringProbabilityOfPassingRule": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Facial Structure", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Eye Color", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Hair Color", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Skin Color", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "0" - } -] \ No newline at end of file diff --git a/internal/genetics/sampleAnalyses/SampleCoupleAnalysis.messagepack b/internal/genetics/sampleAnalyses/SampleCoupleAnalysis.messagepack new file mode 100644 index 0000000000000000000000000000000000000000..6773a86c82f73172fec31ec6c43e5e45c2dd0ac5 GIT binary patch literal 11391 zcmd^FU2GIp6rQ?Lf!A2s8bgah27-&liwOzY(3r(;_r!#lUO{a4=b7yQ9 zU+gasHGL8je5pnh3bu7y0}WL}ors}EMNOJDq_l#hi3W>8Bv_+&|GRf*%4~L<(QzIo znYrhl?|$byGjk^A_6!uuycEC??*6jYo}k_HW}pEtcij!gINYxUvV)FH&k%ap zjbzjaWGHNd81NWiH}7vBC=Fm03T!!SP!P$Jj*(}z<_4ft`*!mRRvr#^haN~-!DhFn@+x`0a209+N(s_rQ8cyL z<@YCS*c3m7aX(2nOvuC}>4l+?xVV>jt1-8k=SO#q+&b@AZsosDTj54WDyeNUQlgnC z8RP=Voe>j1Y~K1-FOY7p-vnPwH0FnQw&~ohe4Tj@3vSmA)jO8k z%*>o)xwSCej^#Ez`ln;L6?`-7SZ;OsgI2iFZ!e?wr0EY#vQ{=NU6S=A{YK8>J^695 zb8D_;Bgl$wbTo~0GZiJNo2j%(-RKO-LbogVk2#iG-aEaH<@VdQ*B#5Ph&|(2ZU?Gn z9m{RvkCla3QZ<6CB-27yCJyt2}NC zZqeDFY{A_$^^h&N&n(?&3-0f|Z{`N~+E`Eh`Ws_bpQG;^P>yidp4$0y*IKK%-!c1h zgPY;^=+(RLum!i^+?*}AOP4=r3vOrMySCtdF}cqWH!sug(ap^2oEN{c=Y7@r)>%Wo zi~T*?bK{Z?xb)xHv081l5wIw+YIYe12HP3o62Ztwj=v zDfuNzU4(^Ak{w2BEwP|hUxlnjQPuG+Q&EME#@0dZ%th}kC|Q^8swsJa$WdAUI1%Xj U)JAgt^6A-1-PPv5x|$C4KiHA_tN;K2 literal 0 HcmV?d00001 diff --git a/internal/genetics/sampleAnalyses/SamplePerson1Analysis.json b/internal/genetics/sampleAnalyses/SamplePerson1Analysis.json deleted file mode 100644 index 7229b03..0000000 --- a/internal/genetics/sampleAnalyses/SamplePerson1Analysis.json +++ /dev/null @@ -1,922 +0,0 @@ -[ - { - "AnalysisType": "Person", - "AnalysisVersion": "1", - "CombinedGenomesExist": "No", - "GenomeIdentifier": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "ItemType": "Metadata" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDisease", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfVariantsTested": "26", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_ProbabilityOfHavingDisease": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_ProbabilityOfPassingADiseaseVariant": "50" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "36965d", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;Yes", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "5706b0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "01a2a0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "bd4106", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "4d6f38", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "c6135a", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "88a7f4", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "058a4d", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "2a4ddf", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "e1bcfb", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "c28795", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "f1965c", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "3420c1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "25d0b4", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "139ab2", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "f7a12e", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "deb2e2", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "884cf0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "b9fad1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "f2448d", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "09b96f", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "2f8651", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "46efe1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "528406", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "e8e8fc", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "e60633", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "d72d30", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "a3b068", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "770086", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "a00f11", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "79d73b", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Sickle Cell Anemia", - "ItemType": "MonogenicDisease", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfVariantsTested": "1", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_ProbabilityOfHavingDisease": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_ProbabilityOfPassingADiseaseVariant": "0" - }, - { - "DiseaseName": "Sickle Cell Anemia", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "50e857", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_HasVariant": "No;No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased": "No" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDisease", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfLociTested": "24", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskScore": "1" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d7891c", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "41c164", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "f3a097", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d4626f", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "A;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.14000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "1" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "84aaa4", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "c8de7a", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.72000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "2" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d30087", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "cafa72", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "8f671c", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "b3e49a", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "8b0b02", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "25cafc", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "34c7e5", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "60ce27", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "328cdf", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "849bc7", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "5af5e3", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "c354fa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "eedc23", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "2ee027", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "fc4bab", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "f8b225", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "4a072c", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "070f24", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "1.00000", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d08516", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "Unknown" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "047b84", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusBasePair": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OddsRatio": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RiskWeight": "Unknown" - }, - { - "ItemType": "Trait", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs182549": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs4988235": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs182549": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4988235": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfRulesTested": "5", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OutcomeScore_Intolerant": "0", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OutcomeScore_Tolerant": "2" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "f4e02c", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_43bf19": "T;C" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "cc3df0", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "Yes", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_a7feff": "T;C" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "8170ee", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_da6b04": "A;G" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "52425f", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "Yes", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_176dde": "A;G" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "4b5c35", - "TraitName": "Lactose Tolerance", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_164acb": "A;G" - }, - { - "ItemType": "Trait", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs11803731": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs17646946": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7349332": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs11803731": "A;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs17646946": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7349332": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfRulesTested": "9", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OutcomeScore_Curly": "3", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_OutcomeScore_Straight": "0" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "fde405", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_0e06e2": "T;C" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "6bd1da", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "Yes", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_2da1b7": "T;C" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "32e377", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_c6760e": "T;C" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "34e6d2", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_9079c9": "A;T" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "cf6cb5", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "Yes", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_d0aad3": "A;T" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "2ba65b", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_f554b5": "A;T" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "ae3274", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_f500c2": "A;G" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "a546bf", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "Yes", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_f1144a": "A;G" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "b8dc0a", - "TraitName": "Hair Texture", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_PassesRule": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_RuleLocusBasePair_468bb3": "A;G" - }, - { - "ItemType": "Trait", - "TraitName": "Facial Structure", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1005999": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs10237838": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs11237982": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12694574": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs13097965": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs17447439": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1747677": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1978859": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs2034127": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs2327089": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs2832438": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs2894450": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs4552364": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs6020940": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs6478394": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7516150": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7552331": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7965082": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs805722": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs9858909": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1005999": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1008591": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1015092": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1019212": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10234405": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10237319": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10237488": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10237838": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10265937": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10266101": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10278187": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10485860": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10843104": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs11191909": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs11237982": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1158810": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs11604811": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12155314": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12358982": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12437560": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12694574": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs13097965": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs13098099": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1562005": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1562006": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1572037": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs16863422": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs16977002": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs16977008": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs16977009": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs17252053": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs17447439": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1747677": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1887276": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1939697": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1939707": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1978859": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1999527": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2034127": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2034128": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2034129": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2108166": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2168809": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2274107": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2327089": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2327101": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2342494": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2422239": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2422241": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2832438": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2894450": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs397723": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4053148": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4353811": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4433629": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4552364": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4633993": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4648379": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4648477": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4648478": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4793389": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6020940": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6020957": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6039266": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6039272": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6056066": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6056119": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6056126": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6462544": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6462562": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6478394": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6555969": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6749293": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6795519": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6950754": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs717463": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7214306": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7516150": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7552331": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7617069": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7628370": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7640340": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7779616": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7781059": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7799331": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7803030": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7807181": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7965082": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7966317": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs805693": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs805694": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs805722": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs8079498": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs875143": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs894883": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs911015": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs911020": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs9692219": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs974448": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs975633": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs9858909": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs9971100": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfRulesTested": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Eye Color", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1003719": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1105879": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1126809": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1129038": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs11957757": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs121908120": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12203592": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12452184": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12593929": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12896399": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12906280": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12913823": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12913832": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1325127": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1393350": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1408799": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1426654": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1667394": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs16891982": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1800401": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1800407": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1800414": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs2070959": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs2733832": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs2748901": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs351385": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs3794604": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs4778138": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs4778218": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs4778241": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs4911414": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs4911442": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs6058017": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs6828137": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs6910861": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7174027": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7183877": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7495174": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs8028689": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs892839": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs916977": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs9782955": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs9894429": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs9971729": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1003719": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10209564": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1105879": "A;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1126809": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs112747614": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1129038": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs11631797": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs116359091": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs11957757": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs121908120": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12203592": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12335410": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12452184": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12543326": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12552712": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12593929": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12614022": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12896399": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12906280": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12913823": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12913832": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs13016869": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1325127": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs13297008": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs138777265": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1393350": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1408799": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs141318671": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1426654": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs147068120": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1667394": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs16891982": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs17184180": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1800401": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1800407": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1800414": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2070959": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2095645": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2238289": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2240203": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2252893": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2385028": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2733832": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2748901": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2835621": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2835630": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2835660": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2854746": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs341147": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs348613": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs35051352": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs351385": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs3768056": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs3794604": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs3809761": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs3912104": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs3935591": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs3940272": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4521336": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4778138": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4778218": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4778241": "A;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4790309": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4911414": "T;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4911442": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6058017": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs622330": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs62330021": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6420484": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6693258": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6828137": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6910861": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6944702": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6997494": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7170852": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7174027": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7183877": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7219915": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs72777200": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7277820": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs728405": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs72928978": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs73488486": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs74409360": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7495174": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs790464": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs8028689": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs892839": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs916977": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs9301973": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs9782955": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs9894429": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs989869": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs9971729": "A;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfRulesTested": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Hair Color", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs11636232": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12203592": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12821256": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12896399": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12913832": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1540771": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1667394": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1805007": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1805008": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs35264875": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs3829241": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs6918152": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7174027": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7183877": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs7495174": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs8028689": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs11636232": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs11855019": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12203592": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12821256": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12896399": "T;T", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12913832": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1540771": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1667394": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1805007": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1805008": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs28777": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs35264875": "T;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs3829241": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs4778211": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs6918152": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7174027": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7183877": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7495174": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs8028689": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs8039195": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfRulesTested": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Skin Color", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1042602": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1126809": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs12203592": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1426654": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs16891982": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs1834640": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs26722": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs2762462": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs642742": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusIsPhased_rs937171": "No", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs10424065": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1042602": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1126809": "A;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs12203592": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs142317543": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1426654": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs16891982": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1800422": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs1834640": "A;A", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs26722": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs2762462": "C;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs3212368": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs3212369": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs642742": "T;C", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7176696": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs7182710": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs784416": "Unknown", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_LocusValue_rs937171": "G;G", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_NumberOfRulesTested": "0" - } -] \ No newline at end of file diff --git a/internal/genetics/sampleAnalyses/SamplePerson1Analysis.messagepack b/internal/genetics/sampleAnalyses/SamplePerson1Analysis.messagepack new file mode 100644 index 0000000000000000000000000000000000000000..bde2a2264c739e856c4b7c9249d74ef6ef9a340d GIT binary patch literal 12030 zcmb`NYj6`)6vq<+#R8qs!RkP%R4589qSS(jqLYTcXtAM=NltBgoh6cN--_7-~6+hg`7`H)FA`#X>S zxqI)~yIELo5g6VhbMiD+k~vW@zH8z60;W8R6~r<&$HfW?SH!8m<#Dp|vaf$l(5uNR zmN__qb!mW{QK6pKeDvmgf%oJHPTuWe3riTO=ZQ{EI~I5Kn5{&O#^wLTaCDeW z5*>_#<2l8XUu0usnG=dF$UHLYs_(FlVrw-d_)PCxqUP48tCq&7@t_l$0|!z&9oVe8 zamzWf=Cj#nYF{yv20L=w9W+?xFQ;g*a>D`|Y4LK8^5K&c3wI}gZ=r&Xd3Lp0W)Z@ zh3kK(!IGz(pux^W-ARK@5y#VDo+J0rUBRRy<0B4L{gKgDuQEPlMgQ zV5)wsu454mEXs0oVvOj)-Y|~_n5)3H5`2sFG{y5hdOL}J~c105nj%%#SDh6;cshJhGx(RSNXU3 zH|>~ucD~QFxNi^4VAvWCKX4f{7e2vTf?_^4jQ zy6zjDh|$#V2?)XJf*cc5?i+lLP&LS9Gd_kg!GNQ@B30GVo17hN-vV}6ml#44UilmHLK^&oKN$rfgi;#zG>wK3ESHXXY|L4`NNwd~ z5JJi&=1l0-M~dzAG(uiTrF2v|AOuqk&HVjxXFv!^v7uY86I{2N#sQ-QjJcUw$fieJ`0MTGMU-@VT2c1)+W;Y?T;kF{gb-5oW3IGA2o@NcABAKv zeGEbKW7OCt$O}oak?r3@2q~BNibD`WQY^lr6+%dXmA%#rLa_9aV)w=r>qWBLz@8OL zAFaXfjLQz$3mRi_J}`Zfm)$ARkQWkC=>@NSlE^8Osv$3=QpUUv-bs>>?6#ludWoF? z&M>50_V#-n@`4Q*RF@T(67(WNXMwl4q`ppSc(^g3|As@Li0i!1VXS}keB!h5W&14WO6&W zk3bSRWl~+pfgzE7zl?wo+zV}+Ta(RjQe^ww0a^#De@^W&XkaOnF;+b!5=9F}(H+Du zItT?u8Y70;V53LMrDd(|ev0DSrMNSwtc{1#CrywO%Km~7eDG=mCcEu4gpfquvIJC& zgv2ib_svMijo!6TV5Ic9Wnf^W^eJX;ihfF0LqR>M6qa&ABqx={+6XbX)j);7uBndM z{hwtaBWOzLdodA0NFyZq7O)v4A%+rgr-+15w4BkOZqu0y&5hugo>L(&SY43F6nERY z78Kf{yCsitDx%CL+C`p~{C}*W+y3bl^qkA9jo`e5+>n(*jZ=^PaS(zH6nmGny)`+4 zqNSqr*npB!dJ^}*)IS^iesYxhhulvlo^SD)=FC2)elAFVT>YFob8K_)4%wza>tJUq z=${5g2dOkSppr2r1H3j!vuD%bLP#aj5S*Q4hmijhc7ysPl=1)NDXQA)mUvGzQPUTE zrqt8rs*ZK3imGFE<}UT~mG63~@1=*1sYlYze6GHK#=or|Nm=)Z`o80MpDtSe2ic{{ AmjD0& literal 0 HcmV?d00001 diff --git a/internal/genetics/sampleAnalyses/SamplePerson2Analysis.json b/internal/genetics/sampleAnalyses/SamplePerson2Analysis.json deleted file mode 100644 index 873cd30..0000000 --- a/internal/genetics/sampleAnalyses/SamplePerson2Analysis.json +++ /dev/null @@ -1,923 +0,0 @@ -[ - { - "AnalysisType": "Person", - "AnalysisVersion": "1", - "CombinedGenomesExist": "No", - "GenomeIdentifier": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "ItemType": "Metadata" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDisease", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfVariantsTested": "26", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOfHavingDisease": "0", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOfPassingADiseaseVariant": "50" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "36965d", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "5706b0", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "01a2a0", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "bd4106", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "4d6f38", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "c6135a", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "Yes;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "88a7f4", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "058a4d", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "2a4ddf", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "e1bcfb", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "c28795", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "f1965c", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "3420c1", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "25d0b4", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "139ab2", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "f7a12e", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "deb2e2", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "884cf0", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "b9fad1", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "f2448d", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "09b96f", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "2f8651", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "46efe1", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "528406", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "e8e8fc", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "e60633", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "d72d30", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "a3b068", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "Unknown" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "770086", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "a00f11", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Cystic Fibrosis", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "79d73b", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Sickle Cell Anemia", - "ItemType": "MonogenicDisease", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfVariantsTested": "1", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOfHavingDisease": "0", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_ProbabilityOfPassingADiseaseVariant": "0" - }, - { - "DiseaseName": "Sickle Cell Anemia", - "ItemType": "MonogenicDiseaseVariant", - "VariantIdentifier": "50e857", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_HasVariant": "No;No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased": "No" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDisease", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfLociTested": "24", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskScore": "1" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d7891c", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.28000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "1" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "41c164", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "f3a097", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d4626f", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "84aaa4", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "c8de7a", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d30087", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.14000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "1" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "cafa72", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "8f671c", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.28000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "2" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "b3e49a", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "8b0b02", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "25cafc", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "34c7e5", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "60ce27", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "328cdf", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "849bc7", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "5af5e3", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "c354fa", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "eedc23", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.05000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "1" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "2ee027", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "fc4bab", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "f8b225", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "4a072c", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "070f24", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "1.00000", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "0" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "d08516", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "Unknown" - }, - { - "DiseaseName": "Breast Cancer", - "ItemType": "PolygenicDiseaseLocus", - "LocusIdentifier": "047b84", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusBasePair": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OddsRatio": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RiskWeight": "Unknown" - }, - { - "ItemType": "Trait", - "TraitName": "Lactose Tolerance", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs182549": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs4988235": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs182549": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4988235": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "5", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OutcomeScore_Intolerant": "0", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OutcomeScore_Tolerant": "3" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "f4e02c", - "TraitName": "Lactose Tolerance", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_43bf19": "T;T" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "cc3df0", - "TraitName": "Lactose Tolerance", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "Yes", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_a7feff": "T;T" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "8170ee", - "TraitName": "Lactose Tolerance", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_da6b04": "A;A" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "52425f", - "TraitName": "Lactose Tolerance", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_176dde": "A;A" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "4b5c35", - "TraitName": "Lactose Tolerance", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "Yes", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_164acb": "A;A" - }, - { - "ItemType": "Trait", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs11803731": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs17646946": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7349332": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs11803731": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs17646946": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7349332": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "9", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OutcomeScore_Curly": "0", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_OutcomeScore_Straight": "6" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "fde405", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "Yes", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_0e06e2": "C;C" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "6bd1da", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_2da1b7": "C;C" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "32e377", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_c6760e": "C;C" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "34e6d2", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "Yes", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_9079c9": "A;A" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "cf6cb5", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_d0aad3": "A;A" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "2ba65b", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_f554b5": "A;A" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "ae3274", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "Yes", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_f500c2": "G;G" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "a546bf", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_f1144a": "G;G" - }, - { - "ItemType": "TraitRule", - "RuleIdentifier": "b8dc0a", - "TraitName": "Hair Texture", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_PassesRule": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_RuleLocusBasePair_468bb3": "G;G" - }, - { - "ItemType": "Trait", - "TraitName": "Facial Structure", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1005999": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs10237838": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs10843104": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs11237982": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12694574": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs13097965": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs17447439": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1747677": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1978859": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs2034127": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs2327089": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs2832438": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs2894450": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs4552364": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs6020940": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs6478394": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7516150": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7552331": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7965082": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs805722": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs9858909": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1005999": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1008591": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1015092": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1019212": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10234405": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10237319": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10237488": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10237838": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10265937": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10266101": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10278187": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10485860": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10843104": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs11191909": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs11237982": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1158810": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs11604811": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12155314": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12358982": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12437560": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12694574": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs13097965": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs13098099": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1562005": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1562006": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1572037": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs16863422": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs16977002": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs16977008": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs16977009": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs17252053": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs17447439": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1747677": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1887276": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1939697": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1939707": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1978859": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1999527": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2034127": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2034128": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2034129": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2108166": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2168809": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2274107": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2327089": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2327101": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2342494": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2422239": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2422241": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2832438": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2894450": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs397723": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4053148": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4353811": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4433629": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4552364": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4633993": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4648379": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4648477": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4648478": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4793389": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6020940": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6020957": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6039266": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6039272": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6056066": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6056119": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6056126": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6462544": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6462562": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6478394": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6555969": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6749293": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6795519": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6950754": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs717463": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7214306": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7516150": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7552331": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7617069": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7628370": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7640340": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7779616": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7781059": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7799331": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7803030": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7807181": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7965082": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7966317": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs805693": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs805694": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs805722": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs8079498": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs875143": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs894883": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs911015": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs911020": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs9692219": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs974448": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs975633": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs9858909": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs9971100": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Eye Color", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1003719": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1105879": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1126809": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1129038": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs11957757": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs121908120": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12203592": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12452184": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12593929": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12896399": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12906280": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12913823": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12913832": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1325127": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1393350": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1408799": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1426654": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1667394": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs16891982": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1800401": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1800407": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1800414": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs2070959": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs2733832": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs2748901": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs351385": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs3794604": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs4778138": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs4778218": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs4778241": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs4911414": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs4911442": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs6058017": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs6828137": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs6910861": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7174027": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7183877": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7495174": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs8028689": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs892839": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs916977": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs9782955": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs9894429": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs9971729": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1003719": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10209564": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1105879": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1126809": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs112747614": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1129038": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs11631797": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs116359091": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs11957757": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs121908120": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12203592": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12335410": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12452184": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12543326": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12552712": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12593929": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12614022": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12896399": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12906280": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12913823": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12913832": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs13016869": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1325127": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs13297008": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs138777265": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1393350": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1408799": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs141318671": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1426654": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs147068120": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1667394": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs16891982": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs17184180": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1800401": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1800407": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1800414": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2070959": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2095645": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2238289": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2240203": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2252893": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2385028": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2733832": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2748901": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2835621": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2835630": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2835660": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2854746": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs341147": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs348613": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs35051352": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs351385": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs3768056": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs3794604": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs3809761": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs3912104": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs3935591": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs3940272": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4521336": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4778138": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4778218": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4778241": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4790309": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4911414": "T;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4911442": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6058017": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs622330": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs62330021": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6420484": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6693258": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6828137": "T;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6910861": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6944702": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6997494": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7170852": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7174027": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7183877": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7219915": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs72777200": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7277820": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs728405": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs72928978": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs73488486": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs74409360": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7495174": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs790464": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs8028689": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs892839": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs916977": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs9301973": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs9782955": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs9894429": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs989869": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs9971729": "A;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Hair Color", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs11636232": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12203592": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12821256": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12896399": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12913832": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1540771": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1667394": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1805007": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1805008": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs35264875": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs3829241": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs6918152": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7174027": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7183877": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs7495174": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs8028689": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs11636232": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs11855019": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12203592": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12821256": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12896399": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12913832": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1540771": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1667394": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1805007": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1805008": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs28777": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs35264875": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs3829241": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs4778211": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs6918152": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7174027": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7183877": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7495174": "A;A", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs8028689": "T;T", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs8039195": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "0" - }, - { - "ItemType": "Trait", - "TraitName": "Skin Color", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1042602": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1126809": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs12203592": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1426654": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs16891982": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs1834640": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs26722": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs2762462": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs642742": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusIsPhased_rs937171": "No", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs10424065": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1042602": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1126809": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs12203592": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs142317543": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1426654": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs16891982": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1800422": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs1834640": "A;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs26722": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs2762462": "C;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs3212368": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs3212369": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs642742": "T;C", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7176696": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs7182710": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs784416": "Unknown", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_LocusValue_rs937171": "G;G", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb_NumberOfRulesTested": "0" - } -] \ No newline at end of file diff --git a/internal/genetics/sampleAnalyses/SamplePerson2Analysis.messagepack b/internal/genetics/sampleAnalyses/SamplePerson2Analysis.messagepack new file mode 100644 index 0000000000000000000000000000000000000000..114c1bd6608b46c93aea03a63f2039db0f40b95a GIT binary patch literal 10050 zcmcJVdr%a09Ki3mz%UIMWm52gqm~LeLS{z8G{?zHPzY|c6s_fMfgP4z+T9Dze~JU& z`RETxtx3&H1r%`lpw9Bo$tF{MI&&V7DNHpeVSOf{y*7%ctY)@BzQu>|fZp>qBB`h6d zp;4!EVuQR zYluLzuZ3m;3+MfD1reC$D=>{fV6(Lo5g2d`?`75@0zas4aswNp3vtgBLa~a`sSe#t~R%m%C@!+O!An{8I~df15TThodO~-;25qc>gTn*@9IXx!hl^K zfASt8FkqLf-)lhxBC|}=0w!NZmtMdw4+tBFSQwx{|83Jwt3WZ5_kDdyLn>usC^jZf z5*#+kA<(W}%9jt`6IAc(diTzCu(b92(`-A zKzVeFRk=w}Z!9X(^8K%Rk%9|}5MuV#lBQ}IlsOMVEWU_l*!)Xb5Q1nit6GHmKOqFC zMNG))n7p#HvnZ*emsVk)utM`;2HgR>LoV5KNE_??1O1LU0jtF5jpZiJd+TLeL;L zWIH6Ae2}TUC3x%1TTSYLqW1uf7L~%`9qm<^Qk?S39MAGXf0dy)b@HMs&O+AX!g?e& z6he^P)fQFwrPe+p+I)}R0A>|VOGNyJK5Nko3(nV#L?q6Jw4n2?26WRv#2KyadvMOt_Z31iJ(MMMZZ1G-Mdg z{lwCLAOx3*&0E2#MBLX(7s2j;prsHj#3x)^A@^|+BiK*vUETMHk+0pn5{B!~wYx%l zjcu>h8=~lc*Fp%g*tG@nc-A=x!Q~;rYmK3}0Mde+?$A^FAq2^Gt-4Qo66{nsE&7`q z$!1Vjtt0Ird$TB;#EW!{m1k+e`wJr3t9h{B3870($E=Z=(0m}dr8SbEuOcA?ox)yj zyJ|ByNp(dE+MzimJ%T--kbf?`b2(1_r=D+;ZZ&&Mm1Q^O>)hm50 + + getNonZeroUnknownDiseaseRiskExistsBool := func()bool{ + + if (diseaseIsDominantOrRecessive == "Recessive"){ + // We know there exists a non-zero risk + // We know that at least 1 of the two people has a known pass-the-variant probability + // If either person had a 0% probability of passing a variant, the + // GetOffspringMonogenicDiseaseProbabilities function would have returned a 0% risk of + /// the offspring having the disease. + // Thus, there exists the possibility of the offspring having the disease + // We will warn the user about this, so they can get tested and make sure they are not + // a carrier for the same disease + + return true + } + // diseaseIsDominantOrRecessive == "Dominant" + + if (userProbabilityIsKnown == true && userProbabilityOfPassingADiseaseVariantInt != 0){ + return true + } + + if (myProbabilityIsKnown == true && myProbabilityOfPassingADiseaseVariant != 0){ + return true + } + + return false + } + + nonZeroUnknownDiseaseRiskExists = getNonZeroUnknownDiseaseRiskExistsBool() + continue } @@ -621,12 +602,14 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA continue } - if (offspringProbabilityOfDisease == 100){ + if (offspringPercentageProbabilityOfDisease == 100){ // Probability that offspring will have a disease is 100% return true, profileVersion, "100", nil } + offspringProbabilityOfDisease := float64(offspringPercentageProbabilityOfDisease)/float64(100) + allDiseaseProbabilitiesList = append(allDiseaseProbabilitiesList, offspringProbabilityOfDisease) } @@ -654,8 +637,12 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA for _, diseaseProbability := range allDiseaseProbabilitiesList{ + if (diseaseProbability < 0 || diseaseProbability > 1){ + return false, 0, "", errors.New("allDiseaseProbabilitiesList contains invalid disease probability.") + } + // We multiply by the probability of no disease - probabilityOfNoMonogenicDiseases *= 1 - diseaseProbability + probabilityOfNoMonogenicDiseases *= (1 - diseaseProbability) } probabilityOfAtLeast1Disease := 1 - probabilityOfNoMonogenicDiseases @@ -773,7 +760,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA // For example, Breast Cancer is not as bad as Epilepsy // LifeView.com weights each disease in their polygenic disease risk score calculation - myPersonChosen, myGenomesExist, myAnalysisIsReady, myGeneticAnalysisMapList, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + myPersonChosen, myGenomesExist, myAnalysisIsReady, myGeneticAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { return false, 0, "", err } if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ @@ -807,7 +794,11 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA for _, locusObject := range diseaseLociList{ - locusIdentifier := locusObject.LocusIdentifier + locusIdentifierHex := locusObject.LocusIdentifier + + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return false, 0, "", err } + locusRSID := locusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) @@ -819,7 +810,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA locusValueAttributeName := "LocusValue_rs" + locusRSIDString - myLocusInfoIsKnown, _, myLocusBasePair, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myGeneticAnalysisMapList, diseaseName, locusIdentifier, myGenomeIdentifier) + myLocusInfoIsKnown, _, myLocusBase1, myLocusBase2, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, locusIdentifier, myGenomeIdentifier) if (err != nil) { return false, 0, "", err } if (myLocusInfoIsKnown == false){ continue @@ -831,7 +822,12 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA continue } - offspringLocusRiskWeight, _, _, _, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBasePair, userLocusBasePair) + userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";") + if (semicolonFound == false){ + return false, 0, "", errors.New("GetAnyProfileAttributeIncludingCalculated called with profile containing invalid " + locusValueAttributeName + ": " + userLocusBasePair) + } + + offspringLocusRiskWeight, _, _, _, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBase1, myLocusBase2, userLocusBase1, userLocusBase2) if (err != nil) { return false, 0, "", err } offspringNumberOfLociTested += 1 @@ -1385,7 +1381,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA // We want this similarity comparison to aid users in their search for mates who look like them and with whom // they are likely to produce offspring who look like them - myPersonChosen, myGenomesExist, myAnalysisIsReady, myGeneticAnalysisMapList, myGenomeIdentifier, iHaveMultipleGenomes, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + myPersonChosen, myGenomesExist, myAnalysisIsReady, myGeneticAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { return false, 0, "", err } if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ @@ -1424,7 +1420,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA traitName := getTraitName() - myTraitLociMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myGeneticAnalysisMapList, traitName, myGenomeIdentifier, iHaveMultipleGenomes) + myTraitLociMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myGeneticAnalysisObject, traitName, myGenomeIdentifier) if (err != nil) { return false, 0, "", err } traitObject, err := traits.GetTraitObject(traitName) @@ -1461,8 +1457,8 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA // This would save space on profiles because we wouldn't have to share an IsPhased bool for each locus (because all loci would be phased) // People who are more knowledgeable about genetics should share their opinions. - myLocusValueBase1 := myLocusValue.Base1 - myLocusValueBase2 := myLocusValue.Base2 + myLocusValueBase1 := myLocusValue.Base1Value + myLocusValueBase2 := myLocusValue.Base2Value userLocusValueBase1, userLocusValueBase2, semicolonExists := strings.Cut(userLocusValue, ";") if (semicolonExists == false){ diff --git a/internal/profiles/myProfileExports/myProfileExports.go b/internal/profiles/myProfileExports/myProfileExports.go index 5a3aed3..304227a 100644 --- a/internal/profiles/myProfileExports/myProfileExports.go +++ b/internal/profiles/myProfileExports/myProfileExports.go @@ -135,7 +135,7 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{ return nil } - myGenomePersonChosen, anyGenomesExist, personAnalysisIsReady, myGeneticAnalysisMapList, genomeIdentifierToShare, multipleGenomesExist, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + myGenomePersonChosen, anyGenomesExist, personAnalysisIsReady, myGeneticAnalysisObject, genomeIdentifierToShare, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { return err } if (myGenomePersonChosen == false){ // User has not linked a genome person. @@ -166,7 +166,7 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{ continue } - probabilitiesKnown, _, _, probabilityOfPassingADiseaseVariant, _, numberOfVariantsTested, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(myGeneticAnalysisMapList, monogenicDiseaseName, genomeIdentifierToShare, multipleGenomesExist) + probabilitiesKnown, _, probabilityOfPassingADiseaseVariant, _, numberOfVariantsTested, _, _, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(myGeneticAnalysisObject, monogenicDiseaseName, genomeIdentifierToShare) if (err != nil) { return err } if (probabilitiesKnown == false){ continue @@ -209,9 +209,12 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{ for _, locusObject := range diseaseLociList{ - locusIdentifier := locusObject.LocusIdentifier + locusIdentifierHex := locusObject.LocusIdentifier - locusValueKnown, _, locusBasePair, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myGeneticAnalysisMapList, diseaseName, locusIdentifier, genomeIdentifierToShare) + locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) + if (err != nil) { return err } + + locusValueKnown, _, locusBase1, locusBase2, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifierToShare) if (err != nil) { return err } if (locusValueKnown == false){ continue @@ -221,7 +224,7 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{ locusRSIDString := helpers.ConvertInt64ToString(locusRSID) - profileMap["LocusValue_rs" + locusRSIDString] = locusBasePair + profileMap["LocusValue_rs" + locusRSIDString] = locusBase1 + ";" + locusBase2 } } @@ -243,15 +246,15 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{ continue } - myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myGeneticAnalysisMapList, traitName, genomeIdentifierToShare, multipleGenomesExist) + myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myGeneticAnalysisObject, traitName, genomeIdentifierToShare) if (err != nil) { return err } for rsID, locusValueObject := range myTraitLocusValuesMap{ rsIDString := helpers.ConvertInt64ToString(rsID) - locusBase1 := locusValueObject.Base1 - locusBase2 := locusValueObject.Base2 + locusBase1 := locusValueObject.Base1Value + locusBase2 := locusValueObject.Base2Value basePairValue := locusBase1 + ";" + locusBase2 diff --git a/resources/geneticReferences/geneticReferences_test.go b/resources/geneticReferences/geneticReferences_test.go index 3f99b23..c76745c 100644 --- a/resources/geneticReferences/geneticReferences_test.go +++ b/resources/geneticReferences/geneticReferences_test.go @@ -27,15 +27,11 @@ func TestGeneticReferences(t *testing.T){ verifyIdentifier := func(inputIdentifier string)bool{ - decodedBytes, err := encoding.DecodeHexStringToBytes(inputIdentifier) + _, err := encoding.DecodeHexStringTo3ByteArray(inputIdentifier) if (err != nil) { return false } - if (len(decodedBytes) != 3){ - return false - } - return true } diff --git a/resources/geneticReferences/polygenicDiseases/breastCancer.go b/resources/geneticReferences/polygenicDiseases/breastCancer.go index 79936ed..1a4d4c1 100644 --- a/resources/geneticReferences/polygenicDiseases/breastCancer.go +++ b/resources/geneticReferences/polygenicDiseases/breastCancer.go @@ -947,7 +947,7 @@ func getBreastCancerDiseaseObject()PolygenicDisease{ DiseaseName: "Breast Cancer", EffectedSex: "Both", - DiseaseDescription: "Cancer growth in the tissue of a person's breast.", + DiseaseDescription: "Cancer growth in the tissue of a person's chest breast.", LociList: breastCancerLociList, GetAverageRiskProbabilitiesFunction: getAverageRiskProbabilitiesFunction, References: referencesMap, diff --git a/utilities/createGeneticModels/createGeneticModels.go b/utilities/createGeneticModels/createGeneticModels.go index 77684fd..af41276 100644 --- a/utilities/createGeneticModels/createGeneticModels.go +++ b/utilities/createGeneticModels/createGeneticModels.go @@ -707,11 +707,10 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f continue } - newGenomeIdentifier, err := helpers.GetNewRandomHexString(16) + newGenomeIdentifier, err := helpers.GetNewRandom16ByteArray() if (err != nil) { return false, false, err } rawGenomeWithMetadata := prepareRawGenomes.RawGenomeWithMetadata{ - GenomeIdentifier: newGenomeIdentifier, GenomeIsPhased: rawGenomeIsPhased, RawGenomeMap: rawGenomeMap, diff --git a/utilities/createSampleGeneticAnalyses/.gitignore b/utilities/createSampleGeneticAnalyses/.gitignore index f296b3a..fabf7b8 100644 --- a/utilities/createSampleGeneticAnalyses/.gitignore +++ b/utilities/createSampleGeneticAnalyses/.gitignore @@ -1,5 +1,5 @@ Person1RawGenome.txt Person2RawGenome.txt -SamplePerson1Analysis.json -SamplePerson2Analysis.json -SampleCoupleAnalysis.json \ No newline at end of file +SamplePerson1Analysis.messagepack +SamplePerson2Analysis.messagepack +SampleCoupleAnalysis.messagepack \ No newline at end of file diff --git a/utilities/createSampleGeneticAnalyses/createSampleGeneticAnalyses.go b/utilities/createSampleGeneticAnalyses/createSampleGeneticAnalyses.go index e303db6..5bbe846 100644 --- a/utilities/createSampleGeneticAnalyses/createSampleGeneticAnalyses.go +++ b/utilities/createSampleGeneticAnalyses/createSampleGeneticAnalyses.go @@ -12,6 +12,7 @@ import "seekia/resources/geneticReferences/monogenicDiseases" import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/resources/geneticReferences/traits" +import "seekia/internal/encoding" import "seekia/internal/localFilesystem" import "seekia/internal/genetics/createGeneticAnalysis" import "seekia/internal/genetics/prepareRawGenomes" @@ -37,7 +38,7 @@ func main(){ // -[]prepareRawGenomes.RawGenomeWithMetadata: Person genomes list // -string: Analysis string // -error - getPersonRawGenomeListAndAnalysis := func(personRawGenomeFilepath string, genomeIdentifier string)(bool, []prepareRawGenomes.RawGenomeWithMetadata, string, error){ + getPersonRawGenomeListAndAnalysis := func(personRawGenomeFilepath string, genomeIdentifierHex string)(bool, []prepareRawGenomes.RawGenomeWithMetadata, string, error){ fileExists, personRawGenomeFileBytes, err := localFilesystem.GetFileContents(personRawGenomeFilepath) if (err != nil){ return false, nil, "", err } @@ -47,6 +48,9 @@ func main(){ personRawGenomeFileString := string(personRawGenomeFileBytes) + genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex) + if (err != nil) { return false, nil, "", err } + genomeIsValid, rawGenomeWithMetadata, err := prepareRawGenomes.CreateRawGenomeWithMetadataObject(genomeIdentifier, personRawGenomeFileString) if (err != nil){ return false, nil, "", err } if (genomeIsValid == false){ @@ -114,18 +118,18 @@ func main(){ return } - err = localFilesystem.CreateOrOverwriteFile([]byte(person1GeneticAnalysis), "./", "SamplePerson1Analysis.json") + err = localFilesystem.CreateOrOverwriteFile([]byte(person1GeneticAnalysis), "./", "SamplePerson1Analysis.messagepack") if (err != nil){ log.Println(err.Error()) return } - err = localFilesystem.CreateOrOverwriteFile([]byte(person2GeneticAnalysis), "./", "SamplePerson2Analysis.json") + err = localFilesystem.CreateOrOverwriteFile([]byte(person2GeneticAnalysis), "./", "SamplePerson2Analysis.messagepack") if (err != nil){ log.Println(err.Error()) return } - err = localFilesystem.CreateOrOverwriteFile([]byte(coupleGeneticAnalysis), "./", "SampleCoupleAnalysis.json") + err = localFilesystem.CreateOrOverwriteFile([]byte(coupleGeneticAnalysis), "./", "SampleCoupleAnalysis.messagepack") if (err != nil){ log.Println(err.Error()) return