diff --git a/Changelog.md b/Changelog.md index 7404b78..58988ca 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Small and insignificant changes may not be included in this log. ## Unversioned Changes +* Implemented neural network prediction for polygenic diseases to replace old method. Added autism and homosexualness to genetic analyses. - *Simon Sarasova* * Increased the quantity of attributes that are extracted from the OpenSNP biobank data archive. - *Simon Sarasova* * Added numeric traits to genetic analyses. - *Simon Sarasova* * Improved Documentation.md and Future-Plans.md. - *Simon Sarasova* diff --git a/Contributors.md b/Contributors.md index d72e5f6..4630e50 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 | 277 \ No newline at end of file +Simon Sarasova | June 13, 2023 | 278 \ No newline at end of file diff --git a/documentation/Future-Plans.md b/documentation/Future-Plans.md index 99b56c6..8ec2093 100644 --- a/documentation/Future-Plans.md +++ b/documentation/Future-Plans.md @@ -289,9 +289,9 @@ There could be several analysis methods. These analysis methods will serve as an Providing an open source ancestral analysis method is essential for race aware mate discovery technology to be credibly neutral. There already exist multiple open source ancestral analysis packages. -### Add Custom Type Illnesses +### Add Complex Disease Diagnosis -Many genetic illnesses are not able to be detected using the methods implemented in the `monogenicDiseases` or the `polygenicDiseases` packages. +Many genetic diseases are not able to be detected using the methods implemented in the `monogenicDiseases` or the `polygenicDiseases` packages. Examples include diseases such as Fragile X and Turner's Syndrome. @@ -299,11 +299,7 @@ A new format called `complexDiseases` could be created. Each disease can have a function that takes in a genome map and returns a diagnosis. -Many of these diseases may require additional data from the raw genome files that is not included in the genome map. - -The `ReadRawGenomeFile` function should be able to read this relevant data. - -The GUI would also have an accompanying set of pages to display these Custom illnesses. +The GUI would also have an accompanying set of pages to display complex diseases. ### Add Polygenic Disease Probability Risk @@ -313,23 +309,17 @@ Meaning, we want to tell the user the estimated probability that they will get a Example: Normal risk = 5%, Your risk = 10% -We should be able to calculate this risk. We know the polygenic disease odds ratio of a base pair `(odds of disease with base pair)/(odds of disease with standard (common) base pair)`. We know the average probability of disease for the general population for each age period. We know the probability of each base pair for the general population. - This will be the most useful statistic for users trying to understand their polygenic disease risk. -Knowing that the probability of a particular type of cancer has increased by 10x is very different depending on the probability of getting the cancer. +Knowing that your risk score for a particular type of cancer is 10/10 is much less useful than understanding your probability of getting the cancer. If the general population probability of getting cancer X is 5%, and the user's adjusted risk is 50%, that is a significant increase. However, if the general population risk is 0.1%, and the user's adjusted risk is 1%, then the user does not need to change their behavior or worry much. -### Add Neural Network Genetic Predictions +### Get Genetic Training Data -The current method for predicting polygenic disease risks and traits is not as informative and accurate as using neural nets. +We use neural networks to predict traits and polygenic diseases. We have to train these networks using example training data. This training data is a collection of people's genomes and the trait/polygenic disease information for each person. -Our current model adds and subtracts the likelihood values of various SNPs that are reported to have an effect on polygenic diseases and traits. - -A much better method is to train a neural net to predict traits and polygenic diseases on a large number of genes. There are methods that exist to find the set of genes that have an effect on each trait/disease. For example, height is said to be effected by ~10,000 SNPs. Many GWAS studies exist which report which genes are responsible for certain traits and diseases. These are the genes to feed into the neural net for each trait/disease. These are also the genes that users will share in their profiles. I have already started to try to build this system. See `geneticPrediction.go` for an implementation of trait prediction using neural networks, and `createCoupleGeneticAnalysis.go` for information on how offspring predictions would work. - -This method requires training data, which is largely unavailable for public use. We need fully open training data, not data that requires registration or permission to download. +Good training data is largely unavailable for public use. We need fully open training data, not data that requires registration or permission to download. [OpenSNP.org](https://opensnp.org) is a free genomic data repository. OpenSNP relies on user submitted data, which can be falsified. OpenSNP should add a verification system so data provided by trustworthy people can be prioritized. @@ -339,10 +329,10 @@ Whoever collects the data needs to choose what data to collect from each person. Some examples of data to collect: -* Collecting polygenic disease information would enable prediction of polygenic disease risk. +* Collecting polygenic disease information enables prediction of polygenic disease risk. * Pictures and scans of participants faces would enable a genetic test for facial structure * Personality tests would enable prediction of personality -* Measuring height would enable prediction of height +* Measuring height enables prediction of height These kinds of genetic tests would allow parents to choose what their offspring will look like, their personality, and their intelligence. @@ -356,10 +346,12 @@ All of this is already possible, but will become easier with the proliferation o ### Add more diseases and traits -This task entails entering disease/trait SNP data from SNPedia.com and other sources. The bases have to be flipped if the orientation on SNPedia is minus. This requires flipping G/C and A/T. At least 3 people should check any added disease SNPs to ensure accuracy. +Adding monogenic diseases entails entering disease SNP data from SNPedia.com and other sources. The bases have to be flipped if the orientation on SNPedia is minus. This requires flipping G/C and A/T. At least 3 people should check any added disease SNPs to ensure accuracy. This is a tedious data entry process with negative consequences if mistakes are made. Many users could falsely believe they have monogenic diseases, which could trigger mental health crises. +Adding polygenic diseases/traits requires training data and access to genome wide association studies. Seekia should also have the ability to perform genome wide association studies to find causal genes for traits. + ### Interactive Map Seekia should have an interactive world map. It would be similar to OpenStreetMaps, but with much less detail. It would only need to contain borders of countries and states as lines. It would be able to display latitude/longitude coordinates on the map as points. diff --git a/gui/helpGui.go b/gui/helpGui.go index 369275e..cf0ef11 100644 --- a/gui/helpGui.go +++ b/gui/helpGui.go @@ -569,7 +569,7 @@ func setPolygenicDiseaseLociExplainerPage(window fyne.Window, previousPage func( description1 := getLabelCentered("Each polygenic disease has a set of associated genome loci.") description2 := getLabelCentered("These are locations on the genome that can be tested to determine disease risk.") - description3 := getLabelCentered("The more loci that your genome contains, the more accurate your disease risk score will be.") + description3 := getLabelCentered("The more loci that your genome sequence contains, the more accurate your disease risk score will be.") page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3) @@ -615,65 +615,6 @@ func setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window fyne.Win } -func setPolygenicDiseaseLocusRiskWeightExplainerPage(window fyne.Window, previousPage func()){ - - title := getPageTitleCentered("Help - Locus Risk Weight") - - backButton := getBackButtonCentered(previousPage) - - subtitle := getPageSubtitleCentered("Locus Risk Weight") - - description1 := getLabelCentered("A polygenic disease risk score is calculated by testing many locations on a genome.") - description2 := getLabelCentered("A genome will have a risk weight for each locus.") - description3 := getLabelCentered("A negative weight reduces the risk of the disease.") - description4 := getLabelCentered("A positive weight increases the risk of the disease.") - description5 := getLabelCentered("A 0 weight has no effect on the risk.") - - page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5) - - setPageContent(page, window) -} - -func setOffspringPolygenicDiseaseLocusRiskWeightExplainerPage(window fyne.Window, previousPage func()){ - - title := getPageTitleCentered("Help - Locus Risk Weight") - - backButton := getBackButtonCentered(previousPage) - - subtitle := getPageSubtitleCentered("Offspring Locus Risk Weight") - - description1 := getLabelCentered("A polygenic disease risk score is calculated by testing many locations on a genome.") - description2 := getLabelCentered("A genome will have a risk weight for each locus.") - description3 := getLabelCentered("A negative weight reduces the risk of the disease.") - description4 := getLabelCentered("A positive weight increases the risk of the disease.") - description5 := getLabelCentered("A 0 weight has no effect on the risk.") - description6 := getLabelCentered("An offspring's locus risk weight represents the average risk weight for all 4 possible locus outcomes.") - - page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6) - - setPageContent(page, window) -} - -func setPolygenicDiseaseLocusRiskWeightProbabilityExplainerPage(window fyne.Window, previousPage func()){ - - title := getPageTitleCentered("Help - Risk Weight Probability") - - backButton := getBackButtonCentered(previousPage) - - subtitle := getPageSubtitleCentered("Locus Risk Weight Probability") - - description1 := getLabelCentered("A polygenic disease risk score is calculated by testing many locations on a genome.") - description2 := getLabelCentered("A genome will have a risk weight for each locus.") - description3 := getLabelCentered("A risk weight probability describes the probability of having that risk weight.") - description4 := getLabelCentered("For example, lets suppose a risk weight of 2 has a probability of 5%") - description5 := getLabelCentered("This means that 5% of people will have a risk weight of 2 at this locus.") - - page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5) - - setPageContent(page, window) -} - - func setDiscreteTraitNeuralNetworkPredictionExplainerPage(window fyne.Window, previousPage func()){ title := getPageTitleCentered("Help - Neural Network Prediction") @@ -768,7 +709,7 @@ func setDiscreteTraitRulesExplainerPage(window fyne.Window, previousPage func()) backButton := getBackButtonCentered(previousPage) - subtitle := getPageSubtitleCentered("Trait Rules") + subtitle := getPageSubtitleCentered("Discrete Trait Rules") description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.") description2 := getLabelCentered("Discrete traits has multiple outcomes, and each outcome has an associated score.") diff --git a/gui/viewAnalysisGui_Couple.go b/gui/viewAnalysisGui_Couple.go index daa2142..2763983 100644 --- a/gui/viewAnalysisGui_Couple.go +++ b/gui/viewAnalysisGui_Couple.go @@ -815,7 +815,6 @@ func setViewCoupleMonogenicDiseaseVariantsPage(window fyne.Window, person1Name 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, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, variantIdentifier [3]byte, previousPage func()){ @@ -994,59 +993,115 @@ func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ return nil, err } + mainGenomePairIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) + diseaseNameLabel := getItalicLabelCentered("Disease Name") offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score") + confidenceRangeLabel := getItalicLabelCentered("Confidence Range") + conflictExistsLabel := getItalicLabelCentered("Conflict Exists?") - emptyLabel := widget.NewLabel("") + emptyLabel4 := widget.NewLabel("") diseaseNameColumn := container.NewVBox(diseaseNameLabel, widget.NewSeparator()) offspringRiskScoreColumn := container.NewVBox(offspringRiskScoreLabel, widget.NewSeparator()) + confidenceRangeColumn := container.NewVBox(confidenceRangeLabel, widget.NewSeparator()) conflictExistsColumn := container.NewVBox(conflictExistsLabel, widget.NewSeparator()) - viewButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) + viewButtonsColumn := container.NewVBox(emptyLabel4, widget.NewSeparator()) - diseaseNamesList, err := polygenicDiseases.GetPolygenicDiseaseNamesList() + diseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList() if (err != nil) { return nil, err } - for _, diseaseName := range diseaseNamesList{ + for _, diseaseObject := range diseaseObjectsList{ - mainGenomePairIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) + diseaseName := diseaseObject.DiseaseName - offspringRiskScoreKnown, _, offspringRiskScoreFormatted, _, _, conflictExists, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, mainGenomePairIdentifier) + neuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(diseaseName) + if (neuralNetworkExists == false){ + // We cannot analyze this disease + continue + } + + diseaseNameText := getBoldLabelCentered(diseaseName) + + analysisExists, offspringRiskScore, predictionConfidenceRangesMap, _, _, _, conflictExists, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, mainGenomePairIdentifier) if (err != nil) { return nil, err } - getRiskScoreLabelText := func()string{ + getRiskScoreLabel := func()fyne.Widget{ - if (offspringRiskScoreKnown == false){ - result := translate("Unknown") + if (analysisExists == false){ + result := widget.NewLabel(translate("Unknown")) return result } - return offspringRiskScoreFormatted + offspringRiskScoreString := helpers.ConvertIntToString(offspringRiskScore) + offspringRiskScoreFormatted := offspringRiskScoreString + "/10" + + riskScoreLabel := getBoldLabel(offspringRiskScoreFormatted) + + return riskScoreLabel } - offspringRiskScoreLabelText := getRiskScoreLabelText() + riskScoreLabel := getRiskScoreLabel() - diseaseNameText := getBoldLabelCentered(diseaseName) - diseaseNameColumn.Add(diseaseNameText) + riskScoreLabelCentered := getWidgetCentered(riskScoreLabel) - offspringRiskScoreText := getBoldLabelCentered(offspringRiskScoreLabelText) - offspringRiskScoreColumn.Add(offspringRiskScoreText) + getConfidenceRangeLabel := func()(fyne.Widget, error){ + + if (analysisExists == false){ + unknownLabel := widget.NewLabel("Unknown") + return unknownLabel, nil + } + + // This is a list of the percentage accuracies in the map + // For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be + // accurate within that range + confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap) + + // We sort the list so the percentage is always the same upon refreshing the page + slices.Sort(confidenceRangePercentagesList) + + closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80) + if (err != nil) { return nil, err } + + closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage] + if (exists == false){ + return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") + } + + closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2) + + closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) + + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" + + confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) + + return confidenceRangeLabel, nil + } + + confidenceRangeLabel, err := getConfidenceRangeLabel() + if (err != nil) { return nil, err } conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) conflictExistsLabel := getBoldLabelCentered(conflictExistsString) - conflictExistsColumn.Add(conflictExistsLabel) viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, currentPage) })) + + diseaseNameColumn.Add(diseaseNameText) + offspringRiskScoreColumn.Add(riskScoreLabelCentered) + confidenceRangeColumn.Add(confidenceRangeLabel) + conflictExistsColumn.Add(conflictExistsLabel) viewButtonsColumn.Add(viewDetailsButton) - + diseaseNameColumn.Add(widget.NewSeparator()) offspringRiskScoreColumn.Add(widget.NewSeparator()) + confidenceRangeColumn.Add(widget.NewSeparator()) conflictExistsColumn.Add(widget.NewSeparator()) viewButtonsColumn.Add(widget.NewSeparator()) } @@ -1055,8 +1110,14 @@ func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso setOffspringPolygenicDiseaseRiskScoreExplainerPage(window, currentPage) }) offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton) - - diseasesGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, offspringRiskScoreColumn) + + confidenceRangeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + //TODO + showUnderConstructionDialog(window) + }) + confidenceRangeColumn.Add(confidenceRangeHelpButton) + + diseasesGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, offspringRiskScoreColumn, confidenceRangeColumn) if (secondGenomePairExists == true){ @@ -1089,10 +1150,25 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, previousPage)} + neuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(diseaseName) + if (neuralNetworkExists == false){ + // We cannot analyze this disease + setErrorEncounteredPage(window, errors.New("setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage called non-analyzable trait: " + diseaseName), previousPage) + return + } + + diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName) + if (err != nil){ + setErrorEncounteredPage(window, err, previousPage) + return + } + + diseaseLociList := diseaseObject.LociList + title := getPageTitleCentered("Viewing Couple Analysis - " + diseaseName) backButton := getBackButtonCentered(previousPage) - + pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) @@ -1127,59 +1203,121 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, emptyLabel1 := widget.NewLabel("") emptyLabel2 := widget.NewLabel("") - offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score") - emptyLabel3 := widget.NewLabel("") emptyLabel4 := widget.NewLabel("") - emptyLabel5 := widget.NewLabel("") - viewGenomePairButtonsColumn := container.NewVBox(emptyLabel1, widget.NewSeparator()) - pairNameColumn := container.NewVBox(emptyLabel2, widget.NewSeparator()) - offspringRiskScoreColumn := container.NewVBox(offspringRiskScoreLabel, widget.NewSeparator()) - viewSampleOffspringsChartButtonsColumn := container.NewVBox(emptyLabel3, widget.NewSeparator()) - viewLifetimeRiskButtonsColumn := container.NewVBox(emptyLabel4, widget.NewSeparator()) - viewOffspringLociButtonsColumn := container.NewVBox(emptyLabel5, widget.NewSeparator()) + offspringLabel := getItalicLabelCentered("Offspring") + riskScoreLabel := getItalicLabelCentered("Risk Score") + + emptyLabel5 := widget.NewLabel("") + confidenceRangeLabel := getItalicLabelCentered("Confidence Range") + + quantityOfLabel := getItalicLabelCentered("Quantity Of") + lociKnownLabel := getItalicLabelCentered("Loci Known") + + emptyLabel6 := widget.NewLabel("") + emptyLabel7 := widget.NewLabel("") + + emptyLabel8 := widget.NewLabel("") + emptyLabel9 := widget.NewLabel("") + + emptyLabel10 := widget.NewLabel("") + emptyLabel11 := widget.NewLabel("") + + viewGenomePairButtonsColumn := container.NewVBox(emptyLabel1, emptyLabel2, widget.NewSeparator()) + pairNameColumn := container.NewVBox(emptyLabel3, emptyLabel4, widget.NewSeparator()) + offspringRiskScoreColumn := container.NewVBox(offspringLabel, riskScoreLabel, widget.NewSeparator()) + confidenceRangeColumn := container.NewVBox(emptyLabel5, confidenceRangeLabel, widget.NewSeparator()) + quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel, lociKnownLabel, widget.NewSeparator()) + viewSampleOffspringsChartButtonsColumn := container.NewVBox(emptyLabel6, emptyLabel7, widget.NewSeparator()) + viewLifetimeRiskButtonsColumn := container.NewVBox(emptyLabel8, emptyLabel9, widget.NewSeparator()) + viewDetailsButtonsColumn := container.NewVBox(emptyLabel10, emptyLabel11, widget.NewSeparator()) addGenomePairRow := func(genomePairName string, person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{ + genomePairNameLabel := getBoldLabelCentered(genomePairName) + genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier) - offspringRiskScoreKnown, _, offspringRiskScoreFormatted, sampleOffspringRiskScoresList, numberOfLociTested, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) + analysisExists, offspringRiskScore, predictionConfidenceRangesMap, quantityOfLociKnown, _, sampleOffspringRiskScoresList, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) if (err != nil) { return err } - getRiskScoreLabelText := func()string{ + getRiskScoreLabel := func()fyne.Widget{ - if (offspringRiskScoreKnown == false){ - result := translate("Unknown") + if (analysisExists == false){ + result := widget.NewLabel(translate("Unknown")) return result } - return offspringRiskScoreFormatted + offspringRiskScoreString := helpers.ConvertIntToString(offspringRiskScore) + offspringRiskScoreFormatted := offspringRiskScoreString + "/10" + + offspringRiskScoreLabel := getBoldLabel(offspringRiskScoreFormatted) + + return offspringRiskScoreLabel } - riskScoreLabelText := getRiskScoreLabelText() + riskScoreLabel := getRiskScoreLabel() - genomePairNameLabel := getBoldLabelCentered(genomePairName) + riskScoreLabelCentered := getWidgetCentered(riskScoreLabel) - offspringRiskScoreLabel := getBoldLabelCentered(riskScoreLabelText) + getConfidenceRangeLabel := func()(fyne.Widget, error){ + + if (analysisExists == false){ + unknownLabel := widget.NewLabel("Unknown") + return unknownLabel, nil + } + + // This is a list of the percentage accuracies in the map + // For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be + // accurate within that range + confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap) + + // We sort the list so the percentage is always the same upon refreshing the page + slices.Sort(confidenceRangePercentagesList) + + closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80) + if (err != nil) { return nil, err } + + closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage] + if (exists == false){ + return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") + } + + closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2) + + closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) + + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" + + confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) + + return confidenceRangeLabel, nil + } + + confidenceRangeLabel, err := getConfidenceRangeLabel() + if (err != nil) { return err } + + totalQuantityOfLoci := len(diseaseLociList) + + quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown) + totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci) + + quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString + + quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted) viewGenomePairButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) }) viewSampleOffspringsChartButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window, diseaseName, sampleOffspringRiskScoresList, numberOfLociTested, currentPage) + setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window, diseaseName, sampleOffspringRiskScoresList, quantityOfLociKnown, currentPage) }) viewOffspringLifetimeRiskButton := widget.NewButtonWithIcon("", theme.HistoryIcon(), func(){ - diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName) - if (err != nil){ - setErrorEncounteredPage(window, err, currentPage) - return - } - getPageMaleOrFemale := func()string{ //TODO: Get user sex from myLocalProfiles @@ -1197,23 +1335,28 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, setViewPersonPolygenicDiseaseLifetimeProbabilitiesPage(window, diseaseName, genomePairName, pageMaleOrFemale, currentPage) }) - viewOffspringLociButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCouplePolygenicDiseaseLociPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) + viewOffspringDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ + //TODO + showUnderConstructionDialog(window) }) viewGenomePairButtonsColumn.Add(viewGenomePairButton) pairNameColumn.Add(genomePairNameLabel) - offspringRiskScoreColumn.Add(offspringRiskScoreLabel) + offspringRiskScoreColumn.Add(riskScoreLabelCentered) + confidenceRangeColumn.Add(confidenceRangeLabel) + quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel) viewSampleOffspringsChartButtonsColumn.Add(viewSampleOffspringsChartButton) viewLifetimeRiskButtonsColumn.Add(viewOffspringLifetimeRiskButton) - viewOffspringLociButtonsColumn.Add(viewOffspringLociButton) + viewDetailsButtonsColumn.Add(viewOffspringDetailsButton) viewGenomePairButtonsColumn.Add(widget.NewSeparator()) pairNameColumn.Add(widget.NewSeparator()) offspringRiskScoreColumn.Add(widget.NewSeparator()) + confidenceRangeColumn.Add(widget.NewSeparator()) + quantityOfLociKnownColumn.Add(widget.NewSeparator()) viewSampleOffspringsChartButtonsColumn.Add(widget.NewSeparator()) viewLifetimeRiskButtonsColumn.Add(widget.NewSeparator()) - viewOffspringLociButtonsColumn.Add(widget.NewSeparator()) + viewDetailsButtonsColumn.Add(widget.NewSeparator()) return nil } @@ -1238,14 +1381,27 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton) - genomesContainer := container.NewHBox(layout.NewSpacer(), viewGenomePairButtonsColumn, pairNameColumn, offspringRiskScoreColumn, viewSampleOffspringsChartButtonsColumn, viewLifetimeRiskButtonsColumn, viewOffspringLociButtonsColumn, layout.NewSpacer()) + confidenceRangeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + showUnderConstructionDialog(window) + //TODO + }) + + confidenceRangeColumn.Add(confidenceRangeHelpButton) + + offspringQuantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + + setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) + }) + + quantityOfLociKnownColumn.Add(offspringQuantityOfLociKnownHelpButton) + + genomesContainer := container.NewHBox(layout.NewSpacer(), viewGenomePairButtonsColumn, pairNameColumn, offspringRiskScoreColumn, confidenceRangeColumn, quantityOfLociKnownColumn, viewSampleOffspringsChartButtonsColumn, viewLifetimeRiskButtonsColumn, viewDetailsButtonsColumn, layout.NewSpacer()) page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), genomesContainer) - + setPageContent(page, window) } - 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, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, previousPage)} @@ -1256,6 +1412,14 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy description := getLabelCentered("Below is the disease information for both genomes in the genome pair.") + diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName) + if (err != nil){ + setErrorEncounteredPage(window, err, previousPage) + return + } + + diseaseLociList := diseaseObject.LociList + diseaseNameLabel := widget.NewLabel("Disease:") diseaseNameText := getBoldLabel(diseaseName) diseaseNameInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ @@ -1279,7 +1443,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy emptyLabelC := widget.NewLabel("") riskScoreLabel := getItalicLabelCentered("Risk Score") - numberOfLabel := getItalicLabelCentered("Number Of") + quantityOfLabel := getItalicLabelCentered("Quantity Of") lociTestedLabel := getItalicLabelCentered("Loci Tested") emptyLabelD := widget.NewLabel("") @@ -1288,7 +1452,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy personNameColumn := container.NewVBox(emptyLabelA, personNameLabel, widget.NewSeparator()) genomeNameColumn := container.NewVBox(emptyLabelB, genomeNameLabel, widget.NewSeparator()) riskScoreColumn := container.NewVBox(emptyLabelC, riskScoreLabel, widget.NewSeparator()) - numberOfLociTestedColumn := container.NewVBox(numberOfLabel, lociTestedLabel, widget.NewSeparator()) + quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel, lociTestedLabel, widget.NewSeparator()) viewGenomeButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) addGenomeRow := func(isPerson1 bool, personName string, inputGenomeIdentifier [16]byte)error{ @@ -1331,38 +1495,52 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy genomeNameLabel := getBoldLabelCentered(genomeName) - personRiskScoreKnown, _, personRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier) + analysisExists, personRiskScore, _, quantityOfLociKnown, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier) if (err != nil) { return err } - getPersonRiskScoreLabelText := func()string{ - if (personRiskScoreKnown == false){ - result := translate("Unknown") + getPersonRiskScoreLabel := func()fyne.Widget{ + + if (analysisExists == false){ + + result := widget.NewLabel(translate("Unknown")) return result } - return personRiskScoreFormatted + + personRiskScoreString := helpers.ConvertIntToString(personRiskScore) + personRiskScoreFormatted := personRiskScoreString + "/10" + personRiskScoreLabel := getBoldLabel(personRiskScoreFormatted) + + return personRiskScoreLabel } - personRiskScoreLabelText := getPersonRiskScoreLabelText() + personRiskScoreLabel := getPersonRiskScoreLabel() - genomeRiskScoreLabel := getBoldLabelCentered(personRiskScoreLabelText) + genomeRiskScoreLabelCentered := getWidgetCentered(personRiskScoreLabel) - numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) - numberOfLociTestedText := getBoldLabelCentered(numberOfLociTestedString) + totalQuantityOfLoci := len(diseaseLociList) + + quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown) + totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci) + + quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString + + quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted) viewGenomeButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGenomePolygenicDiseaseLociPage(window, personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier, genomeName, currentPage) + //TODO + showUnderConstructionDialog(window) }) personNameColumn.Add(personNameLabel) genomeNameColumn.Add(genomeNameLabel) - riskScoreColumn.Add(genomeRiskScoreLabel) - numberOfLociTestedColumn.Add(numberOfLociTestedText) + riskScoreColumn.Add(genomeRiskScoreLabelCentered) + quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel) viewGenomeButtonsColumn.Add(viewGenomeButton) personNameColumn.Add(widget.NewSeparator()) genomeNameColumn.Add(widget.NewSeparator()) riskScoreColumn.Add(widget.NewSeparator()) - numberOfLociTestedColumn.Add(widget.NewSeparator()) + quantityOfLociKnownColumn.Add(widget.NewSeparator()) viewGenomeButtonsColumn.Add(widget.NewSeparator()) return nil @@ -1370,7 +1548,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy person1GenomeIdentifier, person2GenomeIdentifier := helpers.Split32ByteArrayInHalf(genomePairIdentifier) - err := addGenomeRow(true, person1Name, person1GenomeIdentifier) + err = addGenomeRow(true, person1Name, person1GenomeIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -1387,355 +1565,18 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy riskScoreColumn.Add(riskScoreHelpButton) - numberOfLociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) }) - numberOfLociTestedColumn.Add(numberOfLociTestedHelpButton) + quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton) - genomesGrid := container.NewHBox(layout.NewSpacer(), personNameColumn, genomeNameColumn, riskScoreColumn, numberOfLociTestedColumn, viewGenomeButtonsColumn, layout.NewSpacer()) + genomesGrid := container.NewHBox(layout.NewSpacer(), personNameColumn, genomeNameColumn, riskScoreColumn, quantityOfLociKnownColumn, viewGenomeButtonsColumn, layout.NewSpacer()) page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), genomePairRow, widget.NewSeparator(), genomesGrid) setPageContent(page, window) } - -// This function provides a page to view the couple offspring locus probabilities for a particular genome pair -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, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, previousPage)} - - title := getPageTitleCentered("View Offspring Disease Loci - " + diseaseName) - - backButton := getBackButtonCentered(previousPage) - - description1 := widget.NewLabel("Below are the disease loci probabilities for offspring from this genome pair.") - diseaseLociHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPolygenicDiseaseLociExplainerPage(window, currentPage) - }) - description1Row := container.NewHBox(layout.NewSpacer(), description1, diseaseLociHelpButton, layout.NewSpacer()) - - genomePairLabel := widget.NewLabel("Genome Pair:") - genomePairNameLabel := getBoldLabel(genomePairName) - viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) - }) - - genomePairRow := container.NewHBox(layout.NewSpacer(), genomePairLabel, genomePairNameLabel, viewGenomePairInfoButton, layout.NewSpacer()) - - _, _, _, _, numberOfLociTested, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) - - diseaseLociMap, err := polygenicDiseases.GetPolygenicDiseaseLociMap(diseaseName) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - totalNumberOfLoci := len(diseaseLociMap) - totalNumberOfLociString := helpers.ConvertIntToString(totalNumberOfLoci) - - lociTestedLabel := widget.NewLabel("Loci Tested:") - lociTestedText := getBoldLabel(numberOfLociTestedString + "/" + totalNumberOfLociString) - lociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) - }) - - lociTestedRow := container.NewHBox(layout.NewSpacer(), lociTestedLabel, lociTestedText, lociTestedHelpButton, layout.NewSpacer()) - - getLociGrid := func()(*fyne.Container, error){ - - locusNameLabel := getItalicLabelCentered("Locus Name") - - offspringRiskWeightLabel := getItalicLabelCentered("Offspring Risk Weight") - - offspringOddsRatioLabel := getItalicLabelCentered("Offspring Odds Ratio") - - emptyLabel := widget.NewLabel("") - - locusNameColumn := container.NewVBox(locusNameLabel, widget.NewSeparator()) - offspringRiskWeightColumn := container.NewVBox(offspringRiskWeightLabel, widget.NewSeparator()) - offspringOddsRatioColumn := container.NewVBox(offspringOddsRatioLabel, widget.NewSeparator()) - lociInfoButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) - - addLocusRow := func(locusIdentifierHex string)error{ - - locusObject, exists := diseaseLociMap[locusIdentifierHex] - if (exists == false) { - 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(coupleAnalysisObject, diseaseName, locusIdentifier, genomePairIdentifier) - if (err != nil) { return err } - - getOffspringRiskWeightText := func()string{ - - if (offspringRiskWeightKnown == false){ - - unknownTextTranslated := translate("Unknown") - return unknownTextTranslated - } - - offspringRiskWeightString := helpers.ConvertIntToString(offspringRiskWeight) - - return offspringRiskWeightString - } - - offspringRiskWeightText := getOffspringRiskWeightText() - - getOffspringOddsRatioText := func()string{ - - if (offspringOddsRatioKnown == false){ - - unknownTextTranslated := translate("Unknown") - return unknownTextTranslated - } - - return offspringOddsRatioFormatted - } - - offspringOddsRatioText := getOffspringOddsRatioText() - - locusNameLabel := getBoldLabelCentered(locusName) - - locusRiskWeightLabel := getBoldLabelCentered(offspringRiskWeightText) - locusOddsRatioLabel := getBoldLabelCentered(offspringOddsRatioText) - - viewLocusDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, locusIdentifier, currentPage) - }) - - locusNameColumn.Add(locusNameLabel) - offspringRiskWeightColumn.Add(locusRiskWeightLabel) - offspringOddsRatioColumn.Add(locusOddsRatioLabel) - lociInfoButtonsColumn.Add(viewLocusDetailsButton) - - locusNameColumn.Add(widget.NewSeparator()) - offspringRiskWeightColumn.Add(widget.NewSeparator()) - offspringOddsRatioColumn.Add(widget.NewSeparator()) - lociInfoButtonsColumn.Add(widget.NewSeparator()) - - return nil - } - - lociWithKnownRiskWeightList := make([]string, 0) - lociWithUnknownRiskWeightList := make([]string, 0) - - for locusIdentifierHex, _ := range diseaseLociMap{ - - 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, locusIdentifierHex) - } else { - lociWithUnknownRiskWeightList = append(lociWithUnknownRiskWeightList, locusIdentifierHex) - } - } - - // We sort the lists so loci show up in the same order whenever page is refreshed - - helpers.SortStringListToUnicodeOrder(lociWithKnownRiskWeightList) - helpers.SortStringListToUnicodeOrder(lociWithUnknownRiskWeightList) - - for _, locusIdentifier := range lociWithKnownRiskWeightList{ - - err = addLocusRow(locusIdentifier) - if (err != nil) { return nil, err } - } - - for _, locusIdentifier := range lociWithUnknownRiskWeightList{ - - err = addLocusRow(locusIdentifier) - if (err != nil) { return nil, err } - } - - offspringRiskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setOffspringPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) - }) - offspringRiskWeightColumn.Add(offspringRiskWeightHelpButton) - - offspringOddsRatioHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - //TODO - showUnderConstructionDialog(window) - }) - offspringOddsRatioColumn.Add(offspringOddsRatioHelpButton) - - lociGrid := container.NewHBox(layout.NewSpacer(), locusNameColumn, offspringRiskWeightColumn, offspringOddsRatioColumn, lociInfoButtonsColumn, layout.NewSpacer()) - - return lociGrid, nil - } - - lociGrid, err := getLociGrid() - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - page := container.NewVBox(title, backButton, widget.NewSeparator(), description1Row, widget.NewSeparator(), genomePairRow, widget.NewSeparator(), lociTestedRow, widget.NewSeparator(), lociGrid) - - setPageContent(page, window) -} - - -// 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, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, locusIdentifier [3]byte, previousPage func()){ - - currentPage := func(){setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, locusIdentifier, previousPage)} - - title := getPageTitleCentered("Disease Locus Details - " + diseaseName) - - backButton := getBackButtonCentered(previousPage) - - locusIdentifierHex := encoding.EncodeBytesToHexString(locusIdentifier[:]) - - locusObject, err := polygenicDiseases.GetPolygenicDiseaseLocusObject(diseaseName, locusIdentifierHex) - if (err != nil) { - setErrorEncounteredPage(window, err, previousPage) - return - } - - locusRSID := locusObject.LocusRSID - locusRSIDString := helpers.ConvertInt64ToString(locusRSID) - 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, locusIdentifierHex, currentPage) - }) - locusNameRow := container.NewHBox(layout.NewSpacer(), locusNameLabel, locusNameText, locusInfoButton, layout.NewSpacer()) - - getGenomePairsLocusInfoGrid := func()(*fyne.Container, error){ - - emptyLabel := widget.NewLabel("") - - genomePairLabel := getItalicLabelCentered("Genome Pair") - - offspringRiskWeightLabel := getItalicLabelCentered("Offspring Risk Weight") - - offspringOddsRatioLabel := getItalicLabelCentered("Offspring Odds Ratio") - - viewGenomePairInfoButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) - genomePairNameColumn := container.NewVBox(genomePairLabel, widget.NewSeparator()) - offspringRiskWeightColumn := container.NewVBox(offspringRiskWeightLabel, widget.NewSeparator()) - offspringOddsRatioColumn := container.NewVBox(offspringOddsRatioLabel, widget.NewSeparator()) - - addGenomePairRow := func(genomePairName string, genomePairIdentifier [32]byte)error{ - - offspringRiskWeightKnown, offspringRiskWeight, offspringOddsRatioKnown, _, offspringOddsRatioFormatted, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, locusIdentifier, genomePairIdentifier) - if (err != nil) { return err } - - getOffspringRiskWeightText := func()string{ - - if (offspringRiskWeightKnown == false){ - result := translate("Unknown") - return result - } - - offspringRiskWeightString := helpers.ConvertIntToString(offspringRiskWeight) - - return offspringRiskWeightString - } - - offspringRiskWeightText := getOffspringRiskWeightText() - - getOffspringOddsRatioText := func()string{ - if (offspringOddsRatioKnown == false){ - result := translate("Unknown") - return result - } - return offspringOddsRatioFormatted - } - - offspringOddsRatioText := getOffspringOddsRatioText() - - viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) - }) - - genomePairNameLabel := getBoldLabelCentered(genomePairName) - - offspringRiskWeightLabel := getBoldLabelCentered(offspringRiskWeightText) - offspringOddsRatioLabel := getBoldLabelCentered(offspringOddsRatioText) - - viewGenomePairInfoButtonsColumn.Add(viewGenomePairInfoButton) - genomePairNameColumn.Add(genomePairNameLabel) - offspringRiskWeightColumn.Add(offspringRiskWeightLabel) - offspringOddsRatioColumn.Add(offspringOddsRatioLabel) - - viewGenomePairInfoButtonsColumn.Add(widget.NewSeparator()) - genomePairNameColumn.Add(widget.NewSeparator()) - offspringRiskWeightColumn.Add(widget.NewSeparator()) - offspringOddsRatioColumn.Add(widget.NewSeparator()) - - return nil - } - - pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject) - if (err != nil){ return nil, err } - - genomePair1Identifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) - - err = addGenomePairRow("Pair 1", genomePair1Identifier) - if (err != nil) { return nil, err } - - if (secondGenomePairExists == true){ - - genomePair2Identifier := helpers.JoinTwo16ByteArrays(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier) - - err := addGenomePairRow("Pair 2", genomePair2Identifier) - if (err != nil) { return nil, err } - } - - offspringRiskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setOffspringPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) - }) - - offspringOddsRatioHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - //TODO - showUnderConstructionDialog(window) - }) - - offspringRiskWeightColumn.Add(offspringRiskWeightHelpButton) - offspringOddsRatioColumn.Add(offspringOddsRatioHelpButton) - - genomesContainer := container.NewHBox(layout.NewSpacer(), viewGenomePairInfoButtonsColumn, genomePairNameColumn, offspringRiskWeightColumn, offspringOddsRatioColumn, layout.NewSpacer()) - - return genomesContainer, nil - } - - genomePairsLocusInfoGrid, err := getGenomePairsLocusInfoGrid() - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), locusNameRow, widget.NewSeparator(), genomePairsLocusInfoGrid) - - setPageContent(page, window) -} - // This is a page that shows the user 100 sample offspring polygenic disease risk scores on a bar chart // This helps users to visualize the standard deviation of their offspring's disease risk with this user func setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window fyne.Window, diseaseName string, sampleOffspringRiskScoresList []int, numberOfLociTested int, previousPage func()){ @@ -1767,13 +1608,15 @@ func setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window fyne.Window, d return } - diseaseLociMap, err := polygenicDiseases.GetPolygenicDiseaseLociMap(diseaseName) + diseaseLocusObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } - totalNumberOfLoci := len(diseaseLociMap) + diseaseLociList := diseaseLocusObject.LociList + + totalNumberOfLoci := len(diseaseLociList) totalNumberOfLociString := helpers.ConvertIntToString(totalNumberOfLoci) numberOfLociTestedTitle := widget.NewLabel("Number Of Loci Tested:") @@ -1810,14 +1653,11 @@ func setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window fyne.Window, d chartTitle := diseaseName + ": 100 Prospective Offspring Risk Scores" - formatYAxisValuesFunction := func(inputRiskScore float64)(string, error){ + formatYAxisValuesFunction := func(inputOffspringCount float64)(string, error){ - inputRiskScoreInt, err := helpers.FloorFloat64ToInt(inputRiskScore) - if (err != nil){ return "", err } + offspringCountString := helpers.ConvertIntToString(int(inputOffspringCount)) - inputRiskScoreString := helpers.ConvertIntToString(inputRiskScoreInt) - - return inputRiskScoreString, nil + return offspringCountString, nil } offspringsChart, err := createCharts.CreateBarChart(chartTitle, offspringStatisticsDatumsList, formatYAxisValuesFunction, true, " Offspring") @@ -2140,9 +1980,12 @@ func setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe predictionLabel := getItalicLabelCentered("Prediction") confidenceLabel := getItalicLabelCentered("Confidence") - quantityOfLabel := getItalicLabelCentered("Quantity Of") + quantityOfLabel1 := getItalicLabelCentered("Quantity Of") rulesTestedLabel := getItalicLabelCentered("Rules Tested") + quantityOfLabel2 := getItalicLabelCentered("Quantity Of") + lociKnownLabel := getItalicLabelCentered("Loci Known") + emptyLabel5 := widget.NewLabel("") emptyLabel6 := widget.NewLabel("") @@ -2150,7 +1993,8 @@ func setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe pairNameColumn := container.NewVBox(emptyLabel3, genomePairLabel, widget.NewSeparator()) predictedProbabilitiesColumn := container.NewVBox(emptyLabel4, predictedProbabilitiesLabel, widget.NewSeparator()) neuralNetworkPredictionConfidenceColumn := container.NewVBox(predictionLabel, confidenceLabel, widget.NewSeparator()) - quantityOfRulesTestedColumn := container.NewVBox(quantityOfLabel, rulesTestedLabel, widget.NewSeparator()) + quantityOfRulesTestedColumn := container.NewVBox(quantityOfLabel1, rulesTestedLabel, widget.NewSeparator()) + quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel2, lociKnownLabel, widget.NewSeparator()) viewDetailsButtonsColumn := container.NewVBox(emptyLabel5, emptyLabel6, widget.NewSeparator()) addGenomePairRow := func(genomePairName string, person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{ @@ -2176,7 +2020,7 @@ func setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe pairNameColumn.Add(genomePairNameLabel) viewDetailsButtonsColumn.Add(viewAnalysisDetailsButton) - neuralNetworkExists, neuralNetworkAnalysisExists, offspringOutcomeProbabilitiesMap_NeuralNetwork, neuralNetworkPredictionConfidence, _, _, anyRulesExist, rulesAnalysisExists, offspringOutcomeProbabilitiesMap_Rules, _, quantityOfRulesTested, _, _, err := readGeneticAnalysis.GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier) + neuralNetworkExists, neuralNetworkAnalysisExists, offspringOutcomeProbabilitiesMap_NeuralNetwork, neuralNetworkPredictionConfidence, quantityOfLociKnown_NeuralNetwork, _, anyRulesExist, rulesAnalysisExists, offspringOutcomeProbabilitiesMap_Rules, _, quantityOfRulesTested, _, _, err := readGeneticAnalysis.GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier) if (err != nil) { return err } if (neuralNetworkExists == false && anyRulesExist == false){ return errors.New("setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage called with trait that is not analyzable.") @@ -2198,6 +2042,19 @@ func setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe neuralNetworkPredictionConfidenceColumn.Add(predictionConfidenceLabel) } + traitLociList := traitObject.LociList + + totalQuantityOfLoci := len(traitLociList) + + quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown_NeuralNetwork) + totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci) + + quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString + + quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted) + + quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel) + } else { if (anyRulesExist == false){ return errors.New("setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage called with analysis which is missing ") @@ -2278,6 +2135,7 @@ func setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe pairNameColumn.Add(widget.NewLabel("")) neuralNetworkPredictionConfidenceColumn.Add(widget.NewLabel("")) quantityOfRulesTestedColumn.Add(widget.NewLabel("")) + quantityOfLociKnownColumn.Add(widget.NewLabel("")) viewDetailsButtonsColumn.Add(widget.NewLabel("")) } } @@ -2288,6 +2146,7 @@ func setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe predictedProbabilitiesColumn.Add(widget.NewSeparator()) neuralNetworkPredictionConfidenceColumn.Add(widget.NewSeparator()) quantityOfRulesTestedColumn.Add(widget.NewSeparator()) + quantityOfLociKnownColumn.Add(widget.NewSeparator()) viewDetailsButtonsColumn.Add(widget.NewSeparator()) return nil @@ -2321,6 +2180,13 @@ func setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe }) neuralNetworkPredictionConfidenceColumn.Add(neuralNetworkPredictionConfidenceHelpButton) + + quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + showUnderConstructionDialog(window) + //TODO + }) + + quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton) } else { quantityOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage) @@ -2334,6 +2200,7 @@ func setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe if (neuralNetworkExists == true){ genomesContainer.Add(neuralNetworkPredictionConfidenceColumn) + genomesContainer.Add(quantityOfLociKnownColumn) } else { genomesContainer.Add(quantityOfRulesTestedColumn) } @@ -2830,7 +2697,6 @@ func setViewCoupleDiscreteTraitRulesPage(window fyne.Window, person1Name string, } - // 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 setViewCoupleGeneticAnalysisDiscreteTraitRuleDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, previousPage func()){ @@ -2953,30 +2819,21 @@ func setViewCoupleGeneticAnalysisNumericTraitsPage(window fyne.Window, person1Na mainGenomePairIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) - emptyLabel1 := widget.NewLabel("") traitNameLabel := getItalicLabelCentered("Trait Name") - emptyLabel2 := widget.NewLabel("") predictedOutcomeLabel := getItalicLabelCentered("Predicted Outcome") - quantityOfLabel := getItalicLabelCentered("Quantity Of") - lociKnownLabel := getItalicLabelCentered("Loci Known") - - emptyLabel3 := widget.NewLabel("") confidenceRangeLabel := getItalicLabelCentered("Confidence Range") - emptyLabel4 := widget.NewLabel("") conflictExistsLabel := getItalicLabelCentered("Conflict Exists?") emptyLabel5 := widget.NewLabel("") - emptyLabel6 := widget.NewLabel("") - traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator()) - predictedOutcomeColumn := container.NewVBox(emptyLabel2, predictedOutcomeLabel, widget.NewSeparator()) - confidenceRangeColumn := container.NewVBox(emptyLabel3, confidenceRangeLabel, widget.NewSeparator()) - quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel, lociKnownLabel, widget.NewSeparator()) - conflictExistsColumn := container.NewVBox(emptyLabel4, conflictExistsLabel, widget.NewSeparator()) - viewDetailsButtonsColumn := container.NewVBox(emptyLabel5, emptyLabel6, widget.NewSeparator()) + traitNameColumn := container.NewVBox(traitNameLabel, widget.NewSeparator()) + predictedOutcomeColumn := container.NewVBox(predictedOutcomeLabel, widget.NewSeparator()) + confidenceRangeColumn := container.NewVBox(confidenceRangeLabel, widget.NewSeparator()) + conflictExistsColumn := container.NewVBox(conflictExistsLabel, widget.NewSeparator()) + viewDetailsButtonsColumn := container.NewVBox(emptyLabel5, widget.NewSeparator()) traitObjectsList, err := traits.GetTraitObjectsList() if (err != nil) { return nil, err } @@ -3006,25 +2863,28 @@ func setViewCoupleGeneticAnalysisNumericTraitsPage(window fyne.Window, person1Na traitNameLabel := getBoldLabelCentered(traitName) - analysisExists, offspringAverageOutcome, predictionConfidenceRangesMap, quantityOfLociKnown, _, _, conflictExists, err := readGeneticAnalysis.GetOffspringNumericTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, mainGenomePairIdentifier) + analysisExists, offspringAverageOutcome, predictionConfidenceRangesMap, _, _, _, conflictExists, err := readGeneticAnalysis.GetOffspringNumericTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, mainGenomePairIdentifier) if (err != nil) { return nil, err } - getPredictedOutcomeLabel := func()fyne.Widget{ + outcomeFormatter := traitObject.NumericValueFormatter + + getPredictedOutcomeLabel := func()(fyne.Widget, error){ + if (analysisExists == false){ result := widget.NewLabel("Unknown") - return result + return result, nil } - predictedOutcomeString := helpers.ConvertFloat64ToStringRounded(offspringAverageOutcome, 2) - - predictedOutcomeFormatted := predictedOutcomeString + " centimeters" + predictedOutcomeFormatted, err := outcomeFormatter(offspringAverageOutcome, true) + if (err != nil) { return nil, err } outcomeLabel := getBoldLabel(predictedOutcomeFormatted) - return outcomeLabel + return outcomeLabel, nil } - predictedOutcomeLabel := getPredictedOutcomeLabel() + predictedOutcomeLabel, err := getPredictedOutcomeLabel() + if (err != nil) { return nil, err } predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel) @@ -3051,11 +2911,12 @@ func setViewCoupleGeneticAnalysisNumericTraitsPage(window fyne.Window, person1Na return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") } - closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2) + closestConfidenceDistanceString, err := outcomeFormatter(closestToEightyPercentageConfidenceDistance, false) + if (err != nil) { return nil, err } closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) - confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "% Confidence)" + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) @@ -3067,15 +2928,6 @@ func setViewCoupleGeneticAnalysisNumericTraitsPage(window fyne.Window, person1Na confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel) - totalQuantityOfLoci := len(traitLociList) - - quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown) - totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci) - - quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString - - quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted) - conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) conflictExistsLabel := getBoldLabelCentered(conflictExistsString) @@ -3086,14 +2938,12 @@ func setViewCoupleGeneticAnalysisNumericTraitsPage(window fyne.Window, person1Na traitNameColumn.Add(traitNameLabel) predictedOutcomeColumn.Add(predictedOutcomeLabelCentered) confidenceRangeColumn.Add(confidenceRangeLabelCentered) - quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel) conflictExistsColumn.Add(conflictExistsLabel) viewDetailsButtonsColumn.Add(viewDetailsButton) traitNameColumn.Add(widget.NewSeparator()) predictedOutcomeColumn.Add(widget.NewSeparator()) confidenceRangeColumn.Add(widget.NewSeparator()) - quantityOfLociKnownColumn.Add(widget.NewSeparator()) conflictExistsColumn.Add(widget.NewSeparator()) viewDetailsButtonsColumn.Add(widget.NewSeparator()) } @@ -3111,13 +2961,7 @@ func setViewCoupleGeneticAnalysisNumericTraitsPage(window fyne.Window, person1Na confidenceRangeColumn.Add(confidenceRangeHelpButton) - quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - //TODO - showUnderConstructionDialog(window) - }) - quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton) - - traitsGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, predictedOutcomeColumn, confidenceRangeColumn, quantityOfLociKnownColumn) + traitsGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, predictedOutcomeColumn, confidenceRangeColumn) if (secondGenomePairExists == true){ @@ -3250,22 +3094,25 @@ func setViewCoupleGeneticAnalysisNumericTraitDetailsPage(window fyne.Window, per analysisExists, offspringAverageOutcome, predictionConfidenceRangesMap, quantityOfLociKnown, _, sampleOffspringOutcomesList, _, err := readGeneticAnalysis.GetOffspringNumericTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier) if (err != nil) { return err } - getPredictedOutcomeLabel := func()fyne.Widget{ + outcomeFormatter := traitObject.NumericValueFormatter + + getPredictedOutcomeLabel := func()(fyne.Widget, error){ + if (analysisExists == false){ unknownLabel := widget.NewLabel("Unknown") - return unknownLabel + return unknownLabel, nil } - offspringAverageOutcomeString := helpers.ConvertFloat64ToStringRounded(offspringAverageOutcome, 2) - - offspringAverageOutcomeFormatted := offspringAverageOutcomeString + " centimeters" + offspringAverageOutcomeFormatted, err := outcomeFormatter(offspringAverageOutcome, true) + if (err != nil) { return nil, err } predictedOutcomeLabel := getBoldLabel(offspringAverageOutcomeFormatted) - return predictedOutcomeLabel + return predictedOutcomeLabel, nil } - predictedOutcomeLabel := getPredictedOutcomeLabel() + predictedOutcomeLabel, err := getPredictedOutcomeLabel() + if (err != nil) { return err } predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel) @@ -3292,11 +3139,12 @@ func setViewCoupleGeneticAnalysisNumericTraitDetailsPage(window fyne.Window, per return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") } - closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2) + closestConfidenceDistanceString, err := outcomeFormatter(closestToEightyPercentageConfidenceDistance, false) + if (err != nil) { return nil, err } closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) - confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "% Confidence)" + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) @@ -3318,7 +3166,7 @@ func setViewCoupleGeneticAnalysisNumericTraitDetailsPage(window fyne.Window, per quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted) viewSampleOffspringsButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewNumericTraitSampleOffspringRiskScoresChart(window, traitName, sampleOffspringOutcomesList, quantityOfLociKnown, currentPage) + setViewNumericTraitSampleOffspringOutcomesChart(window, traitName, sampleOffspringOutcomesList, quantityOfLociKnown, currentPage) }) viewAnalysisDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ @@ -3388,9 +3236,9 @@ func setViewCoupleGeneticAnalysisNumericTraitDetailsPage(window fyne.Window, per // This is a page that shows the user 100 sample offspring trait outcomes on a bar chart // This helps users to visualize the standard deviation of their offspring's trait outcomes with this user -func setViewNumericTraitSampleOffspringRiskScoresChart(window fyne.Window, traitName string, sampleOffspringOutcomesList []float64, quantityOfLociKnown int, previousPage func()){ +func setViewNumericTraitSampleOffspringOutcomesChart(window fyne.Window, traitName string, sampleOffspringOutcomesList []float64, quantityOfLociKnown int, previousPage func()){ - currentPage := func(){setViewNumericTraitSampleOffspringRiskScoresChart(window, traitName, sampleOffspringOutcomesList, quantityOfLociKnown, previousPage)} + currentPage := func(){setViewNumericTraitSampleOffspringOutcomesChart(window, traitName, sampleOffspringOutcomesList, quantityOfLociKnown, previousPage)} title := getPageTitleCentered("Viewing Sample Offspring Outcomes Chart") @@ -3442,13 +3290,15 @@ func setViewNumericTraitSampleOffspringRiskScoresChart(window fyne.Window, trait getOffspringSampleOutcomesChartImage := func()(image.Image, error){ if (len(sampleOffspringOutcomesList) != 100){ - return nil, errors.New("setViewNumericTraitSampleOffspringRiskScoresChart called with offspringOutcomesList that is not 100 items in length.") + return nil, errors.New("setViewNumericTraitSampleOffspringOutcomesChart called with offspringOutcomesList that is not 100 items in length.") } // We sort the list in ascending order slices.Sort(sampleOffspringOutcomesList) - getOffspringStatisticsDatumsList := func()([]statisticsDatum.StatisticsDatum){ + outcomeFormatter := traitObject.NumericValueFormatter + + getOffspringStatisticsDatumsList := func()([]statisticsDatum.StatisticsDatum, error){ offspringStatisticsDatumsList := make([]statisticsDatum.StatisticsDatum, 0) @@ -3460,12 +3310,13 @@ func setViewNumericTraitSampleOffspringRiskScoresChart(window fyne.Window, trait // We can't split the values into groups // Every offspring has the same value - onlyValueString := helpers.ConvertFloat64ToStringRounded(smallestItem, 2) - - onlyValueFormatted := onlyValueString + " centimeters" + onlyValue := helpers.ConvertFloat64ToString(smallestItem) + + onlyValueFormatted, err := outcomeFormatter(smallestItem, true) + if (err != nil) { return nil, err } newStatisticsDatum := statisticsDatum.StatisticsDatum{ - Label: onlyValueString, + Label: onlyValue, LabelFormatted: onlyValueFormatted, Value: float64(100), ValueFormatted: "100", @@ -3473,14 +3324,14 @@ func setViewNumericTraitSampleOffspringRiskScoresChart(window fyne.Window, trait offspringStatisticsDatumsList = append(offspringStatisticsDatumsList, newStatisticsDatum) - return offspringStatisticsDatumsList + return offspringStatisticsDatumsList, nil } // We split all outcomes into ten groups sizeOfEachGroup := (largestItem-smallestItem)/10 - index := float64(0) + index := float64(smallestItem) for { @@ -3507,10 +3358,12 @@ func setViewNumericTraitSampleOffspringRiskScoresChart(window fyne.Window, trait } } - groupLowerBoundString := helpers.ConvertFloat64ToStringRounded(index, 1) - groupUpperBoundString := helpers.ConvertFloat64ToStringRounded(groupUpperBound, 1) + groupLowerBoundString, err := outcomeFormatter(index, true) + if (err != nil) { return nil, err } + groupUpperBoundString, err := outcomeFormatter(groupUpperBound, true) + if (err != nil) { return nil, err } - groupDescription := groupLowerBoundString + "-" + groupUpperBoundString + " centimeters" + groupDescription := groupLowerBoundString + "-" + groupUpperBoundString offspringCountString := helpers.ConvertIntToString(offspringInGroupCount) @@ -3530,16 +3383,17 @@ func setViewNumericTraitSampleOffspringRiskScoresChart(window fyne.Window, trait index += sizeOfEachGroup } - return offspringStatisticsDatumsList + return offspringStatisticsDatumsList, nil } - offspringStatisticsDatumsList := getOffspringStatisticsDatumsList() + offspringStatisticsDatumsList, err := getOffspringStatisticsDatumsList() + if (err != nil) { return nil, err } chartTitle := traitName + ": 100 Prospective Offspring Values" - formatYAxisValuesFunction := func(inputTraitValue float64)(string, error){ + formatYAxisValuesFunction := func(inputOffspringCount float64)(string, error){ - result := helpers.ConvertFloat64ToStringRounded(inputTraitValue, 2) + result := helpers.ConvertIntToString(int(inputOffspringCount)) return result, nil } diff --git a/gui/viewAnalysisGui_Person.go b/gui/viewAnalysisGui_Person.go index cf1af7c..3426b48 100644 --- a/gui/viewAnalysisGui_Person.go +++ b/gui/viewAnalysisGui_Person.go @@ -918,44 +918,96 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso mainGenomeIdentifier, err := getMainGenomeIdentifier() if (err != nil){ return nil, err } - + diseaseNameLabel := getItalicLabelCentered("Disease Name") riskScoreLabel := getItalicLabelCentered("Risk Score") + confidenceRangeLabel := getItalicLabelCentered("Confidence Range") + conflictExistsLabel := getItalicLabelCentered("Conflict Exists?") - emptyLabel := widget.NewLabel("") + emptyLabel5 := widget.NewLabel("") diseaseNameColumn := container.NewVBox(diseaseNameLabel, widget.NewSeparator()) riskScoreColumn := container.NewVBox(riskScoreLabel, widget.NewSeparator()) + confidenceRangeColumn := container.NewVBox(confidenceRangeLabel, widget.NewSeparator()) conflictExistsColumn := container.NewVBox(conflictExistsLabel, widget.NewSeparator()) - viewButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) + viewButtonsColumn := container.NewVBox(emptyLabel5, widget.NewSeparator()) - polygenicDiseaseNamesList, err := polygenicDiseases.GetPolygenicDiseaseNamesList() + polygenicDiseasesList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList() if (err != nil) { return nil, err } - for _, diseaseName := range polygenicDiseaseNamesList{ + for _, diseaseObject := range polygenicDiseasesList{ - diseaseNameText := getBoldLabelCentered(diseaseName) + diseaseName := diseaseObject.DiseaseName - personRiskScoreKnown, _, personRiskScoreFormatted, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier) + neuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(diseaseName) + if (neuralNetworkExists == false){ + // We can't analyze this trait + continue + } + + diseaseNameLabel := getBoldLabelCentered(diseaseName) + + analysisExists, personRiskScore, predictionConfidenceRangesMap, _, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier) if (err != nil) { return nil, err } - getPersonRiskScoreLabelText := func()string{ + getPersonRiskScoreLabel := func()fyne.Widget{ - if (personRiskScoreKnown == false){ - result := translate("Unknown") + if (analysisExists == false){ + result := widget.NewLabel(translate("Unknown")) return result } - return personRiskScoreFormatted + riskScoreString := helpers.ConvertIntToString(personRiskScore) + riskScoreFormatted := riskScoreString + "/10" + + riskScoreLabel := getBoldLabel(riskScoreFormatted) + + return riskScoreLabel } - personRiskScoreLabelText := getPersonRiskScoreLabelText() + personRiskScoreLabel := getPersonRiskScoreLabel() + personRiskScoreLabelCentered := getWidgetCentered(personRiskScoreLabel) - riskScoreText := getBoldLabelCentered(personRiskScoreLabelText) + getConfidenceRangeLabel := func()(fyne.Widget, error){ + + if (analysisExists == false){ + unknownLabel := widget.NewLabel("Unknown") + return unknownLabel, nil + } + + // This is a list of the percentage accuracies in the map + // For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be + // accurate within that range + confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap) + + // We sort the list so the percentage is always the same upon refreshing the page + slices.Sort(confidenceRangePercentagesList) + + closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80) + if (err != nil) { return nil, err } + + closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage] + if (exists == false){ + return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") + } + + closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2) + + closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) + + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" + + confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) + + return confidenceRangeLabel, nil + } + + confidenceRangeLabel, err := getConfidenceRangeLabel() + if (err != nil) { return nil, err } conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) conflictExistsLabel := getBoldLabelCentered(conflictExistsString) @@ -964,13 +1016,15 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window, personIdentifier, analysisObject, diseaseName, currentPage) })) - diseaseNameColumn.Add(diseaseNameText) - riskScoreColumn.Add(riskScoreText) + diseaseNameColumn.Add(diseaseNameLabel) + riskScoreColumn.Add(personRiskScoreLabelCentered) + confidenceRangeColumn.Add(confidenceRangeLabel) conflictExistsColumn.Add(conflictExistsLabel) viewButtonsColumn.Add(viewDetailsButton) diseaseNameColumn.Add(widget.NewSeparator()) riskScoreColumn.Add(widget.NewSeparator()) + confidenceRangeColumn.Add(widget.NewSeparator()) conflictExistsColumn.Add(widget.NewSeparator()) viewButtonsColumn.Add(widget.NewSeparator()) } @@ -980,7 +1034,13 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso }) riskScoreColumn.Add(riskScoreHelpButton) - diseasesContainer := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, riskScoreColumn) + confidenceRangeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + //TODO + showUnderConstructionDialog(window) + }) + confidenceRangeColumn.Add(confidenceRangeHelpButton) + + diseasesContainer := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, riskScoreColumn, confidenceRangeColumn) if (multipleGenomesExist == true){ @@ -1010,7 +1070,6 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso } - func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, diseaseName string, previousPage func()){ currentPage := func(){setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window, personIdentifier, analysisObject, diseaseName, previousPage)} @@ -1018,7 +1077,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, title := getPageTitleCentered("Viewing Genetic Analysis - " + diseaseName) backButton := getBackButtonCentered(previousPage) - + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) @@ -1035,7 +1094,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, description1 := getLabelCentered("Below is the disease information for this person's genomes.") description2 := getLabelCentered("The first two genomes are created by combining multiple genomes.") - + descriptionsSection := container.NewVBox(description1, description2) return descriptionsSection @@ -1052,32 +1111,38 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, getGenomesContainer := func()(*fyne.Container, error){ - diseaseLociMap, err := polygenicDiseases.GetPolygenicDiseaseLociMap(diseaseName) + diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName) if (err != nil){ return nil, err } - totalNumberOfLoci := len(diseaseLociMap) - totalNumberOfLociString := helpers.ConvertIntToString(totalNumberOfLoci) + diseaseLociList := diseaseObject.LociList - emptyLabelA := widget.NewLabel("") + totalQuantityOfLoci := len(diseaseLociList) + totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci) + + emptyLabel1 := widget.NewLabel("") genomeNameLabel := getItalicLabelCentered("Genome Name") - emptyLabelB := widget.NewLabel("") + emptyLabel2 := widget.NewLabel("") riskScoreLabel := getItalicLabelCentered("Risk Score") - numberOfLabel := getItalicLabelCentered("Number of") - lociTestedLabel := getItalicLabelCentered("Loci Tested") + emptyLabel3 := widget.NewLabel("") + predictionConfidenceLabel := getItalicLabelCentered("Confidence Range") - emptyLabelD := widget.NewLabel("") - emptyLabelE := widget.NewLabel("") + quantityOfLabel := getItalicLabelCentered("Quantity of") + lociKnownLabel := getItalicLabelCentered("Loci Known") - emptyLabelF := widget.NewLabel("") - emptyLabelG := widget.NewLabel("") + emptyLabel4 := widget.NewLabel("") + emptyLabel5 := widget.NewLabel("") - genomeNameColumn := container.NewVBox(emptyLabelA, genomeNameLabel, widget.NewSeparator()) - riskScoreColumn := container.NewVBox(emptyLabelB, riskScoreLabel, widget.NewSeparator()) - numberOfLociTestedColumn := container.NewVBox(numberOfLabel, lociTestedLabel, widget.NewSeparator()) - viewLifetimeRiskButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) - viewLociButtonsColumn := container.NewVBox(emptyLabelF, emptyLabelG, widget.NewSeparator()) + emptyLabel6 := widget.NewLabel("") + emptyLabel7 := widget.NewLabel("") + + genomeNameColumn := container.NewVBox(emptyLabel1, genomeNameLabel, widget.NewSeparator()) + riskScoreColumn := container.NewVBox(emptyLabel2, riskScoreLabel, widget.NewSeparator()) + predictionConfidenceColumn := container.NewVBox(emptyLabel3, predictionConfidenceLabel, widget.NewSeparator()) + quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel, lociKnownLabel, widget.NewSeparator()) + viewLifetimeRiskButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator()) + viewDetailsButtonsColumn := container.NewVBox(emptyLabel6, emptyLabel7, widget.NewSeparator()) addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ @@ -1094,30 +1159,75 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, genomeNameLabel := getBoldLabel(genomeName) genomeNameCell := container.NewHBox(layout.NewSpacer(), viewHelpButton, genomeNameLabel, layout.NewSpacer()) - return genomeNameCell + return genomeNameCell } genomeNameCell := getGenomeNameCell() - diseaseRiskScoreKnown, _, diseaseRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier) + analysisExists, predictedRiskScore, predictionConfidenceRangesMap, quantityOfLociKnown, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier) if (err != nil) { return err } - getRiskScoreLabelText := func()string{ - if (diseaseRiskScoreKnown == false){ - result := translate("Unknown") + getRiskScoreLabel := func()fyne.Widget{ + + if (analysisExists == false){ + + result := widget.NewLabel(translate("Unknown")) return result } - return diseaseRiskScoreFormatted + riskScoreString := helpers.ConvertIntToString(predictedRiskScore) + riskScoreFormatted := riskScoreString + "/10" + + riskScoreLabel := getBoldLabel(riskScoreFormatted) + + return riskScoreLabel } - genomeRiskScoreLabelText := getRiskScoreLabelText() + genomeRiskScoreLabel := getRiskScoreLabel() + genomeRiskScoreLabelCentered := getWidgetCentered(genomeRiskScoreLabel) - riskScoreLabel := getBoldLabelCentered(genomeRiskScoreLabelText) + getConfidenceRangeLabel := func()(fyne.Widget, error){ - genomeNumberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) - numberOfLociTestedLabel := getBoldLabelCentered(genomeNumberOfLociTestedString + "/" + totalNumberOfLociString) + if (analysisExists == false){ + unknownLabel := widget.NewLabel("Unknown") + return unknownLabel, nil + } + + // This is a list of the percentage accuracies in the map + // For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be + // accurate within that range + confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap) + + // We sort the list so the percentage is always the same upon refreshing the page + slices.Sort(confidenceRangePercentagesList) + + closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80) + if (err != nil) { return nil, err } + + closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage] + if (exists == false){ + return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") + } + + closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2) + + closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) + + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" + + confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) + + return confidenceRangeLabel, nil + } + + confidenceRangeLabel, err := getConfidenceRangeLabel() + if (err != nil) { return err } + + confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel) + + genomeQuantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown) + quantityOfLociKnownLabel := getBoldLabelCentered(genomeQuantityOfLociKnownString + "/" + totalQuantityOfLociString) viewLifetimeRiskButton := widget.NewButtonWithIcon("", theme.HistoryIcon(), func(){ @@ -1142,21 +1252,24 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, setViewPersonPolygenicDiseaseLifetimeProbabilitiesPage(window, diseaseName, genomeName, sexToDisplay, currentPage) }) - viewLociButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGenomePolygenicDiseaseLociPage(window, analysisObject, diseaseName, genomeIdentifier, genomeName, currentPage) + viewDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ + //TODO + showUnderConstructionDialog(window) }) genomeNameColumn.Add(genomeNameCell) - riskScoreColumn.Add(riskScoreLabel) - numberOfLociTestedColumn.Add(numberOfLociTestedLabel) + riskScoreColumn.Add(genomeRiskScoreLabelCentered) + predictionConfidenceColumn.Add(confidenceRangeLabelCentered) + quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel) viewLifetimeRiskButtonsColumn.Add(viewLifetimeRiskButton) - viewLociButtonsColumn.Add(viewLociButton) - + viewDetailsButtonsColumn.Add(viewDetailsButton) + genomeNameColumn.Add(widget.NewSeparator()) riskScoreColumn.Add(widget.NewSeparator()) - numberOfLociTestedColumn.Add(widget.NewSeparator()) + predictionConfidenceColumn.Add(widget.NewSeparator()) + quantityOfLociKnownColumn.Add(widget.NewSeparator()) viewLifetimeRiskButtonsColumn.Add(widget.NewSeparator()) - viewLociButtonsColumn.Add(widget.NewSeparator()) + viewDetailsButtonsColumn.Add(widget.NewSeparator()) return nil } @@ -1184,7 +1297,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, } // We show the date the genome was exported - + exportTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeGenomeWasExported, false) if (err != nil){ return "", err } @@ -1205,12 +1318,17 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, }) riskScoreColumn.Add(riskScoreHelpButton) - numberOfLociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + predictionConfidenceHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + showUnderConstructionDialog(window) + }) + predictionConfidenceColumn.Add(predictionConfidenceHelpButton) + + quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) }) - numberOfLociTestedColumn.Add(numberOfLociTestedHelpButton) + quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton) - genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, riskScoreColumn, numberOfLociTestedColumn, viewLifetimeRiskButtonsColumn, viewLociButtonsColumn, layout.NewSpacer()) + genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, riskScoreColumn, predictionConfidenceColumn, quantityOfLociKnownColumn, viewLifetimeRiskButtonsColumn, viewDetailsButtonsColumn, layout.NewSpacer()) return genomesContainer, nil } @@ -1222,7 +1340,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window, } page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), genomesContainer) - + setPageContent(page, window) } @@ -1311,7 +1429,9 @@ func setViewPersonPolygenicDiseaseLifetimeProbabilitiesPage(window fyne.Window, startAgeString := helpers.ConvertIntToString(rowInitialAge) endAgeString := helpers.ConvertIntToString(age) - averageRiskString := helpers.ConvertFloat64ToStringRounded(averageRisk, 2) + averageRiskPercentage := averageRisk * 100 + + averageRiskString := helpers.ConvertFloat64ToStringRounded(averageRiskPercentage, 2) ageRangeLabel := getBoldLabel(startAgeString + " - " + endAgeString) averageRiskLabel := getBoldLabelCentered(averageRiskString + "%") @@ -1347,7 +1467,7 @@ func setViewPersonPolygenicDiseaseLifetimeProbabilitiesPage(window fyne.Window, setErrorEncounteredPage(window, err, previousPage) return } - + ageProbabilityTabs := container.NewAppTabs(maleTab, femaleTab) if (maleOrFemale == "Male"){ @@ -1364,410 +1484,6 @@ 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, 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, geneticAnalysisObject, diseaseName, genomeIdentifier, genomeName, previousPage)} - - title := getPageTitleCentered("View Disease Loci - " + diseaseName) - - backButton := getBackButtonCentered(previousPage) - - description1 := widget.NewLabel("Below are the disease loci results for this genome.") - lociHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPolygenicDiseaseLociExplainerPage(window, currentPage) - }) - description1Row := container.NewHBox(layout.NewSpacer(), description1, lociHelpButton, layout.NewSpacer()) - - getGenomeNameRow := func()*fyne.Container{ - - genomeLabel := widget.NewLabel("Genome:") - genomeNameLabel := getBoldLabel(genomeName) - - if (genomeName == "Only Exclude Conflicts" || genomeName == "Only Include Shared"){ - genomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setCombinedGenomesExplainerPage(window, currentPage) - }) - genomeNameRow := container.NewHBox(layout.NewSpacer(), genomeLabel, genomeNameLabel, genomeHelpButton, layout.NewSpacer()) - return genomeNameRow - } - genomeNameRow := container.NewHBox(layout.NewSpacer(), genomeLabel, genomeNameLabel, layout.NewSpacer()) - return genomeNameRow - } - - genomeNameRow := getGenomeNameRow() - - diseaseLociMap, err := polygenicDiseases.GetPolygenicDiseaseLociMap(diseaseName) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - numberOfLociTested := 0 - - lociList_PositiveWeight := make([]string, 0) - lociList_ZeroWeight := make([]string, 0) - lociList_NegativeWeight := make([]string, 0) - lociList_UnknownWeight := make([]string, 0) - - for locusIdentifierHex, _ := range diseaseLociMap{ - - 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, locusIdentifierHex) - continue - } - numberOfLociTested += 1 - - if (genomeLocusRiskWeight > 0){ - - lociList_PositiveWeight = append(lociList_PositiveWeight, locusIdentifierHex) - - } else if (genomeLocusRiskWeight == 0) { - - lociList_ZeroWeight = append(lociList_ZeroWeight, locusIdentifierHex) - - } else { - // genomeLocusRiskWeight < 0 - lociList_NegativeWeight = append(lociList_NegativeWeight, locusIdentifierHex) - } - } - - numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) - - totalNumberOfLoci := len(diseaseLociMap) - totalNumberOfLociString := helpers.ConvertIntToString(totalNumberOfLoci) - - lociTestedLabel := widget.NewLabel("Loci Tested:") - lociTestedText := getBoldLabel(numberOfLociTestedString + "/" + totalNumberOfLociString) - lociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) - }) - - lociTestedRow := container.NewHBox(layout.NewSpacer(), lociTestedLabel, lociTestedText, lociTestedHelpButton, layout.NewSpacer()) - - getDiseaseLociGrid := func()(*fyne.Container, error){ - - locusNameLabel := getItalicLabelCentered("Locus Name") - riskWeightLabel := getItalicLabelCentered("Risk Weight") - oddsRatioLabel := getItalicLabelCentered("Odds Ratio") - emptyLabel := widget.NewLabel("") - - locusNameColumn := container.NewVBox(locusNameLabel, widget.NewSeparator()) - riskWeightColumn := container.NewVBox(riskWeightLabel, widget.NewSeparator()) - oddsRatioColumn := container.NewVBox(oddsRatioLabel, widget.NewSeparator()) - viewButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) - - addLocusRow := func(locusIdentifierHex string)error{ - - diseaseLocusObject, exists := diseaseLociMap[locusIdentifierHex] - if (exists == false) { - return errors.New("Cannot add locusRow: diseaseLociMap missing locus: " + locusIdentifierHex) - } - - locusRSID := diseaseLocusObject.LocusRSID - locusRSIDString := helpers.ConvertInt64ToString(locusRSID) - locusName := "rs" + locusRSIDString - - locusNameLabel := getBoldLabelCentered(locusName) - - 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{ - if (locusRiskWeightIsKnown == false){ - - result := translate("Unknown") - return result - } - locusRiskWeightString := helpers.ConvertIntToString(genomeLocusRiskWeight) - - return locusRiskWeightString - } - - locusRiskWeightText := getGenomeLocusRiskWeightText() - locusRiskWeightLabel := getBoldLabelCentered(locusRiskWeightText) - - getOddsRatioText := func()string{ - - if (locusOddsRatioIsKnown == false){ - result := translate("Unknown") - return result - } - - return locusOddsRatioFormatted - } - - locusOddsRatioText := getOddsRatioText() - genomeLocusOddsRatioLabel := getBoldLabelCentered(locusOddsRatioText) - - viewLocusButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, geneticAnalysisObject, diseaseName, locusIdentifier, currentPage) - }) - - locusNameColumn.Add(locusNameLabel) - riskWeightColumn.Add(locusRiskWeightLabel) - oddsRatioColumn.Add(genomeLocusOddsRatioLabel) - viewButtonsColumn.Add(viewLocusButton) - - locusNameColumn.Add(widget.NewSeparator()) - riskWeightColumn.Add(widget.NewSeparator()) - oddsRatioColumn.Add(widget.NewSeparator()) - viewButtonsColumn.Add(widget.NewSeparator()) - - return nil - } - - // Items within each group are sorted so they will display the same way whenever user refreshes page - - helpers.SortStringListToUnicodeOrder(lociList_PositiveWeight) - helpers.SortStringListToUnicodeOrder(lociList_NegativeWeight) - helpers.SortStringListToUnicodeOrder(lociList_ZeroWeight) - helpers.SortStringListToUnicodeOrder(lociList_UnknownWeight) - - for _, locusIdentifier := range lociList_PositiveWeight{ - - err = addLocusRow(locusIdentifier) - if (err != nil) { return nil, err } - } - for _, locusIdentifier := range lociList_NegativeWeight{ - - err = addLocusRow(locusIdentifier) - if (err != nil) { return nil, err } - } - for _, locusIdentifier := range lociList_ZeroWeight{ - - err = addLocusRow(locusIdentifier) - if (err != nil) { return nil, err } - } - for _, locusIdentifier := range lociList_UnknownWeight{ - - err = addLocusRow(locusIdentifier) - if (err != nil) { return nil, err } - } - - - riskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) - }) - riskWeightColumn.Add(riskWeightHelpButton) - - oddsRatioHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - //TODO - showUnderConstructionDialog(window) - }) - - oddsRatioColumn.Add(oddsRatioHelpButton) - - diseaseLociGrid := container.NewHBox(layout.NewSpacer(), locusNameColumn, riskWeightColumn, oddsRatioColumn, viewButtonsColumn, layout.NewSpacer()) - - return diseaseLociGrid, nil - } - - diseaseLociGrid, err := getDiseaseLociGrid() - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - page := container.NewVBox(title, backButton, widget.NewSeparator(), description1Row, widget.NewSeparator(), genomeNameRow, widget.NewSeparator(), lociTestedRow, widget.NewSeparator(), diseaseLociGrid) - - setPageContent(page, window) -} - - -// 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, geneticAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, locusIdentifier [3]byte, previousPage func()){ - - currentPage := func(){setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window, geneticAnalysisObject, diseaseName, locusIdentifier, previousPage)} - - title := getPageTitleCentered("Disease Locus Details - " + diseaseName) - - backButton := getBackButtonCentered(previousPage) - - locusIdentifierHex := encoding.EncodeBytesToHexString(locusIdentifier[:]) - - locusObject, err := polygenicDiseases.GetPolygenicDiseaseLocusObject(diseaseName, locusIdentifierHex) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - locusRSID := locusObject.LocusRSID - locusRSIDString := helpers.ConvertInt64ToString(locusRSID) - locusName := "rs" + locusRSIDString - - description := getLabelCentered("Below is the locus result for the person's genomes.") - - locusNameLabel := widget.NewLabel("Locus Name:") - locusNameText := getBoldLabel(locusName) - locusInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - 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(geneticAnalysisObject) - if (err != nil) { return nil, err } - - genomeNameLabel := getItalicLabelCentered("Genome Name") - riskWeightLabel := getItalicLabelCentered("Risk Weight") - oddsRatioLabel := getItalicLabelCentered("Odds Ratio") - - genomeNameColumn := container.NewVBox(genomeNameLabel, widget.NewSeparator()) - riskWeightColumn := container.NewVBox(riskWeightLabel, widget.NewSeparator()) - oddsRatioColumn := container.NewVBox(oddsRatioLabel, widget.NewSeparator()) - - addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ - - genomeRiskWeightKnown, genomeRiskWeight, genomeOddsRatioIsKnown, _, genomeOddsRatioFormatted, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) - if (err != nil) { return err } - - getGenomeRiskWeightText := func()string{ - - if (genomeRiskWeightKnown == false){ - result := translate("Unknown") - - return result - } - - genomeRiskWeightString := helpers.ConvertIntToString(genomeRiskWeight) - return genomeRiskWeightString - } - - genomeRiskWeightText := getGenomeRiskWeightText() - - getGenomeOddsRatioText := func()string{ - - if (genomeOddsRatioIsKnown == false){ - result := translate("Unknown") - - return result - } - - return genomeOddsRatioFormatted - } - - genomeOddsRatioText := getGenomeOddsRatioText() - - getGenomeNameCell := func()*fyne.Container{ - if (isACombinedGenome == false){ - - genomeNameLabel := getBoldLabelCentered(genomeName) - return genomeNameLabel - } - viewHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setCombinedGenomesExplainerPage(window, currentPage) - }) - - genomeNameLabel := getBoldLabel(genomeName) - genomeNameCell := container.NewHBox(layout.NewSpacer(), viewHelpButton, genomeNameLabel, layout.NewSpacer()) - - return genomeNameCell - } - - genomeNameCell := getGenomeNameCell() - genomeNameColumn.Add(genomeNameCell) - - riskWeightLabel := getBoldLabelCentered(genomeRiskWeightText) - riskWeightColumn.Add(riskWeightLabel) - - oddsRatioLabel := getBoldLabelCentered(genomeOddsRatioText) - oddsRatioColumn.Add(oddsRatioLabel) - - genomeNameColumn.Add(widget.NewSeparator()) - riskWeightColumn.Add(widget.NewSeparator()) - oddsRatioColumn.Add(widget.NewSeparator()) - - return nil - } - - if (multipleGenomesExist == true){ - - err := addGenomeRow("Only Exclude Conflicts", onlyExcludeConflictsGenomeIdentifier, true) - if (err != nil){ return nil, err } - - err = addGenomeRow("Only Include Shared", onlyIncludeSharedGenomeIdentifier, true) - if (err != nil){ return nil, err } - } - - for _, genomeIdentifier := range allRawGenomeIdentifiersList{ - - getGenomeName := func()(string, error){ - - genomeFound, _, timeGenomeWasExported, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier) - if (err != nil) { return "", err } - if (genomeFound == false){ - return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") - } - - if (multipleGenomesExist == false){ - return companyName, nil - } - - // We show the date that the genome was exported - - exportTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeGenomeWasExported, false) - if (err != nil){ return "", err } - - genomeName := companyName + " (Exported " + exportTimeAgo + ")" - - return genomeName, nil - } - - genomeName, err := getGenomeName() - if (err != nil) { return nil, err } - - err = addGenomeRow(genomeName, genomeIdentifier, false) - if (err != nil){ return nil, err } - } - - riskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) - }) - riskWeightColumn.Add(riskWeightHelpButton) - - oddsRatioHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - //TODO - showUnderConstructionDialog(window) - }) - oddsRatioColumn.Add(oddsRatioHelpButton) - - genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, riskWeightColumn, oddsRatioColumn, layout.NewSpacer()) - - return genomesContainer, nil - } - - genomesLocusInfoGrid, err := getGenomesLocusInfoGrid() - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), locusNameRow, widget.NewSeparator(), genomesLocusInfoGrid) - - setPageContent(page, window) -} - - func setViewPersonGeneticAnalysisDiscreteTraitsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, previousPage func()){ currentPage := func(){setViewPersonGeneticAnalysisDiscreteTraitsPage(window, personIdentifier, analysisObject, previousPage)} @@ -1985,7 +1701,7 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe title := getPageTitleCentered("Viewing Genetic Analysis - " + traitName) backButton := getBackButtonCentered(previousPage) - + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) @@ -2017,10 +1733,11 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe }) traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, traitInfoButton, layout.NewSpacer()) - neuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName) - getGenomesContainer := func()(*fyne.Container, error){ + traitObject, err := traits.GetTraitObject(traitName) + if (err != nil) { return nil, err } + emptyLabel1 := widget.NewLabel("") genomeNameLabel := getItalicLabelCentered("Genome Name") @@ -2030,35 +1747,21 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe emptyLabel3 := widget.NewLabel("") predictionConfidenceLabel := getItalicLabelCentered("Prediction Confidence") - quantityOfLabel := getItalicLabelCentered("Quantity Of") + quantityOfLabel1 := getItalicLabelCentered("Quantity Of") + lociKnownLabel := getItalicLabelCentered("Loci Known") + + quantityOfLabel2 := getItalicLabelCentered("Quantity Of") rulesTestedLabel := getItalicLabelCentered("Rules Tested") emptyLabel4 := widget.NewLabel("") emptyLabel5 := widget.NewLabel("") - genomeNameColumn := container.NewVBox() - predictedOutcomeColumn := container.NewVBox() - neuralNetworkPredictionConfidenceColumn := container.NewVBox() - numberOfRulesTestedColumn := container.NewVBox(quantityOfLabel, rulesTestedLabel, widget.NewSeparator()) - viewDetailsButtonsColumn := container.NewVBox() - - if (neuralNetworkExists == false){ - // We only need an extra header row if the numberOfRulesTested column is shown - genomeNameColumn.Add(emptyLabel1) - predictedOutcomeColumn.Add(emptyLabel2) - neuralNetworkPredictionConfidenceColumn.Add(emptyLabel3) - viewDetailsButtonsColumn.Add(emptyLabel4) - } - - genomeNameColumn.Add(genomeNameLabel) - predictedOutcomeColumn.Add(predictedOutcomeLabel) - neuralNetworkPredictionConfidenceColumn.Add(predictionConfidenceLabel) - viewDetailsButtonsColumn.Add(emptyLabel5) - - genomeNameColumn.Add(widget.NewSeparator()) - predictedOutcomeColumn.Add(widget.NewSeparator()) - neuralNetworkPredictionConfidenceColumn.Add(widget.NewSeparator()) - viewDetailsButtonsColumn.Add(widget.NewSeparator()) + genomeNameColumn := container.NewVBox(emptyLabel1, genomeNameLabel, widget.NewSeparator()) + predictedOutcomeColumn := container.NewVBox(emptyLabel2, predictedOutcomeLabel, widget.NewSeparator()) + neuralNetworkPredictionConfidenceColumn := container.NewVBox(emptyLabel3, predictionConfidenceLabel, widget.NewSeparator()) + quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel1, lociKnownLabel, widget.NewSeparator()) + numberOfRulesTestedColumn := container.NewVBox(quantityOfLabel2, rulesTestedLabel, widget.NewSeparator()) + viewDetailsButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator()) addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ @@ -2080,7 +1783,7 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe genomeNameCell := getGenomeNameCell() - neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, _, _, anyRulesExist, rulesAnalysisExists, _, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, _, _, err := readGeneticAnalysis.GetPersonDiscreteTraitInfoFromGeneticAnalysis(analysisObject, traitName, genomeIdentifier) + neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, quantityOfLociKnown_NeuralNetwork, _, anyRulesExist, rulesAnalysisExists, _, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, _, _, err := readGeneticAnalysis.GetPersonDiscreteTraitInfoFromGeneticAnalysis(analysisObject, traitName, genomeIdentifier) if (err != nil) { return err } if (neuralNetworkExists == false && anyRulesExist == false){ @@ -2120,61 +1823,48 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel) - getNeuralNetworkConfidenceLabel := func()fyne.Widget{ + if (neuralNetworkExists == true){ - if (neuralNetworkExists == false){ - - emptyLabel := widget.NewLabel("") - - return emptyLabel - } if (neuralNetworkAnalysisExists == false){ - predictedOutcomeConfidenceLabel := getItalicLabel("Unknown") + predictedOutcomeConfidenceLabel := getItalicLabelCentered("Unknown") - return predictedOutcomeConfidenceLabel + neuralNetworkPredictionConfidenceColumn.Add(predictedOutcomeConfidenceLabel) + } else { + + neuralNetworkPredictionConfidenceString := helpers.ConvertIntToString(neuralNetworkPredictionConfidence) + predictedOutcomeConfidenceLabel := getBoldLabelCentered(neuralNetworkPredictionConfidenceString + "%") + + neuralNetworkPredictionConfidenceColumn.Add(predictedOutcomeConfidenceLabel) } - neuralNetworkPredictionConfidenceString := helpers.ConvertIntToString(neuralNetworkPredictionConfidence) - predictedOutcomeConfidenceLabel := getBoldLabel(neuralNetworkPredictionConfidenceString + "%") + traitLociList := traitObject.LociList + totalQuantityOfLoci := len(traitLociList) - return predictedOutcomeConfidenceLabel - } + quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown_NeuralNetwork) + totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci) - predictedOutcomeConfidenceLabel := getNeuralNetworkConfidenceLabel() - predictedOutcomeConfidenceLabelCentered := getWidgetCentered(predictedOutcomeConfidenceLabel) + quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString - getQuantityOfRulesTestedLabel := func()(fyne.Widget, error){ + quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted) - if (anyRulesExist == false){ + quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel) - emptyLabel := widget.NewLabel("") - return emptyLabel, nil - } + } else { traitRulesMap, err := traits.GetTraitRulesMap(traitName) - if (err != nil){ return nil, err } + if (err != nil){ return err } totalNumberOfRules := len(traitRulesMap) totalNumberOfRulesString := helpers.ConvertIntToString(totalNumberOfRules) - if (rulesAnalysisExists == false){ - quantityOfRulesTestedLabel := getItalicLabel("0/" + totalNumberOfRulesString) - return quantityOfRulesTestedLabel, nil - } - quantityOfRulesTestedString := helpers.ConvertIntToString(quantityOfRulesTested) - quantityOfRulesTestedLabel := getBoldLabel(quantityOfRulesTestedString + "/" + totalNumberOfRulesString) + quantityOfRulesTestedLabel := getBoldLabelCentered(quantityOfRulesTestedString + "/" + totalNumberOfRulesString) - return quantityOfRulesTestedLabel, nil + numberOfRulesTestedColumn.Add(quantityOfRulesTestedLabel) } - quantityOfRulesTestedLabel, err := getQuantityOfRulesTestedLabel() - if (err != nil) { return err } - - quantityOfRulesTestedLabelCentered := getWidgetCentered(quantityOfRulesTestedLabel) - viewDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ if (neuralNetworkExists == true){ //TODO @@ -2186,13 +1876,12 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe genomeNameColumn.Add(genomeNameCell) predictedOutcomeColumn.Add(predictedOutcomeLabelCentered) - neuralNetworkPredictionConfidenceColumn.Add(predictedOutcomeConfidenceLabelCentered) - numberOfRulesTestedColumn.Add(quantityOfRulesTestedLabelCentered) - viewDetailsButtonsColumn.Add(viewDetailsButton) + viewDetailsButtonsColumn.Add(viewDetailsButton) genomeNameColumn.Add(widget.NewSeparator()) predictedOutcomeColumn.Add(widget.NewSeparator()) neuralNetworkPredictionConfidenceColumn.Add(widget.NewSeparator()) + quantityOfLociKnownColumn.Add(widget.NewSeparator()) numberOfRulesTestedColumn.Add(widget.NewSeparator()) viewDetailsButtonsColumn.Add(widget.NewSeparator()) @@ -2239,6 +1928,8 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe if (err != nil){ return nil, err } } + neuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName) + if (neuralNetworkExists == true){ neuralNetworksHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ @@ -2264,14 +1955,21 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe }) numberOfRulesTestedColumn.Add(numberOfRulesTestedHelpButton) + quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + //TODO + showUnderConstructionDialog(window) + }) + quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton) + genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, predictedOutcomeColumn) if (neuralNetworkExists == true){ genomesContainer.Add(neuralNetworkPredictionConfidenceColumn) + genomesContainer.Add(quantityOfLociKnownColumn) } else { genomesContainer.Add(numberOfRulesTestedColumn) } - + genomesContainer.Add(viewDetailsButtonsColumn) genomesContainer.Add(layout.NewSpacer()) @@ -2290,7 +1988,6 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe } - // Ths function provides a page to view the discrete trait rules for a particular genome from a genetic analysis func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte, genomeName string, previousPage func()){ @@ -2706,30 +2403,21 @@ func setViewPersonGeneticAnalysisNumericTraitsPage(window fyne.Window, personIde mainGenomeIdentifier, err := getMainGenomeIdentifier() if (err != nil){ return nil, err } - emptyLabel1 := widget.NewLabel("") traitNameLabel := getItalicLabelCentered("Trait Name") - emptyLabel2 := widget.NewLabel("") predictedOutcomeLabel := getItalicLabelCentered("Predicted Outcome") - quantityOfLabel := getItalicLabelCentered("Quantity Of") - lociKnownLabel := getItalicLabelCentered("Loci Known") - - emptyLabel3 := widget.NewLabel("") confidenceRangeLabel := getItalicLabelCentered("Confidence Range") - emptyLabel4 := widget.NewLabel("") conflictExistsLabel := getItalicLabelCentered("Conflict Exists?") emptyLabel5 := widget.NewLabel("") - emptyLabel6 := widget.NewLabel("") - traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator()) - predictedOutcomeColumn := container.NewVBox(emptyLabel2, predictedOutcomeLabel, widget.NewSeparator()) - confidenceRangeColumn := container.NewVBox(emptyLabel3, confidenceRangeLabel, widget.NewSeparator()) - quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel, lociKnownLabel, widget.NewSeparator()) - conflictExistsColumn := container.NewVBox(emptyLabel4, conflictExistsLabel, widget.NewSeparator()) - viewButtonsColumn := container.NewVBox(emptyLabel5, emptyLabel6, widget.NewSeparator()) + traitNameColumn := container.NewVBox(traitNameLabel, widget.NewSeparator()) + predictedOutcomeColumn := container.NewVBox(predictedOutcomeLabel, widget.NewSeparator()) + confidenceRangeColumn := container.NewVBox(confidenceRangeLabel, widget.NewSeparator()) + conflictExistsColumn := container.NewVBox(conflictExistsLabel, widget.NewSeparator()) + viewButtonsColumn := container.NewVBox(emptyLabel5, widget.NewSeparator()) traitObjectsList, err := traits.GetTraitObjectsList() if (err != nil) { return nil, err } @@ -2759,25 +2447,28 @@ func setViewPersonGeneticAnalysisNumericTraitsPage(window fyne.Window, personIde continue } - analysisExists, predictedOutcome, confidenceRangesMap, quantityOfLociKnown, _, conflictExists, err := readGeneticAnalysis.GetPersonNumericTraitInfoFromGeneticAnalysis(analysisObject, traitName, mainGenomeIdentifier) + analysisExists, predictedOutcome, confidenceRangesMap, _, _, conflictExists, err := readGeneticAnalysis.GetPersonNumericTraitInfoFromGeneticAnalysis(analysisObject, traitName, mainGenomeIdentifier) if (err != nil) { return nil, err } - getPredictionLabel := func()fyne.Widget{ + outcomeFormatter := traitObject.NumericValueFormatter + + getPredictionLabel := func()(fyne.Widget, error){ if (analysisExists == false){ result := getItalicLabel("Unknown") - return result + return result, nil } - predictedOutcomeString := helpers.ConvertFloat64ToStringRounded(predictedOutcome, 2) + predictedOutcomeString, err := outcomeFormatter(predictedOutcome, true) + if (err != nil) { return nil, err } - //TODO: Fix units - result := getBoldLabel(predictedOutcomeString + " centimeters") + result := getBoldLabel(predictedOutcomeString) - return result + return result, nil } - predictionLabel := getPredictionLabel() + predictionLabel, err := getPredictionLabel() + if (err != nil) { return nil, err } predictionLabelCentered := getWidgetCentered(predictionLabel) @@ -2804,11 +2495,12 @@ func setViewPersonGeneticAnalysisNumericTraitsPage(window fyne.Window, personIde return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") } - closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2) + closestConfidenceDistanceString, err := outcomeFormatter(closestToEightyPercentageConfidenceDistance, false) + if (err != nil) { return nil, err } closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) - confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "% Confidence)" + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) @@ -2820,34 +2512,22 @@ func setViewPersonGeneticAnalysisNumericTraitsPage(window fyne.Window, personIde confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel) - totalQuantityOfLoci := len(traitLociList) - - quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown) - totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci) - - quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString - - quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted) - conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists) conflictExistsLabel := getBoldLabelCentered(conflictExistsString) viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - showUnderConstructionDialog(window) - //TODO + setViewPersonGeneticAnalysisNumericTraitDetailsPage(window, personIdentifier, analysisObject, traitName, currentPage) })) traitNameColumn.Add(traitNameText) predictedOutcomeColumn.Add(predictionLabelCentered) confidenceRangeColumn.Add(confidenceRangeLabelCentered) - quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel) conflictExistsColumn.Add(conflictExistsLabel) viewButtonsColumn.Add(viewDetailsButton) traitNameColumn.Add(widget.NewSeparator()) predictedOutcomeColumn.Add(widget.NewSeparator()) confidenceRangeColumn.Add(widget.NewSeparator()) - quantityOfLociKnownColumn.Add(widget.NewSeparator()) conflictExistsColumn.Add(widget.NewSeparator()) viewButtonsColumn.Add(widget.NewSeparator()) } @@ -2864,13 +2544,7 @@ func setViewPersonGeneticAnalysisNumericTraitsPage(window fyne.Window, personIde }) confidenceRangeColumn.Add(confidenceRangeHelpButton) - quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - //TODO - showUnderConstructionDialog(window) - }) - quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton) - - traitsContainer := container.NewHBox(layout.NewSpacer(), traitNameColumn, predictedOutcomeColumn, confidenceRangeColumn, quantityOfLociKnownColumn) + traitsContainer := container.NewHBox(layout.NewSpacer(), traitNameColumn, predictedOutcomeColumn, confidenceRangeColumn) if (multipleGenomesExist == true){ @@ -2900,3 +2574,261 @@ func setViewPersonGeneticAnalysisNumericTraitsPage(window fyne.Window, personIde } + +func setViewPersonGeneticAnalysisNumericTraitDetailsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, traitName string, previousPage func()){ + + currentPage := func(){setViewPersonGeneticAnalysisNumericTraitDetailsPage(window, personIdentifier, analysisObject, traitName, previousPage)} + + title := getPageTitleCentered("Viewing Genetic Analysis - " + traitName) + + backButton := getBackButtonCentered(previousPage) + + allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject) + if (err != nil) { + setErrorEncounteredPage(window, err, previousPage) + return + } + + getDescriptionSection := func()*fyne.Container{ + + if (multipleGenomesExist == false){ + description := getLabelCentered("Below is the trait information for this person's genome.") + + return description + } + + description1 := getLabelCentered("Below is the trait information for this person's genomes.") + description2 := getLabelCentered("The first two genomes are created by combining multiple genomes.") + + descriptionsSection := container.NewVBox(description1, description2) + + return descriptionsSection + } + + descriptionSection := getDescriptionSection() + + traitNameLabel := widget.NewLabel("Trait:") + traitNameText := getBoldLabel(traitName) + traitInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ + setViewTraitDetailsPage(window, traitName, currentPage) + }) + traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, traitInfoButton, layout.NewSpacer()) + + getGenomesContainer := func()(*fyne.Container, error){ + + traitObject, err := traits.GetTraitObject(traitName) + if (err != nil){ return nil, err } + + traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric + if (traitIsDiscreteOrNumeric != "Numeric"){ + return nil, errors.New("setViewPersonGeneticAnalysisNumericTraitDetailsPage called with non-numeric trait: " + traitName) + } + + traitLociList := traitObject.LociList + + totalQuantityOfLoci := len(traitLociList) + totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci) + + emptyLabel1 := widget.NewLabel("") + genomeNameLabel := getItalicLabelCentered("Genome Name") + + emptyLabel2 := widget.NewLabel("") + predictedOutcomeLabel := getItalicLabelCentered("Predicted Outcome") + + emptyLabel3 := widget.NewLabel("") + predictionConfidenceLabel := getItalicLabelCentered("Confidence Range") + + quantityOfLabel := getItalicLabelCentered("Quantity of") + lociKnownLabel := getItalicLabelCentered("Loci Known") + + emptyLabel4 := widget.NewLabel("") + emptyLabel5 := widget.NewLabel("") + + genomeNameColumn := container.NewVBox(emptyLabel1, genomeNameLabel, widget.NewSeparator()) + predictedOutcomeColumn := container.NewVBox(emptyLabel2, predictedOutcomeLabel, widget.NewSeparator()) + predictionConfidenceColumn := container.NewVBox(emptyLabel3, predictionConfidenceLabel, widget.NewSeparator()) + quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel, lociKnownLabel, widget.NewSeparator()) + viewDetailsButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator()) + + addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ + + getGenomeNameCell := func()*fyne.Container{ + if (isACombinedGenome == false){ + + genomeNameLabel := getBoldLabelCentered(genomeName) + return genomeNameLabel + } + viewHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + setCombinedGenomesExplainerPage(window, currentPage) + }) + + genomeNameLabel := getBoldLabel(genomeName) + genomeNameCell := container.NewHBox(layout.NewSpacer(), viewHelpButton, genomeNameLabel, layout.NewSpacer()) + + return genomeNameCell + } + + genomeNameCell := getGenomeNameCell() + + analysisExists, predictedOutcome, predictionConfidenceRangesMap, quantityOfLociKnown, _, _, err := readGeneticAnalysis.GetPersonNumericTraitInfoFromGeneticAnalysis(analysisObject, traitName, genomeIdentifier) + if (err != nil) { return err } + + outcomeFormatter := traitObject.NumericValueFormatter + + getPredictedOutcomeLabel := func()(fyne.Widget, error){ + + if (analysisExists == false){ + + result := widget.NewLabel(translate("Unknown")) + + return result, nil + } + + outcomeFormatted, err := outcomeFormatter(predictedOutcome, true) + if (err != nil) { return nil, err } + + outcomeLabel := getBoldLabel(outcomeFormatted) + + return outcomeLabel, nil + } + + predictedOutcomeLabel, err := getPredictedOutcomeLabel() + if (err != nil) { return err } + predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel) + + getConfidenceRangeLabel := func()(fyne.Widget, error){ + + if (analysisExists == false){ + unknownLabel := widget.NewLabel("Unknown") + return unknownLabel, nil + } + + // This is a list of the percentage accuracies in the map + // For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be + // accurate within that range + confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap) + + // We sort the list so the percentage is always the same upon refreshing the page + slices.Sort(confidenceRangePercentagesList) + + closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80) + if (err != nil) { return nil, err } + + closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage] + if (exists == false){ + return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") + } + + closestConfidenceDistanceString, err := outcomeFormatter(closestToEightyPercentageConfidenceDistance, false) + if (err != nil) { return nil, err } + + closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) + + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" + + confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) + + return confidenceRangeLabel, nil + } + + confidenceRangeLabel, err := getConfidenceRangeLabel() + if (err != nil) { return err } + + confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel) + + genomeQuantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown) + quantityOfLociKnownLabel := getBoldLabelCentered(genomeQuantityOfLociKnownString + "/" + totalQuantityOfLociString) + + viewDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ + //TODO + showUnderConstructionDialog(window) + }) + + genomeNameColumn.Add(genomeNameCell) + predictedOutcomeColumn.Add(predictedOutcomeLabelCentered) + predictionConfidenceColumn.Add(confidenceRangeLabelCentered) + quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel) + viewDetailsButtonsColumn.Add(viewDetailsButton) + + genomeNameColumn.Add(widget.NewSeparator()) + predictedOutcomeColumn.Add(widget.NewSeparator()) + predictionConfidenceColumn.Add(widget.NewSeparator()) + quantityOfLociKnownColumn.Add(widget.NewSeparator()) + viewDetailsButtonsColumn.Add(widget.NewSeparator()) + + return nil + } + + if (multipleGenomesExist == true){ + + err := addGenomeRow("Only Exclude Conflicts", onlyExcludeConflictsGenomeIdentifier, true) + if (err != nil){ return nil, err } + + err = addGenomeRow("Only Include Shared", onlyIncludeSharedGenomeIdentifier, true) + if (err != nil){ return nil, err } + } + + for _, genomeIdentifier := range allRawGenomeIdentifiersList{ + + getGenomeName := func()(string, error){ + genomeFound, _, timeGenomeWasExported, _, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier) + if (err != nil) { return "", err } + if (genomeFound == false){ + return "", errors.New("MyGenomeInfo for genome from analysisObject not found.") + } + + if (multipleGenomesExist == false){ + return companyName, nil + } + + // We show the date the genome was exported + + exportTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeGenomeWasExported, false) + if (err != nil){ return "", err } + + genomeName := companyName + " (Exported " + exportTimeAgo + ")" + + return genomeName, nil + } + + genomeName, err := getGenomeName() + if (err != nil) { return nil, err } + + err = addGenomeRow(genomeName, genomeIdentifier, false) + if (err != nil){ return nil, err } + } + + predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + showUnderConstructionDialog(window) + //TODO + }) + predictedOutcomeColumn.Add(predictedOutcomeHelpButton) + + predictionConfidenceHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + showUnderConstructionDialog(window) + }) + predictionConfidenceColumn.Add(predictionConfidenceHelpButton) + + quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + //TODO + showUnderConstructionDialog(window) + }) + quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton) + + genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, predictedOutcomeColumn, predictionConfidenceColumn, quantityOfLociKnownColumn, viewDetailsButtonsColumn, layout.NewSpacer()) + + return genomesContainer, nil + } + + genomesContainer, err := getGenomesContainer() + if (err != nil){ + setErrorEncounteredPage(window, err, previousPage) + return + } + + page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), traitNameRow, widget.NewSeparator(), genomesContainer) + + setPageContent(page, window) +} + + diff --git a/gui/viewGeneticReferencesGui.go b/gui/viewGeneticReferencesGui.go index 8fa57fa..e6452b2 100644 --- a/gui/viewGeneticReferencesGui.go +++ b/gui/viewGeneticReferencesGui.go @@ -1,4 +1,3 @@ - package gui // viewGeneticReferencesGui.go implements pages to display information about genetic diseases and traits @@ -245,217 +244,6 @@ func setViewPolygenicDiseaseDetailsPage(window fyne.Window, diseaseName string, } -func setViewPolygenicDiseaseLocusDetailsPage(window fyne.Window, diseaseName string, locusIdentifier string, previousPage func()){ - - currentPage := func(){setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifier, previousPage)} - - title := getPageTitleCentered("Viewing Locus Details") - - backButton := getBackButtonCentered(previousPage) - - locusObject, err := polygenicDiseases.GetPolygenicDiseaseLocusObject(diseaseName, locusIdentifier) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - locusRSID := locusObject.LocusRSID - locusReferencesMap := locusObject.References - - locusRSIDsList := []int64{locusRSID} - - // We add aliases to locusRSIDsList - - anyAliasesExist, rsidAliasesList, err := locusMetadata.GetRSIDAliases(locusRSID) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - if (anyAliasesExist == true){ - locusRSIDsList = append(locusRSIDsList, rsidAliasesList...) - } - - metadataExists, locusMetadataObject, err := locusMetadata.GetLocusMetadata(locusRSID) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - if (metadataExists == false){ - setErrorEncounteredPage(window, errors.New("setViewPolygenicDiseaseLocusDetailsPage called with locusRSID missing from locusMetadata."), previousPage) - return - } - - diseaseNameLabel := widget.NewLabel("Disease Name:") - diseaseNameText := getBoldLabel(diseaseName) - diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, layout.NewSpacer()) - - getLocusNamesLabelText := func()string{ - - if(len(locusRSIDsList) == 1){ - return "Locus Name:" - } - return "Locus Names:" - } - - locusNamesLabelText := getLocusNamesLabelText() - - locusRSIDStringsList := make([]string, 0, len(locusRSIDsList)) - - for _, locusRSID := range locusRSIDsList{ - - locusRSIDString := helpers.ConvertInt64ToString(locusRSID) - - locusRSIDName := "rs" + locusRSIDString - - locusRSIDStringsList = append(locusRSIDStringsList, locusRSIDName) - } - - locusNamesListString := strings.Join(locusRSIDStringsList, ", ") - locusNamesLabel := widget.NewLabel(locusNamesLabelText) - locusNamesText := getBoldLabel(locusNamesListString) - locusNamesRow := container.NewHBox(layout.NewSpacer(), locusNamesLabel, locusNamesText, layout.NewSpacer()) - - getLocusGeneNameLabelValue := func()string{ - - locusGeneInfoIsKnown := locusMetadataObject.GeneInfoIsKnown - if (locusGeneInfoIsKnown == false){ - return "Unknown" - } - - locusGeneExists := locusMetadataObject.GeneExists - if (locusGeneExists == false){ - return "None" - } - - locusGeneName := locusMetadataObject.GeneNamesList[0] - - return locusGeneName - } - - locusGeneNameLabelValue := getLocusGeneNameLabelValue() - - geneNameLabel := widget.NewLabel("Gene Name:") - geneNameText := getBoldLabel(locusGeneNameLabelValue) - geneNameRow := container.NewHBox(layout.NewSpacer(), geneNameLabel, geneNameText, layout.NewSpacer()) - - viewReferencesButton := getWidgetCentered(widget.NewButtonWithIcon("View References", theme.ListIcon(), func(){ - setViewGeneticAnalysisReferencesPage(window, "Locus", locusReferencesMap, currentPage) - })) - - getBasePairsGrid := func()(*fyne.Container, error){ - - locusRiskWeightsMap := locusObject.RiskWeightsMap - locusBasePairProbabilitiesMap := locusObject.BasePairProbabilitiesMap - - riskWeightLabel := getItalicLabelCentered("Risk Weight") - probabilityLabel := getItalicLabelCentered("Probability Of Weight") - - riskWeightColumn := container.NewVBox(riskWeightLabel, widget.NewSeparator()) - riskWeightProbabilityColumn := container.NewVBox(probabilityLabel, widget.NewSeparator()) - - // We create a new map with duplicates removed - - locusBasePairProbabilitiesMap_DuplicatesRemoved := make(map[string]float64) - - for basePair, basePairProbability := range locusBasePairProbabilitiesMap{ - - baseA, baseB, semicolonFound := strings.Cut(basePair, ";") - if (semicolonFound == false) { - return nil, errors.New("Invalid base pair found in locusBasePairProbabilitiesMap: " + basePair) - } - basePairDuplicate := baseB + ";" + baseA - - existingProbabilityValue, exists := locusBasePairProbabilitiesMap_DuplicatesRemoved[basePairDuplicate] - if (exists == true){ - - // The duplicate has already been added. - // We make sure the probability values match - if (existingProbabilityValue != basePairProbability){ - return nil, errors.New("locusBasePairProbabilitiesMap contains duplicate base pair with different value") - } - continue - } - locusBasePairProbabilitiesMap_DuplicatesRemoved[basePair] = basePairProbability - } - - // All probabilities are mutually exclusive (you can only have 1 base pair for each genome locus) - // Thus, we can add them together to get a total probability for each risk weight - - // Map structure: Risk Weight -> Probability of having weight - riskWeightProbabilitiesMap := make(map[int]float64) - - for basePair, basePairProbability := range locusBasePairProbabilitiesMap_DuplicatesRemoved{ - - getBasePairRiskWeight := func()int{ - - basePairRiskWeight, exists := locusRiskWeightsMap[basePair] - if (exists == false){ - // This base pair has no known weight. We treat it as a 0 weight. - return 0 - } - return basePairRiskWeight - } - - basePairRiskWeight := getBasePairRiskWeight() - - riskWeightProbabilitiesMap[basePairRiskWeight] += basePairProbability - } - - // Now we sort risk weights in order of least to greatest - allRiskWeightsList := helpers.GetListOfMapKeys(riskWeightProbabilitiesMap) - - slices.Sort(allRiskWeightsList) - - for _, riskWeight := range allRiskWeightsList{ - - riskWeightProbability, exists := riskWeightProbabilitiesMap[riskWeight] - if (exists == false){ - return nil, errors.New("Risk weight probability not found in riskWeightProbabilitiesMap") - } - - riskWeightString := helpers.ConvertIntToString(riskWeight) - - riskWeightPercentageProbability := riskWeightProbability * 100 - riskWeightProbabilityString := helpers.ConvertFloat64ToStringRounded(riskWeightPercentageProbability, 2) - riskWeightProbabilityFormatted := "~" + riskWeightProbabilityString + "%" - - riskWeightText := getBoldLabelCentered(riskWeightString) - riskWeightProbabilityText := getBoldLabelCentered(riskWeightProbabilityFormatted) - - riskWeightColumn.Add(riskWeightText) - riskWeightProbabilityColumn.Add(riskWeightProbabilityText) - - riskWeightColumn.Add(widget.NewSeparator()) - riskWeightProbabilityColumn.Add(widget.NewSeparator()) - } - - riskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) - }) - riskWeightColumn.Add(riskWeightHelpButton) - - probabilityHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPolygenicDiseaseLocusRiskWeightProbabilityExplainerPage(window, currentPage) - }) - riskWeightProbabilityColumn.Add(probabilityHelpButton) - - basePairsGrid := container.NewHBox(layout.NewSpacer(), riskWeightColumn, riskWeightProbabilityColumn, layout.NewSpacer()) - - return basePairsGrid, nil - } - - basePairsGrid, err := getBasePairsGrid() - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - page := container.NewVBox(title, backButton, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), locusNamesRow, widget.NewSeparator(), geneNameRow, widget.NewSeparator(), viewReferencesButton, widget.NewSeparator(), basePairsGrid) - - setPageContent(page, window) -} - - func setViewGeneticAnalysisReferencesPage(window fyne.Window, referencesTopic string, referencesMap map[string]string, previousPage func()){ currentPage := func(){setViewGeneticAnalysisReferencesPage(window, referencesTopic, referencesMap, previousPage)} @@ -644,5 +432,3 @@ func setViewDiscreteTraitRuleDetailsPage(window fyne.Window, traitName string, r - - diff --git a/gui/viewProfileGui.go b/gui/viewProfileGui.go index 849591c..89ece49 100644 --- a/gui/viewProfileGui.go +++ b/gui/viewProfileGui.go @@ -2370,6 +2370,8 @@ func setViewMateProfilePage_AncestryComposition(window fyne.Window, userOrOffspr // This is a page to view the total monogenic disease risk for a user's offspring func setViewMateProfilePage_TotalDiseaseRisk(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){ + setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...") + title := getPageTitleCentered(translate("View Profile - Physical")) backButton := getBackButtonCentered(previousPage) @@ -2499,7 +2501,15 @@ func setViewMateProfilePage_TotalDiseaseRisk(window fyne.Window, getAnyUserProfi return } - totalNumberOfPolygenicDiseases := len(allPolygenicDiseaseNamesList) + // We count up every disease that has a prediction model + totalNumberOfPolygenicDiseases := 0 + + for _, diseaseName := range allPolygenicDiseaseNamesList{ + predictionModelExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(diseaseName) + if (predictionModelExists == true){ + totalNumberOfPolygenicDiseases += 1 + } + } totalNumberOfPolygenicDiseasesString := helpers.ConvertIntToString(totalNumberOfPolygenicDiseases) @@ -2891,6 +2901,10 @@ func setViewMateProfilePage_MonogenicDiseases(window fyne.Window, userOrOffsprin func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){ + if (userOrOffspring == "Offspring"){ + setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...") + } + currentPage := func(){setViewMateProfilePage_PolygenicDiseases(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)} title := getPageTitleCentered(translate("View Profile - Physical")) @@ -2917,38 +2931,92 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin getDiseaseInfoGrid := func()(*fyne.Container, error){ - emptyLabel1 := widget.NewLabel("") diseaseNameLabel := getItalicLabelCentered("Disease Name") - emptyLabel2 := widget.NewLabel("") userRiskScoreLabel := getItalicLabelCentered("User Risk Score") - emptyLabel3 := widget.NewLabel("") offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score") - userNumberOfLabel := getItalicLabelCentered("User Number Of") - lociTestedLabelA := getItalicLabelCentered("Loci Tested") + confidenceRangeLabel := getItalicLabelCentered("Confidence Range") - offspringNumberOfLabel := getItalicLabelCentered("Offspring Number Of") - lociTestedLabelB := getItalicLabelCentered("Loci Tested") + emptyLabel1 := widget.NewLabel("") - emptyLabel4 := widget.NewLabel("") - emptyLabel5 := widget.NewLabel("") + emptyLabel2 := widget.NewLabel("") - emptyLabel6 := widget.NewLabel("") - emptyLabel7 := widget.NewLabel("") + diseaseNameColumn := container.NewVBox(diseaseNameLabel, widget.NewSeparator()) + userRiskScoreColumn := container.NewVBox(userRiskScoreLabel, widget.NewSeparator()) + offspringRiskScoreColumn := container.NewVBox(offspringRiskScoreLabel, widget.NewSeparator()) + confidenceRangeColumn := container.NewVBox(confidenceRangeLabel, widget.NewSeparator()) + viewSampleOffspringsChartButtonsColumn := container.NewVBox(emptyLabel1, widget.NewSeparator()) + viewDiseaseInfoButtonsColumn := container.NewVBox(emptyLabel2, widget.NewSeparator()) - diseaseNameColumn := container.NewVBox(emptyLabel1, diseaseNameLabel, widget.NewSeparator()) - userRiskScoreColumn := container.NewVBox(emptyLabel2, userRiskScoreLabel, widget.NewSeparator()) - offspringRiskScoreColumn := container.NewVBox(emptyLabel3, offspringRiskScoreLabel, widget.NewSeparator()) - userNumberOfLociTestedColumn := container.NewVBox(userNumberOfLabel, lociTestedLabelA, widget.NewSeparator()) - offspringNumberOfLociTestedColumn := container.NewVBox(offspringNumberOfLabel, lociTestedLabelB, widget.NewSeparator()) - viewSampleOffspringsChartButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator()) - viewDiseaseInfoButtonsColumn := container.NewVBox(emptyLabel6, emptyLabel7, widget.NewSeparator()) + //Outputs: + // -map[int64]locusValue.LocusValue + // -error + getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){ - myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + if (userOrOffspring == "User"){ + // We don't need to retrieve our locus values map + emptyMap := make(map[int64]locusValue.LocusValue) + return emptyMap, nil + } + + myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + if (err != nil) { return nil, err } + + if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ + emptyMap := make(map[int64]locusValue.LocusValue) + return emptyMap, nil + } + + _, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject) + if (err != nil) { return nil, err } + + myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier] + if (exists == false){ + return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.") + } + + return myGenomeLocusValuesMap, nil + } + + myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap() if (err != nil) { return nil, err } + getConfidenceRangeLabel := func(analysisExists bool, predictionConfidenceRangesMap map[int]float64)(fyne.Widget, error){ + if (analysisExists == false){ + + result := widget.NewLabel("Unknown") + return result, nil + } + + // This is a list of the percentage accuracies in the map + // For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be + // accurate within that range + confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap) + + // We sort the list so the percentage is always the same upon refreshing the page + slices.Sort(confidenceRangePercentagesList) + + closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80) + if (err != nil) { return nil, err } + + closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage] + if (exists == false){ + return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") + } + + closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2) + + closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) + + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" + + confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) + + return confidenceRangeLabel, nil + } + diseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList() if (err != nil) { return nil, err } @@ -2957,36 +3025,18 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin diseaseName := diseaseObject.DiseaseName diseaseLociList := diseaseObject.LociList - //Outputs: - // -map[int64]locusValue.LocusValue - // -error - getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){ - - if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ - emptyMap := make(map[int64]locusValue.LocusValue) - return emptyMap, nil - } - - _, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject) - if (err != nil) { return nil, err } - - myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier] - if (exists == false){ - return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.") - } - - return myGenomeLocusValuesMap, nil + predictionModelExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(diseaseName) + if (predictionModelExists == false){ + // Prediction is not possible for this disease + continue } - myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap() - if (err != nil) { return nil, err } + diseaseNameLabel := getBoldLabelCentered(diseaseName) // Map Structure: Locus rsID -> Locus Value userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue) - for _, locusObject := range diseaseLociList{ - - locusRSID := locusObject.LocusRSID + for _, locusRSID := range diseaseLociList{ locusRSIDString := helpers.ConvertInt64ToString(locusRSID) @@ -3023,81 +3073,100 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin userDiseaseLocusValuesMap[locusRSID] = userLocusValueObject } - userDiseaseInfoIsKnown, userDiseaseRiskScore, userNumberOfLociTested, _, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, userDiseaseLocusValuesMap, true) - if (err != nil) { return nil, err } + if (userOrOffspring == "User"){ - getUserDiseaseRiskScoreString := func()(string, error){ - - if (userDiseaseInfoIsKnown == false){ - result := translate("Unknown") - - return result, nil - } - - userRiskScoreString := helpers.ConvertIntToString(userDiseaseRiskScore) - resultFormatted := userRiskScoreString + "/10" - - return resultFormatted, nil - } - - userDiseaseRiskScoreString, err := getUserDiseaseRiskScoreString() - if (err != nil) { return nil, err } - - anyOffspringLociTested, offspringDiseaseRiskScore, offspringNumberOfLociTested, _, offspringSampleRiskScoresList, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLociList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap) - if (err != nil) { return nil, err } - - getOffspringDiseaseRiskScoreFormatted := func()(string, error){ - - if (anyOffspringLociTested == false){ - result := translate("Unknown") - - return result, nil + neuralNetworkExists, analysisExists, userDiseaseRiskScore, predictionConfidenceRangesMap, _, _, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseAnalysis(diseaseObject, userDiseaseLocusValuesMap, true) + if (err != nil) { return nil, err } + if (neuralNetworkExists == false){ + return nil, errors.New("GetPersonGenomePolygenicDiseaseAnalysis claims model doesn't exist when we already checked.") } - offspringRiskScoreString := helpers.ConvertIntToString(offspringDiseaseRiskScore) - resultFormatted := offspringRiskScoreString + "/10" - return resultFormatted, nil + getUserDiseaseRiskScoreLabel := func()fyne.Widget{ + + if (analysisExists == false){ + + result := widget.NewLabel(translate("Unknown")) + + return result + } + + userRiskScoreString := helpers.ConvertIntToString(userDiseaseRiskScore) + riskScoreFormatted := userRiskScoreString + "/10" + + riskScoreLabel := getBoldLabel(riskScoreFormatted) + + return riskScoreLabel + } + + userDiseaseRiskScoreLabel := getUserDiseaseRiskScoreLabel() + userDiseaseRiskScoreLabelCentered := getWidgetCentered(userDiseaseRiskScoreLabel) + userRiskScoreColumn.Add(userDiseaseRiskScoreLabelCentered) + + confidenceRangeLabel, err := getConfidenceRangeLabel(analysisExists, predictionConfidenceRangesMap) + if (err != nil) { return nil, err } + + confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel) + + confidenceRangeColumn.Add(confidenceRangeLabelCentered) + + } else if (userOrOffspring == "Offspring"){ + + neuralNetworkExists, anyOffspringLociKnown, offspringDiseaseRiskScore, predictionConfidenceRangesMap, offspringSampleRiskScoresList, offspringQuantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseAnalysis(diseaseObject, myGenomeLocusValuesMap, userDiseaseLocusValuesMap) + if (err != nil) { return nil, err } + if (neuralNetworkExists == false){ + return nil, errors.New("GetOffspringPolygenicDiseaseAnalysis claims that neural network doesn't exist when we already checked.") + } + + getOffspringDiseaseRiskScoreLabel := func()fyne.Widget{ + + if (anyOffspringLociKnown == false){ + + result := widget.NewLabel(translate("Unknown")) + + return result + } + + offspringRiskScoreString := helpers.ConvertIntToString(offspringDiseaseRiskScore) + riskScoreFormatted := offspringRiskScoreString + "/10" + + riskScoreLabel := getBoldLabel(riskScoreFormatted) + + return riskScoreLabel + } + + offspringDiseaseRiskScoreLabel := getOffspringDiseaseRiskScoreLabel() + offspringDiseaseRiskScoreLabelCentered := getWidgetCentered(offspringDiseaseRiskScoreLabel) + + offspringRiskScoreColumn.Add(offspringDiseaseRiskScoreLabelCentered) + + confidenceRangeLabel, err := getConfidenceRangeLabel(anyOffspringLociKnown, predictionConfidenceRangesMap) + if (err != nil) { return nil, err } + + confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel) + + confidenceRangeColumn.Add(confidenceRangeLabelCentered) + + viewSampleOffspringsChartButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ + + setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window, diseaseName, offspringSampleRiskScoresList, offspringQuantityOfLociKnown, currentPage) + }) + + viewSampleOffspringsChartButtonsColumn.Add(viewSampleOffspringsChartButton) } - offspringDiseaseRiskScoreFormatted, err := getOffspringDiseaseRiskScoreFormatted() - if (err != nil) { return nil, err } - - totalNumberOfDiseaseLoci := len(diseaseLociList) - totalNumberOfDiseaseLociString := helpers.ConvertIntToString(totalNumberOfDiseaseLoci) - - userNumberOfLociTestedString := helpers.ConvertIntToString(userNumberOfLociTested) - userNumberOfLociTestedFormatted := userNumberOfLociTestedString + "/" + totalNumberOfDiseaseLociString - offspringNumberOfLociTestedString := helpers.ConvertIntToString(offspringNumberOfLociTested) - offspringNumberOfLociTestedFormatted := offspringNumberOfLociTestedString + "/" + totalNumberOfDiseaseLociString - - diseaseNameText := getBoldLabelCentered(diseaseName) - userRiskScoreLabel := getBoldLabelCentered(userDiseaseRiskScoreString) - offspringRiskScoreLabel := getBoldLabelCentered(offspringDiseaseRiskScoreFormatted) - userNumberOfLociTestedLabel := getBoldLabelCentered(userNumberOfLociTestedFormatted) - offspringNumberOfLociTestedLabel := getBoldLabelCentered(offspringNumberOfLociTestedFormatted) - - viewSampleOffspringsChartButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - - setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window, diseaseName, offspringSampleRiskScoresList, offspringNumberOfLociTested, currentPage) - }) - viewDiseaseDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ - setViewMateProfilePage_PolygenicDiseaseLoci(window, diseaseName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage) + //TODO + showUnderConstructionDialog(window) }) - diseaseNameColumn.Add(diseaseNameText) - userRiskScoreColumn.Add(userRiskScoreLabel) - offspringRiskScoreColumn.Add(offspringRiskScoreLabel) - userNumberOfLociTestedColumn.Add(userNumberOfLociTestedLabel) - offspringNumberOfLociTestedColumn.Add(offspringNumberOfLociTestedLabel) - viewSampleOffspringsChartButtonsColumn.Add(viewSampleOffspringsChartButton) + diseaseNameColumn.Add(diseaseNameLabel) + viewDiseaseInfoButtonsColumn.Add(viewDiseaseDetailsButton) diseaseNameColumn.Add(widget.NewSeparator()) userRiskScoreColumn.Add(widget.NewSeparator()) offspringRiskScoreColumn.Add(widget.NewSeparator()) - userNumberOfLociTestedColumn.Add(widget.NewSeparator()) - offspringNumberOfLociTestedColumn.Add(widget.NewSeparator()) + confidenceRangeColumn.Add(widget.NewSeparator()) viewSampleOffspringsChartButtonsColumn.Add(widget.NewSeparator()) viewDiseaseInfoButtonsColumn.Add(widget.NewSeparator()) } @@ -3105,30 +3174,27 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin userRiskScoreHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPolygenicDiseaseRiskScoreExplainerPage(window, currentPage) }) + userRiskScoreColumn.Add(userRiskScoreHelpButton) offspringRiskScoreHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setOffspringPolygenicDiseaseRiskScoreExplainerPage(window, currentPage) }) - userNumberOfLociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) - }) - - offspringNumberOfLociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) - }) - - userRiskScoreColumn.Add(userRiskScoreHelpButton) offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton) - userNumberOfLociTestedColumn.Add(userNumberOfLociTestedHelpButton) - offspringNumberOfLociTestedColumn.Add(offspringNumberOfLociTestedHelpButton) + + confidenceRangeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + showUnderConstructionDialog(window) + //TODO + }) + + confidenceRangeColumn.Add(confidenceRangeHelpButton) if (userOrOffspring == "User"){ - diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, userRiskScoreColumn, userNumberOfLociTestedColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer()) + diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, userRiskScoreColumn, confidenceRangeColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer()) return diseaseInfoGrid, nil } - diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, offspringRiskScoreColumn, offspringNumberOfLociTestedColumn, viewSampleOffspringsChartButtonsColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer()) + diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, offspringRiskScoreColumn, confidenceRangeColumn, viewSampleOffspringsChartButtonsColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer()) return diseaseInfoGrid, nil } @@ -3138,403 +3204,12 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin setErrorEncounteredPage(window, err, previousPage) return } - + page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), userOrOffspringSelectorCentered, widget.NewSeparator(), diseaseInfoGrid) setPageContent(page, window) } -func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName string, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){ - - setLoadingScreen(window, translate("View Profile - Physical"), "Loading " + userOrOffspring + " Disease Loci Info") - - if (userOrOffspring != "User" && userOrOffspring != "Offspring"){ - setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiseaseLoci called with invalid userOrOffspring: " + userOrOffspring), previousPage) - return - } - - currentPage := func(){setViewMateProfilePage_PolygenicDiseaseLoci(window, diseaseName, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)} - - title := getPageTitleCentered(translate("View Profile - Physical")) - - backButton := getBackButtonCentered(previousPage) - - subtitle := getPageSubtitleCentered(translate(userOrOffspring + " Disease Loci Info")) - - diseaseNameLabel := widget.NewLabel(translate("Disease Name:")) - - diseaseNameText := getBoldLabel(translate(diseaseName)) - - viewDiseaseInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewPolygenicDiseaseDetailsPage(window, diseaseName, currentPage) - }) - - diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, viewDiseaseInfoButton, layout.NewSpacer()) - - diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - diseaseLocusObjectsList := diseaseObject.LociList - - numberOfDiseaseLoci := len(diseaseLocusObjectsList) - - // Outputs: - // -map[int64]locusValue.LocusValue: Map Structure: Locus rsID -> Locus Value - // -error - getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){ - - myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() - if (err != nil) { return nil, err } - - if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ - - emptyMap := make(map[int64]locusValue.LocusValue) - return emptyMap, nil - } - - _, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject) - if (err != nil) { return nil, err } - - myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier] - if (exists == false){ - return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.") - } - - return myGenomeLocusValuesMap, nil - } - - myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap() - if (err != nil) { - setErrorEncounteredPage(window, err, previousPage) - return - } - - getUserLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){ - - // Map Structure: Locus rsID -> Locus Value - userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue) - - for _, locusObject := range diseaseLocusObjectsList{ - - locusRSID := locusObject.LocusRSID - - locusRSIDString := helpers.ConvertInt64ToString(locusRSID) - - locusValueAttributeName := "LocusValue_rs" + locusRSIDString - - userLocusValueExists, _, userLocusValue, err := getAnyUserProfileAttributeFunction(locusValueAttributeName) - if (err != nil) { return nil, err } - if (userLocusValueExists == false){ - continue - } - - userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusValue, ";") - if (semicolonFound == false){ - return nil, errors.New("Database corrupt: Contains profile with invalid " + locusValueAttributeName + " value: " + userLocusValue) - } - - userLocusIsPhasedAttributeName := "LocusIsPhased_rs" + locusRSIDString - - userLocusIsPhasedExists, _, userLocusIsPhasedString, err := getAnyUserProfileAttributeFunction(userLocusIsPhasedAttributeName) - if (err != nil) { return nil, err } - if (userLocusIsPhasedExists == false){ - return nil, errors.New("Database corrupt: Contains profile with locusValue but not locusIsPhased status for locus: " + locusRSIDString) - } - - userLocusIsPhased, err := helpers.ConvertYesOrNoStringToBool(userLocusIsPhasedString) - if (err != nil) { return nil, err } - - userLocusValueObject := locusValue.LocusValue{ - Base1Value: userLocusBase1, - Base2Value: userLocusBase2, - LocusIsPhased: userLocusIsPhased, - } - - userDiseaseLocusValuesMap[locusRSID] = userLocusValueObject - } - - return userDiseaseLocusValuesMap, nil - } - - userDiseaseLocusValuesMap, err := getUserLocusValuesMap() - if (err != nil) { - setErrorEncounteredPage(window, err, previousPage) - return - } - - anyUserLociTested, _, userNumberOfLociTested, userDiseaseLocusInfoMap, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseInfo(diseaseLocusObjectsList, userDiseaseLocusValuesMap, true) - if (err != nil) { - setErrorEncounteredPage(window, err, previousPage) - return - } - - anyOffspringLociTested, _, offspringNumberOfLociTested, offspringLociInfoMap, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLocusObjectsList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap) - if (err != nil) { - setErrorEncounteredPage(window, err, previousPage) - return - } - - getNumberOfLociTested := func()int{ - - if (userOrOffspring == "Offspring"){ - - if (anyOffspringLociTested == false){ - return 0 - } - - return offspringNumberOfLociTested - } - - return userNumberOfLociTested - } - - numberOfLociTested := getNumberOfLociTested() - - numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) - totalNumberOfDiseaseLociString := helpers.ConvertIntToString(numberOfDiseaseLoci) - lociTestedString := numberOfLociTestedString + "/" + totalNumberOfDiseaseLociString - - lociTestedLabel := widget.NewLabel("Loci Tested:") - - lociTestedText := getBoldLabel(lociTestedString) - - lociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - if (userOrOffspring == "User"){ - setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) - } else { - setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) - } - }) - - lociTestedRow := container.NewHBox(layout.NewSpacer(), lociTestedLabel, lociTestedText, lociTestedHelpButton, layout.NewSpacer()) - - //TODO: Navigation buttons and multiple pages - - getDiseaseLociGrid := func()(*fyne.Container, error){ - - //TODO: Sort loci - - locusNameLabel := getItalicLabelCentered("Locus Name") - - userRiskWeightLabel := getItalicLabelCentered("User Risk Weight") - offspringRiskWeightLabel := getItalicLabelCentered("Offspring Risk Weight") - - userOddsRatioLabel := getItalicLabelCentered("User Odds Ratio") - offspringOddsRatioLabel := getItalicLabelCentered("Offspring Odds Ratio") - - emptyLabel := widget.NewLabel("") - - locusNameColumn := container.NewVBox(locusNameLabel, widget.NewSeparator()) - userRiskWeightColumn := container.NewVBox(userRiskWeightLabel, widget.NewSeparator()) - offspringRiskWeightColumn := container.NewVBox(offspringRiskWeightLabel, widget.NewSeparator()) - userOddsRatioColumn := container.NewVBox(userOddsRatioLabel, widget.NewSeparator()) - offspringOddsRatioColumn := container.NewVBox(offspringOddsRatioLabel, widget.NewSeparator()) - locusInfoButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) - - for _, locusObject := range diseaseLocusObjectsList{ - - locusIdentifierHex := locusObject.LocusIdentifier - - locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) - if (err != nil) { return nil, err } - - locusRSID := locusObject.LocusRSID - locusRSIDString := helpers.ConvertInt64ToString(locusRSID) - locusName := "rs" + locusRSIDString - - //Outputs: - // -bool: User locus Info is known - // -int: User locus Risk weight - // -bool: User locus odds ratio known - // -string: User locus odds ratio formatted - // -error - getUserLocusInfo := func()(bool, int, bool, string, error){ - - if (anyUserLociTested == false){ - return false, 0, false, "", nil - } - - locusInfoObject, exists := userDiseaseLocusInfoMap[locusIdentifier] - if (exists == false){ - return false, 0, false, "", nil - } - - userLocusRiskWeight := locusInfoObject.RiskWeight - - oddsRatioIsKnown := locusInfoObject.OddsRatioIsKnown - - if (oddsRatioIsKnown == false){ - return true, userLocusRiskWeight, false, "", nil - } - - userLocusOddsRatio := locusInfoObject.OddsRatio - - locusOddsRatioString := helpers.ConvertFloat64ToStringRounded(userLocusOddsRatio, 2) - - locusOddsRatioFormatted := locusOddsRatioString + "x" - - return true, userLocusRiskWeight, true, locusOddsRatioFormatted, nil - } - - userLocusInfoIsKnown, userLocusRiskWeight, userLocusOddsRatioIsKnown, userLocusOddsRatioFormatted, err := getUserLocusInfo() - if (err != nil) { return nil, err } - - getUserRiskWeightString := func()string{ - - if (userLocusInfoIsKnown == false){ - result := translate("Unknown") - return result - } - - userRiskWeightString := helpers.ConvertIntToString(userLocusRiskWeight) - return userRiskWeightString - } - - userRiskWeightString := getUserRiskWeightString() - - getUserOddsRatioString := func()string{ - if (userLocusOddsRatioIsKnown == false){ - result := translate("Unknown") - return result - } - return userLocusOddsRatioFormatted - } - - userOddsRatioString := getUserOddsRatioString() - - //Outputs: - // -bool: Offspring disease locus info known - // -int: Offspring risk weight - // -bool: Offspring odds ratio known - // -string: Offspring odds ratio formatted - // -error - getOffspringDiseaseLocusInfo := func()(bool, int, bool, string, error){ - - if (anyOffspringLociTested == false){ - return false, 0, false, "", nil - } - - offspringLocusInfoObject, exists := offspringLociInfoMap[locusIdentifier] - if (exists == false){ - return false, 0, false, "", nil - } - - offspringLocusRiskWeight := offspringLocusInfoObject.OffspringAverageRiskWeight - offspringOddsRatioIsKnown := offspringLocusInfoObject.OffspringOddsRatioIsKnown - - if (offspringOddsRatioIsKnown == false){ - return true, offspringLocusRiskWeight, false, "", nil - } - - offspringOddsRatio := offspringLocusInfoObject.OffspringAverageOddsRatio - unknownOddsRatiosWeightSum := offspringLocusInfoObject.OffspringAverageUnknownOddsRatiosWeightSum - - getOddsRatioFormatted := func()string{ - - offspringOddsRatioString := helpers.ConvertFloat64ToStringRounded(offspringOddsRatio, 2) - - if (unknownOddsRatiosWeightSum > 0){ - result := offspringOddsRatioString + "x+" - return result - } - if (unknownOddsRatiosWeightSum < 0){ - result := "<" + offspringOddsRatioString + "x" - return result - } - result := offspringOddsRatioString + "x" - return result - } - - oddsRatioFormatted := getOddsRatioFormatted() - return true, offspringLocusRiskWeight, true, oddsRatioFormatted, nil - } - - offspringDiseaseLocusKnown, offspringRiskWeight, offspringOddsRatioKnown, offspringOddsRatioFormatted, err := getOffspringDiseaseLocusInfo() - if (err != nil) { return nil, err } - - getOffspringRiskWeightString := func()string{ - - if (offspringDiseaseLocusKnown == false){ - result := translate("Unknown") - return result - } - - offspringRiskWeightString := helpers.ConvertIntToString(offspringRiskWeight) - return offspringRiskWeightString - } - - offspringRiskWeightString := getOffspringRiskWeightString() - - getOffspringOddsRatioString := func()string{ - - if (offspringDiseaseLocusKnown == false || offspringOddsRatioKnown == false){ - result := translate("Unknown") - return result - } - return offspringOddsRatioFormatted - } - - offspringOddsRatioString := getOffspringOddsRatioString() - - locusNameLabel := getBoldLabelCentered(locusName) - userRiskWeightLabel := getBoldLabelCentered(userRiskWeightString) - offspringRiskWeightLabel := getBoldLabelCentered(offspringRiskWeightString) - userOddsRatioLabel := getBoldLabelCentered(userOddsRatioString) - offspringOddsRatioLabel := getBoldLabelCentered(offspringOddsRatioString) - locusInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ - setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifierHex, currentPage) - }) - - locusNameColumn.Add(locusNameLabel) - userRiskWeightColumn.Add(userRiskWeightLabel) - offspringRiskWeightColumn.Add(offspringRiskWeightLabel) - userOddsRatioColumn.Add(userOddsRatioLabel) - offspringOddsRatioColumn.Add(offspringOddsRatioLabel) - locusInfoButtonsColumn.Add(locusInfoButton) - - locusNameColumn.Add(widget.NewSeparator()) - userRiskWeightColumn.Add(widget.NewSeparator()) - offspringRiskWeightColumn.Add(widget.NewSeparator()) - userOddsRatioColumn.Add(widget.NewSeparator()) - offspringOddsRatioColumn.Add(widget.NewSeparator()) - locusInfoButtonsColumn.Add(widget.NewSeparator()) - } - - userRiskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) - }) - offspringRiskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - setOffspringPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) - }) - - userRiskWeightColumn.Add(userRiskWeightHelpButton) - offspringRiskWeightColumn.Add(offspringRiskWeightHelpButton) - - if (userOrOffspring == "User"){ - - diseaseLociGrid := container.NewHBox(layout.NewSpacer(), locusNameColumn, userRiskWeightColumn, userOddsRatioColumn, locusInfoButtonsColumn, layout.NewSpacer()) - - return diseaseLociGrid, nil - } - - diseaseLociGrid := container.NewHBox(layout.NewSpacer(), locusNameColumn, offspringRiskWeightColumn, offspringOddsRatioColumn, locusInfoButtonsColumn, layout.NewSpacer()) - - return diseaseLociGrid, nil - } - - diseaseLociGrid, err := getDiseaseLociGrid() - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return - } - - page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), lociTestedRow, widget.NewSeparator(), diseaseLociGrid) - - setPageContent(page, window) -} func setViewMateProfilePage_GeneticTraits(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){ @@ -3801,10 +3476,10 @@ func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffs if (traitNeuralNetworkExists == true){ - neuralNetworkExists, anyLociKnown, outcomeProbabilitiesMap, _, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap) + neuralNetworkExists, anyLociKnown, outcomeProbabilitiesMap, _, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitAnalysis_NeuralNetwork(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap) if (err != nil) { return nil, err } if (neuralNetworkExists == false){ - return nil, errors.New("GetOffspringTraitInfo_NeuralNetwork claiming that neural network doesn't exist when we already checked.") + return nil, errors.New("GetOffspringDiscreteTraitAnalysis_NeuralNetwork claiming that neural network doesn't exist when we already checked.") } totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci) @@ -3862,10 +3537,10 @@ func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffs // We use the rules-based analysis - anyRulesExist, rulesAnalysisExists, _, offspringQuantityOfLociKnown, _, outcomeProbabilitiesMap, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_Rules(traitObject, myGenomeLocusValuesMap, userTraitLocusValuesMap) + anyRulesExist, rulesAnalysisExists, _, offspringQuantityOfLociKnown, _, outcomeProbabilitiesMap, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitAnalysis_Rules(traitObject, myGenomeLocusValuesMap, userTraitLocusValuesMap) if (err != nil) { return nil, err } if (anyRulesExist == false){ - return nil, errors.New("GetOffspringDiscreteTraitInfo_Rules claiming that no rules exist when we already checked.") + return nil, errors.New("GetOffspringDiscreteTraitAnalysis_Rules claiming that no rules exist when we already checked.") } lociList_Rules := traitObject.LociList_Rules @@ -4125,10 +3800,10 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str return false, nil, nil } - anyRulesExist, anyOffspringRulesTested, _, _, offspringProbabilityOfPassingRulesMap, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_Rules(traitObject, myLocusValuesMap, userTraitLocusValuesMap) + anyRulesExist, anyOffspringRulesTested, _, _, offspringProbabilityOfPassingRulesMap, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitAnalysis_Rules(traitObject, myLocusValuesMap, userTraitLocusValuesMap) if (err != nil) { return false, nil, err } if (anyRulesExist == false){ - return false, nil, errors.New("GetOffspringDiscreteTraitInfo claiming no trait rules exist when we already checked.") + return false, nil, errors.New("GetOffspringDiscreteTraitAnalysis_Rules claiming no trait rules exist when we already checked.") } if (anyOffspringRulesTested == false){ return false, nil, nil @@ -4411,7 +4086,7 @@ func setViewMateProfilePage_NumericGeneticTraits(window fyne.Window, userOrOffsp setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...") } - //currentPage := func(){setViewMateProfilePage_NumericGeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)} + currentPage := func(){setViewMateProfilePage_NumericGeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)} title := getPageTitleCentered(translate("View Profile - Physical")) @@ -4437,14 +4112,20 @@ func setViewMateProfilePage_NumericGeneticTraits(window fyne.Window, userOrOffsp getTraitsInfoGrid := func()(*fyne.Container, error){ - myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() - if (err != nil) { return nil, err } - //Outputs: // -map[int64]locusValue.LocusValue // -error getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){ + if (userOrOffspring == "User"){ + // We don't need to retrieve our locus values map + emptyMap := make(map[int64]locusValue.LocusValue) + return emptyMap, nil + } + + myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() + if (err != nil) { return nil, err } + if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ emptyMap := make(map[int64]locusValue.LocusValue) return emptyMap, nil @@ -4465,32 +4146,64 @@ func setViewMateProfilePage_NumericGeneticTraits(window fyne.Window, userOrOffsp if (err != nil) { return nil, err } - emptyLabel1 := widget.NewLabel("") traitNameLabel := getItalicLabelCentered("Trait Name") - emptyLabel2 := widget.NewLabel("") userPredictedOutcomeTitle := getItalicLabelCentered("User Predicted Outcome") - emptyLabel3 := widget.NewLabel("") offspringPredictedOutcomeTitle := getItalicLabelCentered("Offspring Predicted Outcome") - predictionTitle := getItalicLabelCentered("Prediction") confidenceRangeTitle := getItalicLabelCentered("Confidence Range") - quantityOfLabel1 := getItalicLabelCentered("Quantity Of") - lociKnownLabel := getItalicLabelCentered("Loci Known") + emptyLabel1 := widget.NewLabel("") - emptyLabel4 := widget.NewLabel("") - emptyLabel5 := widget.NewLabel("") + emptyLabel2 := widget.NewLabel("") - traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator()) - userPredictedOutcomeColumn := container.NewVBox(emptyLabel2, userPredictedOutcomeTitle, widget.NewSeparator()) - offspringPredictedOutcomeColumn := container.NewVBox(emptyLabel3, offspringPredictedOutcomeTitle, widget.NewSeparator()) + traitNameColumn := container.NewVBox(traitNameLabel, widget.NewSeparator()) - predictionConfidenceRangeColumn := container.NewVBox(predictionTitle, confidenceRangeTitle, widget.NewSeparator()) - quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel1, lociKnownLabel, widget.NewSeparator()) - viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator()) + userPredictedOutcomeColumn := container.NewVBox(userPredictedOutcomeTitle, widget.NewSeparator()) + offspringPredictedOutcomeColumn := container.NewVBox(offspringPredictedOutcomeTitle, widget.NewSeparator()) + + predictionConfidenceRangeColumn := container.NewVBox(confidenceRangeTitle, widget.NewSeparator()) + viewSampleOffspringsButtonsColumn := container.NewVBox(emptyLabel1, widget.NewSeparator()) + viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabel2, widget.NewSeparator()) + + getConfidenceRangeLabel := func(traitObject traits.Trait, analysisExists bool, predictionConfidenceRangesMap map[int]float64)(fyne.Widget, error){ + if (analysisExists == false){ + + result := widget.NewLabel("Unknown") + return result, nil + } + + // This is a list of the percentage accuracies in the map + // For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be + // accurate within that range + confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap) + + // We sort the list so the percentage is always the same upon refreshing the page + slices.Sort(confidenceRangePercentagesList) + + closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80) + if (err != nil) { return nil, err } + + closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage] + if (exists == false){ + return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") + } + + outcomeFormatter := traitObject.NumericValueFormatter + + closestConfidenceDistanceString, err := outcomeFormatter(closestToEightyPercentageConfidenceDistance, false) + if (err != nil) { return nil, err } + + closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) + + confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)" + + confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) + + return confidenceRangeLabel, nil + } traitObjectsList, err := traits.GetTraitObjectsList() if (err != nil) { return nil, err } @@ -4498,6 +4211,7 @@ func setViewMateProfilePage_NumericGeneticTraits(window fyne.Window, userOrOffsp for _, traitObject := range traitObjectsList{ traitName := traitObject.TraitName + traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric if (traitIsDiscreteOrNumeric != "Numeric"){ continue @@ -4561,108 +4275,74 @@ func setViewMateProfilePage_NumericGeneticTraits(window fyne.Window, userOrOffsp userTraitLocusValuesMap[rsID] = userLocusValueObject } - //Outputs: - // -bool: Analysis results exist - // -float64: Predicted outcome - // -map[int]float64: Prediction confidence ranges map - // -int: Quantity of known loci - // -error - getAnalysisResults := func()(bool, float64, map[int]float64, int, error){ - - if (userOrOffspring == "User"){ - - traitNeuralNetworkExists, anyLocusValuesAreKnown, predictedOutcome, predictionAccuracyRangesMap, quantityOfLociKnown, _, err := createPersonGeneticAnalysis.GetGenomeNumericTraitAnalysis(traitObject, userTraitLocusValuesMap, true) - if (err != nil) { return false, 0, nil, 0, err } - if (traitNeuralNetworkExists == false){ - return false, 0, nil, 0, errors.New("GetGenomeNumericTraitAnalysis claims neural network doesn't exist for trait, but we already checked.") - } - - return anyLocusValuesAreKnown, predictedOutcome, predictionAccuracyRangesMap, quantityOfLociKnown, nil - } else { - - // userOrOffspring == "Offspring" - - neuralNetworkExists, anyLociKnown, predictedOutcome, predictionAccuracyRangesMap, _, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringNumericTraitInfo(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap) - if (err != nil) { return false, 0, nil, 0, err } - if (neuralNetworkExists == false){ - return false, 0, nil, 0, errors.New("GetOffspringTraitInfo_NeuralNetwork claiming that neural network doesn't exist when we already checked.") - } - - return anyLociKnown, predictedOutcome, predictionAccuracyRangesMap, quantityOfLociKnown, nil - } - } - - analysisExists, predictedOutcome, predictionConfidenceRangesMap, quantityOfLociKnown, err := getAnalysisResults() - if (err != nil) { return nil, err } - - getPredictedOutcomeLabel := func()fyne.Widget{ + getPredictedOutcomeLabel := func(analysisExists bool, predictedOutcome float64)(fyne.Widget, error){ if (analysisExists == false){ unknownLabel := widget.NewLabel("Unknown") - return unknownLabel + return unknownLabel, nil } - predictedOutcomeString := helpers.ConvertFloat64ToStringRounded(predictedOutcome, 2) + outcomeFormatter := traitObject.NumericValueFormatter - //TODO: Retrieve units from traits package - predictedOutcomeFormatted := predictedOutcomeString + " centimeters" + predictedOutcomeFormatted, err := outcomeFormatter(predictedOutcome, true) + if (err != nil) { return nil, err } predictedOutcomeLabel := getBoldLabel(predictedOutcomeFormatted) - return predictedOutcomeLabel + return predictedOutcomeLabel, nil } - predictedOutcomeLabel := getPredictedOutcomeLabel() + if (userOrOffspring == "User"){ - predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel) - - getConfidenceRangeLabel := func()(fyne.Widget, error){ - if (analysisExists == false){ - - result := widget.NewLabel("Unknown") - return result, nil + traitNeuralNetworkExists, anyLocusValuesAreKnown, predictedOutcome, predictionAccuracyRangesMap, _, _, err := createPersonGeneticAnalysis.GetGenomeNumericTraitAnalysis(traitObject, userTraitLocusValuesMap, true) + if (err != nil) { return nil, err } + if (traitNeuralNetworkExists == false){ + return nil, errors.New("GetGenomeNumericTraitAnalysis claims neural network doesn't exist for trait, but we already checked.") } - // This is a list of the percentage accuracies in the map - // For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be - // accurate within that range - confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap) + predictedOutcomeLabel, err := getPredictedOutcomeLabel(anyLocusValuesAreKnown, predictedOutcome) + if (err != nil) { return nil, err } + predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel) - // We sort the list so the percentage is always the same upon refreshing the page - slices.Sort(confidenceRangePercentagesList) + userPredictedOutcomeColumn.Add(predictedOutcomeLabelCentered) - closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80) + confidenceRangeLabel, err := getConfidenceRangeLabel(traitObject, anyLocusValuesAreKnown, predictionAccuracyRangesMap) if (err != nil) { return nil, err } - closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage] - if (exists == false){ - return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.") + confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel) + + predictionConfidenceRangeColumn.Add(confidenceRangeLabelCentered) + + } else { + + // userOrOffspring == "Offspring" + + neuralNetworkExists, anyLociKnown, predictedOutcome, predictionAccuracyRangesMap, sampleOffspringOutcomesList, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringNumericTraitAnalysis(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap) + if (err != nil) { return nil, err } + if (neuralNetworkExists == false){ + return nil, errors.New("GetOffspringNumericTraitAnalysis claiming that neural network doesn't exist when we already checked.") } - closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2) + predictedOutcomeLabel, err := getPredictedOutcomeLabel(anyLociKnown, predictedOutcome) + if (err != nil) { return nil, err } + predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel) - closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage) + offspringPredictedOutcomeColumn.Add(predictedOutcomeLabelCentered) - confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "% Confidence)" + confidenceRangeLabel, err := getConfidenceRangeLabel(traitObject, anyLociKnown, predictionAccuracyRangesMap) + if (err != nil) { return nil, err } - confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted) + confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel) - return confidenceRangeLabel, nil + predictionConfidenceRangeColumn.Add(confidenceRangeLabelCentered) + + viewSampleOffspringsButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ + setViewNumericTraitSampleOffspringOutcomesChart(window, traitName, sampleOffspringOutcomesList, quantityOfLociKnown, currentPage) + }) + + viewSampleOffspringsButtonsColumn.Add(viewSampleOffspringsButton) } - confidenceRangeLabel, err := getConfidenceRangeLabel() - if (err != nil) { return nil, err } - - confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel) - - totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci) - - quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown) - - quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString - - quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted) - viewTraitDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ //TODO showUnderConstructionDialog(window) @@ -4670,76 +4350,46 @@ func setViewMateProfilePage_NumericGeneticTraits(window fyne.Window, userOrOffsp traitNameColumn.Add(traitNameText) - if (userOrOffspring == "User"){ - userPredictedOutcomeColumn.Add(predictedOutcomeLabelCentered) - } else { - offspringPredictedOutcomeColumn.Add(predictedOutcomeLabelCentered) - } - - predictionConfidenceRangeColumn.Add(confidenceRangeLabelCentered) - quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel) viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton) traitNameColumn.Add(widget.NewSeparator()) userPredictedOutcomeColumn.Add(widget.NewSeparator()) offspringPredictedOutcomeColumn.Add(widget.NewSeparator()) predictionConfidenceRangeColumn.Add(widget.NewSeparator()) - quantityOfLociKnownColumn.Add(widget.NewSeparator()) + viewSampleOffspringsButtonsColumn.Add(widget.NewSeparator()) viewTraitDetailsButtonsColumn.Add(widget.NewSeparator()) } - if (userOrOffspring == "User"){ + predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - - //TODO - showUnderConstructionDialog(window) - }) - - userPredictedOutcomeColumn.Add(predictedOutcomeHelpButton) - - } else { - // userOrOffspring == "Offspring" - - predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - - //TODO - showUnderConstructionDialog(window) - }) - - offspringPredictedOutcomeColumn.Add(predictedOutcomeHelpButton) - } - - predictionConfidenceRangeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ - - //TODO - showUnderConstructionDialog(window) - }) - - predictionConfidenceRangeColumn.Add(predictionConfidenceRangeHelpButton) - - quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ //TODO showUnderConstructionDialog(window) }) - quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton) + predictionConfidenceRangeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ + //TODO + showUnderConstructionDialog(window) + }) + + predictionConfidenceRangeColumn.Add(predictionConfidenceRangeHelpButton) traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn) if (userOrOffspring == "User"){ - + userPredictedOutcomeColumn.Add(predictedOutcomeHelpButton) traitsInfoGrid.Add(userPredictedOutcomeColumn) - } else { - // userOrOffspring == "Offspring" - + offspringPredictedOutcomeColumn.Add(predictedOutcomeHelpButton) traitsInfoGrid.Add(offspringPredictedOutcomeColumn) } traitsInfoGrid.Add(predictionConfidenceRangeColumn) - traitsInfoGrid.Add(quantityOfLociKnownColumn) + + if (userOrOffspring == "Offspring"){ + traitsInfoGrid.Add(viewSampleOffspringsButtonsColumn) + } + traitsInfoGrid.Add(viewTraitDetailsButtonsColumn) traitsInfoGrid.Add(layout.NewSpacer()) diff --git a/internal/generate/generate.go b/internal/generate/generate.go index 141d4c8..068c292 100644 --- a/internal/generate/generate.go +++ b/internal/generate/generate.go @@ -945,9 +945,7 @@ func GetFakeProfile(profileType string, identityPublicKey [32]byte, identityPriv diseaseLociList := diseaseObject.LociList - for _, locusObject := range diseaseLociList{ - - locusRSID := locusObject.LocusRSID + for _, locusRSID := range diseaseLociList{ shareableRSIDsMap[locusRSID] = struct{}{} } diff --git a/internal/genetics/createCoupleGeneticAnalysis/createCoupleGeneticAnalysis.go b/internal/genetics/createCoupleGeneticAnalysis/createCoupleGeneticAnalysis.go index 7dfbd75..da8cfb0 100644 --- a/internal/genetics/createCoupleGeneticAnalysis/createCoupleGeneticAnalysis.go +++ b/internal/genetics/createCoupleGeneticAnalysis/createCoupleGeneticAnalysis.go @@ -27,7 +27,6 @@ import "seekia/internal/helpers" import "errors" import mathRand "math/rand/v2" -import "slices" import "maps" import "reflect" @@ -512,7 +511,6 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom for _, diseaseObject := range polygenicDiseaseObjectsList{ diseaseName := diseaseObject.DiseaseName - diseaseLociList := diseaseObject.LociList // This map stores the polygenic disease info for each genome pair // Map Structure: Genome Pair Identifier -> OffspringGenomePairPolygenicDiseaseInfo @@ -532,9 +530,13 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom return errors.New("addGenomePairDiseaseInfoToDiseaseMap called with unknown person2GenomeIdentifier.") } - anyOffspringLocusTested, genomePairOffspringAverageRiskScore, quantityOfLociTested, genomePairOffspringDiseaseLociInfoMap, genomePairSampleOffspringRiskScoresList, err := GetOffspringPolygenicDiseaseInfo(diseaseLociList, person1LocusValuesMap, person2LocusValuesMap) + neuralNetworkExists, anyOffspringLocusKnown, offspringAverageRiskScore, accuracyRangesMap, predictedRiskScoresList, quantityOfLociKnown, quantityOfParentalPhasedLoci, err := GetOffspringPolygenicDiseaseAnalysis(diseaseObject, person1LocusValuesMap, person2LocusValuesMap) if (err != nil) { return err } - if (anyOffspringLocusTested == false){ + if (neuralNetworkExists == false){ + // We cannot analyze this disease + return nil + } + if (anyOffspringLocusKnown == false){ // 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 @@ -542,10 +544,11 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom newOffspringGenomePairPolygenicDiseaseInfo := geneticAnalysis.OffspringGenomePairPolygenicDiseaseInfo{ - QuantityOfLociTested: quantityOfLociTested, - OffspringAverageRiskScore: genomePairOffspringAverageRiskScore, - LociInfoMap: genomePairOffspringDiseaseLociInfoMap, - SampleOffspringRiskScoresList: genomePairSampleOffspringRiskScoresList, + OffspringAverageRiskScore: offspringAverageRiskScore, + PredictionConfidenceRangesMap: accuracyRangesMap, + QuantityOfLociKnown: quantityOfLociKnown, + QuantityOfParentalPhasedLoci: quantityOfParentalPhasedLoci, + SampleOffspringRiskScoresList: predictedRiskScoresList, } genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier) @@ -564,6 +567,11 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom if (err != nil) { return false, "", err } } + if (len(offspringPolygenicDiseaseInfoMap) == 0){ + // No disease analysis was performed + continue + } + newOffspringPolygenicDiseaseInfoObject := geneticAnalysis.OffspringPolygenicDiseaseInfo{ PolygenicDiseaseInfoMap: offspringPolygenicDiseaseInfoMap, } @@ -644,7 +652,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom newOffspringGenomePairTraitInfo := geneticAnalysis.OffspringGenomePairDiscreteTraitInfo{} - neuralNetworkExists, neuralNetworkAnalysisExists, outcomeProbabilitiesMap, averagePredictionConfidence, quantityOfLociTested, quantityOfParentalPhasedLoci, err := GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject, person1LocusValuesMap, person2LocusValuesMap) + neuralNetworkExists, neuralNetworkAnalysisExists, outcomeProbabilitiesMap, averagePredictionConfidence, quantityOfLociTested, quantityOfParentalPhasedLoci, err := GetOffspringDiscreteTraitAnalysis_NeuralNetwork(traitObject, person1LocusValuesMap, person2LocusValuesMap) if (err != nil) { return err } if (neuralNetworkExists == true){ @@ -665,7 +673,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom } } - anyRulesExist, rulesAnalysisExists, quantityOfRulesTested, quantityOfLociKnown, offspringProbabilityOfPassingRulesMap, offspringOutcomeProbabilitiesMap, err := GetOffspringDiscreteTraitInfo_Rules(traitObject, person1LocusValuesMap, person2LocusValuesMap) + anyRulesExist, rulesAnalysisExists, quantityOfRulesTested, quantityOfLociKnown, offspringProbabilityOfPassingRulesMap, offspringOutcomeProbabilitiesMap, err := GetOffspringDiscreteTraitAnalysis_Rules(traitObject, person1LocusValuesMap, person2LocusValuesMap) if (err != nil) { return err } if (anyRulesExist == true){ @@ -764,7 +772,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom return errors.New("addGenomePairTraitInfoToOffspringMap called with unknown person2GenomeIdentifier.") } - neuralNetworkExists, neuralNetworkAnalysisExists, averageOutcome, predictionConfidenceRangesMap, sampleOffspringOutcomesList, quantityOfLociTested, quantityOfParentalPhasedLoci, err := GetOffspringNumericTraitInfo(traitObject, person1LocusValuesMap, person2LocusValuesMap) + neuralNetworkExists, neuralNetworkAnalysisExists, averageOutcome, predictionConfidenceRangesMap, sampleOffspringOutcomesList, quantityOfLociTested, quantityOfParentalPhasedLoci, err := GetOffspringNumericTraitAnalysis(traitObject, person1LocusValuesMap, person2LocusValuesMap) if (err != nil) { return err } if (neuralNetworkExists == false){ // Predictions are not possible for this trait @@ -951,288 +959,109 @@ func GetOffspringMonogenicDiseaseProbabilities(dominantOrRecessive string, perso return true, percentageProbabilityOffspringHasDiseaseInt, true, percentageProbabilityOffspringHasVariantInt, nil } - -// This is used to calculate user polygenic disease info for users -// It is faster to do it this way, because we don't create 100 prospective offspring -// We instead create 4 outcomes for each locus -// We can do this because testing each locus's risk score is independent of every other locus -// This is not true for traits, because trait rules are effected by multiple different loci -// When using the fast method for polygenic diseases, we don't get a sample of 100 offspring disease risk scores. -// The average risk score should still be the same for the fast and normal methods -// This function is also faster because we don't calculate odds ratio information or information about each locus //Outputs: -// -bool: Any loci tested (if false, no offspring polygenic disease information is known) -// -int: Offspring Risk Score (Value between 0-10) -// -int: Number of loci tested -// -error -func GetOffspringPolygenicDiseaseInfo_Fast(diseaseLociList []polygenicDiseases.DiseaseLocus, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, int, int, error){ - - if (len(person1LocusValuesMap) == 0){ - return false, 0, 0, nil - } - if (len(person2LocusValuesMap) == 0){ - return false, 0, 0, nil - } - - // I = Insertion - // D = Deletion - - validAllelesList := []string{"C", "A", "T", "G", "I", "D"} - - numberOfLociTested := 0 - - offspringSummedRiskWeights := 0 - offspringMinimumPossibleRiskWeightSum := 0 - offspringMaximumPossibleRiskWeightSum := 0 - - for _, locusObject := range diseaseLociList{ - - locusRSID := locusObject.LocusRSID - locusRiskWeightsMap := locusObject.RiskWeightsMap - locusMinimumWeight := locusObject.MinimumRiskWeight - locusMaximumWeight := locusObject.MaximumRiskWeight - - person1LocusValueFound, person1LocusBase1Value, person1LocusBase2Value, _, _, err := createPersonGeneticAnalysis.GetLocusValueFromGenomeMap(true, person1LocusValuesMap, locusRSID) - if (err != nil) { return false, 0, 0, err } - if (person1LocusValueFound == false){ - // None of the offspring will have a value for this locus - continue - } - - person2LocusValueFound, person2LocusBase1Value, person2LocusBase2Value, _, _, err := createPersonGeneticAnalysis.GetLocusValueFromGenomeMap(true, person2LocusValuesMap, locusRSID) - if (err != nil) { return false, 0, 0, err } - if (person2LocusValueFound == false){ - // None of the offspring will have a value for this locus - continue - } - - baseIsValid := slices.Contains(validAllelesList, person1LocusBase1Value) - if (baseIsValid == false){ - return false, 0, 0, errors.New("GetOffspringPolygenicDiseaseInfo_Fast called with genomeMap containing invalid locus value base: " + person1LocusBase1Value) - } - baseIsValid = slices.Contains(validAllelesList, person1LocusBase2Value) - if (baseIsValid == false){ - return false, 0, 0, errors.New("GetOffspringPolygenicDiseaseInfo_Fast called with genomeMap containing invalid locus value base: " + person1LocusBase2Value) - } - baseIsValid = slices.Contains(validAllelesList, person2LocusBase1Value) - if (baseIsValid == false){ - return false, 0, 0, errors.New("GetOffspringPolygenicDiseaseInfo_Fast called with genomeMap containing invalid locus value base: " + person2LocusBase1Value) - } - baseIsValid = slices.Contains(validAllelesList, person2LocusBase2Value) - if (baseIsValid == false){ - return false, 0, 0, errors.New("GetOffspringPolygenicDiseaseInfo_Fast called with genomeMap containing invalid locus value base: " + person2LocusBase2Value) - } - - numberOfLociTested += 1 - - offspringBasePairOutcome1 := person1LocusBase1Value + ";" + person2LocusBase1Value - offspringBasePairOutcome2 := person1LocusBase2Value + ";" + person2LocusBase2Value - offspringBasePairOutcome3 := person1LocusBase1Value + ";" + person2LocusBase2Value - offspringBasePairOutcome4 := person1LocusBase2Value + ";" + person2LocusBase1Value - - baseOutcomesList := []string{offspringBasePairOutcome1, offspringBasePairOutcome2, offspringBasePairOutcome3, offspringBasePairOutcome4} - - outcomesSummedRiskWeight := 0 - - for _, outcomeBasePair := range baseOutcomesList{ - - offspringOutcomeRiskWeight, exists := locusRiskWeightsMap[outcomeBasePair] - if (exists == false){ - // We do not know the risk weight for this base pair - // We treat this as a 0 risk weight - continue - } - - outcomesSummedRiskWeight += offspringOutcomeRiskWeight - } - - locusAverageRiskWeight := outcomesSummedRiskWeight/4 - - offspringSummedRiskWeights += locusAverageRiskWeight - - offspringMinimumPossibleRiskWeightSum += locusMinimumWeight - offspringMaximumPossibleRiskWeightSum += locusMaximumWeight - } - - offspringAverageDiseaseRiskScore, err := helpers.ScaleIntProportionally(true, offspringSummedRiskWeights, offspringMinimumPossibleRiskWeightSum, offspringMaximumPossibleRiskWeightSum, 0, 10) - if (err != nil) { return false, 0, 0, err } - - if (numberOfLociTested == 0){ - // No locations were tested - return false, 0, 0, nil - } - - return true, offspringAverageDiseaseRiskScore, numberOfLociTested, nil -} - - -//Outputs: -// -bool: Any loci tested (if false, no offspring polygenic disease information is known) -// -int: Offspring Risk Score (Value between 0-10) -// -int: Number of loci tested -// -map[[3]byte]geneticAnalysis.OffspringPolygenicDiseaseLocusInfo: Offspring Locus information map -// Map Structure: Locus identifier -> OffspringPolygenicDiseaseLocusInfo +// -bool: A neural network exists for this trait +// -bool: Any loci tested (if false, no offspring polygenic disease analysis is known) +// -int: Offspring Average Risk Score (Value between 0-10) +// -map[int]float64: Prediction accuracy ranges map +// -Map Structure: Probability prediction is accurate (X) -> Distance from prediction that must be travelled in both directions to +// create a range in which the true value will fall into, X% of the time // -[]int: Sample offspring risks scores list +// -int: Quantity of loci known +// -int: Quantity of parental phased loci // -error -func GetOffspringPolygenicDiseaseInfo(diseaseLociList []polygenicDiseases.DiseaseLocus, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, int, int, map[[3]byte]geneticAnalysis.OffspringPolygenicDiseaseLocusInfo, []int, error){ +func GetOffspringPolygenicDiseaseAnalysis(diseaseObject polygenicDiseases.PolygenicDisease, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, int, map[int]float64, []int, int, int, error){ + + diseaseName := diseaseObject.DiseaseName + + modelExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(diseaseName) + if (modelExists == false){ + // Prediction is not possible for this trait + return false, false, 0, nil, nil, 0, 0, nil + } if (len(person1LocusValuesMap) == 0){ - return false, 0, 0, nil, nil, nil + return true, false, 0, nil, nil, 0, 0, nil } if (len(person2LocusValuesMap) == 0){ - return false, 0, 0, nil, nil, nil + return true, false, 0, nil, nil, 0, 0, nil } - // First, we create 100 prospective offspring genomes. + diseaseLociList := diseaseObject.LociList - diseaseLociRSIDsList := make([]int64, 0) + // First we count up the quantity of parental phased loci + // We only count the quantity of phased loci for loci which are known for both parents - for _, diseaseLocusObject := range diseaseLociList{ + quantityOfParentalPhasedLoci := 0 - locusRSID := diseaseLocusObject.LocusRSID - diseaseLociRSIDsList = append(diseaseLociRSIDsList, locusRSID) + for _, rsID := range diseaseLociList{ + + person1LocusValue, exists := person1LocusValuesMap[rsID] + if (exists == false){ + continue + } + + person2LocusValue, exists := person2LocusValuesMap[rsID] + if (exists == false){ + continue + } + + person1LocusIsPhased := person1LocusValue.LocusIsPhased + if (person1LocusIsPhased == true){ + quantityOfParentalPhasedLoci += 1 + } + + person2LocusIsPhased := person2LocusValue.LocusIsPhased + if (person2LocusIsPhased == true){ + quantityOfParentalPhasedLoci += 1 + } } - anyLocusValueExists, prospectiveOffspringGenomesList, err := getProspectiveOffspringGenomesList(diseaseLociRSIDsList, person1LocusValuesMap, person2LocusValuesMap) - if (err != nil) { return false, 0, 0, nil, nil, err } + // We create 100 prospective offspring genomes. + + anyLocusValueExists, prospectiveOffspringGenomesList, err := getProspectiveOffspringGenomesList(diseaseLociList, person1LocusValuesMap, person2LocusValuesMap) + if (err != nil) { return false, false, 0, nil, nil, 0, 0, err } if (anyLocusValueExists == false){ - return false, 0, 0, nil, nil, nil + return true, false, 0, nil, nil, 0, 0, nil } - // This will sum every offspring's average disease risk score - offspringAverageRiskScoreSum := 0 + // A list of predicted risk scores for each offspring + predictedRiskScoresList := make([]int, 0) - // This stores a list of every prospective offspring's risk score - sampleOffspringRiskScoresList := make([]int, 0) + accuracyRangesMap := make(map[int]float64) + quantityOfLociTested := 0 - type offspringSummedLocusInfoObject struct{ + for index, offspringGenomeMap := range prospectiveOffspringGenomesList{ - SummedLocusRiskWeights int - - SummedOddsRatios float64 - - NumberOfSummedOddsRatios int - - // This is the number of unknown-odds-ratio-weight sums that we summed up - NumberOfUnknownOddsRatioWeightSums int - - // This is the sum of every unknown-odds-ratio-weight-sum for each prospective offspring for this genome - UnknownOddsRatioWeightSumsSummed int - } - - // Map Structure: Locus Identifier -> offspringSummedLocusInfoObject - offspringLocusInfoSumsMap := make(map[[3]byte]offspringSummedLocusInfoObject) - - for offspringIndex, offspringGenomeMap := range prospectiveOffspringGenomesList{ - - offspringSummedRiskWeights := 0 - offspringMinimumPossibleRiskWeightSum := 0 - offspringMaximumPossibleRiskWeightSum := 0 - - for _, locusObject := range diseaseLociList{ - - locusIdentifierHex := locusObject.LocusIdentifier - - locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) - if (err != nil) { return false, 0, 0, nil, nil, err } - - offspringLocusInfoSumsObject, exists := offspringLocusInfoSumsMap[locusIdentifier] - if (exists == false){ - - if (offspringIndex != 0){ - // We already checked a previous offspring for this locus, and it's value doesn't exist - continue - } - } - - locusRSID := locusObject.LocusRSID - locusRiskWeightsMap := locusObject.RiskWeightsMap - locusOddsRatiosMap := locusObject.OddsRatiosMap - locusMinimumWeight := locusObject.MinimumRiskWeight - locusMaximumWeight := locusObject.MaximumRiskWeight - - basePairValueFound, locusBase1Value, locusBase2Value, _, _, err := createPersonGeneticAnalysis.GetLocusValueFromGenomeMap(true, offspringGenomeMap, locusRSID) - if (err != nil) { return false, 0, 0, nil, nil, err } - if (basePairValueFound == false){ - // None of the offspring will have a value for this locus - continue - } - - locusRiskWeight, locusOddsRatioIsKnown, locusOddsRatio, err := createPersonGeneticAnalysis.GetGenomePolygenicDiseaseLocusRiskInfo(locusRiskWeightsMap, locusOddsRatiosMap, locusBase1Value, locusBase2Value) - if (err != nil) { return false, 0, 0, nil, nil, err } - - offspringLocusInfoSumsObject.SummedLocusRiskWeights += locusRiskWeight - - if (locusOddsRatioIsKnown == true){ - offspringLocusInfoSumsObject.SummedOddsRatios += locusOddsRatio - offspringLocusInfoSumsObject.NumberOfSummedOddsRatios += 1 - } else { - offspringLocusInfoSumsObject.UnknownOddsRatioWeightSumsSummed += locusRiskWeight - offspringLocusInfoSumsObject.NumberOfUnknownOddsRatioWeightSums += 1 - } - - offspringLocusInfoSumsMap[locusIdentifier] = offspringLocusInfoSumsObject - - offspringSummedRiskWeights += locusRiskWeight - - offspringMinimumPossibleRiskWeightSum += locusMinimumWeight - offspringMaximumPossibleRiskWeightSum += locusMaximumWeight + neuralNetworkExists, predictionIsKnown, predictedRiskScore, predictionAccuracyRangesMap, currentQuantityOfLociTested, _, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseAnalysis(diseaseObject, offspringGenomeMap, false) + if (err != nil){ return false, false, 0, nil, nil, 0, 0, err } + if (neuralNetworkExists == false){ + return false, false, 0, nil, nil, 0, 0, errors.New("GetGenomeNumericTraitAnalysis claiming that neural network doesn't exist when we already checked.") + } + if (predictionIsKnown == false){ + return false, false, 0, nil, nil, 0, 0, errors.New("GetGenomeNumericTraitAnalysis claiming that prediction is impossible when we already know at least 1 locus value exists for trait.") } - offspringAverageDiseaseRiskScore, err := helpers.ScaleIntProportionally(true, offspringSummedRiskWeights, offspringMinimumPossibleRiskWeightSum, offspringMaximumPossibleRiskWeightSum, 0, 10) - if (err != nil) { return false, 0, 0, nil, nil, err } + predictedRiskScoresList = append(predictedRiskScoresList, predictedRiskScore) - sampleOffspringRiskScoresList = append(sampleOffspringRiskScoresList, offspringAverageDiseaseRiskScore) - - offspringAverageRiskScoreSum += offspringAverageDiseaseRiskScore + if (index == 0){ + // These values should be the same for each predicted offspring + accuracyRangesMap = predictionAccuracyRangesMap + quantityOfLociTested = currentQuantityOfLociTested + } } - numberOfLociTested := len(offspringLocusInfoSumsMap) + // We calculate the average predicted risk score - if (numberOfLociTested == 0){ - // No locations were tested - return false, 0, 0, nil, nil, nil + outcomesSum := 0 + + for _, predictedRiskScore := range predictedRiskScoresList{ + outcomesSum += predictedRiskScore } - offspringAverageRiskScore := offspringAverageRiskScoreSum/100 + averageRiskScore := outcomesSum/100 - // Map Structure: Locus Identifier -> OffspringPolygenicDiseaseLocusInfo - offspringDiseaseLociInfoMap := make(map[[3]byte]geneticAnalysis.OffspringPolygenicDiseaseLocusInfo) - - for locusIdentifier, summedLocusInfoObject := range offspringLocusInfoSumsMap{ - - summedLocusRiskWeights := summedLocusInfoObject.SummedLocusRiskWeights - summedOddsRatios := summedLocusInfoObject.SummedOddsRatios - numberOfSummedOddsRatios := summedLocusInfoObject.NumberOfSummedOddsRatios - numberOfUnknownOddsRatioWeightSums := summedLocusInfoObject.NumberOfUnknownOddsRatioWeightSums - unknownOddsRatioWeightSumsSummed := summedLocusInfoObject.UnknownOddsRatioWeightSumsSummed - - // There are 100 prospective offspring, so we divide by 100 - locusAverageRiskWeight := summedLocusRiskWeights/100 - - newLocusInfoObject := geneticAnalysis.OffspringPolygenicDiseaseLocusInfo{ - OffspringAverageRiskWeight: locusAverageRiskWeight, - } - - if (numberOfSummedOddsRatios != 0){ - newLocusInfoObject.OffspringOddsRatioIsKnown = true - - offspringAverageOddsRatio := summedOddsRatios/float64(numberOfSummedOddsRatios) - - newLocusInfoObject.OffspringAverageOddsRatio = offspringAverageOddsRatio - } - - if (numberOfUnknownOddsRatioWeightSums != 0){ - - offspringAverageUnknownOddsRatiosWeightSum := unknownOddsRatioWeightSumsSummed/numberOfUnknownOddsRatioWeightSums - - newLocusInfoObject.OffspringAverageUnknownOddsRatiosWeightSum = offspringAverageUnknownOddsRatiosWeightSum - } - - offspringDiseaseLociInfoMap[locusIdentifier] = newLocusInfoObject - } - - return true, offspringAverageRiskScore, numberOfLociTested, offspringDiseaseLociInfoMap, sampleOffspringRiskScoresList, nil + return true, true, averageRiskScore, accuracyRangesMap, predictedRiskScoresList, quantityOfLociTested, quantityOfParentalPhasedLoci, nil } @@ -1245,13 +1074,13 @@ func GetOffspringPolygenicDiseaseInfo(diseaseLociList []polygenicDiseases.Diseas // -int: Quantity of loci tested // -int: Quantity of parental phased loci // -error -func GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, map[string]int, int, int, int, error){ +func GetOffspringDiscreteTraitAnalysis_NeuralNetwork(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, map[string]int, int, int, int, error){ traitName := traitObject.TraitName traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric if (traitIsDiscreteOrNumeric != "Discrete"){ - return false, false, nil, 0, 0, 0, errors.New("GetOffspringDiscreteTraitInfo_NeuralNetwork called with non-discrete trait.") + return false, false, nil, 0, 0, 0, errors.New("GetOffspringDiscreteTraitAnalysis_NeuralNetwork called with non-discrete trait.") } modelExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName) @@ -1343,7 +1172,7 @@ func GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject traits.Trait, perso // -map[string]int: Offspring outcome probabilities map // Map Structure: Outcome Name -> Offspring probability of outcome (0-100) // -error -func GetOffspringDiscreteTraitInfo_Rules(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, int, int, map[[3]byte]int, map[string]int, error){ +func GetOffspringDiscreteTraitAnalysis_Rules(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, int, int, map[[3]byte]int, map[string]int, error){ traitRulesList := traitObject.RulesList @@ -1421,7 +1250,6 @@ func GetOffspringDiscreteTraitInfo_Rules(traitObject traits.Trait, person1LocusV } - //Outputs: // -bool: A neural network exists for this trait // -bool: Analysis exists (at least 1 locus exists for this analysis from both people's genomes @@ -1433,13 +1261,13 @@ func GetOffspringDiscreteTraitInfo_Rules(traitObject traits.Trait, person1LocusV // -int: Quantity of loci known // -int: Quantity of parental phased loci // -error -func GetOffspringNumericTraitInfo(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, float64, map[int]float64, []float64, int, int, error){ +func GetOffspringNumericTraitAnalysis(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, float64, map[int]float64, []float64, int, int, error){ traitName := traitObject.TraitName traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric if (traitIsDiscreteOrNumeric != "Numeric"){ - return false, false, 0, nil, nil, 0, 0, errors.New("GetOffspringNumericTraitInfo called with non-numeric trait.") + return false, false, 0, nil, nil, 0, 0, errors.New("GetOffspringNumericTraitAnalysis called with non-numeric trait.") } modelExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName) diff --git a/internal/genetics/createPersonGeneticAnalysis/createPersonGeneticAnalysis.go b/internal/genetics/createPersonGeneticAnalysis/createPersonGeneticAnalysis.go index 43cf8fd..7e4a318 100644 --- a/internal/genetics/createPersonGeneticAnalysis/createPersonGeneticAnalysis.go +++ b/internal/genetics/createPersonGeneticAnalysis/createPersonGeneticAnalysis.go @@ -8,10 +8,6 @@ package createPersonGeneticAnalysis // Disclaimer: I am a novice in the ways of genetics. This package could be flawed in numerous ways. -// 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 - // 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. @@ -667,80 +663,6 @@ func GetPersonMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw return personMonogenicDiseaseInfoObject, nil } - -//Outputs: -// -bool: Any loci tested -// -int: Person genome risk score (value between 0-10) -// -int: Person Genome Number of loci tested -// -map[[3]byte]geneticAnalysis.PersonGenomePolygenicDiseaseLocusInfo: Person disease locus info map -// Map Structure: Locus Identifier -> PersonGenomePolygenicDiseaseLocusInfo -// -error -func GetPersonGenomePolygenicDiseaseInfo(diseaseLociList []polygenicDiseases.DiseaseLocus, personLocusValuesMap map[int64]locusValue.LocusValue, lookForLocusAliases bool)(bool, int, int, map[[3]byte]geneticAnalysis.PersonGenomePolygenicDiseaseLocusInfo, error){ - - if (len(personLocusValuesMap) == 0){ - return false, 0, 0, nil, nil - } - - // Map Structure: Locus Identifier -> PersonGenomePolygenicDiseaseLocusInfo - genomeLociInfoMap := make(map[[3]byte]geneticAnalysis.PersonGenomePolygenicDiseaseLocusInfo) - - summedDiseaseRiskWeight := 0 - - minimumPossibleRiskWeightSum := 0 - maximumPossibleRiskWeightSum := 0 - - for _, locusObject := range diseaseLociList{ - - locusRSID := locusObject.LocusRSID - locusRiskWeightsMap := locusObject.RiskWeightsMap - locusOddsRatiosMap := locusObject.OddsRatiosMap - locusMinimumWeight := locusObject.MinimumRiskWeight - locusMaximumWeight := locusObject.MaximumRiskWeight - - locusValueFound, locusBase1Value, locusBase2Value, _, _, err := GetLocusValueFromGenomeMap(lookForLocusAliases, personLocusValuesMap, locusRSID) - if (err != nil) { return false, 0, 0, nil, err } - if (locusValueFound == false){ - continue - } - - locusRiskWeight, locusOddsRatioIsKnown, locusOddsRatio, err := GetGenomePolygenicDiseaseLocusRiskInfo(locusRiskWeightsMap, locusOddsRatiosMap, locusBase1Value, locusBase2Value) - if (err != nil) { return false, 0, 0, nil, err } - - newLocusInfoObject := geneticAnalysis.PersonGenomePolygenicDiseaseLocusInfo{ - RiskWeight: locusRiskWeight, - OddsRatioIsKnown: locusOddsRatioIsKnown, - } - - if (locusOddsRatioIsKnown == true){ - newLocusInfoObject.OddsRatio = locusOddsRatio - } - - locusIdentifierHex := locusObject.LocusIdentifier - - locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) - if (err != nil) { return false, 0, 0, nil, err } - - genomeLociInfoMap[locusIdentifier] = newLocusInfoObject - - minimumPossibleRiskWeightSum += locusMinimumWeight - maximumPossibleRiskWeightSum += locusMaximumWeight - - summedDiseaseRiskWeight += locusRiskWeight - } - - numberOfLociTested := len(genomeLociInfoMap) - if (numberOfLociTested == 0){ - // We have no information about this disease for this genome - return false, 0, 0, nil, nil - } - - diseaseRiskScore, err := helpers.ScaleIntProportionally(true, summedDiseaseRiskWeight, minimumPossibleRiskWeightSum, maximumPossibleRiskWeightSum, 0, 10) - if (err != nil) { return false, 0, 0, nil, err } - - return true, diseaseRiskScore, numberOfLociTested, genomeLociInfoMap, nil -} - - //Outputs: // -geneticAnalysis.PersonPolygenicDiseaseInfo // -error @@ -749,8 +671,6 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw // We use this when returning errors emptyDiseaseInfoObject := geneticAnalysis.PersonPolygenicDiseaseInfo{} - diseaseLociList := diseaseObject.LociList - // 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) @@ -762,33 +682,17 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier genomeMap := genomeWithMetadataObject.GenomeMap - // This map stores the loci for this disease and does not contain loci which do not belong to this disease - // Map Structure: rsID -> Locus Value - genomeLocusValuesMap := make(map[int64]locusValue.LocusValue) - - for _, locusObject := range diseaseLociList{ - - locusRSID := locusObject.LocusRSID - - locusValueFound, _, _, _, locusValueObject, err := GetLocusValueFromGenomeMap(true, genomeMap, locusRSID) - if (err != nil) { return emptyDiseaseInfoObject, err } - if (locusValueFound == false){ - continue - } - - genomeLocusValuesMap[locusRSID] = locusValueObject - } - - anyLociTested, personDiseaseRiskScore, genomeNumberOfLociTested, genomeLociInfoMap, err := GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, genomeLocusValuesMap, true) + neuralNetworkExists, anyLociTested, personDiseaseRiskScore, predictionAccuracyRangesMap, genomeQuantityOfLociKnown, genomeQuantityOfPhasedLoci, err := GetPersonGenomePolygenicDiseaseAnalysis(diseaseObject, genomeMap, true) if (err != nil) { return emptyDiseaseInfoObject, err } - if (anyLociTested == false){ + if (neuralNetworkExists == false || anyLociTested == false){ continue } newDiseaseInfoObject := geneticAnalysis.PersonGenomePolygenicDiseaseInfo{ - QuantityOfLociTested: genomeNumberOfLociTested, RiskScore: personDiseaseRiskScore, - LociInfoMap: genomeLociInfoMap, + ConfidenceRangesMap: predictionAccuracyRangesMap, + QuantityOfLociKnown: genomeQuantityOfLociKnown, + QuantityOfPhasedLoci: genomeQuantityOfPhasedLoci, } personPolygenicDiseaseInfoMap[genomeIdentifier] = newDiseaseInfoObject @@ -810,75 +714,20 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw // First we check to see if any of the genomes have different risk scores or NumberOfLociTested - genomeRiskScore := 0 - genomeNumberOfLociTested := 0 + personGenomePolygenicDiseaseInfoObject := geneticAnalysis.PersonGenomePolygenicDiseaseInfo{} firstItemReached := false for _, personGenomeDiseaseInfoObject := range personPolygenicDiseaseInfoMap{ - currentGenomeRiskScore := personGenomeDiseaseInfoObject.RiskScore - currentGenomeNumberOfLociTested := personGenomeDiseaseInfoObject.QuantityOfLociTested - if (firstItemReached == false){ - genomeRiskScore = currentGenomeRiskScore - genomeNumberOfLociTested = currentGenomeNumberOfLociTested + personGenomePolygenicDiseaseInfoObject = personGenomeDiseaseInfoObject 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){ - if (firstItemReached == true){ - // A previous genome has information for this locus, and the current one does not - return true, nil - } - continue - } - - 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 + areEqual := reflect.DeepEqual(personGenomeDiseaseInfoObject, personGenomePolygenicDiseaseInfoObject) + if (areEqual == false){ return true, nil } } @@ -895,6 +744,67 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw } +//Outputs: +// -bool: Neural network exists for disease +// -bool: Any loci tested +// -int: Person genome risk score (value between 0-10) +// -map[int]float64: Confidence ranges map +// -If we want to know how accurate the prediction is with a X% accuracy, how far would we have to expand the +// risk score's range to be accurate, X% of the time? +// -Map Structure: Percentage -> Distance to travel in both directions of prediction +// -int: Person Genome quantity of loci known +// -int: Person genome quantity of phased loci +// -error +func GetPersonGenomePolygenicDiseaseAnalysis(diseaseObject polygenicDiseases.PolygenicDisease, personGenomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, bool, int, map[int]float64, int, int, error){ + + diseaseLociList := diseaseObject.LociList + + getGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){ + + if (checkForAliases == false){ + // We don't need to check for rsID aliases. + return personGenomeMap, nil + } + + // This map contains the locus values for the genome + // If a 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 diseaseLociList{ + + locusBasePairKnown, _, _, _, locusValueObject, err := GetLocusValueFromGenomeMap(checkForAliases, personGenomeMap, locusRSID) + if (err != nil) { return nil, err } + if (locusBasePairKnown == false){ + continue + } + + genomeLocusValuesMap[locusRSID] = locusValueObject + } + + return genomeLocusValuesMap, nil + } + + genomeLocusValuesMap, err := getGenomeLocusValuesMap() + if (err != nil) { return false, false, 0, nil, 0, 0, err } + + diseaseName := diseaseObject.DiseaseName + + neuralNetworkModelExists, riskScorePredictionIsPossible, predictedRiskScore, predictionAccuracyRangesMap, quantityOfLociKnown, quantityOfPhasedLoci, err := geneticPrediction.GetNeuralNetworkNumericAttributePredictionFromGenomeMap(diseaseName, diseaseLociList, genomeLocusValuesMap) + if (err != nil) { return false, false, 0, nil, 0, 0, err } + if (neuralNetworkModelExists == false){ + return false, false, 0, nil, 0, 0, nil + } + if (riskScorePredictionIsPossible == false){ + return true, false, 0, nil, 0, 0, nil + } + + predictedRiskScoreInt := int(predictedRiskScore) + + return true, true, predictedRiskScoreInt, predictionAccuracyRangesMap, quantityOfLociKnown, quantityOfPhasedLoci, nil +} + + //Outputs: // -geneticAnalysis.PersonDiscreteTraitInfo: Trait analysis object // -error @@ -1073,35 +983,6 @@ func GetPersonNumericTraitAnalysis(inputGenomesWithMetadataList []prepareRawGeno return newPersonTraitInfoObject, nil } - -//Outputs: -// -int: Base pair disease locus risk weight -// -bool: Base pair disease locus odds ratio known -// -float64: Base pair disease locus odds ratio -// -error -func GetGenomePolygenicDiseaseLocusRiskInfo(locusRiskWeightsMap map[string]int, locusOddsRatiosMap map[string]float64, locusBase1Value string, locusBase2Value string)(int, bool, float64, error){ - - 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 -} - // We use this to generate discrete trait predictions using a neural network // The alternative prediction method is to use Rules (see GetGenomeTraitAnalysis_Rules) //Outputs: @@ -1357,6 +1238,8 @@ func GetGenomePassesDiscreteTraitRuleStatus(ruleLociList []traits.RuleLocus, gen // -error func GetGenomeNumericTraitAnalysis(traitObject traits.Trait, genomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, bool, float64, map[int]float64, int, int, error){ + traitLociList := traitObject.LociList + getGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){ if (checkForAliases == false){ @@ -1364,8 +1247,6 @@ func GetGenomeNumericTraitAnalysis(traitObject traits.Trait, genomeMap map[int64 return genomeMap, nil } - traitLociList := traitObject.LociList - // This map contains the locus values for the genome // If a locus's entry doesn't exist, its value is unknown // Map Structure: Locus rsID -> Locus Value @@ -1390,7 +1271,7 @@ func GetGenomeNumericTraitAnalysis(traitObject traits.Trait, genomeMap map[int64 traitName := traitObject.TraitName - neuralNetworkModelExists, traitPredictionIsPossible, predictedOutcome, predictionAccuracyRangesMap, quantityOfLociKnown, quantityOfPhasedLoci, err := geneticPrediction.GetNeuralNetworkNumericTraitPredictionFromGenomeMap(traitName, genomeLocusValuesMap) + neuralNetworkModelExists, traitPredictionIsPossible, predictedOutcome, predictionAccuracyRangesMap, quantityOfLociKnown, quantityOfPhasedLoci, err := geneticPrediction.GetNeuralNetworkNumericAttributePredictionFromGenomeMap(traitName, traitLociList, genomeLocusValuesMap) if (err != nil) { return false, false, 0, nil, 0, 0, err } if (neuralNetworkModelExists == false){ return false, false, 0, nil, 0, 0, nil diff --git a/internal/genetics/geneticAnalysis/geneticAnalysis.go b/internal/genetics/geneticAnalysis/geneticAnalysis.go index 9677c8e..220a389 100644 --- a/internal/genetics/geneticAnalysis/geneticAnalysis.go +++ b/internal/genetics/geneticAnalysis/geneticAnalysis.go @@ -107,32 +107,21 @@ type PersonPolygenicDiseaseInfo struct{ type PersonGenomePolygenicDiseaseInfo struct{ - // This describes the quantity of loci tested for this disease - // This should be len(LociInfoMap) - QuantityOfLociTested 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 -} + // This map stores the confidence ranges for the predicted risk score + // If we want to know how accurate the prediction is with a X% accuracy, how far would we have to expand the + // risk score's range to be accurate, X% of the time? + // For example: 50% accuracy requires a +/-2 point range, 80% accuracy requires a +-5 point range + // Map Structure: Accuracy probability (0-100) -> Amount to add to value in both +/- directions so prediction is that accurate + ConfidenceRangesMap map[int]float64 -type PersonGenomePolygenicDiseaseLocusInfo struct{ + // This describes the quantity of loci tested for this disease + QuantityOfLociKnown int - // 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 + QuantityOfPhasedLoci int } @@ -325,7 +314,6 @@ type OffspringMonogenicDiseaseVariantInfo struct{ ProbabilityOf2MutationsUpperBound int } - type OffspringPolygenicDiseaseInfo struct{ // This map stores the polygenic disease info for each genome pair @@ -339,16 +327,23 @@ type OffspringPolygenicDiseaseInfo struct{ type OffspringGenomePairPolygenicDiseaseInfo struct{ - // This should be len(LociInfoMap) - QuantityOfLociTested int - // A number between 1-10 representing the offspring's average risk score // 1 == lowest risk, 10 == highest risk OffspringAverageRiskScore int - // A map of the offspring's locus information - // Map Structure: Locus Identifier -> OffspringPolygenicDiseaseLocusInfo - LociInfoMap map[[3]byte]OffspringPolygenicDiseaseLocusInfo + // This map stores the confidence ranges for the predicted risk score + // If we want to know how accurate the prediction is with a X% accuracy, how far would we have to expand the + // risk score's range to be accurate, X% of the time? + // For example: 50% accuracy requires a +/-2 point range, 80% accuracy requires a +-3 point range + // Map Structure: Accuracy probability (0-100) -> Amount to add to value in both +/- directions so prediction is that accurate + PredictionConfidenceRangesMap map[int]float64 + + QuantityOfLociKnown int + + // This describes the quantity of loci from both parents that are phased + // For example, if there are 10 loci for this trait, and one parent has 10 phased loci and the other has 5, + // this variable will have a value of 15 + QuantityOfParentalPhasedLoci int // This is a list of prospective offspring risk scores // This is useful for plotting on a graph to understand the standard deviation of risk @@ -356,27 +351,6 @@ type OffspringGenomePairPolygenicDiseaseInfo struct{ } -type OffspringPolygenicDiseaseLocusInfo struct{ - - // This is the offspring's average risk weight for this locus value - // A higher weight means a higher risk of the disease - OffspringAverageRiskWeight int - - // This is true if any of the 100 prospective offspring had a known odds ratio for this locus - 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 - OffspringAverageOddsRatio float64 - - // This is the average of the sum of weights for the loci which have no odds ratios for each prospective offspring - // 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 - OffspringAverageUnknownOddsRatiosWeightSum int -} - - type OffspringDiscreteTraitInfo struct{ // This map stores the trait info for each genome pair diff --git a/internal/genetics/geneticPrediction/geneticPrediction.go b/internal/genetics/geneticPrediction/geneticPrediction.go index 720b2d1..858657a 100644 --- a/internal/genetics/geneticPrediction/geneticPrediction.go +++ b/internal/genetics/geneticPrediction/geneticPrediction.go @@ -1,6 +1,6 @@ // geneticPrediction provides functions to train and query neural network models -// These models are currently used to predict traits such as eye color from user genome files +// These models are used to predict attributes such as eye color and autism from user genome files package geneticPrediction @@ -10,6 +10,7 @@ package geneticPrediction // Sorting matches by offspring total polygenic disease score will require inference on dozens of models for each match // We could create slower models that provide more accurate predictions +import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/resources/geneticReferences/traits" import "seekia/resources/geneticPredictionModels" @@ -48,7 +49,7 @@ type NeuralNetwork struct{ // and the OutputLayer is a column representing their phenotype, such as eye color type TrainingData struct{ - // InputLayer stores relevant rsID values for each trait from the user's genomes + // InputLayer stores relevant rsID values for each attribute from the user's genomes // It also stores if each rsID is phased and if each rsID exists InputLayer []float32 @@ -282,9 +283,9 @@ func DecodeBytesToDiscreteTraitPredictionAccuracyInfoMap(inputBytes []byte)(Disc return newDiscreteTraitPredictionAccuracyInfoMap, nil } -type NumericTraitPredictionAccuracyInfoMap map[NumericTraitPredictionInfo]NumericTraitPredictionAccuracyRangesMap +type NumericAttributePredictionAccuracyInfoMap map[NumericAttributePredictionInfo]NumericAttributePredictionAccuracyRangesMap -type NumericTraitPredictionInfo struct{ +type NumericAttributePredictionInfo struct{ // This is a value between 0-100 which describes the percentage of the loci which were tested for the input for the prediction PercentageOfLociTested int @@ -302,10 +303,10 @@ type NumericTraitPredictionInfo struct{ // the true height value will fall into this range 90% of the time. // -50%+: 20 centimeters // -10%+: 10 centimeters -type NumericTraitPredictionAccuracyRangesMap map[int]float64 +type NumericAttributePredictionAccuracyRangesMap map[int]float64 -func EncodeNumericTraitPredictionAccuracyInfoMapToBytes(inputMap NumericTraitPredictionAccuracyInfoMap)([]byte, error){ +func EncodeNumericAttributePredictionAccuracyInfoMapToBytes(inputMap NumericAttributePredictionAccuracyInfoMap)([]byte, error){ buffer := new(bytes.Buffer) @@ -319,22 +320,22 @@ func EncodeNumericTraitPredictionAccuracyInfoMapToBytes(inputMap NumericTraitPre return inputMapBytes, nil } -func DecodeBytesToNumericTraitPredictionAccuracyInfoMap(inputBytes []byte)(NumericTraitPredictionAccuracyInfoMap, error){ +func DecodeBytesToNumericAttributePredictionAccuracyInfoMap(inputBytes []byte)(NumericAttributePredictionAccuracyInfoMap, error){ if (inputBytes == nil){ - return nil, errors.New("DecodeBytesToNumericTraitPredictionAccuracyInfoMap called with nil inputBytes.") + return nil, errors.New("DecodeBytesToNumericAttributePredictionAccuracyInfoMap called with nil inputBytes.") } buffer := bytes.NewBuffer(inputBytes) decoder := gob.NewDecoder(buffer) - var newNumericTraitPredictionAccuracyInfoMap NumericTraitPredictionAccuracyInfoMap + var newNumericAttributePredictionAccuracyInfoMap NumericAttributePredictionAccuracyInfoMap - err := decoder.Decode(&newNumericTraitPredictionAccuracyInfoMap) + err := decoder.Decode(&newNumericAttributePredictionAccuracyInfoMap) if (err != nil){ return nil, err } - return newNumericTraitPredictionAccuracyInfoMap, nil + return newNumericAttributePredictionAccuracyInfoMap, nil } //Outputs: @@ -471,48 +472,37 @@ func GetNeuralNetworkDiscreteTraitPredictionFromGenomeMap(traitName string, geno return true, true, predictedOutcomeName, predictionAccuracy, quantityOfLociKnown, quantityOfPhasedLoci, nil } +// This function is used to predict numeric traits and polygenic disease risk scores //Outputs: -// -bool: Neural network model exists for this trait (trait prediction is possible for this trait) -// -bool: Trait prediction is possible for this user (User has at least 1 known trait locus value) -// -float64: Predicted trait outcome (Example: Height in centimeters) +// -bool: Neural network model exists for this attribute (neural network prediction is possible for this attribute) +// -bool: Attribute prediction is possible for this user (User has at least 1 known attribute locus value) +// -float64: Predicted attribute outcome (Example: Height in centimeters) // -map[int]float64: Accuracy ranges map // -Map Structure: Probability prediction is accurate (X) -> Distance from prediction that must be travelled in both directions to // create a range in which the true value will fall into, X% of the time // -int: Quantity of loci known // -int: Quantity of phased loci // -error -func GetNeuralNetworkNumericTraitPredictionFromGenomeMap(traitName string, genomeMap map[int64]locusValue.LocusValue)(bool, bool, float64, map[int]float64, int, int, error){ +func GetNeuralNetworkNumericAttributePredictionFromGenomeMap(attributeName string, attributeLociList []int64, genomeMap map[int64]locusValue.LocusValue)(bool, bool, float64, map[int]float64, int, int, error){ - traitObject, err := traits.GetTraitObject(traitName) - if (err != nil) { return false, false, 0, nil, 0, 0, err } - - traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric - if (traitIsDiscreteOrNumeric != "Numeric"){ - return false, false, 0, nil, 0, 0, errors.New("GetNeuralNetworkNumericTraitPredictionFromGenomeMap called with non-discrete trait: " + traitName) - } - - // This is a map of rsIDs which influence this trait - traitRSIDsList := traitObject.LociList - - if (len(traitRSIDsList) == 0){ - // Prediction is not possible for this trait - return false, false, 0, nil, 0, 0, nil - } - - predictionModelExists, predictionModelBytes := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName) + predictionModelExists, predictionModelBytes := geneticPredictionModels.GetGeneticPredictionModelBytes(attributeName) if (predictionModelExists == false){ - // Prediction is not possible for this trait + // Prediction is not possible for this attribute return false, false, 0, nil, 0, 0, nil } - traitRSIDsListCopy := slices.Clone(traitRSIDsList) - slices.Sort(traitRSIDsListCopy) + if (len(attributeLociList) == 0){ + return false, false, 0, nil, 0, 0, errors.New("GetNeuralNetworkNumericAttributePredictionFromGenomeMap called with empty attributeLociList for attribute with an existing neural network.") + } - neuralNetworkInput, quantityOfLociKnown, quantityOfPhasedLoci, err := createInputNeuralNetworkLayerFromGenomeMap(traitRSIDsListCopy, genomeMap) + attributeLociListCopy := slices.Clone(attributeLociList) + slices.Sort(attributeLociListCopy) + + neuralNetworkInput, quantityOfLociKnown, quantityOfPhasedLoci, err := createInputNeuralNetworkLayerFromGenomeMap(attributeLociListCopy, genomeMap) if (err != nil) { return false, false, 0, nil, 0, 0, err } if (quantityOfLociKnown == 0){ - // We can't predict anything about this trait for this genome + // We can't predict anything about this attribute for this genome return true, false, 0, nil, 0, 0, nil } @@ -522,25 +512,25 @@ func GetNeuralNetworkNumericTraitPredictionFromGenomeMap(traitName string, genom outputLayer, err := GetNeuralNetworkRawPrediction(&neuralNetworkObject, true, neuralNetworkInput) if (err != nil) { return false, false, 0, nil, 0, 0, err } - predictedOutcomeValue, err := GetNumericOutcomeValueFromOutputLayer(traitName, outputLayer) + predictedOutcomeValue, err := GetNumericOutcomeValueFromOutputLayer(attributeName, outputLayer) if (err != nil) { return false, false, 0, nil, 0, 0, err } - modelTraitAccuracyInfoFile, err := geneticPredictionModels.GetPredictionModelNumericTraitAccuracyInfoBytes(traitName) + modelAccuracyInfoFile, err := geneticPredictionModels.GetPredictionModelNumericAttributeAccuracyInfoBytes(attributeName) if (err != nil) { return false, false, 0, nil, 0, 0, err } - modelTraitAccuracyInfoMap, err := DecodeBytesToNumericTraitPredictionAccuracyInfoMap(modelTraitAccuracyInfoFile) + modelAccuracyInfoMap, err := DecodeBytesToNumericAttributePredictionAccuracyInfoMap(modelAccuracyInfoFile) if (err != nil) { return false, false, 0, nil, 0, 0, err } // We create a prediction confidence ranges map for our prediction getPredictionConfidenceRangesMap := func()map[int]float64{ - totalNumberOfTraitLoci := len(traitRSIDsList) + totalNumberOfAttributeLoci := len(attributeLociListCopy) - proportionOfLociTested := float64(quantityOfLociKnown)/float64(totalNumberOfTraitLoci) + proportionOfLociTested := float64(quantityOfLociKnown)/float64(totalNumberOfAttributeLoci) percentageOfLociTested := int(proportionOfLociTested * 100) - proportionOfPhasedLoci := float64(quantityOfPhasedLoci)/float64(totalNumberOfTraitLoci) + proportionOfPhasedLoci := float64(quantityOfPhasedLoci)/float64(totalNumberOfAttributeLoci) percentageOfPhasedLoci := int(proportionOfPhasedLoci * 100) // This is a value between 0 and 100 that represents the most similar confidence ranges map for this prediction @@ -552,10 +542,10 @@ func GetNeuralNetworkNumericTraitPredictionFromGenomeMap(traitName string, genom // Y = Number of phased loci closestPredictionConfidenceRangesMapDistance := float64(0) - for traitOutcomeInfo, traitPredictionConfidenceRangesMap := range modelTraitAccuracyInfoMap{ + for attributeOutcomeInfo, attributePredictionConfidenceRangesMap := range modelAccuracyInfoMap{ - currentPercentageOfLociTested := traitOutcomeInfo.PercentageOfLociTested - currentPercentageOfPhasedLoci := traitOutcomeInfo.PercentageOfPhasedLoci + currentPercentageOfLociTested := attributeOutcomeInfo.PercentageOfLociTested + currentPercentageOfPhasedLoci := attributeOutcomeInfo.PercentageOfPhasedLoci // Distance Formula for 2 coordinates (x1, y1) and (x2, y2): // distance = √((x2 - x1)^2 + (y2 - y1)^2) @@ -567,12 +557,12 @@ func GetNeuralNetworkNumericTraitPredictionFromGenomeMap(traitName string, genom if (distance == 0){ // We found the exact prediction confidence ranges map - return traitPredictionConfidenceRangesMap + return attributePredictionConfidenceRangesMap } if (closestPredictionConfidenceRangesMap == nil || distance < closestPredictionConfidenceRangesMapDistance){ closestPredictionConfidenceRangesMapDistance = distance - closestPredictionConfidenceRangesMap = traitPredictionConfidenceRangesMap + closestPredictionConfidenceRangesMap = attributePredictionConfidenceRangesMap } } @@ -789,11 +779,11 @@ func GetDiscreteOutcomeNameFromOutputLayer(traitName string, verifyOutputLayer b // This function returns which outcome is being described from a neural network's final output layer -// This is only used for discrete traits +// This is only used for numeric traits and polygenic diseases // Outputs: // -float64: Output Value (example: 150 centimeters) // -error -func GetNumericOutcomeValueFromOutputLayer(traitName string, outputLayer []float32)(float64, error){ +func GetNumericOutcomeValueFromOutputLayer(attributeName string, outputLayer []float32)(float64, error){ if (len(outputLayer) != 1){ return 0, errors.New("GetNumericOutcomeValueFromOutputLayer called with output layer which is not length of 1") @@ -807,15 +797,19 @@ func GetNumericOutcomeValueFromOutputLayer(traitName string, outputLayer []float getOutcomeMinAndMax := func()(float64, float64, error){ - switch traitName{ + switch attributeName{ case "Height":{ // Shortest person of all time: 54 cm // Tallest person of all time: 272 cm return 54, 272, nil } + case "Autism", + "Homosexualness":{ + return 0, 10, nil + } } - return 0, 0, errors.New("GetNumericOutcomeValueFromOutputLayer called with unknown traitName: " + traitName) + return 0, 0, errors.New("GetNumericOutcomeValueFromOutputLayer called with unknown attributeName: " + attributeName) } outcomeMin, outcomeMax, err := getOutcomeMinAndMax() @@ -833,9 +827,9 @@ func GetNumericOutcomeValueFromOutputLayer(traitName string, outputLayer []float // -int: Layer 3 neuron count // -int: Layer 4 neuron count (output layer) // -error -func getNeuralNetworkLayerSizes(traitName string)(int, int, int, int, error){ +func getNeuralNetworkLayerSizes(attributeName string)(int, int, int, int, error){ - switch traitName{ + switch attributeName{ case "Eye Color":{ @@ -856,9 +850,19 @@ func getNeuralNetworkLayerSizes(traitName string)(int, int, int, int, error){ // There is 1 output neuron, representing a height value return 3000, 3, 2, 1, nil } + case "Autism":{ + // There are 1428 input neurons + // There is 1 output neuron, representing an autism value + return 1428, 3, 2, 1, nil + } + case "Homosexualness":{ + // There are 12 input neurons + // There is 1 output neuron, representing a homosexualness value + return 12, 10, 5, 1, nil + } } - return 0, 0, 0, 0, errors.New("getNeuralNetworkLayerSizes called with unknown traitName: " + traitName) + return 0, 0, 0, 0, errors.New("getNeuralNetworkLayerSizes called with unknown attributeName: " + attributeName) } //This function converts a genome allele to a neuron to use in a tensor @@ -903,22 +907,50 @@ func convertAlleleToNeuron(allele string)(float32, error){ // -[]TrainingData: List of TrainingData for the user which we will use to train the model // -error func CreateGeneticPredictionTrainingData_OpenSNP( - traitName string, + attributeName string, userPhenotypeDataObject readBiobankData.PhenotypeData_OpenSNP, userLocusValuesMap map[int64]locusValue.LocusValue)(bool, []TrainingData, error){ - if (traitName != "Eye Color" && traitName != "Lactose Tolerance" && traitName != "Height"){ - return false, nil, errors.New("CreateGeneticPredictionTrainingData_OpenSNP called with unknown traitName: " + traitName) + //Outputs: + // -[]int64: Attribute rsIDs list + // -error + getAttributeLociList := func()([]int64, error){ + + switch attributeName{ + + case "Eye Color", + "Lactose Tolerance", + "Height", + "Homosexualness":{ + + traitObject, err := traits.GetTraitObject(attributeName) + if (err != nil) { return nil, err } + + // This is a list of rsIDs which influence this trait + traitLociList := traitObject.LociList + + return traitLociList, nil + } + case "Autism":{ + + diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(attributeName) + if (err != nil) { return nil, err } + + // This is a list of rsIDs which influence this disease + diseaseLociList := diseaseObject.LociList + + return diseaseLociList, nil + } + } + + return nil, errors.New("CreateGeneticPredictionTrainingData_OpenSNP called with unknown attributeName: " + attributeName) } - traitObject, err := traits.GetTraitObject(traitName) + attributeLociList, err := getAttributeLociList() if (err != nil) { return false, nil, err } - // This is a list of rsIDs which influence this trait - traitRSIDsList := traitObject.LociList - - if (len(traitRSIDsList) == 0){ - return false, nil, errors.New("traitObject contains no rsIDs.") + if (len(attributeLociList) == 0){ + return false, nil, errors.New("getAttributeLociList returning empty attributeLociList.") } // Each layer is represented as a []float32 @@ -927,7 +959,7 @@ func CreateGeneticPredictionTrainingData_OpenSNP( // Each TrainingData holds a variation of the user's genome rsID values // We add many rows with withheld data to improve training data - numberOfInputLayerRows, _, _, numberOfOutputLayerRows, err := getNeuralNetworkLayerSizes(traitName) + numberOfInputLayerRows, _, _, numberOfOutputLayerRows, err := getNeuralNetworkLayerSizes(attributeName) if (err != nil) { return false, nil, err } // Each rsID is represented by 3 neurons: LocusExists/LocusIsPhased, Allele1 Value, Allele2 Value @@ -935,7 +967,7 @@ func CreateGeneticPredictionTrainingData_OpenSNP( // -0 = Locus value is unknown // -0.5 = Locus Is known, phase is unknown // -1 = Locus Is Known, phase is known - expectedNumberOfInputLayerRows := len(traitRSIDsList) * 3 + expectedNumberOfInputLayerRows := len(attributeLociList) * 3 if (numberOfInputLayerRows != expectedNumberOfInputLayerRows){ @@ -944,9 +976,9 @@ func CreateGeneticPredictionTrainingData_OpenSNP( return false, nil, errors.New("numberOfInputLayerRows is not expected: " + expectedNumberOfInputLayerRowsString) } - checkIfAnyTraitLocusValuesExist := func()bool{ + checkIfAnyAttributeLocusValuesExist := func()bool{ - for _, rsID := range traitRSIDsList{ + for _, rsID := range attributeLociList{ _, exists := userLocusValuesMap[rsID] if (exists == true){ @@ -957,29 +989,29 @@ func CreateGeneticPredictionTrainingData_OpenSNP( return false } - anyTraitLocusValuesExist := checkIfAnyTraitLocusValuesExist() - if (anyTraitLocusValuesExist == false){ - // The user's genome does not contain any of this trait's locus values + anyAttributeLocusValuesExist := checkIfAnyAttributeLocusValuesExist() + if (anyAttributeLocusValuesExist == false){ + // The user's genome does not contain any of this attribute's locus values // We will not train on their data return false, nil, nil } // We sort rsIDs in ascending order - traitRSIDsListCopy := slices.Clone(traitRSIDsList) - slices.Sort(traitRSIDsListCopy) + attributeLociListCopy := slices.Clone(attributeLociList) + slices.Sort(attributeLociListCopy) // This function returns the outputLayer for all trainingDatas for this user - // Each outputLayer represents the user's trait value (Example: "Blue" for Eye Color) + // Each outputLayer represents the user's attribute value (Example: "Blue" for Eye Color) // Each outputLayer is identical, because each TrainingData example belongs to the same user // // Outputs: - // -bool: User trait value is known + // -bool: User attribute value is known // -[]float32: Neuron values for layer // -error - getUserTraitValueNeurons := func()(bool, []float32, error){ + getUserAttributeValueNeurons := func()(bool, []float32, error){ - switch traitName{ + switch attributeName{ case "Eye Color":{ @@ -1044,23 +1076,53 @@ func CreateGeneticPredictionTrainingData_OpenSNP( outputLayer := []float32{outputValueFloat32} + return true, outputLayer, nil + } + case "Autism":{ + + userAutismIsKnown := userPhenotypeDataObject.AutismIsKnown + if (userAutismIsKnown == false){ + return false, nil, nil + } + + userAutism := userPhenotypeDataObject.Autism + + outputValueFloat32 := float32(userAutism) + + outputLayer := []float32{outputValueFloat32} + + return true, outputLayer, nil + } + case "Homosexualness":{ + + userHomosexualnessIsKnown := userPhenotypeDataObject.HomosexualnessIsKnown + if (userHomosexualnessIsKnown == false){ + return false, nil, nil + } + + userHomosexualness := userPhenotypeDataObject.Homosexualness + + outputValueFloat32 := float32(userHomosexualness) + + outputLayer := []float32{outputValueFloat32} + return true, outputLayer, nil } } - return false, nil, errors.New("Unknown traitName: " + traitName) + return false, nil, errors.New("Unknown attributeName: " + attributeName) } - userTraitValueExists, userTraitValueNeurons, err := getUserTraitValueNeurons() + userAttributeValueExists, userAttributeValueNeurons, err := getUserAttributeValueNeurons() if (err != nil) { return false, nil, err } - if (userTraitValueExists == false){ + if (userAttributeValueExists == false){ // User cannot be used to train the model. - // They do not have a value for this trait. + // They do not have a value for this attribute. return false, nil, nil } - if (len(userTraitValueNeurons) != numberOfOutputLayerRows){ - return false, nil, errors.New("getUserTraitValueNeurons returning invalid length layer slice.") + if (len(userAttributeValueNeurons) != numberOfOutputLayerRows){ + return false, nil, errors.New("getUserAttributeValueNeurons returning invalid length layer slice.") } // We want the initial training data to be the same for each call of this function that has the same input parameters @@ -1134,11 +1196,11 @@ func CreateGeneticPredictionTrainingData_OpenSNP( anyLocusExists := false - inputLayerLength := len(traitRSIDsListCopy) * 3 + inputLayerLength := len(attributeLociListCopy) * 3 inputLayer := make([]float32, 0, inputLayerLength) - for _, rsID := range traitRSIDsListCopy{ + for _, rsID := range attributeLociListCopy{ randomFloat := pseudorandomNumberGenerator.Float64() if (randomFloat > probabilityOfUsingLoci){ @@ -1209,11 +1271,11 @@ func CreateGeneticPredictionTrainingData_OpenSNP( continue } - userTraitValueNeuronsCopy := slices.Clone(userTraitValueNeurons) + userAttributeValueNeuronsCopy := slices.Clone(userAttributeValueNeurons) newTrainingData := TrainingData{ InputLayer: inputLayer, - OutputLayer: userTraitValueNeuronsCopy, + OutputLayer: userAttributeValueNeuronsCopy, } trainingDataList = append(trainingDataList, newTrainingData) @@ -1222,9 +1284,9 @@ func CreateGeneticPredictionTrainingData_OpenSNP( return true, trainingDataList, nil } -func GetNewUntrainedNeuralNetworkObject(traitName string)(*NeuralNetwork, error){ +func GetNewUntrainedNeuralNetworkObject(attributeName string)(*NeuralNetwork, error){ - layer1NeuronCount, layer2NeuronCount, layer3NeuronCount, layer4NeuronCount, err := getNeuralNetworkLayerSizes(traitName) + layer1NeuronCount, layer2NeuronCount, layer3NeuronCount, layer4NeuronCount, err := getNeuralNetworkLayerSizes(attributeName) if (err != nil) { return nil, err } // This is the graph object we add each layer to @@ -1304,10 +1366,10 @@ func (inputNetwork *NeuralNetwork)getLearnables()gorgonia.Nodes{ // This function will train the neural network // The function is passed a batch of TrainingData examples to train on // Inputs: -// -string: Trait Name -// -bool: Trait is Numeric -// -An example of a numeric trait is Height -// -An example of a discrete trait is Eye Color, which has discrete outcomes (colors) +// -string: Attribute Name +// -bool: Attribute is Numeric +// -An example of a numeric attribute is Height +// -An example of a discrete attribute is Eye Color, which has discrete outcomes (colors) // -*NeuralNetwork // -func()(bool, bool, TrainingData, error): Function to get the next training data. // -Outputs: @@ -1318,9 +1380,9 @@ func (inputNetwork *NeuralNetwork)getLearnables()gorgonia.Nodes{ // Outputs: // -bool: Process completed (was not stopped mid-way) // -error -func TrainNeuralNetwork(traitName string, traitIsNumeric bool, neuralNetworkObject *NeuralNetwork, getNextTrainingData func()(bool, bool, TrainingData, error))(bool, error){ +func TrainNeuralNetwork(attributeName string, attributeIsNumeric bool, neuralNetworkObject *NeuralNetwork, getNextTrainingData func()(bool, bool, TrainingData, error))(bool, error){ - layer1NeuronCount, _, _, layer4NeuronCount, err := getNeuralNetworkLayerSizes(traitName) + layer1NeuronCount, _, _, layer4NeuronCount, err := getNeuralNetworkLayerSizes(attributeName) if (err != nil) { return false, err } neuralNetworkGraph := neuralNetworkObject.graph @@ -1340,7 +1402,7 @@ func TrainNeuralNetwork(traitName string, traitIsNumeric bool, neuralNetworkObje gorgonia.WithShape(1, layer4NeuronCount), ) - err = neuralNetworkObject.buildNeuralNetwork(trainingDataInputNode, traitIsNumeric) + err = neuralNetworkObject.buildNeuralNetwork(trainingDataInputNode, attributeIsNumeric) if (err != nil) { return false, err } // This computes the loss (how accurate was our prediction) @@ -1431,7 +1493,7 @@ func TrainNeuralNetwork(traitName string, traitIsNumeric bool, neuralNetworkObje // Outputs: // -[]float32: Output neurons // -error -func GetNeuralNetworkRawPrediction(inputNeuralNetwork *NeuralNetwork, traitIsNumeric bool, inputLayer []float32)([]float32, error){ +func GetNeuralNetworkRawPrediction(inputNeuralNetwork *NeuralNetwork, attributeIsNumeric bool, inputLayer []float32)([]float32, error){ neuralNetworkGraph := inputNeuralNetwork.graph @@ -1455,7 +1517,7 @@ func GetNeuralNetworkRawPrediction(inputNeuralNetwork *NeuralNetwork, traitIsNum if (err != nil) { return nil, err } - err = inputNeuralNetwork.buildNeuralNetwork(inputNode, traitIsNumeric) + err = inputNeuralNetwork.buildNeuralNetwork(inputNode, attributeIsNumeric) if (err != nil){ return nil, err } // Now we create a virtual machine to compute the prediction @@ -1479,7 +1541,7 @@ func GetNeuralNetworkRawPrediction(inputNeuralNetwork *NeuralNetwork, traitIsNum // This function will take a neural network and input layer and build the network to be able to compute a prediction // We need to run a virtual machine after calling this function in order for the prediction to be generated -func (inputNetwork *NeuralNetwork)buildNeuralNetwork(inputLayer *gorgonia.Node, traitIsNumeric bool)error{ +func (inputNetwork *NeuralNetwork)buildNeuralNetwork(inputLayer *gorgonia.Node, predictionIsNumeric bool)error{ // We copy node pointer (says to do this in a resource i'm reading) @@ -1518,7 +1580,7 @@ func (inputNetwork *NeuralNetwork)buildNeuralNetwork(inputLayer *gorgonia.Node, return errors.New("Layer 3 multiplication failed: " + err.Error()) } - if (traitIsNumeric == false){ + if (predictionIsNumeric == false){ // We SoftMax the output to get the prediction diff --git a/internal/genetics/readGeneticAnalysis/readGeneticAnalysis.go b/internal/genetics/readGeneticAnalysis/readGeneticAnalysis.go index 1039c3a..bae6a1b 100644 --- a/internal/genetics/readGeneticAnalysis/readGeneticAnalysis.go +++ b/internal/genetics/readGeneticAnalysis/readGeneticAnalysis.go @@ -483,193 +483,74 @@ func GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisOb return true, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, probabilityOf0MutationsFormatted, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, probabilityOf1MutationFormatted, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, probabilityOf2MutationsFormatted, nil } - //Outputs: -// -bool: Polygenic Disease Risk Score known (any loci values exist) -// -int: Person Disease risk score -// -string: Person Disease risk score formatted (has "/10" suffix) -// -int: Quantity of loci tested -// -bool: Conflict exists +// -bool: Any analysis exists +// -int: Predicted risk score (0-10) +// -map[int]float64: Prediction confidence ranges map +// -Map Structure: Percentage probability of accurate prediction -> distance of range in both directions from prediction +// -int: Quantity of loci known +// -int: Quantity of phased loci +// -bool: Conflict exists (between any of these results for each genome) // -error -func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, int, string, int, bool, error){ +func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, int, map[int]float64, int, int, bool, error){ personPolygenicDiseasesMap := personAnalysisObject.PolygenicDiseasesMap - personPolygenicDiseaseInfo, exists := personPolygenicDiseasesMap[diseaseName] + personDiseaseInfoObject, exists := personPolygenicDiseasesMap[diseaseName] if (exists == false){ - return false, 0, "", 0, false, nil + return false, 0, nil, 0, 0, false, nil } - personPolygenicDiseaseInfoMap := personPolygenicDiseaseInfo.PolygenicDiseaseInfoMap + personDiseaseInfoMap := personDiseaseInfoObject.PolygenicDiseaseInfoMap + conflictExists := personDiseaseInfoObject.ConflictExists - genomePolygenicDiseaseInfo, exists := personPolygenicDiseaseInfoMap[genomeIdentifier] + personGenomeDiseaseInfoObject, exists := personDiseaseInfoMap[genomeIdentifier] if (exists == false){ - return false, 0, "", 0, false, nil + return false, 0, nil, 0, 0, false, nil } - conflictExists := personPolygenicDiseaseInfo.ConflictExists + predictedRiskScore := personGenomeDiseaseInfoObject.RiskScore + confidenceRangesMap := personGenomeDiseaseInfoObject.ConfidenceRangesMap + quantityOfLociKnown := personGenomeDiseaseInfoObject.QuantityOfLociKnown + quantityOfPhasedLoci := personGenomeDiseaseInfoObject.QuantityOfPhasedLoci - personDiseaseRiskScore := genomePolygenicDiseaseInfo.RiskScore - - personDiseaseRiskScoreString := helpers.ConvertIntToString(personDiseaseRiskScore) - - personDiseaseRiskScoreFormatted := personDiseaseRiskScoreString + "/10" - - quantityOfLociTested := genomePolygenicDiseaseInfo.QuantityOfLociTested - - return true, personDiseaseRiskScore, personDiseaseRiskScoreFormatted, quantityOfLociTested, conflictExists, nil + return true, predictedRiskScore, confidenceRangesMap, quantityOfLociKnown, quantityOfPhasedLoci, conflictExists, nil } //Outputs: -// -bool: Offspring Disease Risk Score known -// -int: Offspring average disease risk score -// -string: Offspring Disease average risk score formatted (has "/10" suffix) -// -[]int: Sample Offspring Risk Scores List -// -int: Quantity of loci tested -// -bool: Conflict exists +// -bool: Analysis exists +// -int: Average offspring risk score (0-10) +// -map[int]float64: Prediction confidence ranges map +// -int: Quantity of loci known +// -int: Quantity of Parental phased loci +// -[]int: 100 Sample offspring risk scores +// -bool: Conflict exists (Between this genome pair and other genome pairs) // -error -func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, string, []int, int, bool, error){ +func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, map[int]float64, int, int, []int, bool, error){ - couplePolygenicDiseasesMap := coupleAnalysisObject.PolygenicDiseasesMap + offspringDiseasesMap := coupleAnalysisObject.PolygenicDiseasesMap - couplePolygenicDiseaseInfo, exists := couplePolygenicDiseasesMap[diseaseName] + diseaseInfoObject, exists := offspringDiseasesMap[diseaseName] if (exists == false){ - return false, 0, "", nil, 0, false, nil + return false, 0, nil, 0, 0, nil, false, nil } - polygenicDiseaseInfoMap := couplePolygenicDiseaseInfo.PolygenicDiseaseInfoMap + diseaseInfoMap := diseaseInfoObject.PolygenicDiseaseInfoMap + conflictExists := diseaseInfoObject.ConflictExists - genomePairPolygenicDiseaseInfo, exists := polygenicDiseaseInfoMap[genomePairIdentifier] + genomePairDiseaseInfoObject, exists := diseaseInfoMap[genomePairIdentifier] if (exists == false){ - return false, 0, "", nil, 0, false, nil + return false, 0, nil, 0, 0, nil, false, nil } - conflictExists := couplePolygenicDiseaseInfo.ConflictExists + offspringAverageRiskScore := genomePairDiseaseInfoObject.OffspringAverageRiskScore + predictionConfidenceRangesMap := genomePairDiseaseInfoObject.PredictionConfidenceRangesMap + quantityOfLociKnown := genomePairDiseaseInfoObject.QuantityOfLociKnown + quantityOfParentalPhasedLoci := genomePairDiseaseInfoObject.QuantityOfParentalPhasedLoci + sampleOffspringRiskScoresList := genomePairDiseaseInfoObject.SampleOffspringRiskScoresList - quantityOfLociTested := genomePairPolygenicDiseaseInfo.QuantityOfLociTested - - offspringAverageRiskScore := genomePairPolygenicDiseaseInfo.OffspringAverageRiskScore - - offspringAverageRiskScoreString := helpers.ConvertIntToString(offspringAverageRiskScore) - - offspringAverageRiskScoreFormatted := offspringAverageRiskScoreString + "/10" - - sampleOffspringRiskScoresList := genomePairPolygenicDiseaseInfo.SampleOffspringRiskScoresList - - return true, offspringAverageRiskScore, offspringAverageRiskScoreFormatted, sampleOffspringRiskScoresList, quantityOfLociTested, conflictExists, nil -} - -//Outputs: -// -bool: Risk Weight and base pair known -// -int: Locus risk weight -// -bool: Locus odds ratio known -// -float64: Locus odds ratio -// -string: Locus odds ratio formatted (with x suffix) -// -error -func GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalyisObject geneticAnalysis.PersonAnalysis, diseaseName string, locusIdentifier [3]byte, genomeIdentifier [16]byte)(bool, int, bool, float64, string, error){ - - personPolygenicDiseasesMap := personAnalyisObject.PolygenicDiseasesMap - - personPolygenicDiseaseMap, exists := personPolygenicDiseasesMap[diseaseName] - if (exists == false){ - return false, 0, false, 0, "", nil - } - - 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 - - locusOddsRatioIsKnown := locusInfoObject.OddsRatioIsKnown - if (locusOddsRatioIsKnown == false){ - return true, locusRiskWeight, false, 0, "", nil - } - - locusOddsRatio := locusInfoObject.OddsRatio - - genomeLocusOddsRatioString := helpers.ConvertFloat64ToStringRounded(locusOddsRatio, 2) - - locusOddsRatioFormatted := genomeLocusOddsRatioString + "x" - - return true, locusRiskWeight, true, locusOddsRatio, locusOddsRatioFormatted, nil -} - -//Outputs: -// -bool: Offspring risk weight known -// -int: Offspring risk weight -// -bool: Offspring odds ratio known -// -float64: Offspring odds ratio -// -string: Offspring odds ratio formatted (with + and < from unknownFactors weight sum and x suffix) -// -error -func GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, locusIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, bool, float64, string, error){ - - offspringPolygenicDiseasesMap := coupleAnalysisObject.PolygenicDiseasesMap - - offspringPolygenicDiseaseInfo, exists := offspringPolygenicDiseasesMap[diseaseName] - if (exists == false){ - return false, 0, false, 0, "", nil - } - - offspringPolygenicDiseaseMap := offspringPolygenicDiseaseInfo.PolygenicDiseaseInfoMap - - genomePairPolygenicDiseaseInfo, exists := offspringPolygenicDiseaseMap[genomePairIdentifier] - if (exists == false){ - return false, 0, false, 0, "", nil - } - - genomePairLociInfoMap := genomePairPolygenicDiseaseInfo.LociInfoMap - - locusInfoObject, exists := genomePairLociInfoMap[locusIdentifier] - if (exists == false){ - return false, 0, false, 0, "", nil - } - - offspringAverageRiskWeight := locusInfoObject.OffspringAverageRiskWeight - offspringOddsRatioIsKnown := locusInfoObject.OffspringOddsRatioIsKnown - - if (offspringOddsRatioIsKnown == false){ - return true, offspringAverageRiskWeight, false, 0, "", nil - } - - offspringAverageOddsRatio := locusInfoObject.OffspringAverageOddsRatio - - getOddsRatioFormatted := func()string{ - - offspringAverageUnknownOddsRatiosWeightSum := locusInfoObject.OffspringAverageUnknownOddsRatiosWeightSum - - offspringAverageOddsRatioString := helpers.ConvertFloat64ToStringRounded(offspringAverageOddsRatio, 2) - - if (offspringAverageUnknownOddsRatiosWeightSum == 0){ - result := offspringAverageOddsRatioString + "x" - - return result - } - if (offspringAverageUnknownOddsRatiosWeightSum < 0){ - result := "<" + offspringAverageOddsRatioString + "x" - - return result - } - // offspringAverageUnknownOddsRatiosWeightSum > 0 - result := offspringAverageOddsRatioString + "x+" - - return result - } - - oddsRatioFormatted := getOddsRatioFormatted() - - return true, offspringAverageRiskWeight, true, offspringAverageOddsRatio, oddsRatioFormatted, nil + return true, offspringAverageRiskScore, predictionConfidenceRangesMap, quantityOfLociKnown, quantityOfParentalPhasedLoci, sampleOffspringRiskScoresList, conflictExists, nil } //Outputs: @@ -892,7 +773,6 @@ func GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject g } - //Outputs: // -bool: Any analysis exists // -float64: Predicted outcome (Example: Height in centimeters) @@ -964,6 +844,8 @@ func GetOffspringNumericTraitInfoFromGeneticAnalysis(coupleAnalysisObject geneti return true, offspringAverageOutcome, predictionConfidenceRangesMap, quantityOfLociKnown, quantityOfParentalPhasedLoci, sampleOffspringOutcomesList, conflictExists, nil } + + // We use this function to verify a person genetic analysis is well formed //TODO: Perform sanity checks on data func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis)error{ @@ -1015,25 +897,9 @@ func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnal for _, genomeIdentifier := range allGenomeIdentifiersList{ - _, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier) + _, _, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier) if (err != nil) { return err } } - - diseaseLocusObjectsList := diseaseObject.LociList - - for _, diseaseLocusObject := range diseaseLocusObjectsList{ - - locusIdentifierHex := diseaseLocusObject.LocusIdentifier - - locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) - if (err != nil) { return err } - - for _, genomeIdentifier := range allGenomeIdentifiersList{ - - _, _, _, _, _, err := GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) - if (err != nil) { return err } - } - } } traitObjectsList, err := traits.GetTraitObjectsList() @@ -1138,25 +1004,9 @@ func VerifyCoupleGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnal for _, genomePairIdentifier := range allGenomePairIdentifiersList{ - _, _, _, _, _, _, err := GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) + _, _, _, _, _, _, _, err := GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) if (err != nil) { return err } } - - diseaseLocusObjectsList := diseaseObject.LociList - - for _, diseaseLocusObject := range diseaseLocusObjectsList{ - - locusIdentifierHex := diseaseLocusObject.LocusIdentifier - - locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) - if (err != nil) { return err } - - for _, genomePairIdentifier := range allGenomePairIdentifiersList{ - - _, _, _, _, _, err := GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, locusIdentifier, genomePairIdentifier) - if (err != nil) { return err } - } - } } traitObjectsList, err := traits.GetTraitObjectsList() diff --git a/internal/genetics/sampleAnalyses/SampleCoupleAnalysis.messagepack b/internal/genetics/sampleAnalyses/SampleCoupleAnalysis.messagepack index f85b810..52b1029 100644 Binary files a/internal/genetics/sampleAnalyses/SampleCoupleAnalysis.messagepack and b/internal/genetics/sampleAnalyses/SampleCoupleAnalysis.messagepack differ diff --git a/internal/genetics/sampleAnalyses/SamplePerson1Analysis.messagepack b/internal/genetics/sampleAnalyses/SamplePerson1Analysis.messagepack index 8d0f514..f4135b6 100644 Binary files a/internal/genetics/sampleAnalyses/SamplePerson1Analysis.messagepack and b/internal/genetics/sampleAnalyses/SamplePerson1Analysis.messagepack differ diff --git a/internal/genetics/sampleAnalyses/SamplePerson2Analysis.messagepack b/internal/genetics/sampleAnalyses/SamplePerson2Analysis.messagepack index dff7be3..1cb3aff 100644 Binary files a/internal/genetics/sampleAnalyses/SamplePerson2Analysis.messagepack and b/internal/genetics/sampleAnalyses/SamplePerson2Analysis.messagepack differ diff --git a/internal/helpers/helpers.go b/internal/helpers/helpers.go index 37b6236..e70870a 100644 --- a/internal/helpers/helpers.go +++ b/internal/helpers/helpers.go @@ -49,6 +49,29 @@ func ConvertCentimetersToFeetInchesTranslatedString(centimeters float64)(string, return "", errors.New("ConvertCentimetersToFeetInchesTranslatedString called with invalid centimeters.") } + getInchUnits := func()string{ + + if (inputInches == 1){ + result := translation.TranslateTextFromEnglishToMyLanguage("inch") + return result + } + + inchesTranslated := translation.TranslateTextFromEnglishToMyLanguage("inches") + + return inchesTranslated + } + + inchUnits := getInchUnits() + + inputInchesString := ConvertFloat64ToStringRounded(inputInches, 1) + + if (inputFeet == 0){ + + formattedResult := inputInchesString + " " + inchUnits + + return formattedResult, nil + } + getFeetUnits := func()string{ if (inputFeet <= 1){ @@ -64,22 +87,7 @@ func ConvertCentimetersToFeetInchesTranslatedString(centimeters float64)(string, feetUnits := getFeetUnits() - getInchUnits := func()string{ - - if (inputInches == 1){ - result := translation.TranslateTextFromEnglishToMyLanguage("inch") - return result - } - - inchesTranslated := translation.TranslateTextFromEnglishToMyLanguage("inches") - - return inchesTranslated - } - - inchUnits := getInchUnits() - inputFeetString := ConvertIntToString(inputFeet) - inputInchesString := ConvertFloat64ToStringRounded(inputInches, 1) formattedResult := inputFeetString + " " + feetUnits + ", " + inputInchesString + " " + inchUnits diff --git a/internal/profiles/calculatedAttributes/calculatedAttributes.go b/internal/profiles/calculatedAttributes/calculatedAttributes.go index 9b66823..b589b12 100644 --- a/internal/profiles/calculatedAttributes/calculatedAttributes.go +++ b/internal/profiles/calculatedAttributes/calculatedAttributes.go @@ -684,9 +684,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA diseaseLociList := diseaseObject.LociList - for _, locusObject := range diseaseLociList{ - - locusRSID := locusObject.LocusRSID + for _, locusRSID := range diseaseLociList{ locusRSIDString := helpers.ConvertInt64ToString(locusRSID) @@ -723,9 +721,9 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA userDiseaseLocusValuesMap[locusRSID] = userLocusValueObject } - anyLocusTested, userDiseaseRiskScore, _, _, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, userDiseaseLocusValuesMap, true) + neuralNetworkExists, anyLocusTested, userDiseaseRiskScore, _, _, _, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseAnalysis(diseaseObject, userDiseaseLocusValuesMap, true) if (err != nil) { return false, 0, "", err } - if (anyLocusTested == false){ + if (neuralNetworkExists == false || anyLocusTested == false){ continue } @@ -803,9 +801,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA // Map Structure: rsID -> Locus Value userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue) - for _, locusObject := range diseaseLociList{ - - locusRSID := locusObject.LocusRSID + for _, locusRSID := range diseaseLociList{ locusRSIDString := helpers.ConvertInt64ToString(locusRSID) @@ -842,9 +838,9 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA userDiseaseLocusValuesMap[locusRSID] = newLocusValueObject } - anyLocusValuesTested, offspringAverageRiskScore, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo_Fast(diseaseLociList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap) + neuralNetworkExists, anyLocusValuesTested, offspringAverageRiskScore, _, _, _, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseAnalysis(diseaseObject, myGenomeLocusValuesMap, userDiseaseLocusValuesMap) if (err != nil) { return false, 0, "", err } - if (anyLocusValuesTested == false){ + if (neuralNetworkExists == false || anyLocusValuesTested == false){ continue } diff --git a/internal/profiles/myProfileExports/myProfileExports.go b/internal/profiles/myProfileExports/myProfileExports.go index 0569c50..339c78f 100644 --- a/internal/profiles/myProfileExports/myProfileExports.go +++ b/internal/profiles/myProfileExports/myProfileExports.go @@ -214,11 +214,9 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{ lociList := diseaseObject.LociList - for _, locusObject := range lociList{ + for _, rsID := range lociList{ - locusRSID := locusObject.LocusRSID - - myLociToShareMap[locusRSID] = struct{}{} + myLociToShareMap[rsID] = struct{}{} } } diff --git a/internal/profiles/profileFormat/profileFormat.go b/internal/profiles/profileFormat/profileFormat.go index a736596..6ea1a16 100644 --- a/internal/profiles/profileFormat/profileFormat.go +++ b/internal/profiles/profileFormat/profileFormat.go @@ -2267,11 +2267,9 @@ func initializeProfileAttributeObjectsList()error{ diseaseLociList := diseaseObject.LociList - for _, locusObject := range diseaseLociList{ + for _, rsID := range diseaseLociList{ - locusRSID := locusObject.LocusRSID - - shareableRSIDsMap[locusRSID] = struct{}{} + shareableRSIDsMap[rsID] = struct{}{} } } diff --git a/internal/profiles/profileFormat/profileFormat_test.go b/internal/profiles/profileFormat/profileFormat_test.go index 6411b43..a47f024 100644 --- a/internal/profiles/profileFormat/profileFormat_test.go +++ b/internal/profiles/profileFormat/profileFormat_test.go @@ -210,9 +210,7 @@ func TestProfileGeneticReferences(t *testing.T){ diseaseLociList := diseaseObject.LociList - for _, locusObject := range diseaseLociList{ - - locusRSID := locusObject.LocusRSID + for _, locusRSID := range diseaseLociList{ locusRSIDString := helpers.ConvertInt64ToString(locusRSID) diff --git a/resources/geneticPredictionModels/geneticPredictionModels.go b/resources/geneticPredictionModels/geneticPredictionModels.go index 3562906..d2fdf03 100644 --- a/resources/geneticPredictionModels/geneticPredictionModels.go +++ b/resources/geneticPredictionModels/geneticPredictionModels.go @@ -21,6 +21,12 @@ var predictionModel_LactoseTolerance []byte //go:embed predictionModels/HeightModel.gob var predictionModel_Height []byte +//go:embed predictionModels/AutismModel.gob +var predictionModel_Autism []byte + +//go:embed predictionModels/HomosexualnessModel.gob +var predictionModel_Homosexualness []byte + //Outputs: // -bool: Model exists @@ -38,6 +44,12 @@ func GetGeneticPredictionModelBytes(traitName string)(bool, []byte){ case "Height":{ return true, predictionModel_Height } + case "Autism":{ + return true, predictionModel_Autism + } + case "Homosexualness":{ + return true, predictionModel_Homosexualness + } } return false, nil @@ -65,19 +77,33 @@ func GetPredictionModelDiscreteTraitAccuracyInfoBytes(traitName string)([]byte, return nil, errors.New("GetPredictionModelDiscreteTraitAccuracyInfoBytes called with unknown traitName: " + traitName) } + //go:embed predictionModelAccuracies/HeightModelAccuracy.gob var predictionAccuracy_Height []byte -// The files returned by this function are .gob encoded geneticPrediction.NumericTraitPredictionAccuracyInfoMap objects -func GetPredictionModelNumericTraitAccuracyInfoBytes(traitName string)([]byte, error){ +//go:embed predictionModelAccuracies/AutismModelAccuracy.gob +var predictionAccuracy_Autism []byte - switch traitName{ +//go:embed predictionModelAccuracies/HomosexualnessModelAccuracy.gob +var predictionAccuracy_Homosexualness []byte + + +// The files returned by this function are .gob encoded geneticPrediction.NumericAttributePredictionAccuracyInfoMap objects +func GetPredictionModelNumericAttributeAccuracyInfoBytes(attributeName string)([]byte, error){ + + switch attributeName{ case "Height":{ return predictionAccuracy_Height, nil } + case "Autism":{ + return predictionAccuracy_Autism, nil + } + case "Homosexualness":{ + return predictionAccuracy_Homosexualness, nil + } } - return nil, errors.New("GetPredictionModelNumericTraitAccuracyInfoBytes called with unknown traitName: " + traitName) + return nil, errors.New("GetPredictionModelNumericAttributeAccuracyInfoBytes called with unknown attributeName: " + attributeName) } diff --git a/resources/geneticPredictionModels/geneticPredictionModels_test.go b/resources/geneticPredictionModels/geneticPredictionModels_test.go index da206d7..bccc1f8 100644 --- a/resources/geneticPredictionModels/geneticPredictionModels_test.go +++ b/resources/geneticPredictionModels/geneticPredictionModels_test.go @@ -9,13 +9,13 @@ import "seekia/internal/genetics/geneticPrediction" func TestGeneticPredictionModels(t *testing.T){ - traitNamesList := []string{"Eye Color", "Lactose Tolerance", "Height"} + attributeNamesList := []string{"Eye Color", "Lactose Tolerance", "Height", "Autism"} - for _, traitName := range traitNamesList{ + for _, attributeName := range attributeNamesList{ - modelFound, modelBytes := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName) + modelFound, modelBytes := geneticPredictionModels.GetGeneticPredictionModelBytes(attributeName) if (modelFound == false){ - t.Fatalf("GetGeneticPredictionModelBytes failed to find model for trait: " + traitName) + t.Fatalf("GetGeneticPredictionModelBytes failed to find model for trait: " + attributeName) } _, err := geneticPrediction.DecodeBytesToNeuralNetworkObject(modelBytes) @@ -43,18 +43,18 @@ func TestGeneticPredictionModelAccuracies(t *testing.T){ } } - numericTraitNamesList := []string{"Height"} + numericAttributeNamesList := []string{"Height", "Autism", "Homosexualness"} - for _, traitName := range numericTraitNamesList{ + for _, attributeName := range numericAttributeNamesList{ - accuracyInfoBytes, err := geneticPredictionModels.GetPredictionModelNumericTraitAccuracyInfoBytes(traitName) + accuracyInfoBytes, err := geneticPredictionModels.GetPredictionModelNumericAttributeAccuracyInfoBytes(attributeName) if (err != nil){ - t.Fatalf("GetPredictionModelNumericTraitAccuracyInfoBytes failed: " + err.Error()) + t.Fatalf("GetPredictionModelNumericAttributeAccuracyInfoBytes failed: " + err.Error()) } - _, err = geneticPrediction.DecodeBytesToNumericTraitPredictionAccuracyInfoMap(accuracyInfoBytes) + _, err = geneticPrediction.DecodeBytesToNumericAttributePredictionAccuracyInfoMap(accuracyInfoBytes) if (err != nil){ - t.Fatalf("DecodeBytesToNumericTraitPredictionAccuracyInfoMap failed: " + err.Error()) + t.Fatalf("DecodeBytesToNumericAttributePredictionAccuracyInfoMap failed: " + err.Error()) } } } diff --git a/resources/geneticPredictionModels/predictionModelAccuracies/AutismModelAccuracy.gob b/resources/geneticPredictionModels/predictionModelAccuracies/AutismModelAccuracy.gob new file mode 100644 index 0000000..a44dca6 Binary files /dev/null and b/resources/geneticPredictionModels/predictionModelAccuracies/AutismModelAccuracy.gob differ diff --git a/resources/geneticPredictionModels/predictionModelAccuracies/EyeColorModelAccuracy.gob b/resources/geneticPredictionModels/predictionModelAccuracies/EyeColorModelAccuracy.gob index 4464c40..bdb40c9 100644 Binary files a/resources/geneticPredictionModels/predictionModelAccuracies/EyeColorModelAccuracy.gob and b/resources/geneticPredictionModels/predictionModelAccuracies/EyeColorModelAccuracy.gob differ diff --git a/resources/geneticPredictionModels/predictionModelAccuracies/HeightModelAccuracy.gob b/resources/geneticPredictionModels/predictionModelAccuracies/HeightModelAccuracy.gob index b2e886a..7f707ec 100644 Binary files a/resources/geneticPredictionModels/predictionModelAccuracies/HeightModelAccuracy.gob and b/resources/geneticPredictionModels/predictionModelAccuracies/HeightModelAccuracy.gob differ diff --git a/resources/geneticPredictionModels/predictionModelAccuracies/HomosexualnessModelAccuracy.gob b/resources/geneticPredictionModels/predictionModelAccuracies/HomosexualnessModelAccuracy.gob new file mode 100644 index 0000000..b8542be Binary files /dev/null and b/resources/geneticPredictionModels/predictionModelAccuracies/HomosexualnessModelAccuracy.gob differ diff --git a/resources/geneticPredictionModels/predictionModelAccuracies/LactoseToleranceModelAccuracy.gob b/resources/geneticPredictionModels/predictionModelAccuracies/LactoseToleranceModelAccuracy.gob index 1ec8a15..c16c609 100644 Binary files a/resources/geneticPredictionModels/predictionModelAccuracies/LactoseToleranceModelAccuracy.gob and b/resources/geneticPredictionModels/predictionModelAccuracies/LactoseToleranceModelAccuracy.gob differ diff --git a/resources/geneticPredictionModels/predictionModels/AutismModel.gob b/resources/geneticPredictionModels/predictionModels/AutismModel.gob new file mode 100644 index 0000000..51059e7 Binary files /dev/null and b/resources/geneticPredictionModels/predictionModels/AutismModel.gob differ diff --git a/resources/geneticPredictionModels/predictionModels/EyeColorModel.gob b/resources/geneticPredictionModels/predictionModels/EyeColorModel.gob index 42434f5..2d9ff8f 100644 Binary files a/resources/geneticPredictionModels/predictionModels/EyeColorModel.gob and b/resources/geneticPredictionModels/predictionModels/EyeColorModel.gob differ diff --git a/resources/geneticPredictionModels/predictionModels/HomosexualnessModel.gob b/resources/geneticPredictionModels/predictionModels/HomosexualnessModel.gob new file mode 100644 index 0000000..6ef07db Binary files /dev/null and b/resources/geneticPredictionModels/predictionModels/HomosexualnessModel.gob differ diff --git a/resources/geneticReferences/attributeLoci/attributeLoci.go b/resources/geneticReferences/attributeLoci/attributeLoci.go new file mode 100644 index 0000000..b173474 --- /dev/null +++ b/resources/geneticReferences/attributeLoci/attributeLoci.go @@ -0,0 +1,8 @@ + +// attributeLoci provides loci associated with various bodily attributes +// For example, this package stores all loci associated with the brain +// We can then use this same set of loci to predict all attributes pertaining to the brain, such as autism, depression, anxiety, etc. +// We need this package because we can't have packages that import from polygenicDiseases->traits and traits->polygenicDiseases + +package attributeLoci + diff --git a/resources/geneticReferences/attributeLoci/autism.go b/resources/geneticReferences/attributeLoci/autism.go new file mode 100644 index 0000000..94e2bbe --- /dev/null +++ b/resources/geneticReferences/attributeLoci/autism.go @@ -0,0 +1,605 @@ +package attributeLoci + +import "maps" + +// Outputs: +// -map[int64]map[string]string +// -Map Structure: rsID -> map[ReferenceName]ReferenceLink +func GetAutismLoci()map[int64]map[string]string{ + + // Map Structure: rsID -> (map[Reference Name]Reference Link) + locusReferencesMap := make(map[int64]map[string]string) + + locus1_ReferencesMap := make(map[string]string) + locus1_ReferencesMap["SNPedia.com - rs10513025"] = "https://www.snpedia.com/index.php/Rs10513025" + + locusReferencesMap[10513025] = locus1_ReferencesMap + + locus2_ReferencesMap := make(map[string]string) + locus2_ReferencesMap["SNPedia.com - rs2710102"] = "https://www.snpedia.com/index.php/Rs2710102" + + locusReferencesMap[2710102] = locus2_ReferencesMap + + locus3_ReferencesMap := make(map[string]string) + locus3_ReferencesMap["SNPedia.com - rs7794745"] = "https://www.snpedia.com/index.php/Rs7794745" + + locusReferencesMap[7794745] = locus3_ReferencesMap + + locus4_ReferencesMap := make(map[string]string) + locus4_ReferencesMap["SNPedia.com - rs1858830"] = "https://www.snpedia.com/index.php/Rs1858830" + + locusReferencesMap[1858830] = locus4_ReferencesMap + + locus5_ReferencesMap := make(map[string]string) + locus5_ReferencesMap["SNPedia.com - rs1322784"] = "https://www.snpedia.com/index.php/Rs1322784" + + locusReferencesMap[1322784] = locus5_ReferencesMap + + locus6_ReferencesMap := make(map[string]string) + locus6_ReferencesMap["SNPedia.com - rs1804197"] = "https://www.snpedia.com/index.php/Rs1804197" + + locusReferencesMap[1804197] = locus6_ReferencesMap + + locus7_ReferencesMap := make(map[string]string) + locus7_ReferencesMap["SNPedia.com - rs265981"] = "https://www.snpedia.com/index.php/Rs265981" + + locusReferencesMap[265981] = locus7_ReferencesMap + + locus8_ReferencesMap := make(map[string]string) + locus8_ReferencesMap["SNPedia.com - rs4532"] = "https://www.snpedia.com/index.php/Rs4532" + + locusReferencesMap[4532] = locus8_ReferencesMap + + locus9_ReferencesMap := make(map[string]string) + locus9_ReferencesMap["SNPedia.com - rs686"] = "https://www.snpedia.com/index.php/Rs686" + + locusReferencesMap[686] = locus9_ReferencesMap + + locus10_ReferencesMap := make(map[string]string) + locus10_ReferencesMap["SNPedia.com - rs6766410"] = "https://www.snpedia.com/index.php/Rs6766410" + + locusReferencesMap[6766410] = locus10_ReferencesMap + + locus11_ReferencesMap := make(map[string]string) + locus11_ReferencesMap["SNPedia.com - rs6807362"] = "https://www.snpedia.com/index.php/Rs6807362" + + locusReferencesMap[6807362] = locus11_ReferencesMap + + locus12_ReferencesMap := make(map[string]string) + locus12_ReferencesMap["SNPedia.com - rs1143674"] = "https://www.snpedia.com/index.php/Rs1143674" + + locusReferencesMap[1143674] = locus12_ReferencesMap + + locus13_ReferencesMap := make(map[string]string) + locus13_ReferencesMap["SNPedia.com - rs2745557"] = "https://www.snpedia.com/index.php/Rs2745557" + + locusReferencesMap[2745557] = locus13_ReferencesMap + + locus14_ReferencesMap := make(map[string]string) + locus14_ReferencesMap["SNPedia.com - rs2217262"] = "https://www.snpedia.com/index.php/Rs2217262" + + locusReferencesMap[2217262] = locus14_ReferencesMap + + locus15_ReferencesMap := make(map[string]string) + locus15_ReferencesMap["SNPedia.com - rs373126732"] = "https://www.snpedia.com/index.php/Rs373126732" + + locusReferencesMap[373126732] = locus15_ReferencesMap + + locus16_ReferencesMap := make(map[string]string) + locus16_ReferencesMap["SNPedia.com - rs184718561"] = "https://www.snpedia.com/index.php/Rs184718561" + + locusReferencesMap[184718561] = locus16_ReferencesMap + + locus17_ReferencesMap := make(map[string]string) + locus17_ReferencesMap["SNPedia.com - rs1445442"] = "https://www.snpedia.com/index.php/Rs1445442" + + locusReferencesMap[1445442] = locus17_ReferencesMap + + locus18_ReferencesMap := make(map[string]string) + locus18_ReferencesMap["SNPedia.com - rs2421826"] = "https://www.snpedia.com/index.php/Rs2421826" + + locusReferencesMap[2421826] = locus18_ReferencesMap + + locus19_ReferencesMap := make(map[string]string) + locus19_ReferencesMap["SNPedia.com - rs1358054"] = "https://www.snpedia.com/index.php/Rs1358054" + + locusReferencesMap[1358054] = locus19_ReferencesMap + + locus20_ReferencesMap := make(map[string]string) + locus20_ReferencesMap["SNPedia.com - rs722628"] = "https://www.snpedia.com/index.php/Rs722628" + + locusReferencesMap[722628] = locus20_ReferencesMap + + locus21_ReferencesMap := make(map[string]string) + locus21_ReferencesMap["SNPedia.com - rs536861"] = "https://www.snpedia.com/index.php/Rs536861" + + locusReferencesMap[536861] = locus21_ReferencesMap + + locus22_ReferencesMap := make(map[string]string) + locus22_ReferencesMap["SNPedia.com - rs757972971"] = "https://www.snpedia.com/index.php/Rs757972971" + + locusReferencesMap[757972971] = locus22_ReferencesMap + + + referencesMap_LocusList1 := make(map[string]string) + referencesMap_LocusList1["Understanding the impact of SNPs associated with autism spectrum disorder on biological pathways in the human fetal and adult cortex"] = "https://www.nature.com/articles/s41598-021-95447-z" + + lociList1 := []int64{ + 13217619, + 115329265, + 116137698, + 141342723, + 75782365, + 151267808, + 7746199, + 114115252, + 4298967, + 1782810, + 6921919, + 9467711, + 115707823, + 116633139, + 115123779, + 116326873, + 9834970, + 144762289, + 9348739, + 4481150, + 12129573, + 116408368, + 11191419, + 115242751, + 116385615, + 114882497, + 114867672, + 12658451, + 202906, + 13212562, + 7085104, + 1702294, + 114276265, + 116427960, + 59574136, + 114041423, + 7531118, + 114964506, + 111639056, + 6939532, + 6940116, + 116663187, + 114904464, + 145547914, + 9269271, + 114963521, + 140502984, + 61867293, + 115035678, + 9274390, + 11688767, + 78110044, + 150680405, + 10883832, + 7752195, + 115497191, + 116676919, + 11191582, + 115344853, + 144911693, + 71395455, + 5758265, + 2007044, + 149979052, + 115682897, + 3001723, + 1024582, + 115625073, + 9273177, + 61472021, + 12668848, + 184153866, + 115558405, + 150430679, + 115687605, + 35324223, + 9274299, + 138984909, + 145076523, + 55661361, + 911186, + 144304366, + 10149470, + 144660248, + 13218591, + 114455101, + 185717927, + 144649399, + 114086406, + 11682175, + 142972412, + 138748649, + 7405404, + 11693528, + 12958048, + 35225200, + 114950038, + 140865314, + 4129585, + 12887734, + 36057735, + 115052633, + 186129480, + 2507989, + 2021722, + 140505938, + 2388334, + 3617, + 114274203, + 281768, + 115937317, + 144018888, + 2535629, + 4906364, + 180778602, + 707939, + 8084351, + 80318442, + 186229361, + 9461856, + 113397282, + 28681284, + 113205291, + 2851447, + 4380187, + 115960997, + 1793889, + 142790902, + 111312615, + 144532965, + 75968099, + 115661163, + 1518367, + 193267147, + 41293179, + 200986, + 34787248, + 140364877, + 13240464, + 1625579, + 4702, + 2514218, + 778353, + 325506, + 182908437, + 149721896, + 6434928, + 4713071, + 11753207, + 191843781, + 116182620, + 2760981, + 116067082, + 142601889, + 147976543, + 116254153, + 8054556, + 114204022, + 115165987, + 9636107, + 41563, + 35828350, + 764284, + 115325719, + 7193263, + 149915948, + 17843707, + 79879286, + 631399, + 732381, + 1150688, + 189600472, + 3798869, + 5757717, + 145501595, + 4642619, + 117616320, + 12704290, + 2176546, + 149787317, + 11570190, + 4391122, + 7071123, + 12712388, + 4307059, + 369637, + 114291394, + 11740474, + 12925872, + 116460775, + 114838832, + 10791097, + 35610290, + 114812317, + 9469174, + 7801375, + 114508985, + 6704768, + 4580973, + 147875011, + 7893279, + 12966547, + 9922678, + 111294930, + 6047287, + 34215985, + 2693698, + 12826178, + 2237234, + 11210892, + 67756423, + 9787523, + 10108980, + 2057884, + 1498232, + 8042374, + 142520578, + 114771361, + 114810457, + 17194490, + 145470632, + 36063234, + 2332700, + 1615350, + 3735025, + 115283957, + 75059851, + 1730054, + 116593970, + 4523957, + 169738, + 35346733, + 12954356, + 7907645, + 2910032, + 9270074, + 1899546, + 6071524, + 11874716, + 72761442, + 3132556, + 116139966, + 139547629, + 28724212, + 6855246, + 72934570, + 147793969, + 115487448, + 4619651, + 7521492, + 2103655, + 880090, + 1806153, + 11787216, + 115915654, + 11223651, + 62378245, + 8009147, + 7191183, + 77502336, + 3849046, + 1131275, + 61747867, + 116047537, + 41293330, + 61789073, + 7914558, + 10043984, + 10514301, + 117956829, + 4647903, + 4916723, + 28669119, + 35774874, + 4244354, + 1452075, + 56223946, + 2434529, + 115641444, + 149998036, + 184123737, + 10994359, + 9360557, + 80256351, + 6125656, + 247910, + 3812984, + 915057, + 17659437, + 11641947, + 139099016, + 72687362, + 57709857, + 11210195, + 3020736, + 12592967, + 5995756, + 385492, + 115443066, + 9371601, + 59979824, + 6694545, + 1484144, + 832190, + 9267057, + 4309187, + 149544854, + 116502302, + 191269336, + 1006737, + 10265001, + 6969410, + 1080500, + 171748, + 139480376, + 17292804, + 174592, + 1620977, + 184538485, + 191239160, + 301798, + 10211550, + 10994397, + 9677504, + 144158419, + 2098651, + 8321, + 11231640, + 77135925, + 12474906, + 2300861, + 2391769, + 10520163, + 9607782, + 55648125, + 10099100, + 16854048, + 35131895, + 1977199, + 145607970, + 115569272, + 116552815, + 6803008, + 35998080, + 10791111, + 2944591, + 1353545, + 115437294, + 133047, + 9274657, + 11191580, + 11191454, + 7618871, + 10745841, + 61882743, + 116755193, + 142462188, + 7200826, + 27419, + 2414718, + 2842198, + 12552, + 395138, + 760648, + 1002656, + 2898883, + 13072940, + 12443170, + 114441450, + 146201420, + 184981897, + 138850297, + 8032315, + 7184114, + 115136442, + 2767713, + 2828478, + 9879311, + 114142645, + 111977918, + 7819570, + 12522290, + 112209031, + 10491964, + 11658257, + 62526783, + 6471814, + 11866581, + 12894153, + 2391734, + 2522831, + 2003490, + 301799, + 1226412, + 1950829, + 8453, + 926938, + 6537825, + 111931861, + 115963308, + 149961934, + 61847307, + 146827975, + 1339227, + 36350, + 7432375, + 9656169, + 28758902, + 427691, + 2293751, + 182087722, + 73416724, + 61884307, + 188190243, + 41294271, + 114830752, + 7004633, + 7785663, + 8066384, + 188099135, + 4730387, + 11887562, + 2801578, + 4242470, + 746839, + 3827735, + 11582563, + 11102807, + 7511633, + 11102800, + 11585926, + 6661053, + 11589568, + 4141463, + 201910565, + 71190156, + 353547, + 880446, + 2115780, + 114277634, + 140849564, + 76994193, + 114875775, + 7122181, + 221902, + 12576775, + 10503253, + 2799573, + 4495234, + 4526442, + 4682973, + 12898460, + 2047568, + 2910032, + 1501361, + } + + for _, rsID := range lociList1{ + + existingMap, exists := locusReferencesMap[rsID] + if (exists == false){ + locusReferencesMap[rsID] = maps.Clone(referencesMap_LocusList1) + } else { + + // We merge the maps + + for key, value := range referencesMap_LocusList1{ + existingMap[key] = value + } + + locusReferencesMap[rsID] = existingMap + } + } + + return locusReferencesMap +} + + diff --git a/resources/geneticReferences/geneticReferences_test.go b/resources/geneticReferences/geneticReferences_test.go index 57b5a27..4ca39d3 100644 --- a/resources/geneticReferences/geneticReferences_test.go +++ b/resources/geneticReferences/geneticReferences_test.go @@ -199,6 +199,7 @@ func TestGeneticReferences(t *testing.T){ diseaseName := diseaseObject.DiseaseName diseaseDescription := diseaseObject.DiseaseDescription diseaseEffectedSex := diseaseObject.EffectedSex + diseaseLocusReferencesMap := diseaseObject.LocusReferencesMap diseaseLociList := diseaseObject.LociList diseaseReferencesMap := diseaseObject.References @@ -218,81 +219,33 @@ func TestGeneticReferences(t *testing.T){ t.Fatalf("PolygenicDisease effected sex is invalid: " + diseaseEffectedSex) } + for rsID, referencesMap := range diseaseLocusReferencesMap{ + + containsItem := slices.Contains(diseaseLociList, rsID) + if (containsItem == false){ + t.Fatalf("Polygenic disease diseaseLocusReferencesMap contains disease locus that is not inside of the disease's loci list.") + } + + allRSIDsMap[rsID] = struct{}{} + + referencesAreValid := verifyReferencesMap(referencesMap) + if (referencesAreValid == false){ + t.Fatalf("PolygenicDisease references map is invalid for disease locus.") + } + } + + containsDuplicates, _ := helpers.CheckIfListContainsDuplicates(diseaseLociList) + if (containsDuplicates == true){ + t.Fatalf("Polygenic disease object contains diseaseLociList with duplicate rsIDs.") + } + + if (len(diseaseLocusReferencesMap) > len(diseaseLociList)){ + t.Fatalf("Polygenic disease contains locus references map that is longer than the diseaseLociList") + } + referencesAreValid := verifyReferencesMap(diseaseReferencesMap) if (referencesAreValid == false){ t.Fatalf("PolygenicDisease references map is invalid for disease: " + diseaseName) - } - - // We use this map to make sure each disease locus references a unique rsid - allPolygenicDiseaseRSIDsMap := make(map[int64]struct{}) - - for _, locusObject := range diseaseLociList{ - - locusIdentifier := locusObject.LocusIdentifier - locusRSID := locusObject.LocusRSID - riskWeightsMap := locusObject.RiskWeightsMap - oddsRatiosMap := locusObject.OddsRatiosMap - minimumWeight := locusObject.MinimumRiskWeight - maximumWeight := locusObject.MaximumRiskWeight - - allRSIDsMap[locusRSID] = struct{}{} - - identifierIsValid := verifyIdentifier(locusIdentifier) - if (identifierIsValid == false){ - t.Fatalf(diseaseName + " Invalid locus identifier found: " + locusIdentifier) - } - - _, exists := allIdentifiersMap[locusIdentifier] - if (exists == true){ - t.Fatalf(diseaseName + " Duplicate locus identifier found: " + locusIdentifier) - } - allIdentifiersMap[locusIdentifier] = struct{}{} - - _, exists = allPolygenicDiseaseRSIDsMap[locusRSID] - if (exists == true){ - rsidString := helpers.ConvertInt64ToString(locusRSID) - t.Fatalf(diseaseName + " RSID Collision found: " + rsidString) - } - - allPolygenicDiseaseRSIDsMap[locusRSID] = struct{}{} - - if (len(riskWeightsMap) == 0){ - t.Fatalf("Empty base weights map found: " + locusIdentifier) - } - - trueMinimumWeight := 100000 - trueMaximumWeight := -100000 - - for basePair, basePairWeight := range riskWeightsMap{ - - isValid := verifyBasePair(basePair) - if (isValid == false){ - t.Fatalf("Base pair weights map contains invalid base pair: " + locusIdentifier) - } - - if (basePairWeight < trueMinimumWeight){ - trueMinimumWeight = basePairWeight - } - if (basePairWeight > trueMaximumWeight){ - trueMaximumWeight = basePairWeight - } - } - - if (trueMinimumWeight != minimumWeight){ - t.Fatalf(diseaseName + ": Invalid minimum base pair weight found: " + locusIdentifier) - } - if (trueMaximumWeight != maximumWeight){ - t.Fatalf(diseaseName + ": Invalid maximum base pair weight found: " + locusIdentifier) - } - - for basePair, _ := range oddsRatiosMap{ - isValid := verifyBasePair(basePair) - if (isValid == false){ - t.Fatalf("Odds ratio weights map contains invalid base pair: " + locusIdentifier) - } - } - - //TODO: Make sure that duplicate base pairs have same weight, odds ratios and probabilities } } diff --git a/resources/geneticReferences/locusMetadata/LocusMetadata.gob b/resources/geneticReferences/locusMetadata/LocusMetadata.gob index d510079..c81c03b 100644 Binary files a/resources/geneticReferences/locusMetadata/LocusMetadata.gob and b/resources/geneticReferences/locusMetadata/LocusMetadata.gob differ diff --git a/resources/geneticReferences/modifyLocusMetadata/modifyLocusMetadata.go b/resources/geneticReferences/modifyLocusMetadata/modifyLocusMetadata.go index 012e1ee..962b9cc 100644 --- a/resources/geneticReferences/modifyLocusMetadata/modifyLocusMetadata.go +++ b/resources/geneticReferences/modifyLocusMetadata/modifyLocusMetadata.go @@ -43,7 +43,8 @@ func AddLocusMetadata(inputLociToAddList []locusMetadata.LocusMetadata)(int, []b _, exists := newLocusMetadataRSIDsMap[rsID] if (exists == true){ - return 0, nil, errors.New("inputLociToAddList contains multiple locus metadatas with a duplicate rsID.") + rsIDString := helpers.ConvertInt64ToString(rsID) + return 0, nil, errors.New("inputLociToAddList contains multiple locus metadatas with a duplicate rsID: " + rsIDString) } newLocusMetadataRSIDsMap[rsID] = struct{}{} diff --git a/resources/geneticReferences/polygenicDiseases/autism.go b/resources/geneticReferences/polygenicDiseases/autism.go new file mode 100644 index 0000000..d36c6e5 --- /dev/null +++ b/resources/geneticReferences/polygenicDiseases/autism.go @@ -0,0 +1,51 @@ +package polygenicDiseases + +import "errors" + +import "seekia/resources/geneticReferences/attributeLoci" + +import "seekia/internal/helpers" + + +func getAutismDiseaseObject()PolygenicDisease{ + + autismLocusReferencesMap := attributeLoci.GetAutismLoci() + + autismLociList := helpers.GetListOfMapKeys(autismLocusReferencesMap) + + referencesMap := make(map[string]string) + referencesMap["SNPedia.com - Autism"] = "https://www.snpedia.com/index.php/Autism" + + // https://www.cdc.gov/mmwr/volumes/72/ss/ss7202a1.htm + // For 2020, one in 36 children aged 8 years (approximately 4% of boys and 1% of girls) was estimated to have ASD. + + getAverageRiskProbabilitiesFunction := func(maleOrFemale string, inputAge int)(float64, error){ + + if (maleOrFemale == "Male"){ + return 0.04, nil + } + + if (maleOrFemale != "Female"){ + return 0, errors.New("Trying to get breast cancer risk probability for invalid maleOrFemale: " + maleOrFemale) + } + + //TODO: Add different probabilities per age + + return 0.01, nil + } + + autismObject := PolygenicDisease{ + + DiseaseName: "Autism", + EffectedSex: "Both", + DiseaseDescription: "A mental disorder characterized by inability to engage in normal social interactions and intense self-absorption, and usually accompanied by other symptoms such as language dysfunctions and repetitive behavior.", + // Taken from: The American Heritage® Dictionary of the English Language, 5th Edition + LocusReferencesMap: autismLocusReferencesMap, + LociList: autismLociList, + GetAverageRiskProbabilitiesFunction: getAverageRiskProbabilitiesFunction, + References: referencesMap, + } + + return autismObject +} + diff --git a/resources/geneticReferences/polygenicDiseases/breastCancer.go b/resources/geneticReferences/polygenicDiseases/breastCancer.go index 1a4d4c1..e011d2a 100644 --- a/resources/geneticReferences/polygenicDiseases/breastCancer.go +++ b/resources/geneticReferences/polygenicDiseases/breastCancer.go @@ -2,863 +2,143 @@ package polygenicDiseases import "errors" +import "seekia/internal/helpers" + func getBreastCancerDiseaseObject()PolygenicDisease{ + // Map Structure: rsID -> (map[Reference Name]Reference Link) + locusReferencesMap := make(map[int64]map[string]string) + locus1_ReferencesMap := make(map[string]string) locus1_ReferencesMap["SNPedia.com - rs16942"] = "https://www.snpedia.com/index.php/Rs16942" - locus1_RiskWeightsMap := make(map[string]int) - locus1_RiskWeightsMap["T;T"] = 0 - locus1_RiskWeightsMap["T;C"] = 1 - locus1_RiskWeightsMap["C;T"] = 1 - locus1_RiskWeightsMap["C;C"] = 1 - - locus1_OddsRatiosMap := make(map[string]float64) - locus1_OddsRatiosMap["T;T"] = 1 - locus1_OddsRatiosMap["T;C"] = 1.14 - locus1_OddsRatiosMap["C;T"] = 1.14 - locus1_OddsRatiosMap["C;C"] = 1.28 - - locus1_BasePairProbabilitiesMap := make(map[string]float64) - locus1_BasePairProbabilitiesMap["T;T"] = .45 - locus1_BasePairProbabilitiesMap["T;C"] = .45 - locus1_BasePairProbabilitiesMap["C;T"] = .45 - locus1_BasePairProbabilitiesMap["C;C"] = .10 - - locus1_Object := DiseaseLocus{ - - LocusIdentifier: "d7891c", - LocusRSID: 16942, - RiskWeightsMap: locus1_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 1, - OddsRatiosMap: locus1_OddsRatiosMap, - BasePairProbabilitiesMap: locus1_BasePairProbabilitiesMap, - References: locus1_ReferencesMap, - } + locusReferencesMap[16942] = locus1_ReferencesMap locus2_ReferencesMap := make(map[string]string) locus2_ReferencesMap["SNPedia.com - rs1045485"] = "https://www.snpedia.com/index.php/Rs1045485" - - locus2_RiskWeightsMap := make(map[string]int) - locus2_RiskWeightsMap["C;C"] = -2 - locus2_RiskWeightsMap["C;G"] = -1 - locus2_RiskWeightsMap["G;C"] = -1 - locus2_RiskWeightsMap["G;G"] = 0 - locus2_OddsRatiosMap := make(map[string]float64) - - locus2_OddsRatiosMap["C;C"] = 0.74 - locus2_OddsRatiosMap["C;G"] = 0.89 - locus2_OddsRatiosMap["G;C"] = 0.89 - locus2_OddsRatiosMap["G;G"] = 1 - - locus2_BasePairProbabilitiesMap := make(map[string]float64) - locus2_BasePairProbabilitiesMap["C;C"] = .01 - locus2_BasePairProbabilitiesMap["C;G"] = .04 - locus2_BasePairProbabilitiesMap["G;C"] = .04 - locus2_BasePairProbabilitiesMap["G;G"] = .95 - - locus2_Object := DiseaseLocus{ - - LocusIdentifier: "41c164", - LocusRSID: 1045485, - RiskWeightsMap: locus2_RiskWeightsMap, - MinimumRiskWeight: -2, - MaximumRiskWeight: 0, - OddsRatiosMap: locus2_OddsRatiosMap, - BasePairProbabilitiesMap: locus2_BasePairProbabilitiesMap, - References: locus2_ReferencesMap, - } + locusReferencesMap[1045485] = locus2_ReferencesMap locus3_ReferencesMap := make(map[string]string) locus3_ReferencesMap["SNPedia.com - rs34330"] = "https://www.snpedia.com/index.php/Rs34330" - - locus3_RiskWeightsMap := make(map[string]int) - locus3_RiskWeightsMap["C;C"] = 0 - locus3_RiskWeightsMap["T;T"] = 1 - locus3_OddsRatiosMap := make(map[string]float64) - locus3_OddsRatiosMap["C;C"] = 0 - locus3_OddsRatiosMap["T;T"] = 1.22 - - locus3_BasePairProbabilitiesMap := make(map[string]float64) - locus3_BasePairProbabilitiesMap["C;C"] = .40 - locus3_BasePairProbabilitiesMap["C;T"] = .45 - locus3_BasePairProbabilitiesMap["T;C"] = .45 - locus3_BasePairProbabilitiesMap["T;T"] = .15 - - locus3_Object := DiseaseLocus{ - - LocusIdentifier: "f3a097", - LocusRSID: 34330, - RiskWeightsMap: locus3_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 1, - OddsRatiosMap: locus3_OddsRatiosMap, - BasePairProbabilitiesMap: locus3_BasePairProbabilitiesMap, - References: locus3_ReferencesMap, - } + locusReferencesMap[34330] = locus3_ReferencesMap locus4_ReferencesMap := make(map[string]string) locus4_ReferencesMap["SNPedia.com - rs144848"] = "https://www.snpedia.com/index.php/Rs144848" - - locus4_RiskWeightsMap := make(map[string]int) - locus4_RiskWeightsMap["A;A"] = 0 - locus4_RiskWeightsMap["C;A"] = 1 - locus4_RiskWeightsMap["A;C"] = 1 - locus4_RiskWeightsMap["C;C"] = 2 - locus4_OddsRatiosMap := make(map[string]float64) - locus4_OddsRatiosMap["A;A"] = 1 - locus4_OddsRatiosMap["A;C"] = 1.14 - locus4_OddsRatiosMap["C;A"] = 1.14 - locus4_OddsRatiosMap["C;C"] = 1.31 - - locus4_BasePairProbabilitiesMap := make(map[string]float64) - locus4_BasePairProbabilitiesMap["A;A"] = .55 - locus4_BasePairProbabilitiesMap["C;A"] = .36 - locus4_BasePairProbabilitiesMap["A;C"] = .36 - locus4_BasePairProbabilitiesMap["C;C"] = .09 - - locus4_Object := DiseaseLocus{ - - LocusIdentifier: "d4626f", - LocusRSID: 144848, - RiskWeightsMap: locus4_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus4_OddsRatiosMap, - BasePairProbabilitiesMap: locus4_BasePairProbabilitiesMap, - References: locus4_ReferencesMap, - } + locusReferencesMap[144848] = locus4_ReferencesMap locus5_ReferencesMap := make(map[string]string) locus5_ReferencesMap["SNPedia.com - rs766173"] = "https://www.snpedia.com/index.php/Rs766173" - - locus5_RiskWeightsMap := make(map[string]int) - locus5_RiskWeightsMap["A;A"] = 0 - locus5_RiskWeightsMap["A;C"] = 1 - locus5_RiskWeightsMap["C;A"] = 1 - locus5_RiskWeightsMap["C;C"] = 2 - locus5_OddsRatiosMap := make(map[string]float64) - locus5_OddsRatiosMap["A;A"] = 1 - locus5_OddsRatiosMap["A;C"] = 1.14 - locus5_OddsRatiosMap["C;A"] = 1.14 - locus5_OddsRatiosMap["C;C"] = 1.28 - - locus5_BasePairProbabilitiesMap := make(map[string]float64) - locus5_BasePairProbabilitiesMap["A;A"] = .85 - locus5_BasePairProbabilitiesMap["A;C"] = .13 - locus5_BasePairProbabilitiesMap["C;A"] = .13 - locus5_BasePairProbabilitiesMap["C;C"] = .02 - - locus5_Object := DiseaseLocus{ - - LocusIdentifier: "84aaa4", - LocusRSID: 766173, - RiskWeightsMap: locus5_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus5_OddsRatiosMap, - BasePairProbabilitiesMap: locus5_BasePairProbabilitiesMap, - References: locus5_ReferencesMap, - } + locusReferencesMap[766173] = locus5_ReferencesMap locus6_ReferencesMap := make(map[string]string) locus6_ReferencesMap["SNPedia.com - rs1799950"] = "https://www.snpedia.com/index.php/Rs1799950" - locus6_RiskWeightsMap := make(map[string]int) - locus6_RiskWeightsMap["T;T"] = 0 - locus6_RiskWeightsMap["T;C"] = 1 - locus6_RiskWeightsMap["C;T"] = 1 - locus6_RiskWeightsMap["C;C"] = 2 - - locus6_OddsRatiosMap := make(map[string]float64) - locus6_OddsRatiosMap["T;T"] = 1 - locus6_OddsRatiosMap["T;C"] = 1.5 - locus6_OddsRatiosMap["C;T"] = 1.5 - locus6_OddsRatiosMap["C;C"] = 1.72 - - locus6_BasePairProbabilitiesMap := make(map[string]float64) - locus6_BasePairProbabilitiesMap["T;T"] = .98 - locus6_BasePairProbabilitiesMap["T;C"] = .02 - locus6_BasePairProbabilitiesMap["C;T"] = .02 - locus6_BasePairProbabilitiesMap["C;C"] = .001 - - locus6_Object := DiseaseLocus{ - - LocusIdentifier: "c8de7a", - LocusRSID: 1799950, - RiskWeightsMap: locus6_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus6_OddsRatiosMap, - BasePairProbabilitiesMap: locus6_BasePairProbabilitiesMap, - References: locus6_ReferencesMap, - } + locusReferencesMap[1799950] = locus6_ReferencesMap locus7_ReferencesMap := make(map[string]string) locus7_ReferencesMap["SNPedia.com - rs4986850"] = "https://www.snpedia.com/index.php/Rs4986850" - - locus7_RiskWeightsMap := make(map[string]int) - locus7_RiskWeightsMap["C;C"] = 0 - locus7_RiskWeightsMap["T;C"] = 1 - locus7_RiskWeightsMap["C;T"] = 1 - locus7_RiskWeightsMap["T;T"] = 2 - locus7_OddsRatiosMap := make(map[string]float64) - locus7_OddsRatiosMap["C;C"] = 1 - locus7_OddsRatiosMap["T;C"] = 1.14 - locus7_OddsRatiosMap["C;T"] = 1.14 - locus7_OddsRatiosMap["T;T"] = 1.28 - - locus7_BasePairProbabilitiesMap := make(map[string]float64) - locus7_BasePairProbabilitiesMap["C;C"] = .92 - locus7_BasePairProbabilitiesMap["T;C"] = .07 - locus7_BasePairProbabilitiesMap["C;T"] = .07 - locus7_BasePairProbabilitiesMap["T;T"] = .01 - - locus7_Object := DiseaseLocus{ - - LocusIdentifier: "d30087", - LocusRSID: 4986850, - RiskWeightsMap: locus7_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus7_OddsRatiosMap, - BasePairProbabilitiesMap: locus7_BasePairProbabilitiesMap, - References: locus7_ReferencesMap, - } + locusReferencesMap[4986850] = locus7_ReferencesMap locus8_ReferencesMap := make(map[string]string) locus8_ReferencesMap["SNPedia.com - rs2227945"] = "https://www.snpedia.com/index.php/Rs2227945" - - locus8_RiskWeightsMap := make(map[string]int) - locus8_RiskWeightsMap["T;T"] = 0 - locus8_RiskWeightsMap["T;C"] = 1 - locus8_RiskWeightsMap["C;T"] = 1 - locus8_RiskWeightsMap["C;C"] = 2 - locus8_OddsRatiosMap := make(map[string]float64) - locus8_OddsRatiosMap["T;T"] = 1 - locus8_OddsRatiosMap["T;C"] = 1.14 - locus8_OddsRatiosMap["C;T"] = 1.14 - locus8_OddsRatiosMap["C;C"] = 1.28 - - locus8_BasePairProbabilitiesMap := make(map[string]float64) - locus8_BasePairProbabilitiesMap["T;T"] = .97 - locus8_BasePairProbabilitiesMap["T;C"] = .03 - locus8_BasePairProbabilitiesMap["C;T"] = .03 - locus8_BasePairProbabilitiesMap["C;C"] = .001 - - locus8_Object := DiseaseLocus{ - - LocusIdentifier: "cafa72", - LocusRSID: 2227945, - RiskWeightsMap: locus8_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus8_OddsRatiosMap, - BasePairProbabilitiesMap: locus8_BasePairProbabilitiesMap, - References: locus8_ReferencesMap, - } + locusReferencesMap[2227945] = locus8_ReferencesMap locus9_ReferencesMap := make(map[string]string) locus9_ReferencesMap["SNPedia.com - rs1799966"] = "https://www.snpedia.com/index.php/Rs1799966" - - locus9_RiskWeightsMap := make(map[string]int) - locus9_RiskWeightsMap["T;T"] = 0 - locus9_RiskWeightsMap["T;C"] = 1 - locus9_RiskWeightsMap["C;T"] = 1 - locus9_RiskWeightsMap["C;C"] = 2 - locus9_OddsRatiosMap := make(map[string]float64) - locus9_OddsRatiosMap["T;T"] = 1 - locus9_OddsRatiosMap["T;C"] = 1.14 - locus9_OddsRatiosMap["C;T"] = 1.14 - locus9_OddsRatiosMap["C;C"] = 1.28 - - locus9_BasePairProbabilitiesMap := make(map[string]float64) - locus9_BasePairProbabilitiesMap["T;T"] = .45 - locus9_BasePairProbabilitiesMap["T;C"] = .45 - locus9_BasePairProbabilitiesMap["C;T"] = .45 - locus9_BasePairProbabilitiesMap["C;C"] = .10 - - locus9_Object := DiseaseLocus{ - - LocusIdentifier: "8f671c", - LocusRSID: 1799966, - RiskWeightsMap: locus9_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus9_OddsRatiosMap, - BasePairProbabilitiesMap: locus9_BasePairProbabilitiesMap, - References: locus9_ReferencesMap, - } + locusReferencesMap[1799966] = locus9_ReferencesMap locus10_ReferencesMap := make(map[string]string) locus10_ReferencesMap["SNPedia.com - rs4987117"] = "https://www.snpedia.com/index.php/Rs4987117" - - locus10_RiskWeightsMap := make(map[string]int) - locus10_RiskWeightsMap["C;C"] = 0 - locus10_RiskWeightsMap["T;C"] = 1 - locus10_RiskWeightsMap["C;T"] = 1 - locus10_RiskWeightsMap["T;T"] = 2 - locus10_OddsRatiosMap := make(map[string]float64) - locus10_OddsRatiosMap["C;C"] = 1 - locus10_OddsRatiosMap["T;C"] = 1.14 - locus10_OddsRatiosMap["C;T"] = 1.14 - locus10_OddsRatiosMap["T;T"] = 1.28 - - locus10_BasePairProbabilitiesMap := make(map[string]float64) - locus10_BasePairProbabilitiesMap["C;C"] = .98 - locus10_BasePairProbabilitiesMap["T;C"] = .02 - locus10_BasePairProbabilitiesMap["C;T"] = .02 - locus10_BasePairProbabilitiesMap["T;T"] = .001 - - locus10_Object := DiseaseLocus{ - - LocusIdentifier: "b3e49a", - LocusRSID: 4987117, - RiskWeightsMap: locus10_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus10_OddsRatiosMap, - BasePairProbabilitiesMap: locus10_BasePairProbabilitiesMap, - References: locus10_ReferencesMap, - } + locusReferencesMap[4987117] = locus10_ReferencesMap locus11_ReferencesMap := make(map[string]string) locus11_ReferencesMap["SNPedia.com - rs1799954"] = "https://www.snpedia.com/index.php/Rs1799954" - - locus11_RiskWeightsMap := make(map[string]int) - locus11_RiskWeightsMap["C;C"] = 0 - locus11_RiskWeightsMap["T;C"] = 1 - locus11_RiskWeightsMap["C;T"] = 1 - locus11_RiskWeightsMap["T;T"] = 2 - locus11_OddsRatiosMap := make(map[string]float64) - locus11_OddsRatiosMap["C;C"] = 1 - locus11_OddsRatiosMap["T;C"] = 1.14 - locus11_OddsRatiosMap["C;T"] = 1.14 - locus11_OddsRatiosMap["T;T"] = 1.28 - - locus11_BasePairProbabilitiesMap := make(map[string]float64) - locus11_BasePairProbabilitiesMap["C;C"] = .97 - locus11_BasePairProbabilitiesMap["T;C"] = .03 - locus11_BasePairProbabilitiesMap["C;T"] = .03 - locus11_BasePairProbabilitiesMap["T;T"] = .001 - - locus11_Object := DiseaseLocus{ - - LocusIdentifier: "8b0b02", - LocusRSID: 1799954, - RiskWeightsMap: locus11_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus11_OddsRatiosMap, - BasePairProbabilitiesMap: locus11_BasePairProbabilitiesMap, - References: locus11_ReferencesMap, - } + locusReferencesMap[1799954] = locus11_ReferencesMap locus12_ReferencesMap := make(map[string]string) locus12_ReferencesMap["SNPedia.com - rs11571746"] = "https://www.snpedia.com/index.php/Rs11571746" - locus12_RiskWeightsMap := make(map[string]int) - locus12_RiskWeightsMap["T;T"] = 0 - locus12_RiskWeightsMap["T;C"] = 1 - locus12_RiskWeightsMap["C;T"] = 1 - locus12_RiskWeightsMap["C;C"] = 2 - - locus12_OddsRatiosMap := make(map[string]float64) - locus12_OddsRatiosMap["T;T"] = 1 - locus12_OddsRatiosMap["T;C"] = 1.14 - locus12_OddsRatiosMap["C;T"] = 1.14 - locus12_OddsRatiosMap["C;C"] = 1.28 - - locus12_BasePairProbabilitiesMap := make(map[string]float64) - locus12_BasePairProbabilitiesMap["T;T"] = .98 - locus12_BasePairProbabilitiesMap["T;C"] = .02 - locus12_BasePairProbabilitiesMap["C;T"] = .02 - locus12_BasePairProbabilitiesMap["C;C"] = .001 - - locus12_Object := DiseaseLocus{ - - LocusIdentifier: "25cafc", - LocusRSID: 11571746, - RiskWeightsMap: locus12_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus12_OddsRatiosMap, - BasePairProbabilitiesMap: locus12_BasePairProbabilitiesMap, - References: locus12_ReferencesMap, - } + locusReferencesMap[11571746] = locus12_ReferencesMap locus13_ReferencesMap := make(map[string]string) locus13_ReferencesMap["SNPedia.com - rs11571747"] = "https://www.snpedia.com/index.php/Rs11571747" - locus13_RiskWeightsMap := make(map[string]int) - locus13_RiskWeightsMap["A;A"] = 0 - locus13_RiskWeightsMap["A;C"] = 1 - locus13_RiskWeightsMap["C;A"] = 1 - locus13_RiskWeightsMap["C;C"] = 2 - - locus13_OddsRatiosMap := make(map[string]float64) - locus13_OddsRatiosMap["A;A"] = 1 - locus13_OddsRatiosMap["A;C"] = 1.14 - locus13_OddsRatiosMap["C;A"] = 1.14 - locus13_OddsRatiosMap["C;C"] = 1.28 - - locus13_BasePairProbabilitiesMap := make(map[string]float64) - locus13_BasePairProbabilitiesMap["A;A"] = .99 - locus13_BasePairProbabilitiesMap["A;C"] = .001 - locus13_BasePairProbabilitiesMap["C;A"] = .001 - locus13_BasePairProbabilitiesMap["C;C"] = .001 - - locus13_Object := DiseaseLocus{ - - LocusIdentifier: "34c7e5", - LocusRSID: 11571747, - RiskWeightsMap: locus13_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus13_OddsRatiosMap, - BasePairProbabilitiesMap: locus13_BasePairProbabilitiesMap, - References: locus13_ReferencesMap, - } + locusReferencesMap[11571747] = locus13_ReferencesMap locus14_ReferencesMap := make(map[string]string) locus14_ReferencesMap["SNPedia.com - rs4987047"] = "https://www.snpedia.com/index.php/Rs4987047" - locus14_RiskWeightsMap := make(map[string]int) - locus14_RiskWeightsMap["A;A"] = 0 - locus14_RiskWeightsMap["A;T"] = 1 - locus14_RiskWeightsMap["T;A"] = 1 - locus14_RiskWeightsMap["T;T"] = 2 - - locus14_OddsRatiosMap := make(map[string]float64) - locus14_OddsRatiosMap["A;A"] = 1 - locus14_OddsRatiosMap["A;T"] = 1.14 - locus14_OddsRatiosMap["T;A"] = 1.14 - locus14_OddsRatiosMap["T;T"] = 1.28 - - locus14_BasePairProbabilitiesMap := make(map[string]float64) - locus14_BasePairProbabilitiesMap["A;A"] = .94 - locus14_BasePairProbabilitiesMap["A;T"] = .94 - locus14_BasePairProbabilitiesMap["T;A"] = .05 - locus14_BasePairProbabilitiesMap["T;T"] = .01 - - locus14_Object := DiseaseLocus{ - - LocusIdentifier: "60ce27", - LocusRSID: 4987047, - RiskWeightsMap: locus14_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus14_OddsRatiosMap, - BasePairProbabilitiesMap: locus14_BasePairProbabilitiesMap, - References: locus14_ReferencesMap, - } + locusReferencesMap[4987047] = locus14_ReferencesMap locus15_ReferencesMap := make(map[string]string) locus15_ReferencesMap["SNPedia.com - rs11571833"] = "https://www.snpedia.com/index.php/Rs11571833" - locus15_RiskWeightsMap := make(map[string]int) - locus15_RiskWeightsMap["A;A"] = 0 - locus15_RiskWeightsMap["T;A"] = 1 - locus15_RiskWeightsMap["A;T"] = 1 - locus15_RiskWeightsMap["T;T"] = 2 + locusReferencesMap[11571833] = locus15_ReferencesMap - locus15_OddsRatiosMap := make(map[string]float64) - locus15_OddsRatiosMap["A;A"] = 1 - locus15_OddsRatiosMap["T;A"] = 1.14 - locus15_OddsRatiosMap["A;T"] = 1.14 - locus15_OddsRatiosMap["T;T"] = 1.28 - - locus15_BasePairProbabilitiesMap := make(map[string]float64) - locus15_BasePairProbabilitiesMap["A;A"] = .99 - locus15_BasePairProbabilitiesMap["T;A"] = .01 - locus15_BasePairProbabilitiesMap["A;T"] = .01 - locus15_BasePairProbabilitiesMap["T;T"] = .001 - - locus15_Object := DiseaseLocus{ - - LocusIdentifier: "328cdf", - LocusRSID: 11571833, - RiskWeightsMap: locus15_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus15_OddsRatiosMap, - BasePairProbabilitiesMap: locus15_BasePairProbabilitiesMap, - References: locus15_ReferencesMap, - } - locus16_ReferencesMap := make(map[string]string) locus16_ReferencesMap["SNPedia.com - rs1801426"] = "https://www.snpedia.com/index.php/Rs1801426" - locus16_RiskWeightsMap := make(map[string]int) - locus16_RiskWeightsMap["A;A"] = 0 - locus16_RiskWeightsMap["G;A"] = 1 - locus16_RiskWeightsMap["A;G"] = 1 - locus16_RiskWeightsMap["G;G"] = 2 - - locus16_OddsRatiosMap := make(map[string]float64) - locus16_OddsRatiosMap["A;A"] = 1 - locus16_OddsRatiosMap["G;A"] = 1.14 - locus16_OddsRatiosMap["A;G"] = 1.14 - locus16_OddsRatiosMap["G;G"] = 1.28 - - locus16_BasePairProbabilitiesMap := make(map[string]float64) - locus16_BasePairProbabilitiesMap["A;A"] = .90 - locus16_BasePairProbabilitiesMap["G;A"] = .09 - locus16_BasePairProbabilitiesMap["A;G"] = .09 - locus16_BasePairProbabilitiesMap["G;G"] = .01 - - locus16_Object := DiseaseLocus{ - - LocusIdentifier: "849bc7", - LocusRSID: 1801426, - RiskWeightsMap: locus16_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus16_OddsRatiosMap, - BasePairProbabilitiesMap: locus16_BasePairProbabilitiesMap, - References: locus16_ReferencesMap, - } + locusReferencesMap[1801426] = locus16_ReferencesMap locus17_ReferencesMap := make(map[string]string) locus17_ReferencesMap["SNPedia.com - rs3218707"] = "https://www.snpedia.com/index.php/Rs3218707" - locus17_RiskWeightsMap := make(map[string]int) - locus17_RiskWeightsMap["G;G"] = 0 - locus17_RiskWeightsMap["G;C"] = 1 - locus17_RiskWeightsMap["C;G"] = 1 - locus17_RiskWeightsMap["C;C"] = 2 - - locus17_OddsRatiosMap := make(map[string]float64) - locus17_OddsRatiosMap["G;G"] = 1 - locus17_OddsRatiosMap["G;C"] = 1.14 - locus17_OddsRatiosMap["C;G"] = 1.14 - locus17_OddsRatiosMap["C;C"] = 1.28 - - locus17_BasePairProbabilitiesMap := make(map[string]float64) - locus17_BasePairProbabilitiesMap["G;G"] = .96 - locus17_BasePairProbabilitiesMap["G;C"] = .04 - locus17_BasePairProbabilitiesMap["C;G"] = .04 - locus17_BasePairProbabilitiesMap["C;C"] = .001 - - locus17_Object := DiseaseLocus{ - - LocusIdentifier: "5af5e3", - LocusRSID: 3218707, - RiskWeightsMap: locus17_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus17_OddsRatiosMap, - BasePairProbabilitiesMap: locus17_BasePairProbabilitiesMap, - References: locus17_ReferencesMap, - } + locusReferencesMap[3218707] = locus17_ReferencesMap locus18_ReferencesMap := make(map[string]string) locus18_ReferencesMap["SNPedia.com - rs4987945"] = "https://www.snpedia.com/index.php/Rs4987945" - locus18_RiskWeightsMap := make(map[string]int) - locus18_RiskWeightsMap["C;C"] = 0 - locus18_RiskWeightsMap["C;G"] = 1 - locus18_RiskWeightsMap["G;C"] = 1 - locus18_RiskWeightsMap["G;G"] = 2 - - locus18_OddsRatiosMap := make(map[string]float64) - locus18_OddsRatiosMap["C;C"] = 1 - locus18_OddsRatiosMap["C;G"] = 1.14 - locus18_OddsRatiosMap["G;C"] = 1.14 - locus18_OddsRatiosMap["G;G"] = 1.28 - - locus18_BasePairProbabilitiesMap := make(map[string]float64) - locus18_BasePairProbabilitiesMap["C;C"] = .95 - locus18_BasePairProbabilitiesMap["C;G"] = .04 - locus18_BasePairProbabilitiesMap["G;C"] = .04 - locus18_BasePairProbabilitiesMap["G;G"] = .01 - - locus18_Object := DiseaseLocus{ - - LocusIdentifier: "c354fa", - LocusRSID: 4987945, - RiskWeightsMap: locus18_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus18_OddsRatiosMap, - BasePairProbabilitiesMap: locus18_BasePairProbabilitiesMap, - References: locus18_ReferencesMap, - } + locusReferencesMap[4987945] = locus18_ReferencesMap locus19_ReferencesMap := make(map[string]string) locus19_ReferencesMap["SNPedia.com - rs4986761"] = "https://www.snpedia.com/index.php/Rs4986761" - locus19_RiskWeightsMap := make(map[string]int) - locus19_RiskWeightsMap["T;T"] = 0 - locus19_RiskWeightsMap["C;T"] = 1 - locus19_RiskWeightsMap["T;C"] = 1 - locus19_RiskWeightsMap["C;C"] = 2 - - locus19_OddsRatiosMap := make(map[string]float64) - locus19_OddsRatiosMap["T;T"] = 1 - locus19_OddsRatiosMap["C;T"] = 1.05 - locus19_OddsRatiosMap["T;C"] = 1.05 - locus19_OddsRatiosMap["C;C"] = 1.51 - - locus19_BasePairProbabilitiesMap := make(map[string]float64) - locus19_BasePairProbabilitiesMap["T;T"] = .99 - locus19_BasePairProbabilitiesMap["C;T"] = .01 - locus19_BasePairProbabilitiesMap["T;C"] = .01 - locus19_BasePairProbabilitiesMap["C;C"] = .001 - - locus19_Object := DiseaseLocus{ - - LocusIdentifier: "eedc23", - LocusRSID: 4986761, - RiskWeightsMap: locus19_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus19_OddsRatiosMap, - BasePairProbabilitiesMap: locus19_BasePairProbabilitiesMap, - References: locus19_ReferencesMap, - } + locusReferencesMap[4986761] = locus19_ReferencesMap locus20_ReferencesMap := make(map[string]string) locus20_ReferencesMap["SNPedia.com - rs3218695"] = "https://www.snpedia.com/index.php/Rs3218695" - locus20_RiskWeightsMap := make(map[string]int) - locus20_RiskWeightsMap["C;C"] = 0 - locus20_RiskWeightsMap["C;A"] = 1 - locus20_RiskWeightsMap["A;C"] = 1 - locus20_RiskWeightsMap["A;A"] = 2 - - locus20_OddsRatiosMap := make(map[string]float64) - locus20_OddsRatiosMap["C;C"] = 1 - locus20_OddsRatiosMap["C;A"] = 1.14 - locus20_OddsRatiosMap["A;C"] = 1.14 - locus20_OddsRatiosMap["A;A"] = 1.28 - - locus20_BasePairProbabilitiesMap := make(map[string]float64) - locus20_BasePairProbabilitiesMap["C;C"] = .98 - locus20_BasePairProbabilitiesMap["C;A"] = .02 - locus20_BasePairProbabilitiesMap["A;C"] = .02 - locus20_BasePairProbabilitiesMap["A;A"] = .001 - - locus20_Object := DiseaseLocus{ - - LocusIdentifier: "2ee027", - LocusRSID: 3218695, - RiskWeightsMap: locus20_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus20_OddsRatiosMap, - BasePairProbabilitiesMap: locus20_BasePairProbabilitiesMap, - References: locus20_ReferencesMap, - } + locusReferencesMap[3218695] = locus20_ReferencesMap locus21_ReferencesMap := make(map[string]string) locus21_ReferencesMap["SNPedia.com - rs1800056"] = "https://www.snpedia.com/index.php/Rs1800056" - locus21_RiskWeightsMap := make(map[string]int) - locus21_RiskWeightsMap["T;T"] = 0 - locus21_RiskWeightsMap["C;T"] = 1 - locus21_RiskWeightsMap["T;C"] = 1 - locus21_RiskWeightsMap["C;C"] = 2 - - locus21_OddsRatiosMap := make(map[string]float64) - locus21_OddsRatiosMap["T;T"] = 1 - locus21_OddsRatiosMap["C;T"] = 1.05 - locus21_OddsRatiosMap["T;C"] = 1.05 - locus21_OddsRatiosMap["C;C"] = 1.51 - - locus21_BasePairProbabilitiesMap := make(map[string]float64) - locus21_BasePairProbabilitiesMap["T;T"] = .97 - locus21_BasePairProbabilitiesMap["C;T"] = .03 - locus21_BasePairProbabilitiesMap["T;C"] = .03 - locus21_BasePairProbabilitiesMap["C;C"] = .001 - - locus21_Object := DiseaseLocus{ - - LocusIdentifier: "fc4bab", - LocusRSID: 1800056, - RiskWeightsMap: locus21_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus21_OddsRatiosMap, - BasePairProbabilitiesMap: locus21_BasePairProbabilitiesMap, - References: locus21_ReferencesMap, - } + locusReferencesMap[1800056] = locus21_ReferencesMap locus22_ReferencesMap := make(map[string]string) locus22_ReferencesMap["SNPedia.com - rs1800057"] = "https://www.snpedia.com/index.php/Rs1800057" - locus22_RiskWeightsMap := make(map[string]int) - locus22_RiskWeightsMap["C;C"] = 0 - locus22_RiskWeightsMap["C;G"] = 1 - locus22_RiskWeightsMap["G;C"] = 1 - locus22_RiskWeightsMap["G;G"] = 2 - - locus22_OddsRatiosMap := make(map[string]float64) - locus22_OddsRatiosMap["C;C"] = 1 - locus22_OddsRatiosMap["C;G"] = 1.05 - locus22_OddsRatiosMap["G;C"] = 1.05 - locus22_OddsRatiosMap["G;G"] = 1.51 - - locus22_BasePairProbabilitiesMap := make(map[string]float64) - locus22_BasePairProbabilitiesMap["C;C"] = .97 - locus22_BasePairProbabilitiesMap["C;G"] = .03 - locus22_BasePairProbabilitiesMap["G;C"] = .03 - locus22_BasePairProbabilitiesMap["G;G"] = .001 - - locus22_Object := DiseaseLocus{ - - LocusIdentifier: "f8b225", - LocusRSID: 1800057, - RiskWeightsMap: locus22_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus22_OddsRatiosMap, - BasePairProbabilitiesMap: locus22_BasePairProbabilitiesMap, - References: locus22_ReferencesMap, - } + locusReferencesMap[1800057] = locus22_ReferencesMap locus23_ReferencesMap := make(map[string]string) locus23_ReferencesMap["SNPedia.com - rs3092856"] = "https://www.snpedia.com/index.php/Rs3092856" - locus23_RiskWeightsMap := make(map[string]int) - locus23_RiskWeightsMap["C;C"] = 0 - locus23_RiskWeightsMap["C;T"] = 1 - locus23_RiskWeightsMap["T;C"] = 1 - locus23_RiskWeightsMap["T;T"] = 2 - - locus23_OddsRatiosMap := make(map[string]float64) - locus23_OddsRatiosMap["C;C"] = 1 - locus23_OddsRatiosMap["C;T"] = 1.14 - locus23_OddsRatiosMap["T;C"] = 1.14 - locus23_OddsRatiosMap["T;T"] = 1.28 - - locus23_BasePairProbabilitiesMap := make(map[string]float64) - locus23_BasePairProbabilitiesMap["C;C"] = .95 - locus23_BasePairProbabilitiesMap["C;T"] = .05 - locus23_BasePairProbabilitiesMap["T;C"] = .05 - locus23_BasePairProbabilitiesMap["T;T"] = .001 - - locus23_Object := DiseaseLocus{ - - LocusIdentifier: "4a072c", - LocusRSID: 3092856, - RiskWeightsMap: locus23_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus23_OddsRatiosMap, - BasePairProbabilitiesMap: locus23_BasePairProbabilitiesMap, - References: locus23_ReferencesMap, - } + locusReferencesMap[3092856] = locus23_ReferencesMap locus24_ReferencesMap := make(map[string]string) locus24_ReferencesMap["SNPedia.com - rs1800058"] = "https://www.snpedia.com/index.php/Rs1800058" - locus24_RiskWeightsMap := make(map[string]int) - locus24_RiskWeightsMap["C;C"] = 0 - locus24_RiskWeightsMap["C;T"] = 1 - locus24_RiskWeightsMap["T;C"] = 1 - locus24_RiskWeightsMap["T;T"] = 2 - - locus24_OddsRatiosMap := make(map[string]float64) - locus24_OddsRatiosMap["C;C"] = 1 - locus24_OddsRatiosMap["C;T"] = 1.05 - locus24_OddsRatiosMap["T;C"] = 1.05 - locus24_OddsRatiosMap["T;T"] = 1.51 - - locus24_BasePairProbabilitiesMap := make(map[string]float64) - locus24_BasePairProbabilitiesMap["C;C"] = .95 - locus24_BasePairProbabilitiesMap["C;T"] = .04 - locus24_BasePairProbabilitiesMap["T;C"] = .04 - locus24_BasePairProbabilitiesMap["T;T"] = .01 - - locus24_Object := DiseaseLocus{ - - LocusIdentifier: "070f24", - LocusRSID: 1800058, - RiskWeightsMap: locus24_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus24_OddsRatiosMap, - BasePairProbabilitiesMap: locus24_BasePairProbabilitiesMap, - References: locus24_ReferencesMap, - } + locusReferencesMap[1800058] = locus24_ReferencesMap locus25_ReferencesMap := make(map[string]string) locus25_ReferencesMap["SNPedia.com - rs1801673"] = "https://www.snpedia.com/index.php/Rs1801673" - locus25_RiskWeightsMap := make(map[string]int) - locus25_RiskWeightsMap["A;A"] = 0 - locus25_RiskWeightsMap["A;T"] = 1 - locus25_RiskWeightsMap["T;A"] = 1 - locus25_RiskWeightsMap["T;T"] = 2 - - locus25_OddsRatiosMap := make(map[string]float64) - locus25_OddsRatiosMap["A;A"] = 1 - locus25_OddsRatiosMap["A;T"] = 1.14 - locus25_OddsRatiosMap["T;A"] = 1.14 - locus25_OddsRatiosMap["T;T"] = 1.28 - - locus25_BasePairProbabilitiesMap := make(map[string]float64) - locus25_BasePairProbabilitiesMap["A;A"] = .99 - locus25_BasePairProbabilitiesMap["A;T"] = .01 - locus25_BasePairProbabilitiesMap["T;A"] = .01 - locus25_BasePairProbabilitiesMap["T;T"] = .001 - - locus25_Object := DiseaseLocus{ - - LocusIdentifier: "d08516", - LocusRSID: 1801673, - RiskWeightsMap: locus25_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus25_OddsRatiosMap, - BasePairProbabilitiesMap: locus25_BasePairProbabilitiesMap, - References: locus25_ReferencesMap, - } + locusReferencesMap[1801673] = locus25_ReferencesMap locus26_ReferencesMap := make(map[string]string) locus26_ReferencesMap["SNPedia.com - rs17879961"] = "https://www.snpedia.com/index.php/Rs17879961" - locus26_RiskWeightsMap := make(map[string]int) - locus26_RiskWeightsMap["A;A"] = 0 - locus26_RiskWeightsMap["A;G"] = 1 - locus26_RiskWeightsMap["G;A"] = 1 - locus26_RiskWeightsMap["G;G"] = 2 - - locus26_OddsRatiosMap := make(map[string]float64) - locus26_OddsRatiosMap["A;A"] = 1 - locus26_OddsRatiosMap["A;G"] = 1.14 - locus26_OddsRatiosMap["G;A"] = 1.14 - locus26_OddsRatiosMap["G;G"] = 1.28 - - locus26_BasePairProbabilitiesMap := make(map[string]float64) - locus26_BasePairProbabilitiesMap["A;A"] = .98 - locus26_BasePairProbabilitiesMap["A;G"] = .02 - locus26_BasePairProbabilitiesMap["G;A"] = .02 - locus26_BasePairProbabilitiesMap["G;G"] = .001 - - locus26_Object := DiseaseLocus{ - - LocusIdentifier: "047b84", - LocusRSID: 17879961, - RiskWeightsMap: locus26_RiskWeightsMap, - MinimumRiskWeight: 0, - MaximumRiskWeight: 2, - OddsRatiosMap: locus26_OddsRatiosMap, - BasePairProbabilitiesMap: locus26_BasePairProbabilitiesMap, - References: locus26_ReferencesMap, - } + locusReferencesMap[17879961] = locus26_ReferencesMap // TODO: //-https://www.snpedia.com/index.php/Rs1042522 @@ -883,7 +163,7 @@ func getBreastCancerDiseaseObject()PolygenicDisease{ //-https://www.snpedia.com/index.php/Rs7895676 //-https://www.snpedia.com/index.php/Rs140068132 - breastCancerLociList := []DiseaseLocus{locus1_Object, locus2_Object, locus3_Object, locus4_Object, locus5_Object, locus6_Object, locus7_Object, locus8_Object, locus9_Object, locus10_Object, locus11_Object, locus12_Object, locus13_Object, locus14_Object, locus15_Object, locus16_Object, locus17_Object, locus18_Object, locus19_Object, locus20_Object, locus21_Object, locus22_Object, locus23_Object, locus24_Object, locus25_Object, locus26_Object} + breastCancerLociList := helpers.GetListOfMapKeys(locusReferencesMap) referencesMap := make(map[string]string) referencesMap["SNPedia.com - Breast Cancer"] = "https://www.snpedia.com/index.php/Breast_cancer" @@ -948,6 +228,7 @@ func getBreastCancerDiseaseObject()PolygenicDisease{ DiseaseName: "Breast Cancer", EffectedSex: "Both", DiseaseDescription: "Cancer growth in the tissue of a person's chest breast.", + LocusReferencesMap: locusReferencesMap, LociList: breastCancerLociList, GetAverageRiskProbabilitiesFunction: getAverageRiskProbabilitiesFunction, References: referencesMap, diff --git a/resources/geneticReferences/polygenicDiseases/polygenicDiseases.go b/resources/geneticReferences/polygenicDiseases/polygenicDiseases.go index 24014b0..bef6697 100644 --- a/resources/geneticReferences/polygenicDiseases/polygenicDiseases.go +++ b/resources/geneticReferences/polygenicDiseases/polygenicDiseases.go @@ -1,5 +1,5 @@ -// polygenicDiseases provides information about polygenic diseases and the SNP base changes that effect a person's risk of becoming victim to them. +// polygenicDiseases provides information about polygenic diseases and the loci that influence them package polygenicDiseases @@ -8,42 +8,8 @@ package polygenicDiseases // Polygenic disease probabilities are less accurate, because individual base pair changes only cause comparatively small changes in the disease risk. // Polygenic diseases are also more influenced by environmental factors, further decreasing risk accuracy. -//TODO: Eventually we want to use neural networks for polygenic disease prediction. -// This package is currently a less accurate solution until we get access to the necessary training data. - import "errors" -// DiseaseLocus is a location on a human genome that has an effect on the disease -type DiseaseLocus struct{ - - // 3 byte identifier, encoded in Hex. - LocusIdentifier string - - // RSID that represents this locus - // If multiple RSIDs represent the same locus, use the first rsID for the locus in the locusMetadata package - LocusRSID int64 - - // Map Structure: Base pair -> Effect weight (Positive number = increased risk, negative number = decreased risk) - // The number only indicates a general effect of the base, for bases for which we do not have risk probability statistics - // 0 indicates that the base has no effect - RiskWeightsMap map[string]int - - // Minimum and maximum values in above map - MinimumRiskWeight int - MaximumRiskWeight int - - // Map Structure: Base Pair -> Odds ratio of BasePair/Normal (common) Base pair - // Number is greater than 1 = Increases risk, Number is less than 1 = decreases risk - // 1 indicates that the base has no impact - OddsRatiosMap map[string]float64 - - // Map Structure: Base Pair -> Probability that a person will have that base pair for the general population - BasePairProbabilitiesMap map[string]float64 - - // Map Structure: Reference name -> Reference link - References map[string]string -} - type PolygenicDisease struct{ @@ -54,7 +20,14 @@ type PolygenicDisease struct{ // Is either "Mate"/"Female"/"Both" EffectedSex string - LociList []DiseaseLocus + // This is a list of rsIDs which are known to have an effect on this disease + // We use these loci to predict trait outcomes with neural networks. + // Map Structure: rsID -> (map[Reference Name]Reference Link) + LocusReferencesMap map[int64]map[string]string + + // This is a list of all loci used to predict this disease's risk + // This should be a list of the keys in LocusReferencesMap + LociList []int64 // Inputs: // -string: "Mate"/"Female" @@ -64,10 +37,12 @@ type PolygenicDisease struct{ // -error GetAverageRiskProbabilitiesFunction func(string, int)(float64, error) + // This map contains scientific resources about this disease // Map Structure: Reference name -> Reference link References map[string]string } + var polygenicDiseaseNamesList []string var polygenicDiseaseObjectsList []PolygenicDisease @@ -75,8 +50,9 @@ var polygenicDiseaseObjectsList []PolygenicDisease func InitializePolygenicDiseaseVariables(){ breastCancerObject := getBreastCancerDiseaseObject() + autismObject := getAutismDiseaseObject() - polygenicDiseaseObjectsList = []PolygenicDisease{breastCancerObject} + polygenicDiseaseObjectsList = []PolygenicDisease{breastCancerObject, autismObject} polygenicDiseaseNamesList = make([]string, 0, len(polygenicDiseaseObjectsList)) @@ -94,7 +70,7 @@ func InitializePolygenicDiseaseVariables(){ func GetPolygenicDiseaseNamesList()([]string, error){ if (polygenicDiseaseNamesList == nil){ - return nil, errors.New("GetDiseaseNamesList called when list is not initialized.") + return nil, errors.New("GetPolygenicDiseaseNamesList called when list is not initialized.") } return polygenicDiseaseNamesList, nil @@ -127,40 +103,3 @@ func GetPolygenicDiseaseObject(diseaseName string)(PolygenicDisease, error){ return PolygenicDisease{}, errors.New("GetPolygenicDiseaseObject called with unknown disease name: " + diseaseName) } - -//Outputs: -// -map[string]DiseaseLocus: Map of LocusIdentifier -> LocusObject -// -error (will return err if diseaseName is not found) -func GetPolygenicDiseaseLociMap(diseaseName string)(map[string]DiseaseLocus, error){ - - diseaseObject, err := GetPolygenicDiseaseObject(diseaseName) - if (err != nil) { return nil, err } - - diseaseLociList := diseaseObject.LociList - - diseaseLociMap := make(map[string]DiseaseLocus) - - for _, locusObject := range diseaseLociList{ - - locusIdentifier := locusObject.LocusIdentifier - diseaseLociMap[locusIdentifier] = locusObject - } - - return diseaseLociMap, nil -} - -func GetPolygenicDiseaseLocusObject(diseaseName string, locusIdentifier string)(DiseaseLocus, error){ - - diseaseLociMap, err := GetPolygenicDiseaseLociMap(diseaseName) - if (err != nil){ return DiseaseLocus{}, err } - - locusObject, exists := diseaseLociMap[locusIdentifier] - if (exists == false){ - return DiseaseLocus{}, errors.New("GetDiseaseLocusObject called with unknown locus identifier: " + locusIdentifier) - } - - return locusObject, nil -} - - - diff --git a/resources/geneticReferences/traits/eyeColor.go b/resources/geneticReferences/traits/eyeColor.go index e24439b..f490003 100644 --- a/resources/geneticReferences/traits/eyeColor.go +++ b/resources/geneticReferences/traits/eyeColor.go @@ -164,6 +164,7 @@ func getEyeColorTraitObject()Trait{ LociList_Rules: []int64{}, RulesList: []TraitRule{}, OutcomesList: []string{"Blue", "Green", "Hazel", "Brown"}, + NumericValueFormatter: nil, ReferencesMap: referencesMap, } diff --git a/resources/geneticReferences/traits/facialStructure.go b/resources/geneticReferences/traits/facialStructure.go index b5c2086..b054ad7 100644 --- a/resources/geneticReferences/traits/facialStructure.go +++ b/resources/geneticReferences/traits/facialStructure.go @@ -142,6 +142,7 @@ func getFacialStructureTraitObject()Trait{ LociList_Rules: []int64{}, RulesList: []TraitRule{}, OutcomesList: []string{}, + NumericValueFormatter: nil, ReferencesMap: referencesMap, } diff --git a/resources/geneticReferences/traits/hairColor.go b/resources/geneticReferences/traits/hairColor.go index 6e71b10..fab291d 100644 --- a/resources/geneticReferences/traits/hairColor.go +++ b/resources/geneticReferences/traits/hairColor.go @@ -62,6 +62,7 @@ func getHairColorTraitObject()Trait{ LociList_Rules: []int64{}, RulesList: []TraitRule{}, OutcomesList: []string{}, + NumericValueFormatter: nil, ReferencesMap: referencesMap, } diff --git a/resources/geneticReferences/traits/hairTexture.go b/resources/geneticReferences/traits/hairTexture.go index b731eda..46fd4e6 100644 --- a/resources/geneticReferences/traits/hairTexture.go +++ b/resources/geneticReferences/traits/hairTexture.go @@ -245,6 +245,7 @@ func getHairTextureTraitObject()Trait{ LociList_Rules: lociList_Rules, RulesList: hairTextureRulesList, OutcomesList: outcomesList, + NumericValueFormatter: nil, ReferencesMap: referencesMap, } diff --git a/resources/geneticReferences/traits/height.go b/resources/geneticReferences/traits/height.go index b0152bb..09ef4c0 100644 --- a/resources/geneticReferences/traits/height.go +++ b/resources/geneticReferences/traits/height.go @@ -1,11 +1,13 @@ package traits +import "seekia/internal/globalSettings" import "seekia/internal/helpers" import "maps" import _ "embed" +import "errors" import "encoding/gob" import "bytes" @@ -50,6 +52,40 @@ func getHeightTraitObject()(Trait, error){ referencesMap := make(map[string]string) referencesMap["GIANT consortium - Meta-analyses of Genome-Wide Association Studies - 2022 - Height"] = "https://portals.broadinstitute.org/collaboration/giant/index.php/GIANT_consortium_data_files" + numericValueFormatter := func(inputHeight float64, _ bool)(string, error){ + + getMyMetricOrImperial := func()(string, error){ + + exists, metricOrImperial, err := globalSettings.GetSetting("MetricOrImperial") + if (err != nil) { return "", err } + if (exists == false){ + return "Metric", nil + } + if (metricOrImperial != "Metric" && metricOrImperial != "Imperial"){ + return "", errors.New("Malformed globalSettings: Invalid metricOrImperial: " + metricOrImperial) + } + + return metricOrImperial, nil + } + + myMetricOrImperial, err := getMyMetricOrImperial() + if (err != nil){ return "", err } + if (myMetricOrImperial == "Metric"){ + centimetersString := helpers.ConvertFloat64ToStringRounded(inputHeight, 2) + + //TODO: Translate units + + centimetersWithUnits := centimetersString + " centimeters" + + return centimetersWithUnits, nil + } + + feetInchesString, err := helpers.ConvertCentimetersToFeetInchesTranslatedString(inputHeight) + if (err != nil) { return "", err } + + return feetInchesString, nil + } + heightObject := Trait{ TraitName: "Height", TraitDescription: "The distance between the top of a standing person head and the floor.", @@ -59,6 +95,7 @@ func getHeightTraitObject()(Trait, error){ LociList_Rules: []int64{}, RulesList: []TraitRule{}, OutcomesList: []string{}, + NumericValueFormatter: numericValueFormatter, ReferencesMap: referencesMap, } diff --git a/resources/geneticReferences/traits/homosexualness.go b/resources/geneticReferences/traits/homosexualness.go new file mode 100644 index 0000000..b4e9618 --- /dev/null +++ b/resources/geneticReferences/traits/homosexualness.go @@ -0,0 +1,59 @@ +package traits + +import "seekia/internal/helpers" + +import "maps" + +func getHomosexualnessTraitObject()Trait{ + + // Map Structure: rsID -> References Map + locusReferencesMap := make(map[int64]map[string]string) + + referencesMap_List1 := make(map[string]string) + referencesMap_List1["Large-scale GWAS reveals insights into the genetic architecture of same-sex sexual behavior"] = "https://www.science.org/doi/10.1126/science.aat7693" + + lociList_1 := []int64{ + 10261857, + 28371400, + 34730029, + 11114975, + } + + for _, rsID := range lociList_1{ + locusReferencesMap[rsID] = maps.Clone(referencesMap_List1) + } + + homosexualnessLociList := helpers.GetListOfMapKeys(locusReferencesMap) + + referencesMap := make(map[string]string) + referencesMap["Large-scale GWAS reveals insights into the genetic architecture of same-sex sexual behavior"] = "https://www.science.org/doi/10.1126/science.aat7693" + + valueFormatter := func(inputHomosexualness float64, showUnits bool)(string, error){ + + inputHomosexualnessString := helpers.ConvertIntToString(int(inputHomosexualness)) + + if (showUnits == false){ + return inputHomosexualnessString, nil + } + + formattedValue := inputHomosexualnessString + "/10" + + return formattedValue, nil + } + + homosexualnessObject := Trait{ + TraitName: "Homosexualness", + TraitDescription: "Feelings of sexual attraction towards people who belong to a person's own sex.", + DiscreteOrNumeric: "Numeric", + LocusReferencesMap: locusReferencesMap, + LociList: homosexualnessLociList, + LociList_Rules: []int64{}, + RulesList: []TraitRule{}, + OutcomesList: []string{}, + NumericValueFormatter: valueFormatter, + ReferencesMap: referencesMap, + } + + return homosexualnessObject +} + diff --git a/resources/geneticReferences/traits/lactoseTolerance.go b/resources/geneticReferences/traits/lactoseTolerance.go index 807a93b..ed3f7d6 100644 --- a/resources/geneticReferences/traits/lactoseTolerance.go +++ b/resources/geneticReferences/traits/lactoseTolerance.go @@ -140,6 +140,7 @@ func getLactoseToleranceTraitObject()Trait{ LociList_Rules: lociList_Rules, RulesList: lactoseToleranceRulesList, OutcomesList: outcomesList, + NumericValueFormatter: nil, ReferencesMap: referencesMap, } diff --git a/resources/geneticReferences/traits/skinColor.go b/resources/geneticReferences/traits/skinColor.go index 699b4b5..5a6869f 100644 --- a/resources/geneticReferences/traits/skinColor.go +++ b/resources/geneticReferences/traits/skinColor.go @@ -82,6 +82,7 @@ func getSkinColorTraitObject()Trait{ LociList_Rules: []int64{}, RulesList: []TraitRule{}, OutcomesList: []string{}, + NumericValueFormatter: nil, ReferencesMap: referencesMap, } diff --git a/resources/geneticReferences/traits/traits.go b/resources/geneticReferences/traits/traits.go index d636499..ea849ae 100644 --- a/resources/geneticReferences/traits/traits.go +++ b/resources/geneticReferences/traits/traits.go @@ -22,7 +22,7 @@ type Trait struct{ // These loci may not have any associated rules // We use these loci to predict trait outcomes with neural networks. // We also use these loci to calculate Racial Similarity. - // Map Structure: rsID -> (map[ReferenceName]Reference Link) + // Map Structure: rsID -> (map[Reference Name]Reference Link) LocusReferencesMap map[int64]map[string]string // This is a list of all loci used to predict this trait @@ -43,6 +43,17 @@ type Trait struct{ // If the trait is Numeric, or their or no rules nor a neural network, then this list will be empty. OutcomesList []string + // This function returns a formatted, translated representation of a numeric value for this trait + // For example, "150 centimeters", "5 foot 10 inches", "4/10" + // Inputs: + // -float64: The value to format + // -bool: Show "/10" units + // -The "/10" units are used for certain traits, where the unit value is not a real-world measurement (such as length, weight, etc.) + // -An example of an "/10" trait is Homosexuality, which is represented by a value between 0 and 10. + // -We don't want to "/10" if we are displaying a confidence range (Example: +/- 5.2) + // -If we did, then people would get confused by thinking the unit represents a fraction + NumericValueFormatter func(float64, bool)(string, error) + // This map contains scientific resources about this trait // Map structure: Reference name -> Reference link ReferencesMap map[string]string @@ -99,11 +110,13 @@ func InitializeTraitVariables()error{ eyeColorObject := getEyeColorTraitObject() hairColorObject := getHairColorTraitObject() skinColorObject := getSkinColorTraitObject() + homosexualnessObject := getHomosexualnessTraitObject() heightObject, err := getHeightTraitObject() if (err != nil){ return err } - traitObjectsList = []Trait{lactoseToleranceObject, hairTextureObject, facialStructureObject, eyeColorObject, hairColorObject, skinColorObject, heightObject} + + traitObjectsList = []Trait{lactoseToleranceObject, hairTextureObject, facialStructureObject, eyeColorObject, hairColorObject, skinColorObject, heightObject, homosexualnessObject} traitNamesList = make([]string, 0, len(traitObjectsList)) locusRSIDsMap = make(map[string]int64) diff --git a/utilities/createGeneticModels/createGeneticModels.go b/utilities/createGeneticModels/createGeneticModels.go index 09cb691..b93f3b8 100644 --- a/utilities/createGeneticModels/createGeneticModels.go +++ b/utilities/createGeneticModels/createGeneticModels.go @@ -1,6 +1,6 @@ // createGeneticModels.go provides an interface to create genetic prediction models -// These are neural networks which predict traits such as eye color from raw genome files +// These are neural networks which predict attributes such as eye color and autism from raw genome files // The OpenSNP.org dataset is used, and more datasets will be added in the future. // You must download the dataset and extract it. The instructions are described in the utility. // The trained models are saved in the /resources/geneticPredictionModels package for use in the Seekia app. @@ -16,6 +16,7 @@ import "fyne.io/fyne/v2/layout" import "fyne.io/fyne/v2/dialog" import "fyne.io/fyne/v2/data/binding" +import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/resources/geneticReferences/traits" import "seekia/resources/geneticReferences/locusMetadata" @@ -24,6 +25,7 @@ import "seekia/internal/genetics/locusValue" import "seekia/internal/genetics/prepareRawGenomes" import "seekia/internal/genetics/readRawGenomes" import "seekia/internal/genetics/geneticPrediction" +import "seekia/internal/globalSettings" import "seekia/internal/helpers" import "seekia/internal/imagery" import "seekia/internal/localFilesystem" @@ -46,12 +48,20 @@ import "time" func main(){ + polygenicDiseases.InitializePolygenicDiseaseVariables() + err := traits.InitializeTraitVariables() if (err != nil){ panic(err) return } + err = globalSettings.InitializeGlobalSettingsDatastore() + if (err != nil){ + panic(err) + return + } + app := app.New() customTheme := getCustomFyneTheme() @@ -209,7 +219,7 @@ func setHomePage(window fyne.Window){ title := getBoldLabelCentered("Create Genetic Models Utility") description1 := getLabelCentered("This utility is used to create the genetic prediction models.") - description2 := getLabelCentered("These models are used to predict traits such as eye color from raw genome files.") + description2 := getLabelCentered("These models are used to predict attributes such as eye color and autism from raw genome files.") description3 := getLabelCentered("Seekia aims to have open source and reproducible genetic prediction technology.") step1Label := getLabelCentered("Step 1:") @@ -722,16 +732,16 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f _, err = localFilesystem.CreateFolder("./TrainingData") if (err != nil) { return false, false, err } - //TODO: Add more traits - traitNamesList := []string{"Eye Color", "Lactose Tolerance", "Height"} + //TODO: Add more attributes + attributeNamesList := []string{"Eye Color", "Lactose Tolerance", "Height", "Autism", "Homosexualness"} - // We create the folders for each trait's training data + // We create the folders for each attribute's training data - for _, traitName := range traitNamesList{ + for _, attributeName := range attributeNamesList{ - traitNameWithoutWhitespace := strings.ReplaceAll(traitName, " ", "") + attributeNameWithoutWhitespace := strings.ReplaceAll(attributeName, " ", "") - folderpath := goFilepath.Join("./TrainingData/", traitNameWithoutWhitespace) + folderpath := goFilepath.Join("./TrainingData/", attributeNameWithoutWhitespace) _, err = localFilesystem.CreateFolder(folderpath) if (err != nil) { return false, false, err } @@ -891,13 +901,13 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f continue } - for _, traitName := range traitNamesList{ + for _, attributeName := range attributeNamesList{ - traitNameWithoutWhitespace := strings.ReplaceAll(traitName, " ", "") + attributeNameWithoutWhitespace := strings.ReplaceAll(attributeName, " ", "") - trainingDataFolderpath := goFilepath.Join("./TrainingData", traitNameWithoutWhitespace) + trainingDataFolderpath := goFilepath.Join("./TrainingData", attributeNameWithoutWhitespace) - userDataExists, userTrainingDataList, err := geneticPrediction.CreateGeneticPredictionTrainingData_OpenSNP(traitName, userPhenotypeDataObject, userLociValuesMap) + userDataExists, userTrainingDataList, err := geneticPrediction.CreateGeneticPredictionTrainingData_OpenSNP(attributeName, userPhenotypeDataObject, userLociValuesMap) if (err != nil) { return false, false, err } if (userDataExists == false){ // User cannot be used for training @@ -991,35 +1001,35 @@ func setTrainModelsPage(window fyne.Window, previousPage func()){ description1 := getLabelCentered("Press the button below to begin training a genetic model.") description2 := getLabelCentered("This will train a neural network using the user training data.") description3 := getLabelCentered("This will take a while.") - description4 := getLabelCentered("You must select a trait model to train.") + description4 := getLabelCentered("You must select a model to train.") - traitNamesList := []string{"Eye Color", "Lactose Tolerance", "Height"} + attributeNamesList := []string{"Eye Color", "Lactose Tolerance", "Height", "Autism", "Homosexualness"} - traitNameSelector := widget.NewSelect(traitNamesList, nil) + attributeNameSelector := widget.NewSelect(attributeNamesList, nil) beginTrainingButton := getWidgetCentered(widget.NewButtonWithIcon("Begin Training Model", theme.MediaPlayIcon(), func(){ - selectedTraitIndex := traitNameSelector.SelectedIndex() - if (selectedTraitIndex < 0){ - title := "No Trait Selected" - dialogMessage1 := getLabelCentered("You must select a trait model to train.") + selectedAttributeIndex := attributeNameSelector.SelectedIndex() + if (selectedAttributeIndex < 0){ + title := "No Attribute Selected" + dialogMessage1 := getLabelCentered("You must select an attribute model to train.") dialogContent := container.NewVBox(dialogMessage1) dialog.ShowCustom(title, "Close", dialogContent, window) return } - traitName := traitNameSelector.Selected - setStartAndMonitorTrainModelPage(window, traitName, currentPage) + attributeName := attributeNameSelector.Selected + setStartAndMonitorTrainModelPage(window, attributeName, currentPage) })) - traitNameSelectorCentered := getWidgetCentered(traitNameSelector) + attributeNameSelectorCentered := getWidgetCentered(attributeNameSelector) - page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), traitNameSelectorCentered, widget.NewSeparator(), beginTrainingButton) + page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), attributeNameSelectorCentered, widget.NewSeparator(), beginTrainingButton) window.SetContent(page) } -func setStartAndMonitorTrainModelPage(window fyne.Window, traitName string, previousPage func()){ +func setStartAndMonitorTrainModelPage(window fyne.Window, attributeName string, previousPage func()){ title := getBoldLabelCentered("Train Model") @@ -1154,7 +1164,7 @@ func setStartAndMonitorTrainModelPage(window fyne.Window, traitName string, prev _, err := localFilesystem.CreateFolder("./TrainedModels") if (err != nil) { return false, err } - trainingSetFilepathsList, _, err := getTrainingAndTestingDataFilepathLists(traitName) + trainingSetFilepathsList, _, err := getTrainingAndTestingDataFilepathLists(attributeName) if (err != nil) { return false, err } // Now we deterministically randomize the order of the trainingSetFilepathsList @@ -1165,7 +1175,7 @@ func setStartAndMonitorTrainModelPage(window fyne.Window, traitName string, prev }) // We create a new neural network object to train - neuralNetworkObject, err := geneticPrediction.GetNewUntrainedNeuralNetworkObject(traitName) + neuralNetworkObject, err := geneticPrediction.GetNewUntrainedNeuralNetworkObject(attributeName) if (err != nil) { return false, err } // The number of rounds of training for the training data set @@ -1253,21 +1263,28 @@ func setStartAndMonitorTrainModelPage(window fyne.Window, traitName string, prev return false, true, trainingDataObject, nil } - traitObject, err := traits.GetTraitObject(traitName) - if (err != nil) { return false, err } + getAttributeIsNumericBool := func()(bool, error){ - getTraitIsNumericBool := func()bool{ + switch attributeName{ - traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric - if (traitIsDiscreteOrNumeric == "Numeric"){ - return true + case "Height", + "Autism", + "Homosexualness":{ + return true, nil + } + case "Lactose Tolerance", + "Eye Color":{ + return false, nil + } } - return false + + return false, errors.New("setStartAndMonitorTrainModelPage called with unknown attributeName: " + attributeName) } - traitIsNumeric := getTraitIsNumericBool() + attributeIsNumeric, err := getAttributeIsNumericBool() + if (err != nil) { return false, err } - processCompleted, err := geneticPrediction.TrainNeuralNetwork(traitName, traitIsNumeric, neuralNetworkObject, getNextTrainingDataFunction) + processCompleted, err := geneticPrediction.TrainNeuralNetwork(attributeName, attributeIsNumeric, neuralNetworkObject, getNextTrainingDataFunction) if (err != nil) { return false, err } if (processCompleted == false){ return false, nil @@ -1279,9 +1296,9 @@ func setStartAndMonitorTrainModelPage(window fyne.Window, traitName string, prev neuralNetworkBytes, err := geneticPrediction.EncodeNeuralNetworkObjectToBytes(*neuralNetworkObject) if (err != nil) { return false, err } - traitNameWithoutWhitespaces := strings.ReplaceAll(traitName, " ", "") + attributeNameWithoutWhitespaces := strings.ReplaceAll(attributeName, " ", "") - neuralNetworkFilename := traitNameWithoutWhitespaces + "Model.gob" + neuralNetworkFilename := attributeNameWithoutWhitespaces + "Model.gob" err = localFilesystem.CreateOrOverwriteFile(neuralNetworkBytes, "./TrainedModels/", neuralNetworkFilename) if (err != nil) { return false, err } @@ -1343,36 +1360,36 @@ func setTestModelsPage(window fyne.Window, previousPage func()){ description3 := getLabelCentered("The testing data is not used to train the models.") description4 := getLabelCentered("The results of the testing will be displayed at the end.") description5 := getLabelCentered("The results will also be saved in the ModelAccuracies folder.") - description6 := getLabelCentered("You must select a trait model to test.") + description6 := getLabelCentered("You must select a model to test.") - traitNamesList := []string{"Eye Color", "Lactose Tolerance", "Height"} + attributeNamesList := []string{"Eye Color", "Lactose Tolerance", "Height", "Autism", "Homosexualness"} - traitNameSelector := widget.NewSelect(traitNamesList, nil) + attributeNameSelector := widget.NewSelect(attributeNamesList, nil) beginTestingButton := getWidgetCentered(widget.NewButtonWithIcon("Begin Testing Model", theme.MediaPlayIcon(), func(){ - selectedTraitIndex := traitNameSelector.SelectedIndex() - if (selectedTraitIndex < 0){ - title := "No Trait Selected" - dialogMessage1 := getLabelCentered("You must select a trait model to test.") + selectedAttributeIndex := attributeNameSelector.SelectedIndex() + if (selectedAttributeIndex < 0){ + title := "No Attribute Selected" + dialogMessage1 := getLabelCentered("You must select a model to test.") dialogContent := container.NewVBox(dialogMessage1) dialog.ShowCustom(title, "Close", dialogContent, window) return } - traitName := traitNameSelector.Selected + attributeName := attributeNameSelector.Selected - setStartAndMonitorTestModelPage(window, traitName, currentPage) + setStartAndMonitorTestModelPage(window, attributeName, currentPage) })) - traitNameSelectorCentered := getWidgetCentered(traitNameSelector) + attributeNameSelectorCentered := getWidgetCentered(attributeNameSelector) - page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, widget.NewSeparator(), traitNameSelectorCentered, widget.NewSeparator(), beginTestingButton) + page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, widget.NewSeparator(), attributeNameSelectorCentered, widget.NewSeparator(), beginTestingButton) window.SetContent(page) } -func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previousPage func()){ +func setStartAndMonitorTestModelPage(window fyne.Window, attributeName string, previousPage func()){ title := getBoldLabelCentered("Testing Model") @@ -1409,17 +1426,35 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ window.SetContent(page) - testModelFunction := func(){ + getAttributeIsNumericBool := func()(bool, error){ - traitObject, err := traits.GetTraitObject(traitName) - if (err != nil){ - setErrorEncounteredPage(window, err, previousPage) - return + switch attributeName{ + + case "Height", + "Autism", + "Homosexualness":{ + return true, nil + } + case "Lactose Tolerance", + "Eye Color":{ + return false, nil + } } - traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric + return false, errors.New("setStartAndMonitorTrainModelPage called with unknown attributeName: " + attributeName) + } - if (traitIsDiscreteOrNumeric == "Discrete"){ + attributeIsNumeric, err := getAttributeIsNumericBool() + if (err != nil) { + setErrorEncounteredPage(window, errors.New("setStartAndMonitorTestModelPage called with unknown attributeName: " + attributeName), previousPage) + return + } + + if (attributeIsNumeric == false){ + + // attribute is a Discrete trait + + testModelFunction := func(){ //Outputs: // -bool: Process completed (true == was not stopped mid-way) @@ -1448,10 +1483,10 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ traitPredictionInfoMap := make(map[geneticPrediction.DiscreteTraitOutcomeInfo]TraitAccuracyStatisticsValue) - _, testingSetFilepathsList, err := getTrainingAndTestingDataFilepathLists(traitName) + _, testingSetFilepathsList, err := getTrainingAndTestingDataFilepathLists(attributeName) if (err != nil) { return false, nil, err } - traitNameWithoutWhitespaces := strings.ReplaceAll(traitName, " ", "") + traitNameWithoutWhitespaces := strings.ReplaceAll(attributeName, " ", "") // We read the trained model for this trait modelFilename := traitNameWithoutWhitespaces + "Model.gob" @@ -1504,10 +1539,10 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ return false, nil, errors.New("Neural network prediction output length does not match expected output length.") } - correctOutcomeName, err := geneticPrediction.GetDiscreteOutcomeNameFromOutputLayer(traitName, true, trainingDataExpectedOutputLayer) + correctOutcomeName, err := geneticPrediction.GetDiscreteOutcomeNameFromOutputLayer(attributeName, true, trainingDataExpectedOutputLayer) if (err != nil) { return false, nil, err } - predictedOutcomeName, err := geneticPrediction.GetDiscreteOutcomeNameFromOutputLayer(traitName, true, predictionLayer) + predictedOutcomeName, err := geneticPrediction.GetDiscreteOutcomeNameFromOutputLayer(attributeName, true, predictionLayer) if (err != nil) { return false, nil, err } getPredictionIsCorrectBool := func()bool{ @@ -1674,30 +1709,37 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ return } - setViewModelTestingDiscreteTraitResultsPage(window, traitName, traitPredictionAccuracyInfoMap, previousPage) + setViewModelTestingDiscreteTraitResultsPage(window, attributeName, traitPredictionAccuracyInfoMap, previousPage) + } - } else { + go testModelFunction() + + return - // traitIsDiscreteOrNumeric == "Numeric" + } else { + + // attribute is Numeric + + testModelFunction := func(){ //Outputs: // -bool: Process completed (true == was not stopped mid-way) - // -geneticPrediction.NumericTraitPredictionAccuracyInfoMap + // -geneticPrediction.NumericAttributePredictionAccuracyInfoMap // -error - testModel := func()(bool, geneticPrediction.NumericTraitPredictionAccuracyInfoMap, error){ + testModel := func()(bool, geneticPrediction.NumericAttributePredictionAccuracyInfoMap, error){ // We use this map to count up the information about predictions // We use information from this map to construct the final accuracy information map - // Map Structure: NumericTraitPredictionInfo -> []float64 (List of distances for each prediction) - traitPredictionInfoMap := make(map[geneticPrediction.NumericTraitPredictionInfo][]float64) + // Map Structure: NumericAttributePredictionInfo -> []float64 (List of distances for each prediction) + attributePredictionInfoMap := make(map[geneticPrediction.NumericAttributePredictionInfo][]float64) - _, testingSetFilepathsList, err := getTrainingAndTestingDataFilepathLists(traitName) + _, testingSetFilepathsList, err := getTrainingAndTestingDataFilepathLists(attributeName) if (err != nil) { return false, nil, err } - traitNameWithoutWhitespaces := strings.ReplaceAll(traitName, " ", "") + attributeNameWithoutWhitespaces := strings.ReplaceAll(attributeName, " ", "") - // We read the trained model for this trait - modelFilename := traitNameWithoutWhitespaces + "Model.gob" + // We read the trained model for this attribute + modelFilename := attributeNameWithoutWhitespaces + "Model.gob" trainedModelFilepath := goFilepath.Join("./TrainedModels/", modelFilename) @@ -1749,10 +1791,10 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ return false, nil, errors.New("Neural network numeric prediction output layer length is not 1.") } - correctOutcomeValue, err := geneticPrediction.GetNumericOutcomeValueFromOutputLayer(traitName, trainingDataExpectedOutputLayer) + correctOutcomeValue, err := geneticPrediction.GetNumericOutcomeValueFromOutputLayer(attributeName, trainingDataExpectedOutputLayer) if (err != nil) { return false, nil, err } - predictedOutcomeValue, err := geneticPrediction.GetNumericOutcomeValueFromOutputLayer(traitName, predictionLayer) + predictedOutcomeValue, err := geneticPrediction.GetNumericOutcomeValueFromOutputLayer(attributeName, predictionLayer) if (err != nil) { return false, nil, err } numberOfKnownLoci, numberOfKnownAndPhasedLoci, numberOfLoci, err := geneticPrediction.GetLociInfoFromNetworkInputLayer(trainingDataInputLayer) @@ -1764,19 +1806,19 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ proportionOfPhasedLoci := float64(numberOfKnownAndPhasedLoci)/float64(numberOfKnownLoci) percentageOfPhasedLoci := int(100*proportionOfPhasedLoci) - newNumericTraitPredictionInfo := geneticPrediction.NumericTraitPredictionInfo{ + newNumericAttributePredictionInfo := geneticPrediction.NumericAttributePredictionInfo{ PercentageOfLociTested: percentageOfLociTested, PercentageOfPhasedLoci: percentageOfPhasedLoci, } distanceFromCorrectValue := math.Abs(predictedOutcomeValue - correctOutcomeValue) - existingList, exists := traitPredictionInfoMap[newNumericTraitPredictionInfo] + existingList, exists := attributePredictionInfoMap[newNumericAttributePredictionInfo] if (exists == false){ - traitPredictionInfoMap[newNumericTraitPredictionInfo] = []float64{distanceFromCorrectValue} + attributePredictionInfoMap[newNumericAttributePredictionInfo] = []float64{distanceFromCorrectValue} } else { existingList = append(existingList, distanceFromCorrectValue) - traitPredictionInfoMap[newNumericTraitPredictionInfo] = existingList + attributePredictionInfoMap[newNumericAttributePredictionInfo] = existingList } exampleIndexString := helpers.ConvertIntToString(index+1) @@ -1789,20 +1831,20 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ progressPercentageBinding.Set(newProgressFloat64) } - // Now we construct the TraitAccuracyInfoMap + // Now we construct the AttributeAccuracyInfoMap // This map stores the accuracy for each QuantityOfKnownLoci/QuantityOfPhasedLoci - traitPredictionAccuracyInfoMap := make(map[geneticPrediction.NumericTraitPredictionInfo]geneticPrediction.NumericTraitPredictionAccuracyRangesMap) + attributePredictionAccuracyInfoMap := make(map[geneticPrediction.NumericAttributePredictionInfo]geneticPrediction.NumericAttributePredictionAccuracyRangesMap) - for traitPredictionInfo, predictionDistancesList := range traitPredictionInfoMap{ + for attributePredictionInfo, predictionDistancesList := range attributePredictionInfoMap{ if (len(predictionDistancesList) == 0){ - return false, nil, errors.New("traitPredictionInfoMap contains empty predictionDistancesList.") + return false, nil, errors.New("attributePredictionInfoMap contains empty predictionDistancesList.") } // Map Structure: Accuracy Percentage (AP) -> Amount needed to deviate from prediction // for the value to be accurate (AP)% of the time - newNumericTraitPredictionAccuracyRangesMap := make(map[int]float64) + newNumericAttributePredictionAccuracyRangesMap := make(map[int]float64) if (len(predictionDistancesList) < 5){ // We don't have enough data to create an accuracyRanges map. @@ -1825,7 +1867,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ continue } - _, exists := newNumericTraitPredictionAccuracyRangesMap[percentageOfPredictionsWithinDistance] + _, exists := newNumericAttributePredictionAccuracyRangesMap[percentageOfPredictionsWithinDistance] if (exists == true){ // There exists a value for this percentage already // This happens because we convert a float64 to an int @@ -1835,33 +1877,33 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ continue } - newNumericTraitPredictionAccuracyRangesMap[percentageOfPredictionsWithinDistance] = distance + newNumericAttributePredictionAccuracyRangesMap[percentageOfPredictionsWithinDistance] = distance } - traitPredictionAccuracyInfoMap[traitPredictionInfo] = newNumericTraitPredictionAccuracyRangesMap + attributePredictionAccuracyInfoMap[attributePredictionInfo] = newNumericAttributePredictionAccuracyRangesMap } // Testing is complete. // We save the info map as a file in the ModelAccuracies folder - fileBytes, err := geneticPrediction.EncodeNumericTraitPredictionAccuracyInfoMapToBytes(traitPredictionAccuracyInfoMap) + fileBytes, err := geneticPrediction.EncodeNumericAttributePredictionAccuracyInfoMapToBytes(attributePredictionAccuracyInfoMap) if (err != nil) { return false, nil, err } _, err = localFilesystem.CreateFolder("./ModelAccuracies") if (err != nil) { return false, nil, err } - modelAccuracyFilename := traitNameWithoutWhitespaces + "ModelAccuracy.gob" + modelAccuracyFilename := attributeNameWithoutWhitespaces + "ModelAccuracy.gob" err = localFilesystem.CreateOrOverwriteFile(fileBytes, "./ModelAccuracies/", modelAccuracyFilename) if (err != nil) { return false, nil, err } progressPercentageBinding.Set(1) - return true, traitPredictionAccuracyInfoMap, nil + return true, attributePredictionAccuracyInfoMap, nil } - processIsComplete, traitPredictionAccuracyInfoMap, err := testModel() + processIsComplete, attributePredictionAccuracyInfoMap, err := testModel() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return @@ -1871,11 +1913,12 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ return } - setViewModelTestingNumericTraitResultsPage(window, traitName, traitPredictionAccuracyInfoMap, previousPage) + setViewModelTestingNumericAttributeResultsPage(window, attributeName, attributePredictionAccuracyInfoMap, previousPage) + return } - } - go testModelFunction() + go testModelFunction() + } } // This is a page to view the details of testing for a specific trait's model @@ -2043,18 +2086,18 @@ func setViewModelTestingDiscreteTraitResultsPage(window fyne.Window, traitName s } -// This is a page to view the details of testing for a specific trait's model -func setViewModelTestingNumericTraitResultsPage(window fyne.Window, traitName string, traitAccuracyInfoMap geneticPrediction.NumericTraitPredictionAccuracyInfoMap, exitPage func()){ +// This is a page to view the details of testing for a numeric attribute's model +func setViewModelTestingNumericAttributeResultsPage(window fyne.Window, attributeName string, attributeAccuracyInfoMap geneticPrediction.NumericAttributePredictionAccuracyInfoMap, exitPage func()){ - title := getBoldLabelCentered("Numeric Trait Prediction Accuracy Details") + title := getBoldLabelCentered("Numeric Attribute Prediction Accuracy Details") exitButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.CancelIcon(), exitPage)) - description1 := getLabelCentered("The results of the prediction accuracy for this trait are below.") + description1 := getLabelCentered("The results of the prediction accuracy for this attribute are below.") - traitNameTitle := widget.NewLabel("Trait Name:") - traitNameLabel := getBoldLabel(traitName) - traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameTitle, traitNameLabel, layout.NewSpacer()) + attributeNameTitle := widget.NewLabel("Attribute Name:") + attributeNameLabel := getBoldLabel(attributeName) + attributeNameRow := container.NewHBox(layout.NewSpacer(), attributeNameTitle, attributeNameLabel, layout.NewSpacer()) description2 := getLabelCentered("Each value is a range that the prediction must be widened by to be accurate X% of the time.") description3 := getLabelCentered("For example, for a height prediction to be accurate 90% of the time, allow a +/-10 cm range.") @@ -2078,14 +2121,41 @@ func setViewModelTestingNumericTraitResultsPage(window fyne.Window, traitName st accuracyRangeColumn_34to66 := container.NewVBox(accuracyRangeTitle2, knownLociLabel_34to66, widget.NewSeparator()) accuracyRangeColumn_67to100 := container.NewVBox(accuracyRangeTitle3, knownLociLabel_67to100, widget.NewSeparator()) - traitObject, err := traits.GetTraitObject(traitName) - if (err != nil) { return nil, err } + // We get the formatter for the distance values + // This converts raw predictions to formatted values + // Example: 100 -> "100 centimeters" - traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric - if (traitIsDiscreteOrNumeric != "Numeric"){ - return nil, errors.New("setViewModelTestingNumericTraitResultsPage called with non-discrete trait: " + traitName) + getAttributeValueFormatter := func()(func(float64, bool)(string, error), error){ + switch attributeName{ + case "Homosexuality", + "Height":{ + + traitObject, err := traits.GetTraitObject(attributeName) + if (err != nil) { return nil, err } + + numericValueFormatter := traitObject.NumericValueFormatter + + return numericValueFormatter, nil + } + } + + // attribute is a polygenic disease + + result := func(inputValue float64, _ bool)(string, error){ + + // Input value is a value between 0 and 10 + + inputValueFormatted := helpers.ConvertIntToString(int(inputValue)) + + return inputValueFormatted, nil + } + + return result, nil } + attributeValueFormatter, err := getAttributeValueFormatter() + if (err != nil){ return nil, err } + probabilityMinimumRange := 1 for { @@ -2125,11 +2195,11 @@ func setViewModelTestingNumericTraitResultsPage(window fyne.Window, traitName st predictionAccuracyDistancesSum_67to100 := float64(0) distancesCount_67to100 := 0 - for traitOutcomeInfo, traitPredictionAccuracyRangesMap := range traitAccuracyInfoMap{ + for attributeOutcomeInfo, attributePredictionAccuracyRangesMap := range attributeAccuracyInfoMap{ - percentageOfLociTested := traitOutcomeInfo.PercentageOfLociTested + percentageOfLociTested := attributeOutcomeInfo.PercentageOfLociTested - for percentageCorrect, distance := range traitPredictionAccuracyRangesMap{ + for percentageCorrect, distance := range attributePredictionAccuracyRangesMap{ if (percentageCorrect < probabilityMinimumRange || percentageCorrect > probabilityMaximumRange){ continue @@ -2153,24 +2223,27 @@ func setViewModelTestingNumericTraitResultsPage(window fyne.Window, traitName st } } - getAverageAccuracyText := func(distancesSum float64, distancesCount int)string{ + getAverageAccuracyText := func(distancesSum float64, distancesCount int)(string, error){ if (distancesCount == 0){ - return "Unknown" + return "Unknown", nil } averageDistance := distancesSum/float64(distancesCount) - averageDistanceString := helpers.ConvertFloat64ToStringRounded(averageDistance, 1) + averageDistanceFormatted, err := attributeValueFormatter(averageDistance, false) + if (err != nil) { return "", err } - //TODO: Retrieve units from traits package? - result := "+/- " + averageDistanceString + " centimeters" + result := "+/- " + averageDistanceFormatted - return result + return result, nil } - averageDistanceText_0to33 := getAverageAccuracyText(predictionAccuracyDistancesSum_0to33, distancesCount_0to33) - averageDistanceText_34to66 := getAverageAccuracyText(predictionAccuracyDistancesSum_34to66, distancesCount_34to66) - averageDistanceText_67to100 := getAverageAccuracyText(predictionAccuracyDistancesSum_67to100, distancesCount_67to100) + averageDistanceText_0to33, err := getAverageAccuracyText(predictionAccuracyDistancesSum_0to33, distancesCount_0to33) + if (err != nil){ return nil, err } + averageDistanceText_34to66, err := getAverageAccuracyText(predictionAccuracyDistancesSum_34to66, distancesCount_34to66) + if (err != nil){ return nil, err } + averageDistanceText_67to100, err := getAverageAccuracyText(predictionAccuracyDistancesSum_67to100, distancesCount_67to100) + if (err != nil){ return nil, err } averageDistanceLabel_0to33 := getBoldLabelCentered(averageDistanceText_0to33) averageDistanceLabel_34to66 := getBoldLabelCentered(averageDistanceText_34to66) @@ -2204,7 +2277,7 @@ func setViewModelTestingNumericTraitResultsPage(window fyne.Window, traitName st return } - page := container.NewVBox(title, exitButton, widget.NewSeparator(), description1, widget.NewSeparator(), traitNameRow, widget.NewSeparator(), description2, description3, widget.NewSeparator(), resultsGrid) + page := container.NewVBox(title, exitButton, widget.NewSeparator(), description1, widget.NewSeparator(), attributeNameRow, widget.NewSeparator(), description2, description3, widget.NewSeparator(), resultsGrid) pageScrollable := container.NewVScroll(page) @@ -2212,20 +2285,16 @@ func setViewModelTestingNumericTraitResultsPage(window fyne.Window, traitName st } -// This function returns a list of training data and testing data filepaths for a trait. +// This function returns a list of training data and testing data filepaths for an attribute. //Outputs: // -[]string: Sorted list of training data filepaths // -[]string: Unsorted list of testing data filepaths // -error -func getTrainingAndTestingDataFilepathLists(traitName string)([]string, []string, error){ +func getTrainingAndTestingDataFilepathLists(attributeName string)([]string, []string, error){ - if (traitName != "Eye Color" && traitName != "Lactose Tolerance" && traitName != "Height"){ - return nil, nil, errors.New("getTrainingAndTestingDataFilepathLists called with invalid traitName: " + traitName) - } + attributeNameWithoutWhitespaces := strings.ReplaceAll(attributeName, " ", "") - traitNameWithoutWhitespaces := strings.ReplaceAll(traitName, " ", "") - - trainingDataFolderpath := goFilepath.Join("./TrainingData/", traitNameWithoutWhitespaces) + trainingDataFolderpath := goFilepath.Join("./TrainingData/", attributeNameWithoutWhitespaces) filesList, err := os.ReadDir(trainingDataFolderpath) if (err != nil) { return nil, nil, err } @@ -2238,7 +2307,7 @@ func getTrainingAndTestingDataFilepathLists(traitName string)([]string, []string filepathIsFolder := filesystemObject.IsDir() if (filepathIsFolder == true){ // Folder is corrupt - return nil, nil, errors.New("Training data is corrupt for trait: " + traitName) + return nil, nil, errors.New("Training data is corrupt for attribute: " + attributeName) } fileName := filesystemObject.Name() @@ -2249,24 +2318,32 @@ func getTrainingAndTestingDataFilepathLists(traitName string)([]string, []string numberOfTrainingDataFiles := len(trainingDataFilenamesMap) if (numberOfTrainingDataFiles == 0){ - return nil, nil, errors.New("No training data exists for trait: " + traitName) + return nil, nil, errors.New("No training data exists for attribute: " + attributeName) } getNumberOfExpectedTrainingDatas := func()(int, error){ - if (traitName == "Eye Color"){ + switch attributeName{ - return 113648, nil + case "Eye Color":{ + return 149894, nil - } else if (traitName == "Lactose Tolerance"){ - - return 24872, nil - } else if (traitName == "Height"){ - - return 92281, nil + } + case "Lactose Tolerance":{ + return 24872, nil + } + case "Height":{ + return 92281, nil + } + case "Autism":{ + return 32118, nil + } + case "Homosexualness":{ + return 14500, nil + } } - return 0, errors.New("Unknown traitName: " + traitName) + return 0, errors.New("Unknown attributeName: " + attributeName) } numberOfExpectedTrainingDatas, err := getNumberOfExpectedTrainingDatas() @@ -2276,7 +2353,7 @@ func getTrainingAndTestingDataFilepathLists(traitName string)([]string, []string numberOfTrainingDataFilesString := helpers.ConvertIntToString(numberOfTrainingDataFiles) - return nil, nil, errors.New(traitName + " quantity of training datas is unexpected: " + numberOfTrainingDataFilesString) + return nil, nil, errors.New(attributeName + " quantity of training datas is unexpected: " + numberOfTrainingDataFilesString) } // We sort the training data to be in a deterministically random order @@ -2331,7 +2408,7 @@ func getTrainingAndTestingDataFilepathLists(traitName string)([]string, []string numberOfUsers := len(userIdentifiersList) if (numberOfUsers < 250){ - return nil, nil, errors.New("Too few training data examples for trait: " + traitName) + return nil, nil, errors.New("Too few training data examples for attribute: " + attributeName) } // We use 200 users for testing (validation), so we don't train using them