Added new genetic attributes to the calculatedAttributes package. Added the ability to view and sort by these attributes in the GUI.

This commit is contained in:
Simon Sarasova 2024-08-14 11:04:19 +00:00
parent 972252c788
commit b6f5612bbc
No known key found for this signature in database
GPG key ID: EEDA4103C9C36944
7 changed files with 773 additions and 96 deletions

View file

@ -6,6 +6,7 @@ Small and insignificant changes may not be included in this log.
## Unversioned Changes ## Unversioned Changes
* Added new genetic attributes to the calculatedAttributes package. Added the ability to view and sort by these attributes in the GUI. - *Simon Sarasova*
* Upgraded Golang to version 1.23. - *Simon Sarasova* * Upgraded Golang to version 1.23. - *Simon Sarasova*
* Added the Obesity disease to genetic analyses. - *Simon Sarasova* * Added the Obesity disease to genetic analyses. - *Simon Sarasova*
* Implemented neural network prediction for polygenic diseases to replace old method. Added autism and homosexualness to genetic analyses. - *Simon Sarasova* * Implemented neural network prediction for polygenic diseases to replace old method. Added autism and homosexualness to genetic analyses. - *Simon Sarasova*

View file

@ -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 Name | Date Of First Commit | Number Of Commits
--- | --- | --- --- | --- | ---
Simon Sarasova | June 13, 2023 | 280 Simon Sarasova | June 13, 2023 | 281

View file

@ -1024,23 +1024,44 @@ func setSelectMatchesSortByAttributePage(window fyne.Window, previousPage func()
err = addAttributeSelectButton("Physical", "OffspringTotalPolygenicDiseaseRiskScore", "Ascending") err = addAttributeSelectButton("Physical", "OffspringTotalPolygenicDiseaseRiskScore", "Ascending")
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
offspringLactoseToleranceProbabilityButton := widget.NewButton("Offspring Lactose Tolerance Probability", func(){ err = addAttributeSelectButton("Physical", "AutismRiskScore", "Ascending")
//TODO if (err != nil) { return nil, err }
showUnderConstructionDialog(window) err = addAttributeSelectButton("Physical", "OffspringAutismRiskScore", "Ascending")
}) if (err != nil) { return nil, err }
physicalAttributeButtonsGrid.Add(offspringLactoseToleranceProbabilityButton)
offspringCurlyHairProbabilityButton := widget.NewButton("Offspring Curly Hair Probability", func(){ err = addAttributeSelectButton("Physical", "ObesityRiskScore", "Ascending")
//TODO if (err != nil) { return nil, err }
showUnderConstructionDialog(window) err = addAttributeSelectButton("Physical", "OffspringObesityRiskScore", "Ascending")
}) if (err != nil) { return nil, err }
physicalAttributeButtonsGrid.Add(offspringCurlyHairProbabilityButton)
offspringStraightHairProbabilityButton := widget.NewButton("Offspring Straight Hair Probability", func(){ err = addAttributeSelectButton("Physical", "OffspringBlueEyesProbability", "Descending")
//TODO if (err != nil) { return nil, err }
showUnderConstructionDialog(window) err = addAttributeSelectButton("Physical", "OffspringGreenEyesProbability", "Descending")
}) if (err != nil) { return nil, err }
physicalAttributeButtonsGrid.Add(offspringStraightHairProbabilityButton) err = addAttributeSelectButton("Physical", "OffspringHazelEyesProbability", "Descending")
if (err != nil) { return nil, err }
err = addAttributeSelectButton("Physical", "OffspringBrownEyesProbability", "Descending")
if (err != nil) { return nil, err }
err = addAttributeSelectButton("Physical", "OffspringLactoseToleranceProbability", "Descending")
if (err != nil) { return nil, err }
err = addAttributeSelectButton("Physical", "OffspringStraightHairProbability", "Descending")
if (err != nil) { return nil, err }
err = addAttributeSelectButton("Physical", "OffspringCurlyHairProbability", "Descending")
if (err != nil) { return nil, err }
// Numeric Traits:
err = addAttributeSelectButton("Physical", "HomosexualnessScore", "Ascending")
if (err != nil) { return nil, err }
err = addAttributeSelectButton("Physical", "OffspringHomosexualnessScore", "Ascending")
if (err != nil) { return nil, err }
err = addAttributeSelectButton("Physical", "PredictedHeight", "Descending")
if (err != nil) { return nil, err }
err = addAttributeSelectButton("Physical", "OffspringPredictedHeight", "Descending")
if (err != nil) { return nil, err }
err = addAttributeSelectButton("Physical", "23andMe_NeanderthalVariants", "Descending") err = addAttributeSelectButton("Physical", "23andMe_NeanderthalVariants", "Descending")
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
@ -1555,7 +1576,6 @@ func setAddAttributeToCustomMatchDisplayPage(window fyne.Window, previousPage fu
"23andMe_MaternalHaplogroup", "23andMe_MaternalHaplogroup",
"23andMe_PaternalHaplogroup", "23andMe_PaternalHaplogroup",
"23andMe_NeanderthalVariants", "23andMe_NeanderthalVariants",
"OffspringProbabilityOfAnyMonogenicDisease",
"EyeColorSimilarity", "EyeColorSimilarity",
"EyeColorGenesSimilarity", "EyeColorGenesSimilarity",
"HairColorSimilarity", "HairColorSimilarity",
@ -1568,6 +1588,27 @@ func setAddAttributeToCustomMatchDisplayPage(window fyne.Window, previousPage fu
"23andMe_AncestralSimilarity", "23andMe_AncestralSimilarity",
"23andMe_MaternalHaplogroupSimilarity", "23andMe_MaternalHaplogroupSimilarity",
"23andMe_PaternalHaplogroupSimilarity", "23andMe_PaternalHaplogroupSimilarity",
"OffspringProbabilityOfAnyMonogenicDisease",
"TotalPolygenicDiseaseRiskScore",
"OffspringTotalPolygenicDiseaseRiskScore",
"AutismRiskScore",
"OffspringAutismRiskScore",
"ObesityRiskScore",
"OffspringObesityRiskScore",
"PredictedEyeColor",
"OffspringBlueEyesProbability",
"OffspringGreenEyesProbability",
"OffspringHazelEyesProbability",
"OffspringBrownEyesProbability",
"PredictedLactoseTolerance",
"OffspringLactoseToleranceProbability",
"PredictedHairTexture",
"OffspringStraightHairProbability",
"OffspringCurlyHairProbability",
"HomosexualnessScore",
"OffspringHomosexualnessScore",
"PredictedHeight",
"OffspringPredictedHeight",
} }
lifestyleAttributeNamesList := []string{ lifestyleAttributeNamesList := []string{

View file

@ -449,7 +449,7 @@ func StartUpdatingMyMatches(networkType byte)error{
return nil return nil
} }
newScaledPercentageInt, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 50, 80) newScaledPercentageInt, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 50, 95)
if (err != nil) { return err } if (err != nil) { return err }
newProgressFloat := float64(newScaledPercentageInt)/100 newProgressFloat := float64(newScaledPercentageInt)/100

View file

@ -5,7 +5,7 @@
package attributeDisplay package attributeDisplay
//TODO: Deal with singular/multiple values and how that changes an attribute value's units //TODO: Deal with singular/multiple values and how that changes an attribute value's units
// For example: 1 variant, 2 variants // For example: 1 variant, 2 variants, 1 centimeter, 2 centimeters
import "seekia/resources/worldLocations" import "seekia/resources/worldLocations"
import "seekia/resources/worldLanguages" import "seekia/resources/worldLanguages"
@ -43,6 +43,7 @@ func GetProfileAttributeDisplayInfo(attributeName string)(string, bool, func(str
} }
formatPercentageFunction := func(input string)(string, error){ formatPercentageFunction := func(input string)(string, error){
valueFloat64, err := helpers.ConvertStringToFloat64(input) valueFloat64, err := helpers.ConvertStringToFloat64(input)
if (err != nil){ if (err != nil){
return "", errors.New("formatPercentageFunction called with non-numeric " + attributeName + " value: " + input) return "", errors.New("formatPercentageFunction called with non-numeric " + attributeName + " value: " + input)
@ -52,7 +53,8 @@ func GetProfileAttributeDisplayInfo(attributeName string)(string, bool, func(str
return "", errors.New("formatPercentageFunction called with invalid " + attributeName + " percentage value: " + input) return "", errors.New("formatPercentageFunction called with invalid " + attributeName + " percentage value: " + input)
} }
result := input + "%" result := helpers.ConvertIntToString(int(valueFloat64))
return result, nil return result, nil
} }
@ -197,24 +199,6 @@ func GetProfileAttributeDisplayInfo(attributeName string)(string, bool, func(str
return titleTranslated, false, passValueFunction, "", noResponseTranslated, nil return titleTranslated, false, passValueFunction, "", noResponseTranslated, nil
} }
case "CatsRating":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Cats Rating")
return titleTranslated, true, passValueFunction, "/10", noResponseTranslated, nil
}
case "DogsRating":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Dogs Rating")
return titleTranslated, true, passValueFunction, "/10", noResponseTranslated, nil
}
case "PetsRating":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Pets Rating")
return titleTranslated, true, passValueFunction, "/10", noResponseTranslated, nil
}
case "GenderIdentity":{ case "GenderIdentity":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Gender Identity") titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Gender Identity")
@ -231,20 +215,23 @@ func GetProfileAttributeDisplayInfo(attributeName string)(string, bool, func(str
return titleTranslated, false, translateGenderFunction, "", noResponseTranslated, nil return titleTranslated, false, translateGenderFunction, "", noResponseTranslated, nil
} }
case "FruitRating", case "FruitRating",
"VegetablesRating", "VegetablesRating",
"NutsRating", "NutsRating",
"GrainsRating", "GrainsRating",
"DairyRating", "DairyRating",
"SeafoodRating", "SeafoodRating",
"BeefRating", "BeefRating",
"PorkRating", "PorkRating",
"PoultryRating", "PoultryRating",
"EggsRating", "EggsRating",
"BeansRating":{ "BeansRating",
"PetsRating",
"DogsRating",
"CatsRating":{
foodName := strings.TrimSuffix(attributeName, "Rating") thingName := strings.TrimSuffix(attributeName, "Rating")
attributeTitle := foodName + " Rating" attributeTitle := thingName + " Rating"
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage(attributeTitle) titleTranslated := translation.TranslateTextFromEnglishToMyLanguage(attributeTitle)
@ -422,14 +409,92 @@ func GetProfileAttributeDisplayInfo(attributeName string)(string, bool, func(str
return titleTranslated, true, passValueFunction, "/4", noResponseTranslated, nil return titleTranslated, true, passValueFunction, "/4", noResponseTranslated, nil
} }
case "Height":{ case "Height",
"PredictedHeight",
"OffspringPredictedHeight":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Height") getAttributeTitle := func()string{
switch attributeName{
case "Height":{
return "Height"
}
case "PredictedHeight":{
return "Predicted Height"
}
}
return "Offspring Predicted Height"
}
attributeTitle := getAttributeTitle()
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage(attributeTitle)
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 "", false, nil, "", "", err }
formatHeightFunction := func(input string)(string, error){
inputCentimeters, err := helpers.ConvertStringToFloat64(input)
if (err != nil) { return "", err }
if (myMetricOrImperial == "Metric"){
centimetersString := helpers.ConvertFloat64ToStringRounded(inputCentimeters, 2)
return centimetersString, nil
}
feetInchesString, err := helpers.ConvertCentimetersToFeetInchesTranslatedString(inputCentimeters)
if (err != nil) { return "", err }
return feetInchesString, nil
}
getUnitsTranslated := func()string{
if (myMetricOrImperial == "Metric"){
unitsTranslated := translation.TranslateTextFromEnglishToMyLanguage("Centimeters")
return unitsTranslated
}
// There are no units to add
// The value is in the format "5 feet, 10 inches"
return ""
}
unitsTranslated := getUnitsTranslated()
unitsTranslated := translation.TranslateTextFromEnglishToMyLanguage("centimeters")
unitsWithPadding := " " + unitsTranslated unitsWithPadding := " " + unitsTranslated
return titleTranslated, true, roundNumberFunction, unitsWithPadding, noResponseTranslated, nil getUnavailableText := func()string{
if (attributeName == "Height"){
return noResponseTranslated
}
return unknownTranslated
}
unavailableText := getUnavailableText()
return titleTranslated, true, formatHeightFunction, unitsWithPadding, unavailableText, nil
} }
case "Sex":{ case "Sex":{
@ -452,24 +517,6 @@ func GetProfileAttributeDisplayInfo(attributeName string)(string, bool, func(str
return titleTranslated, false, translateValueFunction, "", unknownTranslated, nil return titleTranslated, false, translateValueFunction, "", unknownTranslated, nil
} }
case "OffspringProbabilityOfAnyMonogenicDisease":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Probability Of Any Monogenic Disease")
return titleTranslated, true, formatPercentageFunction, "", unknownTranslated, nil
}
case "TotalPolygenicDiseaseRiskScore":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Total Polygenic Disease Risk Score")
return titleTranslated, true, passValueFunction, "/100", noResponseTranslated, nil
}
case "OffspringTotalPolygenicDiseaseRiskScore":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Total Polygenic Disease Risk Score")
return titleTranslated, true, passValueFunction, "/100", unknownTranslated, nil
}
case "23andMe_AncestryComposition":{ case "23andMe_AncestryComposition":{
// There is no way to display this as text, we use the gui instead // There is no way to display this as text, we use the gui instead
@ -867,6 +914,24 @@ func GetProfileAttributeDisplayInfo(attributeName string)(string, bool, func(str
return titleTranslated, true, passValueFunction, "%", unknownTranslated, nil return titleTranslated, true, passValueFunction, "%", unknownTranslated, nil
} }
case "OffspringProbabilityOfAnyMonogenicDisease":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Probability Of Any Monogenic Disease")
return titleTranslated, true, formatPercentageFunction, "%", unknownTranslated, nil
}
case "TotalPolygenicDiseaseRiskScore":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Total Polygenic Disease Risk Score")
return titleTranslated, true, passValueFunction, "/100", noResponseTranslated, nil
}
case "OffspringTotalPolygenicDiseaseRiskScore":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Total Polygenic Disease Risk Score")
return titleTranslated, true, passValueFunction, "/100", unknownTranslated, nil
}
case "OffspringProbabilityOfAnyMonogenicDisease_NumberOfDiseasesTested":{ case "OffspringProbabilityOfAnyMonogenicDisease_NumberOfDiseasesTested":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Probability Of Any Monogenic Disease - Number Of Diseases Tested") titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Probability Of Any Monogenic Disease - Number Of Diseases Tested")
@ -897,6 +962,107 @@ func GetProfileAttributeDisplayInfo(attributeName string)(string, bool, func(str
return titleTranslated, true, passValueFunction, unitsWithPadding, "", nil return titleTranslated, true, passValueFunction, unitsWithPadding, "", nil
} }
case "AutismRiskScore",
"OffspringAutismRiskScore",
"ObesityRiskScore",
"OffspringObesityRiskScore",
"HomosexualnessScore",
"OffspringHomosexualnessScore":{
getAttributeTitle := func()(string, error){
switch attributeName{
case "AutismRiskScore":{
return "Autism Risk Score", nil
}
case "OffspringAutismRiskScore":{
return "Offspring Autism Risk Score", nil
}
case "ObesityRiskScore":{
return "Obesity Risk Score", nil
}
case "OffspringObesityRiskScore":{
return "Offspring Obesity Risk Score", nil
}
case "HomosexualnessScore":{
return "Homosexualness Score", nil
}
case "OffspringHomosexualnessScore":{
return "Offspring Homosexualness Score", nil
}
}
return "", errors.New("getAttributeTitle reached with unknown attributeName: " + attributeName)
}
attributeTitle, err := getAttributeTitle()
if (err != nil) { return "", false, nil, "", "", err }
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage(attributeTitle)
return titleTranslated, true, passValueFunction, "/10", unknownTranslated, nil
}
case "PredictedEyeColor":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Predicted Eye Color")
return titleTranslated, false, translateValueFunction, "", unknownTranslated, nil
}
case "PredictedHairTexture":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Predicted Hair Texture")
return titleTranslated, false, translateValueFunction, "", unknownTranslated, nil
}
case "PredictedLactoseTolerance":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Predicted Lactose Tolerance")
return titleTranslated, false, translateValueFunction, "", unknownTranslated, nil
}
case "OffspringBlueEyesProbability":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Blue Eyes Probability")
return titleTranslated, false, formatPercentageFunction, "%", unknownTranslated, nil
}
case "OffspringGreenEyesProbability":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Green Eyes Probability")
return titleTranslated, false, formatPercentageFunction, "%", unknownTranslated, nil
}
case "OffspringHazelEyesProbability":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Hazel Eyes Probability")
return titleTranslated, false, formatPercentageFunction, "%", unknownTranslated, nil
}
case "OffspringBrownEyesProbability":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Brown Eyes Probability")
return titleTranslated, false, formatPercentageFunction, "%", unknownTranslated, nil
}
case "OffspringLactoseToleranceProbability":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Lactose Tolerance Probability")
return titleTranslated, false, formatPercentageFunction, "%", unknownTranslated, nil
}
case "OffspringStraightHairProbability":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Straight Hair Probability")
return titleTranslated, false, formatPercentageFunction, "%", unknownTranslated, nil
}
case "OffspringCurlyHairProbability":{
titleTranslated := translation.TranslateTextFromEnglishToMyLanguage("Offspring Curly Hair Probability")
return titleTranslated, false, formatPercentageFunction, "%", unknownTranslated, nil
}
} }
attributeHasMonogenicDiseasePrefix := strings.HasPrefix(attributeName, "MonogenicDisease_") attributeHasMonogenicDiseasePrefix := strings.HasPrefix(attributeName, "MonogenicDisease_")

View file

@ -46,10 +46,6 @@ import "slices"
//TODO: //TODO:
// -LastActive // -LastActive
// -OffspringLactoseToleranceProbability
// Used to sort users based on probability of lactose tolerance
// This allows the user to sort matches based on whose offspring is most likely to be lactose tolerant
// -Offspring Probability for all traits
// -DietSimilarity // -DietSimilarity
@ -62,12 +58,6 @@ var calculatedAttributesList = []string{
"Distance", "Distance",
"IsSameSex", "IsSameSex",
"23andMe_OffspringNeanderthalVariants", "23andMe_OffspringNeanderthalVariants",
"OffspringProbabilityOfAnyMonogenicDisease",
"OffspringProbabilityOfAnyMonogenicDisease_NumberOfDiseasesTested",
"TotalPolygenicDiseaseRiskScore",
"TotalPolygenicDiseaseRiskScore_NumberOfDiseasesTested",
"OffspringTotalPolygenicDiseaseRiskScore",
"OffspringTotalPolygenicDiseaseRiskScore_NumberOfDiseasesTested",
"SearchTermsCount", "SearchTermsCount",
"HasMessagedMe", "HasMessagedMe",
"IHaveMessaged", "IHaveMessaged",
@ -95,6 +85,46 @@ var calculatedAttributesList = []string{
"23andMe_MaternalHaplogroupSimilarity", "23andMe_MaternalHaplogroupSimilarity",
"23andMe_PaternalHaplogroupSimilarity", "23andMe_PaternalHaplogroupSimilarity",
"NumberOfReviews", "NumberOfReviews",
"OffspringProbabilityOfAnyMonogenicDisease",
"OffspringProbabilityOfAnyMonogenicDisease_NumberOfDiseasesTested",
// Polygenic Diseases:
"TotalPolygenicDiseaseRiskScore",
"TotalPolygenicDiseaseRiskScore_NumberOfDiseasesTested",
"OffspringTotalPolygenicDiseaseRiskScore",
"OffspringTotalPolygenicDiseaseRiskScore_NumberOfDiseasesTested",
"AutismRiskScore",
"OffspringAutismRiskScore",
"ObesityRiskScore",
"OffspringObesityRiskScore",
// Discrete Traits:
"PredictedEyeColor",
"OffspringBlueEyesProbability",
"OffspringGreenEyesProbability",
"OffspringHazelEyesProbability",
"OffspringBrownEyesProbability",
"PredictedLactoseTolerance",
"OffspringLactoseToleranceProbability",
"PredictedHairTexture",
"OffspringStraightHairProbability",
"OffspringCurlyHairProbability",
// Numeric Traits:
"HomosexualnessScore",
"OffspringHomosexualnessScore",
"PredictedHeight",
"OffspringPredictedHeight",
} }
// We use a map for faster lookups // We use a map for faster lookups
@ -677,15 +707,22 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
// Each risk score is a number between 0 and 1 // Each risk score is a number between 0 and 1
allDiseasesAverageRiskScoreNumerator := float64(0) allDiseasesAverageRiskScoreNumerator := float64(0)
for _, diseaseObject := range polygenicDiseaseObjectsList{ // Map Structure: Locus rsID -> Locus Value
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
// Map Structure: Locus rsID -> Locus Value for _, diseaseObject := range polygenicDiseaseObjectsList{
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
diseaseLociList := diseaseObject.LociList diseaseLociList := diseaseObject.LociList
for _, locusRSID := range diseaseLociList{ for _, locusRSID := range diseaseLociList{
_, exists := userDiseaseLocusValuesMap[locusRSID]
if (exists == true){
// We already added this locus
// This can happen if two diseases share the same locus
continue
}
locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
locusValueAttributeName := "LocusValue_rs" + locusRSIDString locusValueAttributeName := "LocusValue_rs" + locusRSIDString
@ -769,11 +806,8 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
// We have not linked a genome person and performed a genetic analysis // We have not linked a genome person and performed a genetic analysis
// The total monogenic disease risk is unknown // The total polygenic disease risk is unknown
// We can still predict disease risk for individual recessive disorders when one person has no variants, but
// not the total probability for all monogenic diseases
// This is because everyone is a carrier for at least some recessive monogenic disorders
//
return false, profileVersion, "", nil return false, profileVersion, "", nil
} }
@ -794,15 +828,22 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
// Each risk score is a number between 0 and 1 // Each risk score is a number between 0 and 1
allDiseasesAverageRiskScoreNumerator := float64(0) allDiseasesAverageRiskScoreNumerator := float64(0)
// Map Structure: rsID -> Locus Value
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, diseaseObject := range polygenicDiseaseObjectsList{ for _, diseaseObject := range polygenicDiseaseObjectsList{
diseaseLociList := diseaseObject.LociList diseaseLociList := diseaseObject.LociList
// Map Structure: rsID -> Locus Value
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, locusRSID := range diseaseLociList{ for _, locusRSID := range diseaseLociList{
_, exists := userDiseaseLocusValuesMap[locusRSID]
if (exists == true){
// We already added this locus
// This can happen if two diseases share the same locus
continue
}
locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
locusValueAttributeName := "LocusValue_rs" + locusRSIDString locusValueAttributeName := "LocusValue_rs" + locusRSIDString
@ -834,7 +875,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
Base2Value: userLocusBase2, Base2Value: userLocusBase2,
LocusIsPhased: userLocusIsPhased, LocusIsPhased: userLocusIsPhased,
} }
userDiseaseLocusValuesMap[locusRSID] = newLocusValueObject userDiseaseLocusValuesMap[locusRSID] = newLocusValueObject
} }
@ -867,6 +908,386 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
return true, profileVersion, allDiseasesAverageRiskScoreString, nil return true, profileVersion, allDiseasesAverageRiskScoreString, nil
} }
case "AutismRiskScore",
"ObesityRiskScore":{
// These are polygenic diseases
// We get the risk score for the user
diseaseName := strings.TrimSuffix(attributeName, "RiskScore")
diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName)
if (err != nil){ return false, 0, "", err }
diseaseLociList := diseaseObject.LociList
userDiseaseLocusValuesMap, err := getUserGenomeLocusValuesMap(diseaseLociList, getProfileAttributesFunction)
if (err != nil) { return false, 0, "", err }
neuralNetworkExists, anyLocusTested, userDiseaseRiskScore, _, _, _, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseAnalysis(diseaseObject, userDiseaseLocusValuesMap, true)
if (err != nil) { return false, 0, "", err }
if (neuralNetworkExists == false){
return false, 0, "", errors.New("Neural network missing for disease: " + diseaseName)
}
if (anyLocusTested == false){
// Disease risk is unknown
return false, profileVersion, "", nil
}
riskScoreString := helpers.ConvertIntToString(userDiseaseRiskScore)
return true, profileVersion, riskScoreString, nil
}
case "OffspringAutismRiskScore",
"OffspringObesityRiskScore":{
// These are polygenic diseases
// We get the risk score for the offspring
myPersonChosen, myGenomesExist, myAnalysisIsReady, myGeneticAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return false, 0, "", err }
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
// We have not linked a genome person and performed a genetic analysis
// All offspring polygenic disease risks are unknown
return false, profileVersion, "", nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return false, 0, "", err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, 0, "", errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis which has GenomesMap which is missing my genome identifier.")
}
diseaseNameWithOffspring := strings.TrimSuffix(attributeName, "RiskScore")
diseaseName := strings.TrimPrefix(diseaseNameWithOffspring, "Offspring")
diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName)
if (err != nil){ return false, 0, "", err }
diseaseLociList := diseaseObject.LociList
userDiseaseLocusValuesMap, err := getUserGenomeLocusValuesMap(diseaseLociList, getProfileAttributesFunction)
if (err != nil) { return false, 0, "", err }
neuralNetworkExists, anyLocusValuesTested, offspringAverageRiskScore, _, _, _, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseAnalysis(diseaseObject, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) { return false, 0, "", err }
if (neuralNetworkExists == false){
return false, 0, "", errors.New("No neural network exists for disease: " + diseaseName)
}
if (anyLocusValuesTested == false){
// No disease loci are known
return false, profileVersion, "", nil
}
offspringAverageRiskScoreString := helpers.ConvertIntToString(offspringAverageRiskScore)
return true, profileVersion, offspringAverageRiskScoreString, nil
}
case "PredictedEyeColor",
"PredictedLactoseTolerance",
"PredictedHairTexture":{
//Outputs:
// -string: Trait name
getTraitName := func()(string, error){
switch attributeName{
case "PredictedEyeColor":{
return "Eye Color", nil
}
case "PredictedLactoseTolerance":{
return "Lactose Tolerance", nil
}
case "PredictedHairTexture":{
return "Hair Texture", nil
}
}
return "", errors.New("Discrete trait calculated attribute reached with unknown attributeName: " + attributeName)
}
traitName, err := getTraitName()
if (err != nil) { return false, 0, "", err }
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return false, 0, "", err }
traitLociList := traitObject.LociList
userTraitLocusValuesMap, err := getUserGenomeLocusValuesMap(traitLociList, getProfileAttributesFunction)
if (err != nil) { return false, 0, "", err }
neuralNetworkExists, neuralNetworkOutcomeIsKnown, predictedOutcome, _, _, _, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return false, 0, "", err }
if (neuralNetworkExists == true){
if (neuralNetworkOutcomeIsKnown == false){
return false, 0, "", nil
}
return true, profileVersion, predictedOutcome, nil
}
anyRulesExist, _, _, _, predictedOutcomeExists, predictedOutcome, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_Rules(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return false, 0, "", err }
if (anyRulesExist == false){
return false, 0, "", errors.New("Discrete trait calculated attribute exists for trait without a neural network or rules: " + traitName)
}
if (predictedOutcomeExists == false){
return false, 0, "", nil
}
return true, profileVersion, predictedOutcome, nil
}
case "OffspringBlueEyesProbability",
"OffspringGreenEyesProbability",
"OffspringHazelEyesProbability",
"OffspringBrownEyesProbability",
"OffspringLactoseToleranceProbability",
"OffspringStraightHairProbability",
"OffspringCurlyHairProbability":{
//TODO: Add ability to retrieve confidence/quantity of loci known and sort/filter by those attributes
myPersonChosen, myGenomesExist, myAnalysisIsReady, myGeneticAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return false, 0, "", err }
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
// We have not linked a genome person and performed a genetic analysis
// All offspring trait predictions are unknown
return false, profileVersion, "", nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return false, 0, "", err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, 0, "", errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis which has GenomesMap which is missing my genome identifier.")
}
// These are discrete traits
// We get the outcome probability for the offspring
//Outputs:
// -string: Trait name
// -string: Outcome name
getTraitAndOutcomeName := func()(string, string, error){
switch attributeName{
case "OffspringBlueEyesProbability":{
return "Eye Color", "Blue", nil
}
case "OffspringGreenEyesProbability":{
return "Eye Color", "Green", nil
}
case "OffspringHazelEyesProbability":{
return "Eye Color", "Hazel", nil
}
case "OffspringBrownEyesProbability":{
return "Eye Color", "Brown", nil
}
case "OffspringLactoseToleranceProbability":{
return "Lactose Tolerance", "Tolerant", nil
}
case "OffspringStraightHairProbability":{
return "Hair Texture", "Straight", nil
}
case "OffspringCurlyHairProbability":{
return "Hair Texture", "Curly", nil
}
}
return "", "", errors.New("Offspring discrete trait calculated attribute reached with unknown attributeName: " + attributeName)
}
traitName, outcomeName, err := getTraitAndOutcomeName()
if (err != nil) { return false, 0, "", err }
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return false, 0, "", err }
traitLociList := traitObject.LociList
userTraitLocusValuesMap, err := getUserGenomeLocusValuesMap(traitLociList, getProfileAttributesFunction)
if (err != nil) { return false, 0, "", err }
neuralNetworkExists, anyLociKnown, outcomeProbabilitiesMap, _, _, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitAnalysis_NeuralNetwork(traitObject, myGenomeLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, 0, "", err }
if (neuralNetworkExists == true){
if (anyLociKnown == false){
// Trait prediction is not possible
return false, 0, "", nil
}
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
return true, profileVersion, "0", nil
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
return true, profileVersion, outcomeProbabilityString, nil
}
anyRulesExist, rulesAnalysisExists, _, _, _, outcomeProbabilitiesMap, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitAnalysis_Rules(traitObject, myGenomeLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, 0, "", err }
if (anyRulesExist == false){
return false, 0, "", errors.New("Calculation of offspring attribute for discrete trait called with trait missing neural network or rules: " + traitName)
}
if (rulesAnalysisExists == false){
// Analysis is impossible for this trait
return false, 0, "", nil
}
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
return true, profileVersion, "0", nil
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
return true, profileVersion, outcomeProbabilityString, nil
}
case "HomosexualnessScore",
"PredictedHeight":{
// These are numeric traits
// We calculate the value for the user
//Outputs:
// -string: Trait name
// -bool: Is a score between 0-10
// -error
getTraitNameAndIsAScoreBool := func()(string, bool, error){
switch attributeName{
case "HomosexualnessScore":{
return "Homosexualness", true, nil
}
case "PredictedHeight":{
return "Height", false, nil
}
}
return "", false, errors.New("User numeric trait value calculation called with unknown attributeName: " + attributeName)
}
traitName, traitIsAScore, err := getTraitNameAndIsAScoreBool()
if (err != nil) { return false, 0, "", err }
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return false, 0, "", err }
traitLociList := traitObject.LociList
userTraitLocusValuesMap, err := getUserGenomeLocusValuesMap(traitLociList, getProfileAttributesFunction)
if (err != nil) { return false, 0, "", err }
traitNeuralNetworkExists, anyLocusValuesAreKnown, predictedOutcome, _, _, _, err := createPersonGeneticAnalysis.GetGenomeNumericTraitAnalysis(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return false, 0, "", err }
if (traitNeuralNetworkExists == false){
return false, 0, "", errors.New("Numeric trait attribute calculation reached for trait with no neural network: " + traitName)
}
if (anyLocusValuesAreKnown == false){
// Trait prediction is impossible
return false, 0, "", nil
}
if (traitIsAScore == true){
predictedScoreString := helpers.ConvertIntToString(int(predictedOutcome))
return true, profileVersion, predictedScoreString, nil
}
predictedOutcomeString := helpers.ConvertFloat64ToString(predictedOutcome)
return true, profileVersion, predictedOutcomeString, nil
}
case "OffspringHomosexualnessScore",
"OffspringPredictedHeight":{
// These are numeric traits
// We calculate the value for the offspring
myPersonChosen, myGenomesExist, myAnalysisIsReady, myGeneticAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return false, 0, "", err }
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
// We have not linked a genome person and performed a genetic analysis
// All offspring trait predictions are unknown
return false, profileVersion, "", nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return false, 0, "", err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, 0, "", errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis which has GenomesMap which is missing my genome identifier.")
}
//Outputs:
// -string: Trait name
// -bool: Is a score between 0-10
// -error
getTraitNameAndIsAScoreBool := func()(string, bool, error){
switch attributeName{
case "OffspringHomosexualnessScore":{
return "Homosexualness", true, nil
}
case "OffspringPredictedHeight":{
return "Height", false, nil
}
}
return "", false, errors.New("Offspring numeric trait value calculation called with unknown attributeName: " + attributeName)
}
traitName, traitIsAScore, err := getTraitNameAndIsAScoreBool()
if (err != nil) { return false, 0, "", err }
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return false, 0, "", err }
traitLociList := traitObject.LociList
userTraitLocusValuesMap, err := getUserGenomeLocusValuesMap(traitLociList, getProfileAttributesFunction)
if (err != nil) { return false, 0, "", err }
neuralNetworkExists, anyLociKnown, predictedOutcome, _, _, _, _, err := createCoupleGeneticAnalysis.GetOffspringNumericTraitAnalysis(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap)
if (err != nil) { return false, 0, "", err }
if (neuralNetworkExists == false){
return false, 0, "", errors.New("Offspring Numeric trait attribute calculation reached for trait with no neural network: " + traitName)
}
if (anyLociKnown == false){
// Prediction is impossible
return false, profileVersion, "", nil
}
if (traitIsAScore == true){
predictedScoreString := helpers.ConvertIntToString(int(predictedOutcome))
return true, profileVersion, predictedScoreString, nil
}
predictedOutcomeString := helpers.ConvertFloat64ToString(predictedOutcome)
return true, profileVersion, predictedOutcomeString, nil
}
case "SearchTermsCount":{ case "SearchTermsCount":{
myDesireExists, myDesiredChoicesListString, err := myLocalDesires.GetDesire("SearchTerms") myDesireExists, myDesiredChoicesListString, err := myLocalDesires.GetDesire("SearchTerms")
@ -1645,7 +2066,55 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
return false, 0, "", errors.New("GetAnyProfileAttributeIncludingCalculated called with unknown attribute: " + attributeName) return false, 0, "", errors.New("GetAnyProfileAttributeIncludingCalculated called with unknown attribute: " + attributeName)
} }
// This function constructs a traitLocusValuesMap from a user's profile and a set of loci
//Outputs:
// -map[int64]locusValue.LocusValue: Genome map for provided loci
// -error
func getUserGenomeLocusValuesMap(lociList []int64, getProfileAttributesFunction func(string)(bool, int, string, error))(map[int64]locusValue.LocusValue, error){
// We construct the user's locus values map
// Map Structure: Locus rsID -> locusValue.LocusValue
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, rsID := range lociList{
rsIDString := helpers.ConvertInt64ToString(rsID)
userLocusValueAttributeName := "LocusValue_rs" + rsIDString
userLocusValueIsKnown, _, userLocusValue, err := getProfileAttributesFunction(userLocusValueAttributeName)
if (err != nil) { return nil, err }
if (userLocusValueIsKnown == false){
continue
}
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusValue, ";")
if (semicolonFound == false){
return nil, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + " value: " + userLocusValue)
}
userLocusIsPhasedAttributeName := "LocusIsPhased_rs" + rsIDString
userLocusIsPhasedExists, _, userLocusIsPhasedString, err := getProfileAttributesFunction(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: " + rsIDString)
}
userLocusIsPhased, err := helpers.ConvertYesOrNoStringToBool(userLocusIsPhasedString)
if (err != nil) { return nil, err }
userLocusValueObject := locusValue.LocusValue{
Base1Value: userLocusBase1,
Base2Value: userLocusBase2,
LocusIsPhased: userLocusIsPhased,
}
userTraitLocusValuesMap[rsID] = userLocusValueObject
}
return userTraitLocusValuesMap, nil
}

View file

@ -26,7 +26,7 @@ func TestCalculatedAttributes(t *testing.T){
calculatedAttributesList := calculatedAttributes.GetCalculatedAttributesList() calculatedAttributesList := calculatedAttributes.GetCalculatedAttributesList()
for i:=0; i<100; i++{ for i:=0; i<10; i++{
identityPublicKey, identityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys() identityPublicKey, identityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
if (err != nil){ if (err != nil){