Improved the creation process of genetic analyses in various ways. A sample of offspring polygenic disease risk scores are now created and viewable by users.

This commit is contained in:
Simon Sarasova 2024-06-07 00:04:13 +00:00
parent ee976f49b3
commit 649bf318c2
No known key found for this signature in database
GPG key ID: EEDA4103C9C36944
17 changed files with 1606 additions and 1417 deletions

View file

@ -6,6 +6,7 @@ Small and insignificant changes may not be included in this log.
## Unversioned Changes ## Unversioned Changes
* Improved the creation process of genetic analyses in various ways. A sample of offspring polygenic disease risk scores are now created and viewable by users. - *Simon Sarasova*
* Improved the genetic analysis creation process in various ways. Recombination breakpoints are more accurately predicted now. - *Simon Sarasova* * Improved the genetic analysis creation process in various ways. Recombination breakpoints are more accurately predicted now. - *Simon Sarasova*
* Improved the identity hash generation tool. The fastest quantity of goroutines is now identified and used. - *Simon Sarasova* * Improved the identity hash generation tool. The fastest quantity of goroutines is now identified and used. - *Simon Sarasova*
* Improved the creation procedures, encoding format, and graphical presentation of genetic analyses. Map lists have been replaced by custom objects. - *Simon Sarasova* * Improved the creation procedures, encoding format, and graphical presentation of genetic analyses. Map lists have been replaced by custom objects. - *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 | 250 Simon Sarasova | June 13, 2023 | 251

View file

@ -18,6 +18,7 @@ import "seekia/internal/helpers"
import "seekia/internal/network/appNetworkType/getAppNetworkType" import "seekia/internal/network/appNetworkType/getAppNetworkType"
import "seekia/internal/profiles/attributeDisplay" import "seekia/internal/profiles/attributeDisplay"
import "seekia/internal/profiles/userStatistics" import "seekia/internal/profiles/userStatistics"
import "seekia/internal/statisticsDatum"
import "errors" import "errors"
import "image" import "image"
@ -146,13 +147,13 @@ func setViewUserAttributeStatisticsPage_BarChart(
showYAxisPercentage bool, showYAxisPercentage bool,
statisticsReady bool, statisticsReady bool,
anyUsersExist bool, anyUsersExist bool,
statisticsItemsList []userStatistics.StatisticsItem, statisticsDatumsList []statisticsDatum.StatisticsDatum,
groupingPerformed bool, groupingPerformed bool,
groupedStatisticsItemsList []userStatistics.StatisticsItem, groupedStatisticsDatumsList []statisticsDatum.StatisticsDatum,
chartImage image.Image, chartImage image.Image,
previousPage func()){ previousPage func()){
currentPage := func(){setViewUserAttributeStatisticsPage_BarChart(window, identityType, xAxisAttributeName, yAxisAttribute, yAxisUnits, showYAxisPercentage, statisticsReady, anyUsersExist, statisticsItemsList, groupingPerformed, groupedStatisticsItemsList, chartImage, previousPage)} currentPage := func(){setViewUserAttributeStatisticsPage_BarChart(window, identityType, xAxisAttributeName, yAxisAttribute, yAxisUnits, showYAxisPercentage, statisticsReady, anyUsersExist, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, chartImage, previousPage)}
pageIdentifier, err := helpers.GetNewRandomHexString(16) pageIdentifier, err := helpers.GetNewRandomHexString(16)
if (err != nil) { if (err != nil) {
@ -264,7 +265,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
} }
viewDataButton := widget.NewButtonWithIcon("View Data", theme.ListIcon(), func(){ viewDataButton := widget.NewButtonWithIcon("View Data", theme.ListIcon(), func(){
setViewUserStatisticsDataPage(window, xAxisAttributeTitle, yAxisAttribute, showYAxisPercentage, statisticsItemsList, groupingPerformed, groupedStatisticsItemsList, true, xAxisUnits, yAxisUnits, currentPage) setViewUserStatisticsDataPage(window, xAxisAttributeTitle, yAxisAttribute, showYAxisPercentage, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, true, xAxisUnits, yAxisUnits, currentPage)
}) })
viewFullscreenButton := widget.NewButtonWithIcon("View Fullscreen", theme.ZoomInIcon(), func(){ viewFullscreenButton := widget.NewButtonWithIcon("View Fullscreen", theme.ZoomInIcon(), func(){
@ -336,7 +337,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
go updateLoadingBindingFunction() go updateLoadingBindingFunction()
totalAnalyzedUsers, statisticsItemsList, groupingPerformed, groupedStatisticsItemsList, formatYAxisValuesFunction, err := userStatistics.GetUserStatisticsItemsLists_BarChart(identityType, appNetworkType, xAxisAttributeName, xAxisIsNumerical, formatXAxisValuesFunction, unknownXAxisValuesTextTranslated, yAxisAttribute) totalAnalyzedUsers, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, formatYAxisValuesFunction, err := userStatistics.GetUserStatisticsDatumsLists_BarChart(identityType, appNetworkType, xAxisAttributeName, xAxisIsNumerical, formatXAxisValuesFunction, unknownXAxisValuesTextTranslated, yAxisAttribute)
if (err != nil) { if (err != nil) {
functionCompleteBoolMutex.Lock() functionCompleteBoolMutex.Lock()
@ -350,7 +351,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
return return
} }
if (len(statisticsItemsList) == 0){ if (len(statisticsDatumsList) == 0){
functionCompleteBoolMutex.Lock() functionCompleteBoolMutex.Lock()
functionCompleteBool = true functionCompleteBool = true
@ -363,14 +364,14 @@ func setViewUserAttributeStatisticsPage_BarChart(
return return
} }
getChartStatisticsItemsList := func()[]userStatistics.StatisticsItem{ getChartStatisticsDatumsList := func()[]statisticsDatum.StatisticsDatum{
if (groupingPerformed == false){ if (groupingPerformed == false){
return statisticsItemsList return statisticsDatumsList
} }
return groupedStatisticsItemsList return groupedStatisticsDatumsList
} }
chartStatisticsItemsList := getChartStatisticsItemsList() chartStatisticsDatumsList := getChartStatisticsDatumsList()
getChartTitle := func()string{ getChartTitle := func()string{
@ -401,7 +402,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
chartTitle := getChartTitle() chartTitle := getChartTitle()
newChartImage, err := createCharts.CreateBarChart(chartTitle, chartStatisticsItemsList, formatYAxisValuesFunction, true, yAxisUnits) newChartImage, err := createCharts.CreateBarChart(chartTitle, chartStatisticsDatumsList, formatYAxisValuesFunction, true, yAxisUnits)
if (err != nil) { if (err != nil) {
functionCompleteBoolMutex.Lock() functionCompleteBoolMutex.Lock()
@ -421,7 +422,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
pageHasChanged := checkIfPageHasChangedFunction() pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){ if (pageHasChanged == false){
setViewUserAttributeStatisticsPage_BarChart(window, identityType, xAxisAttributeName, yAxisAttribute, yAxisUnits, showYAxisPercentage, true, true, statisticsItemsList, groupingPerformed, groupedStatisticsItemsList, newChartImage, previousPage) setViewUserAttributeStatisticsPage_BarChart(window, identityType, xAxisAttributeName, yAxisAttribute, yAxisUnits, showYAxisPercentage, true, true, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, newChartImage, previousPage)
} }
} }
@ -441,13 +442,13 @@ func setViewUserAttributeStatisticsPage_DonutChart(
attributeName string, attributeName string,
statisticsReady bool, statisticsReady bool,
anyUsersExist bool, anyUsersExist bool,
statisticsItemsList []userStatistics.StatisticsItem, statisticsDatumsList []statisticsDatum.StatisticsDatum,
groupingPerformed bool, groupingPerformed bool,
groupedStatisticsItemsList []userStatistics.StatisticsItem, groupedStatisticsDatumsList []statisticsDatum.StatisticsDatum,
chartImage image.Image, chartImage image.Image,
previousPage func()){ previousPage func()){
currentPage := func(){setViewUserAttributeStatisticsPage_DonutChart(window, identityType, attributeName, statisticsReady, anyUsersExist, statisticsItemsList, groupingPerformed, groupedStatisticsItemsList, chartImage, previousPage)} currentPage := func(){setViewUserAttributeStatisticsPage_DonutChart(window, identityType, attributeName, statisticsReady, anyUsersExist, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, chartImage, previousPage)}
pageIdentifier, err := helpers.GetNewRandomHexString(16) pageIdentifier, err := helpers.GetNewRandomHexString(16)
if (err != nil) { if (err != nil) {
@ -545,7 +546,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
viewDataButton := widget.NewButtonWithIcon("View Data", theme.ListIcon(), func(){ viewDataButton := widget.NewButtonWithIcon("View Data", theme.ListIcon(), func(){
setViewUserStatisticsDataPage(window, attributeTitle, "Number Of Users", true, statisticsItemsList, groupingPerformed, groupedStatisticsItemsList, true, attributeUnits, "Users", currentPage) setViewUserStatisticsDataPage(window, attributeTitle, "Number Of Users", true, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, true, attributeUnits, "Users", currentPage)
}) })
viewFullscreenButton := widget.NewButtonWithIcon("View Fullscreen", theme.ZoomInIcon(), func(){ viewFullscreenButton := widget.NewButtonWithIcon("View Fullscreen", theme.ZoomInIcon(), func(){
@ -616,7 +617,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
go updateLoadingBindingFunction() go updateLoadingBindingFunction()
totalAnalyzedUsers, statisticsItemsList, groupingPerformed, groupedStatisticsItemsList, err := userStatistics.GetUserStatisticsItemsLists_DonutChart(identityType, appNetworkType, attributeName, attributeIsNumerical, formatAttributeValuesFunction, unknownValuesTextTranslated) totalAnalyzedUsers, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, err := userStatistics.GetUserStatisticsDatumsLists_DonutChart(identityType, appNetworkType, attributeName, attributeIsNumerical, formatAttributeValuesFunction, unknownValuesTextTranslated)
if (err != nil) { if (err != nil) {
functionCompleteBoolMutex.Lock() functionCompleteBoolMutex.Lock()
@ -630,7 +631,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
return return
} }
if (len(statisticsItemsList) == 0){ if (len(statisticsDatumsList) == 0){
functionCompleteBoolMutex.Lock() functionCompleteBoolMutex.Lock()
functionCompleteBool = true functionCompleteBool = true
@ -643,14 +644,14 @@ func setViewUserAttributeStatisticsPage_DonutChart(
return return
} }
getChartStatisticsItemsList := func()[]userStatistics.StatisticsItem{ getChartStatisticsDatumsList := func()[]statisticsDatum.StatisticsDatum{
if (groupingPerformed == false){ if (groupingPerformed == false){
return statisticsItemsList return statisticsDatumsList
} }
return groupedStatisticsItemsList return groupedStatisticsDatumsList
} }
chartStatisticsItemsList := getChartStatisticsItemsList() chartStatisticsDatumsList := getChartStatisticsDatumsList()
getChartTitle := func()string{ getChartTitle := func()string{
@ -672,7 +673,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
chartTitle := getChartTitle() chartTitle := getChartTitle()
newChartImage, err := createCharts.CreateDonutChart(chartTitle, chartStatisticsItemsList) newChartImage, err := createCharts.CreateDonutChart(chartTitle, chartStatisticsDatumsList)
if (err != nil){ if (err != nil){
functionCompleteBoolMutex.Lock() functionCompleteBoolMutex.Lock()
@ -692,7 +693,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
pageHasChanged := checkIfPageHasChangedFunction() pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){ if (pageHasChanged == false){
setViewUserAttributeStatisticsPage_DonutChart(window, identityType, attributeName, true, true, statisticsItemsList, groupingPerformed, groupedStatisticsItemsList, newChartImage, previousPage) setViewUserAttributeStatisticsPage_DonutChart(window, identityType, attributeName, true, true, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, newChartImage, previousPage)
} }
} }
@ -934,9 +935,9 @@ func setViewUserStatisticsDataPage(
attributeTitle string, attributeTitle string,
rightColumnName string, rightColumnName string,
showPercentageColumn bool, showPercentageColumn bool,
statisticsItemsList []userStatistics.StatisticsItem, statisticsDatumsList []statisticsDatum.StatisticsDatum,
groupingPerformed bool, groupingPerformed bool,
groupedStatisticsItemsList []userStatistics.StatisticsItem, groupedStatisticsDatumsList []statisticsDatum.StatisticsDatum,
showGroupedStatistics bool, showGroupedStatistics bool,
attributeColumnUnits string, attributeColumnUnits string,
rightColumnUnits string, rightColumnUnits string,
@ -954,13 +955,13 @@ func setViewUserStatisticsDataPage(
if (showGroupedStatistics == false){ if (showGroupedStatistics == false){
showDataGroupedButton := getWidgetCentered(widget.NewButton("Show Data Grouped", func(){ showDataGroupedButton := getWidgetCentered(widget.NewButton("Show Data Grouped", func(){
setViewUserStatisticsDataPage(window, attributeTitle, rightColumnName, showPercentageColumn, statisticsItemsList, true, groupedStatisticsItemsList, true, attributeColumnUnits, rightColumnUnits, previousPage) setViewUserStatisticsDataPage(window, attributeTitle, rightColumnName, showPercentageColumn, statisticsDatumsList, true, groupedStatisticsDatumsList, true, attributeColumnUnits, rightColumnUnits, previousPage)
})) }))
header.Add(showDataGroupedButton) header.Add(showDataGroupedButton)
} else { } else {
showDataRawButton := getWidgetCentered(widget.NewButton("Show Data Raw", func(){ showDataRawButton := getWidgetCentered(widget.NewButton("Show Data Raw", func(){
setViewUserStatisticsDataPage(window, attributeTitle, rightColumnName, showPercentageColumn, statisticsItemsList, true, groupedStatisticsItemsList, false, attributeColumnUnits, rightColumnUnits, previousPage) setViewUserStatisticsDataPage(window, attributeTitle, rightColumnName, showPercentageColumn, statisticsDatumsList, true, groupedStatisticsDatumsList, false, attributeColumnUnits, rightColumnUnits, previousPage)
})) }))
header.Add(showDataRawButton) header.Add(showDataRawButton)
@ -968,14 +969,14 @@ func setViewUserStatisticsDataPage(
header.Add(widget.NewSeparator()) header.Add(widget.NewSeparator())
} }
getStatisticsItemsListToShow := func()[]userStatistics.StatisticsItem{ getStatisticsDatumsListToShow := func()[]statisticsDatum.StatisticsDatum{
if (groupingPerformed == true && showGroupedStatistics == true){ if (groupingPerformed == true && showGroupedStatistics == true){
return groupedStatisticsItemsList return groupedStatisticsDatumsList
} }
return statisticsItemsList return statisticsDatumsList
} }
statisticsItemsToShowList := getStatisticsItemsListToShow() statisticsDatumsToShowList := getStatisticsDatumsListToShow()
getStatisticsDataGrid := func()(*fyne.Container, error){ getStatisticsDataGrid := func()(*fyne.Container, error){
@ -983,38 +984,38 @@ func setViewUserStatisticsDataPage(
if (showPercentageColumn == true){ if (showPercentageColumn == true){
for _, item := range statisticsItemsToShowList{ for _, datum := range statisticsDatumsToShowList{
itemValue := item.Value datumValue := datum.Value
allValuesSummed += itemValue allValuesSummed += datumValue
} }
} }
attributeColumnValuesList := make([]string, 0, len(statisticsItemsToShowList)) attributeColumnValuesList := make([]string, 0, len(statisticsDatumsToShowList))
rightColumnValuesList := make([]string, 0, len(statisticsItemsToShowList)) rightColumnValuesList := make([]string, 0, len(statisticsDatumsToShowList))
percentageColumnValuesList := make([]string, 0, len(statisticsItemsToShowList)) percentageColumnValuesList := make([]string, 0, len(statisticsDatumsToShowList))
for _, item := range statisticsItemsToShowList{ for _, datum := range statisticsDatumsToShowList{
// Each item is represents a row in the data grid. // Each datum is represents a row in the data grid.
itemLabelFormatted := item.LabelFormatted datumLabelFormatted := datum.LabelFormatted
itemValueFormatted := item.ValueFormatted datumValueFormatted := datum.ValueFormatted
attributeColumnValuesList = append(attributeColumnValuesList, itemLabelFormatted) attributeColumnValuesList = append(attributeColumnValuesList, datumLabelFormatted)
rightColumnValuesList = append(rightColumnValuesList, itemValueFormatted) rightColumnValuesList = append(rightColumnValuesList, datumValueFormatted)
if (showPercentageColumn == true){ if (showPercentageColumn == true){
itemValue := item.Value datumValue := datum.Value
getValuePercentage := func()float64{ getValuePercentage := func()float64{
if (allValuesSummed == 0){ if (allValuesSummed == 0){
return 0 return 0
} }
valuePercentage := (itemValue/allValuesSummed)*100 valuePercentage := (datumValue/allValuesSummed)*100
return valuePercentage return valuePercentage
} }

View file

@ -8,20 +8,24 @@ import "fyne.io/fyne/v2/container"
import "fyne.io/fyne/v2/layout" import "fyne.io/fyne/v2/layout"
import "fyne.io/fyne/v2/theme" import "fyne.io/fyne/v2/theme"
import "fyne.io/fyne/v2/widget" import "fyne.io/fyne/v2/widget"
import "fyne.io/fyne/v2/canvas"
import "seekia/resources/geneticReferences/monogenicDiseases" import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/resources/geneticReferences/polygenicDiseases"
import "seekia/resources/geneticReferences/traits" import "seekia/resources/geneticReferences/traits"
import "seekia/internal/appMemory" import "seekia/internal/appMemory"
import "seekia/internal/createCharts"
import "seekia/internal/encoding" import "seekia/internal/encoding"
import "seekia/internal/genetics/geneticAnalysis" import "seekia/internal/genetics/geneticAnalysis"
import "seekia/internal/genetics/myGenomes" import "seekia/internal/genetics/myGenomes"
import "seekia/internal/genetics/myPeople" import "seekia/internal/genetics/myPeople"
import "seekia/internal/genetics/readGeneticAnalysis" import "seekia/internal/genetics/readGeneticAnalysis"
import "seekia/internal/helpers" import "seekia/internal/helpers"
import "seekia/internal/statisticsDatum"
import "slices" import "slices"
import "image"
import "errors" import "errors"
@ -1006,7 +1010,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso
mainGenomePairIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier) mainGenomePairIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier)
offspringRiskScoreKnown, _, offspringRiskScoreFormatted, _, conflictExists, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, mainGenomePairIdentifier) offspringRiskScoreKnown, _, offspringRiskScoreFormatted, _, _, conflictExists, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, mainGenomePairIdentifier)
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
getRiskScoreLabelText := func()string{ getRiskScoreLabelText := func()string{
@ -1116,25 +1120,27 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
}) })
diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, diseaseNameInfoButton, layout.NewSpacer()) diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, diseaseNameInfoButton, layout.NewSpacer())
emptyLabelA := widget.NewLabel("") emptyLabel1 := widget.NewLabel("")
emptyLabelB := widget.NewLabel("") emptyLabel2 := widget.NewLabel("")
offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score") offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score")
emptyLabelC := widget.NewLabel("") emptyLabel3 := widget.NewLabel("")
emptyLabelD := widget.NewLabel("") emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
viewGenomePairButtonsColumn := container.NewVBox(emptyLabelA, widget.NewSeparator()) viewGenomePairButtonsColumn := container.NewVBox(emptyLabel1, widget.NewSeparator())
pairNameColumn := container.NewVBox(emptyLabelB, widget.NewSeparator()) pairNameColumn := container.NewVBox(emptyLabel2, widget.NewSeparator())
offspringRiskScoreColumn := container.NewVBox(offspringRiskScoreLabel, widget.NewSeparator()) offspringRiskScoreColumn := container.NewVBox(offspringRiskScoreLabel, widget.NewSeparator())
viewLifetimeRiskButtonsColumn := container.NewVBox(emptyLabelC, widget.NewSeparator()) viewSampleOffspringsChartButtonsColumn := container.NewVBox(emptyLabel3, widget.NewSeparator())
viewOffspringLociButtonsColumn := container.NewVBox(emptyLabelD, widget.NewSeparator()) viewLifetimeRiskButtonsColumn := container.NewVBox(emptyLabel4, widget.NewSeparator())
viewOffspringLociButtonsColumn := container.NewVBox(emptyLabel5, widget.NewSeparator())
addGenomePairRow := func(genomePairName string, person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{ addGenomePairRow := func(genomePairName string, person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier) genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier)
offspringRiskScoreKnown, _, offspringRiskScoreFormatted, _, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) offspringRiskScoreKnown, _, offspringRiskScoreFormatted, sampleOffspringRiskScoresList, numberOfLociTested, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier)
if (err != nil) { return err } if (err != nil) { return err }
getRiskScoreLabelText := func()string{ getRiskScoreLabelText := func()string{
@ -1158,6 +1164,10 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage) setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, diseaseName, genomePairIdentifier, genomePairName, currentPage)
}) })
viewSampleOffspringsChartButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window, diseaseName, sampleOffspringRiskScoresList, numberOfLociTested, currentPage)
})
viewOffspringLifetimeRiskButton := widget.NewButtonWithIcon("", theme.HistoryIcon(), func(){ viewOffspringLifetimeRiskButton := widget.NewButtonWithIcon("", theme.HistoryIcon(), func(){
diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName) diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName)
@ -1190,12 +1200,15 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
viewGenomePairButtonsColumn.Add(viewGenomePairButton) viewGenomePairButtonsColumn.Add(viewGenomePairButton)
pairNameColumn.Add(genomePairNameLabel) pairNameColumn.Add(genomePairNameLabel)
offspringRiskScoreColumn.Add(offspringRiskScoreLabel) offspringRiskScoreColumn.Add(offspringRiskScoreLabel)
viewSampleOffspringsChartButtonsColumn.Add(viewSampleOffspringsChartButton)
viewLifetimeRiskButtonsColumn.Add(viewOffspringLifetimeRiskButton) viewLifetimeRiskButtonsColumn.Add(viewOffspringLifetimeRiskButton)
viewOffspringLociButtonsColumn.Add(viewOffspringLociButton) viewOffspringLociButtonsColumn.Add(viewOffspringLociButton)
viewGenomePairButtonsColumn.Add(widget.NewSeparator()) viewGenomePairButtonsColumn.Add(widget.NewSeparator())
pairNameColumn.Add(widget.NewSeparator()) pairNameColumn.Add(widget.NewSeparator())
offspringRiskScoreColumn.Add(widget.NewSeparator()) offspringRiskScoreColumn.Add(widget.NewSeparator())
viewSampleOffspringsChartButtonsColumn.Add(widget.NewSeparator())
viewLifetimeRiskButtonsColumn.Add(widget.NewSeparator())
viewOffspringLociButtonsColumn.Add(widget.NewSeparator()) viewOffspringLociButtonsColumn.Add(widget.NewSeparator())
return nil return nil
@ -1221,7 +1234,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton) offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton)
genomesContainer := container.NewHBox(layout.NewSpacer(), viewGenomePairButtonsColumn, pairNameColumn, offspringRiskScoreColumn, viewOffspringLociButtonsColumn, layout.NewSpacer()) genomesContainer := container.NewHBox(layout.NewSpacer(), viewGenomePairButtonsColumn, pairNameColumn, offspringRiskScoreColumn, viewSampleOffspringsChartButtonsColumn, viewLifetimeRiskButtonsColumn, viewOffspringLociButtonsColumn, layout.NewSpacer())
page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), genomesContainer) page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), genomesContainer)
@ -1314,7 +1327,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy
genomeNameLabel := getBoldLabelCentered(genomeName) genomeNameLabel := getBoldLabelCentered(genomeName)
personRiskScoreKnown, _, personRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier) personRiskScoreKnown, _, personRiskScoreFormatted, _, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier)
if (err != nil) { return err } if (err != nil) { return err }
getPersonRiskScoreLabelText := func()string{ getPersonRiskScoreLabelText := func()string{
@ -1408,12 +1421,12 @@ func setViewCouplePolygenicDiseaseLociPage(window fyne.Window, person1Name strin
genomePairRow := container.NewHBox(layout.NewSpacer(), genomePairLabel, genomePairNameLabel, viewGenomePairInfoButton, layout.NewSpacer()) genomePairRow := container.NewHBox(layout.NewSpacer(), genomePairLabel, genomePairNameLabel, viewGenomePairInfoButton, layout.NewSpacer())
_, _, _, numberOfLociTested, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) _, _, _, _, numberOfLociTested, _, err := readGeneticAnalysis.GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier)
if (err != nil){ if (err != nil){
setErrorEncounteredPage(window, err, previousPage) setErrorEncounteredPage(window, err, previousPage)
return return
} }
numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested)
diseaseLociMap, err := polygenicDiseases.GetPolygenicDiseaseLociMap(diseaseName) diseaseLociMap, err := polygenicDiseases.GetPolygenicDiseaseLociMap(diseaseName)
@ -1719,6 +1732,133 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
setPageContent(page, window) 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()){
currentPage := func(){setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window, diseaseName, sampleOffspringRiskScoresList, numberOfLociTested, previousPage)}
title := getPageTitleCentered("Viewing Sample Offspring Risk Scores Chart")
backButton := getBackButtonCentered(previousPage)
description := widget.NewLabel("Below is a chart of 100 sample offspring risk scores for this disease.")
descriptionHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
descriptionRow := container.NewHBox(layout.NewSpacer(), description, descriptionHelpButton, layout.NewSpacer())
diseaseNameTitle := widget.NewLabel("Disease Name:")
diseaseNameLabel := getBoldLabel(diseaseName)
diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameTitle, diseaseNameLabel, layout.NewSpacer())
if (len(sampleOffspringRiskScoresList) == 0){
description2 := getBoldLabelCentered("There is no offspring information available for this disease.")
description3 := getBoldLabelCentered("This is because there were no disease loci for which both prospective parents had information.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionRow, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), description2, description3)
setPageContent(page, window)
return
}
diseaseLociMap, err := polygenicDiseases.GetPolygenicDiseaseLociMap(diseaseName)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
totalNumberOfLoci := len(diseaseLociMap)
totalNumberOfLociString := helpers.ConvertIntToString(totalNumberOfLoci)
numberOfLociTestedTitle := widget.NewLabel("Number Of Loci Tested:")
numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested)
numberOfLociTestedLabel := getBoldLabel(numberOfLociTestedString + "/" + totalNumberOfLociString)
lociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage)
})
numberOfLociTestedRow := container.NewHBox(layout.NewSpacer(), numberOfLociTestedTitle, numberOfLociTestedLabel, lociTestedHelpButton, layout.NewSpacer())
getOffspringSampleRiskScoresChartImage := func()(image.Image, error){
// Map Structure: Risk Score -> Number of offspring with that risk score
offspringRiskScoreCountsMap := make(map[int]int)
for _, offspringRiskScore := range sampleOffspringRiskScoresList{
offspringRiskScoreCountsMap[offspringRiskScore] += 1
}
//TODO: Move StatisticsDatum to its own package, because we are using it for non-user purposes, and will continue to do so
offspringStatisticsDatumsList := make([]statisticsDatum.StatisticsDatum, 0)
for riskScore:=0; riskScore <= 10; riskScore += 1{
getOffspringCount := func()int{
offspringCount, exists := offspringRiskScoreCountsMap[riskScore]
if (exists == false){
return 0
}
return offspringCount
}
offspringCount := getOffspringCount()
riskScoreString := helpers.ConvertIntToString(riskScore)
offspringCountString := helpers.ConvertIntToString(offspringCount)
newStatisticsDatum := statisticsDatum.StatisticsDatum{
Label: riskScoreString + "/10",
LabelFormatted: riskScoreString + "/10",
Value: float64(offspringCount),
ValueFormatted: offspringCountString,
}
offspringStatisticsDatumsList = append(offspringStatisticsDatumsList, newStatisticsDatum)
}
chartTitle := diseaseName + ": 100 Prospective Offspring Risk Scores"
formatYAxisValuesFunction := func(inputRiskScore float64)(string, error){
inputRiskScoreInt, err := helpers.FloorFloat64ToInt(inputRiskScore)
if (err != nil){ return "", err }
inputRiskScoreString := helpers.ConvertIntToString(inputRiskScoreInt)
return inputRiskScoreString, nil
}
offspringsChart, err := createCharts.CreateBarChart(chartTitle, offspringStatisticsDatumsList, formatYAxisValuesFunction, true, " Offspring")
if (err != nil) { return nil, err }
return offspringsChart, nil
}
offspringRiskScoresChartImage, err := getOffspringSampleRiskScoresChartImage()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
viewChartFullscreenButton := getWidgetCentered(widget.NewButtonWithIcon("View Fullscreen", theme.ZoomInIcon(), func(){
setViewFullpageImagePage(window, offspringRiskScoresChartImage, currentPage)
}))
pageHeader := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionRow, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), numberOfLociTestedRow, widget.NewSeparator())
chartFyneImage := canvas.NewImageFromImage(offspringRiskScoresChartImage)
chartFyneImage.FillMode = canvas.ImageFillContain
page := container.NewBorder(pageHeader, viewChartFullscreenButton, nil, nil, chartFyneImage)
setPageContent(page, window)
}
func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, previousPage func()){ func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, previousPage func()){

View file

@ -935,7 +935,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso
diseaseNameText := getBoldLabelCentered(diseaseName) diseaseNameText := getBoldLabelCentered(diseaseName)
personRiskScoreKnown, _, personRiskScoreFormatted, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier) personRiskScoreKnown, _, personRiskScoreFormatted, _, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier)
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
getPersonRiskScoreLabelText := func()string{ getPersonRiskScoreLabelText := func()string{
@ -1096,7 +1096,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
genomeNameCell := getGenomeNameCell() genomeNameCell := getGenomeNameCell()
diseaseRiskScoreKnown, _, diseaseRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier) diseaseRiskScoreKnown, _, diseaseRiskScoreFormatted, _, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier)
if (err != nil) { return err } if (err != nil) { return err }
getRiskScoreLabelText := func()string{ getRiskScoreLabelText := func()string{
@ -1418,7 +1418,7 @@ func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnal
return return
} }
locusRiskWeightIsKnown, genomeLocusRiskWeight, _, _, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) locusRiskWeightIsKnown, genomeLocusRiskWeight, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier)
if (err != nil) { if (err != nil) {
setErrorEncounteredPage(window, err, previousPage) setErrorEncounteredPage(window, err, previousPage)
return return
@ -1484,7 +1484,7 @@ func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnal
locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex)
if (err != nil) { return err } if (err != nil) { return err }
locusRiskWeightIsKnown, genomeLocusRiskWeight, _, _, locusOddsRatioIsKnown, _, locusOddsRatioFormatted, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) locusRiskWeightIsKnown, genomeLocusRiskWeight, locusOddsRatioIsKnown, _, locusOddsRatioFormatted, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier)
if (err != nil) { return err } if (err != nil) { return err }
getGenomeLocusRiskWeightText := func()string{ getGenomeLocusRiskWeightText := func()string{
@ -1635,7 +1635,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{ addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{
genomeRiskWeightKnown, genomeRiskWeight, _, _, genomeOddsRatioKnown, _, genomeOddsRatioFormatted, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) genomeRiskWeightKnown, genomeRiskWeight, genomeOddsRatioIsKnown, _, genomeOddsRatioFormatted, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier)
if (err != nil) { return err } if (err != nil) { return err }
getGenomeRiskWeightText := func()string{ getGenomeRiskWeightText := func()string{
@ -1654,7 +1654,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
getGenomeOddsRatioText := func()string{ getGenomeOddsRatioText := func()string{
if (genomeOddsRatioKnown == false){ if (genomeOddsRatioIsKnown == false){
result := translate("Unknown") result := translate("Unknown")
return result return result

View file

@ -2567,9 +2567,6 @@ func setViewMateProfilePage_TotalDiseaseRisk(window fyne.Window, getAnyUserProfi
numberOfOffspringPolygenicDiseasesTestedRow := container.NewHBox(layout.NewSpacer(), numberOfOffspringPolygenicDiseasesTestedTitle, numberOfOffspringPolygenicDiseasesTestedLabel, layout.NewSpacer()) numberOfOffspringPolygenicDiseasesTestedRow := container.NewHBox(layout.NewSpacer(), numberOfOffspringPolygenicDiseasesTestedTitle, numberOfOffspringPolygenicDiseasesTestedLabel, layout.NewSpacer())
//TODO: Add help buttons //TODO: Add help buttons
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), offspringProbabilityOfAnyMonogenicDiseaseRow, numberOfOffspringMonogenicDiseasesTestedRow, widget.NewSeparator(), userTotalPolygenicDiseaseRiskScoreRow, numberOfUserPolygenicDiseasesTestedRow, widget.NewSeparator(), offspringTotalPolygenicDiseaseRiskScoreRow, numberOfOffspringPolygenicDiseasesTestedRow) page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), offspringProbabilityOfAnyMonogenicDiseaseRow, numberOfOffspringMonogenicDiseasesTestedRow, widget.NewSeparator(), userTotalPolygenicDiseaseRiskScoreRow, numberOfUserPolygenicDiseasesTestedRow, widget.NewSeparator(), offspringTotalPolygenicDiseaseRiskScoreRow, numberOfOffspringPolygenicDiseasesTestedRow)
@ -2876,13 +2873,13 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
getDiseaseInfoGrid := func()(*fyne.Container, error){ getDiseaseInfoGrid := func()(*fyne.Container, error){
emptyLabelA := widget.NewLabel("") emptyLabel1 := widget.NewLabel("")
diseaseNameLabel := getItalicLabelCentered("Disease Name") diseaseNameLabel := getItalicLabelCentered("Disease Name")
emptyLabelB := widget.NewLabel("") emptyLabel2 := widget.NewLabel("")
userRiskScoreLabel := getItalicLabelCentered("User Risk Score") userRiskScoreLabel := getItalicLabelCentered("User Risk Score")
emptyLabelC := widget.NewLabel("") emptyLabel3 := widget.NewLabel("")
offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score") offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score")
userNumberOfLabel := getItalicLabelCentered("User Number Of") userNumberOfLabel := getItalicLabelCentered("User Number Of")
@ -2891,15 +2888,19 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
offspringNumberOfLabel := getItalicLabelCentered("Offspring Number Of") offspringNumberOfLabel := getItalicLabelCentered("Offspring Number Of")
lociTestedLabelB := getItalicLabelCentered("Loci Tested") lociTestedLabelB := getItalicLabelCentered("Loci Tested")
emptyLabelD := widget.NewLabel("") emptyLabel4 := widget.NewLabel("")
emptyLabelE := widget.NewLabel("") emptyLabel5 := widget.NewLabel("")
diseaseNameColumn := container.NewVBox(emptyLabelA, diseaseNameLabel, widget.NewSeparator()) emptyLabel6 := widget.NewLabel("")
userRiskScoreColumn := container.NewVBox(emptyLabelB, userRiskScoreLabel, widget.NewSeparator()) emptyLabel7 := widget.NewLabel("")
offspringRiskScoreColumn := container.NewVBox(emptyLabelC, offspringRiskScoreLabel, 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()) userNumberOfLociTestedColumn := container.NewVBox(userNumberOfLabel, lociTestedLabelA, widget.NewSeparator())
offspringNumberOfLociTestedColumn := container.NewVBox(offspringNumberOfLabel, lociTestedLabelB, widget.NewSeparator()) offspringNumberOfLociTestedColumn := container.NewVBox(offspringNumberOfLabel, lociTestedLabelB, widget.NewSeparator())
viewDiseaseInfoButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) viewSampleOffspringsChartButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
viewDiseaseInfoButtonsColumn := container.NewVBox(emptyLabel6, emptyLabel7, widget.NewSeparator())
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
@ -2912,129 +2913,98 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
diseaseName := diseaseObject.DiseaseName diseaseName := diseaseObject.DiseaseName
diseaseLociList := diseaseObject.LociList diseaseLociList := diseaseObject.LociList
userRiskWeightSum := 0 //Outputs:
userMinimumPossibleRiskWeightSum := 0 // -map[int64]locusValue.LocusValue
userMaximumPossibleRiskWeightSum := 0 // -error
userNumberOfLociTested := 0 getMyDiseaseLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
offspringRiskWeightSum := 0 if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
offspringMinimumPossibleRiskWeightSum := 0 emptyMap := make(map[int64]locusValue.LocusValue)
offspringMaximumPossibleRiskWeightSum := 0 return emptyMap, nil
offspringNumberOfLociTested := 0 }
anyMyLociValuesExist, _, _, myDiseaseLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, myGenomeIdentifier)
if (err != nil) { return nil, err }
if (anyMyLociValuesExist == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
return myDiseaseLocusValuesMap, nil
}
myDiseaseLocusValuesMap, err := getMyDiseaseLocusValuesMap()
if (err != nil) { return nil, err }
// Map Structure: Locus rsID -> Locus Value
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, locusObject := range diseaseLociList{ for _, locusObject := range diseaseLociList{
locusIdentifierHex := locusObject.LocusIdentifier
locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex)
if (err != nil){ return nil, err }
locusRSID := locusObject.LocusRSID locusRSID := locusObject.LocusRSID
locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
locusRiskWeightsMap := locusObject.RiskWeightsMap
locusOddsRatiosMap := locusObject.OddsRatiosMap
locusMinimumRiskWeight := locusObject.MinimumRiskWeight
locusMaximumRiskWeight := locusObject.MaximumRiskWeight
locusValueAttributeName := "LocusValue_rs" + locusRSIDString locusValueAttributeName := "LocusValue_rs" + locusRSIDString
userLocusBasePairExists, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(locusValueAttributeName) userLocusBasePairExists, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(locusValueAttributeName)
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
if (userLocusBasePairExists == true){ if (userLocusBasePairExists == false){
continue
userNumberOfLociTested += 1
userMinimumPossibleRiskWeightSum += locusMinimumRiskWeight
userMaximumPossibleRiskWeightSum += locusMaximumRiskWeight
userLocusRiskWeight, exists := locusRiskWeightsMap[userLocusBasePair]
if (exists == false){
// We do not know the risk weight for this base pair
// We treat this as a 0 risk weight
} else {
userRiskWeightSum += userLocusRiskWeight
}
} }
//Outputs: userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";")
// -bool: My locus base pair exists if (semicolonFound == false){
// -string: My locus base 1 return nil, errors.New("Database corrupt: Contains profile with invalid " + locusValueAttributeName + " value: " + userLocusBasePair)
// -string: My locus base 2
// -error
getMyLocusInfo := func()(bool, string, string, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
return false, "", "", nil
}
locusInfoKnown, _, locusBase1, locusBase2, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, locusIdentifier, myGenomeIdentifier)
if (err != nil){ return false, "", "", err }
if (locusInfoKnown == false){
return false, "", "", nil
}
return true, locusBase1, locusBase2, nil
} }
myLocusBasePairExists, myLocusBase1, myLocusBase2, err := getMyLocusInfo() userLocusValue := locusValue.LocusValue{
if (err != nil) { return nil, err } Base1Value: userLocusBase1,
Base2Value: userLocusBase2,
if (userLocusBasePairExists == true && myLocusBasePairExists == true){ //TODO: Share LocusIsPhased information in user profiles and retrieve it into this value
LocusIsPhased: false,
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";")
if (semicolonFound == false){
return nil, errors.New("Database contains profile containing invalid " + locusValueAttributeName + ": " + userLocusBasePair)
}
offspringLocusRiskWeight, _, _, _, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBase1, myLocusBase2, userLocusBase1, userLocusBase2)
if (err != nil) { return nil, err }
offspringNumberOfLociTested += 1
offspringMinimumPossibleRiskWeightSum += locusMinimumRiskWeight
offspringMaximumPossibleRiskWeightSum += locusMaximumRiskWeight
offspringRiskWeightSum += offspringLocusRiskWeight
} }
userDiseaseLocusValuesMap[locusRSID] = userLocusValue
} }
userDiseaseInfoIsKnown, userDiseaseRiskScore, userNumberOfLociTested, _, err := createGeneticAnalysis.GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, userDiseaseLocusValuesMap, true)
if (err != nil) { return nil, err }
getUserDiseaseRiskScoreString := func()(string, error){ getUserDiseaseRiskScoreString := func()(string, error){
if (userNumberOfLociTested == 0){ if (userDiseaseInfoIsKnown == false){
result := translate("Unknown") result := translate("Unknown")
return result, nil return result, nil
} }
userRiskScore, err := helpers.ScaleNumberProportionally(true, userRiskWeightSum, userMinimumPossibleRiskWeightSum, userMaximumPossibleRiskWeightSum, 0, 10) userRiskScoreString := helpers.ConvertIntToString(userDiseaseRiskScore)
if (err != nil) { return "", err }
userRiskScoreString := helpers.ConvertIntToString(userRiskScore)
resultFormatted := userRiskScoreString + "/10" resultFormatted := userRiskScoreString + "/10"
return resultFormatted, nil return resultFormatted, nil
} }
userDiseaseRiskScore, err := getUserDiseaseRiskScoreString() userDiseaseRiskScoreString, err := getUserDiseaseRiskScoreString()
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
getOffspringDiseaseRiskScoreString := func()(string, error){ anyOffspringLociTested, offspringDiseaseRiskScore, offspringNumberOfLociTested, _, offspringSampleRiskScoresList, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLociList, myDiseaseLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) { return nil, err }
if (offspringNumberOfLociTested == 0){ getOffspringDiseaseRiskScoreFormatted := func()(string, error){
if (anyOffspringLociTested == false){
result := translate("Unknown") result := translate("Unknown")
return result, nil return result, nil
} }
offspringRiskScore, err := helpers.ScaleNumberProportionally(true, offspringRiskWeightSum, offspringMinimumPossibleRiskWeightSum, offspringMaximumPossibleRiskWeightSum, 0, 10) offspringRiskScoreString := helpers.ConvertIntToString(offspringDiseaseRiskScore)
if (err != nil) { return "", err }
offspringRiskScoreString := helpers.ConvertIntToString(offspringRiskScore)
resultFormatted := offspringRiskScoreString + "/10" resultFormatted := offspringRiskScoreString + "/10"
return resultFormatted, nil return resultFormatted, nil
} }
offspringDiseaseRiskScore, err := getOffspringDiseaseRiskScoreString() offspringDiseaseRiskScoreFormatted, err := getOffspringDiseaseRiskScoreFormatted()
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
totalNumberOfDiseaseLoci := len(diseaseLociList) totalNumberOfDiseaseLoci := len(diseaseLociList)
@ -3046,10 +3016,16 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
offspringNumberOfLociTestedFormatted := offspringNumberOfLociTestedString + "/" + totalNumberOfDiseaseLociString offspringNumberOfLociTestedFormatted := offspringNumberOfLociTestedString + "/" + totalNumberOfDiseaseLociString
diseaseNameText := getBoldLabelCentered(diseaseName) diseaseNameText := getBoldLabelCentered(diseaseName)
userRiskScoreLabel := getBoldLabelCentered(userDiseaseRiskScore) userRiskScoreLabel := getBoldLabelCentered(userDiseaseRiskScoreString)
offspringRiskScoreLabel := getBoldLabelCentered(offspringDiseaseRiskScore) offspringRiskScoreLabel := getBoldLabelCentered(offspringDiseaseRiskScoreFormatted)
userNumberOfLociTestedLabel := getBoldLabelCentered(userNumberOfLociTestedFormatted) userNumberOfLociTestedLabel := getBoldLabelCentered(userNumberOfLociTestedFormatted)
offspringNumberOfLociTestedLabel := getBoldLabelCentered(offspringNumberOfLociTestedFormatted) offspringNumberOfLociTestedLabel := getBoldLabelCentered(offspringNumberOfLociTestedFormatted)
viewSampleOffspringsChartButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window, diseaseName, offspringSampleRiskScoresList, offspringNumberOfLociTested, currentPage)
})
viewDiseaseDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ viewDiseaseDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewMateProfilePage_PolygenicDiseaseLoci(window, diseaseName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage) setViewMateProfilePage_PolygenicDiseaseLoci(window, diseaseName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage)
}) })
@ -3059,6 +3035,7 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
offspringRiskScoreColumn.Add(offspringRiskScoreLabel) offspringRiskScoreColumn.Add(offspringRiskScoreLabel)
userNumberOfLociTestedColumn.Add(userNumberOfLociTestedLabel) userNumberOfLociTestedColumn.Add(userNumberOfLociTestedLabel)
offspringNumberOfLociTestedColumn.Add(offspringNumberOfLociTestedLabel) offspringNumberOfLociTestedColumn.Add(offspringNumberOfLociTestedLabel)
viewSampleOffspringsChartButtonsColumn.Add(viewSampleOffspringsChartButton)
viewDiseaseInfoButtonsColumn.Add(viewDiseaseDetailsButton) viewDiseaseInfoButtonsColumn.Add(viewDiseaseDetailsButton)
diseaseNameColumn.Add(widget.NewSeparator()) diseaseNameColumn.Add(widget.NewSeparator())
@ -3066,6 +3043,7 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
offspringRiskScoreColumn.Add(widget.NewSeparator()) offspringRiskScoreColumn.Add(widget.NewSeparator())
userNumberOfLociTestedColumn.Add(widget.NewSeparator()) userNumberOfLociTestedColumn.Add(widget.NewSeparator())
offspringNumberOfLociTestedColumn.Add(widget.NewSeparator()) offspringNumberOfLociTestedColumn.Add(widget.NewSeparator())
viewSampleOffspringsChartButtonsColumn.Add(widget.NewSeparator())
viewDiseaseInfoButtonsColumn.Add(widget.NewSeparator()) viewDiseaseInfoButtonsColumn.Add(widget.NewSeparator())
} }
@ -3076,18 +3054,18 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
offspringRiskScoreHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ offspringRiskScoreHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringPolygenicDiseaseRiskScoreExplainerPage(window, currentPage) setOffspringPolygenicDiseaseRiskScoreExplainerPage(window, currentPage)
}) })
userNumberOfLociTestedButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ userNumberOfLociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage)
}) })
offspringNumberOfLociTestedButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ offspringNumberOfLociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage)
}) })
userRiskScoreColumn.Add(userRiskScoreHelpButton) userRiskScoreColumn.Add(userRiskScoreHelpButton)
offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton) offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton)
userNumberOfLociTestedColumn.Add(userNumberOfLociTestedButton) userNumberOfLociTestedColumn.Add(userNumberOfLociTestedHelpButton)
offspringNumberOfLociTestedColumn.Add(offspringNumberOfLociTestedButton) offspringNumberOfLociTestedColumn.Add(offspringNumberOfLociTestedHelpButton)
if (userOrOffspring == "User"){ if (userOrOffspring == "User"){
diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, userRiskScoreColumn, userNumberOfLociTestedColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer()) diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, userRiskScoreColumn, userNumberOfLociTestedColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer())
@ -3095,7 +3073,7 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
return diseaseInfoGrid, nil return diseaseInfoGrid, nil
} }
diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, offspringRiskScoreColumn, offspringNumberOfLociTestedColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer()) diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, offspringRiskScoreColumn, offspringNumberOfLociTestedColumn, viewSampleOffspringsChartButtonsColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer())
return diseaseInfoGrid, nil return diseaseInfoGrid, nil
} }
@ -3148,114 +3126,107 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
numberOfDiseaseLoci := len(diseaseLocusObjectsList) numberOfDiseaseLoci := len(diseaseLocusObjectsList)
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() // Outputs:
// -map[int64]locusValue.LocusValue: Map Structure: Locus rsID -> Locus Value
// -error
getMyDiseaseLocusValuesMap := 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
}
anyLocusValuesExist, _, _, myLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, myGenomeIdentifier)
if (err != nil) { return nil, err }
if (anyLocusValuesExist == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
return myLocusValuesMap, nil
}
myDiseaseLocusValuesMap, err := getMyDiseaseLocusValuesMap()
if (err != nil) { if (err != nil) {
setErrorEncounteredPage(window, err, previousPage) setErrorEncounteredPage(window, err, previousPage)
return return
} }
//Outputs: getUserLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
// -bool: User locus Info is known
// -int: User locus Risk weight
// -string: Locus base pair
// -bool: User locus odds ratio known
// -string: User locus odds ratio formatted
// -error
getUserLocusInfo := func(locusRSID int64, locusRiskWeightsMap map[string]int, locusOddsRatiosMap map[string]float64)(bool, int, string, bool, string, error){
locusRSIDString := helpers.ConvertInt64ToString(locusRSID) // Map Structure: Locus rsID -> Locus Value
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
locusValueAttributeName := "LocusValue_rs" + locusRSIDString
userLocusBasePairExists, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(locusValueAttributeName)
if (err != nil) { return false, 0, "", false, "", err }
if (userLocusBasePairExists == false){
return false, 0, "", false, "", nil
}
userLocusRiskWeight, exists := locusRiskWeightsMap[userLocusBasePair]
if (exists == false){
// We do not know the risk weight for this base pair
// We treat this as a 0 risk weight
return true, 0, userLocusBasePair, false, "", nil
}
locusOddsRatio, exists := locusOddsRatiosMap[userLocusBasePair]
if (exists == false){
return true, userLocusRiskWeight, userLocusBasePair, false, "", nil
}
locusOddsRatioString := helpers.ConvertFloat64ToStringRounded(locusOddsRatio, 2)
locusOddsRatioFormatted := locusOddsRatioString + "x"
return true, userLocusRiskWeight, userLocusBasePair, true, locusOddsRatioFormatted, nil
}
//Outputs:
// -bool: My locus Info is known
// -string: My locus base 1
// -string: My locus base 2
// -error
getMyLocusInfo := func(locusIdentifierHex string)(bool, string, string, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
return false, "", "", nil
}
locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex)
if (err != nil) { return false, "", "", err }
locusInfoKnown, _, locusBase1, locusBase2, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, locusIdentifier, myGenomeIdentifier)
if (err != nil){ return false, "", "", err }
if (locusInfoKnown == false){
return false, "", "", nil
}
return true, locusBase1, locusBase2, nil
}
getNumberOfLociTested := func()(int, error){
if (userOrOffspring == "Offspring"){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
return 0, nil
}
}
numberOfLociTested := 0
for _, locusObject := range diseaseLocusObjectsList{ for _, locusObject := range diseaseLocusObjectsList{
locusIdentifier := locusObject.LocusIdentifier
locusRSID := locusObject.LocusRSID locusRSID := locusObject.LocusRSID
locusRiskWeightsMap := locusObject.RiskWeightsMap
locusOddsRatiosMap := locusObject.OddsRatiosMap
userLocusInfoIsKnown, _, _, _, _, err := getUserLocusInfo(locusRSID, locusRiskWeightsMap, locusOddsRatiosMap) locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
if (err != nil) { return 0, err }
if (userLocusInfoIsKnown == false){ locusValueAttributeName := "LocusValue_rs" + locusRSIDString
userLocusBasePairExists, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(locusValueAttributeName)
if (err != nil) { return nil, err }
if (userLocusBasePairExists == false){
continue continue
} }
if (userOrOffspring == "Offspring") { userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";")
if (semicolonFound == false){
myLocusInfoKnown, _, _, err := getMyLocusInfo(locusIdentifier) return nil, errors.New("Database corrupt: Contains profile with invalid " + locusValueAttributeName + " value: " + userLocusBasePair)
if (err != nil) { return 0, err }
if (myLocusInfoKnown == false){
continue
}
} }
numberOfLociTested += 1 userLocusValue := locusValue.LocusValue{
Base1Value: userLocusBase1,
Base2Value: userLocusBase2,
//TODO: Share LocusIsPhased information in user profiles and retrieve it into this value
LocusIsPhased: false,
}
userDiseaseLocusValuesMap[locusRSID] = userLocusValue
} }
return numberOfLociTested, nil return userDiseaseLocusValuesMap, nil
} }
numberOfLociTested, err := getNumberOfLociTested() userDiseaseLocusValuesMap, err := getUserLocusValuesMap()
if (err != nil){ if (err != nil) {
setErrorEncounteredPage(window, err, previousPage) setErrorEncounteredPage(window, err, previousPage)
return return
} }
anyUserLociTested, _, userNumberOfLociTested, userDiseaseLocusInfoMap, err := createGeneticAnalysis.GetPersonGenomePolygenicDiseaseInfo(diseaseLocusObjectsList, userDiseaseLocusValuesMap, true)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
anyOffspringLociTested, _, offspringNumberOfLociTested, offspringLociInfoMap, _, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLocusObjectsList, myDiseaseLocusValuesMap, 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) numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested)
totalNumberOfDiseaseLociString := helpers.ConvertIntToString(numberOfDiseaseLoci) totalNumberOfDiseaseLociString := helpers.ConvertIntToString(numberOfDiseaseLoci)
lociTestedString := numberOfLociTestedString + "/" + totalNumberOfDiseaseLociString lociTestedString := numberOfLociTestedString + "/" + totalNumberOfDiseaseLociString
@ -3299,19 +3270,50 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
for _, locusObject := range diseaseLocusObjectsList{ for _, locusObject := range diseaseLocusObjectsList{
locusIdentifier := locusObject.LocusIdentifier locusIdentifierHex := locusObject.LocusIdentifier
locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex)
if (err != nil) { return nil, err }
locusRSID := locusObject.LocusRSID locusRSID := locusObject.LocusRSID
locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
locusName := "rs" + locusRSIDString locusName := "rs" + locusRSIDString
locusRiskWeightsMap := locusObject.RiskWeightsMap //Outputs:
locusOddsRatiosMap := locusObject.OddsRatiosMap // -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){
userLocusInfoIsKnown, userLocusRiskWeight, userLocusBasePair, userLocusOddsRatioKnown, userLocusOddsRatioFormatted, err := getUserLocusInfo(locusRSID, locusRiskWeightsMap, locusOddsRatiosMap) if (anyUserLociTested == false){
if (err != nil) { return nil, err } return false, 0, false, "", nil
}
myLocusInfoIsKnown, myLocusBase1, myLocusBase2, err := getMyLocusInfo(locusIdentifier) 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 } if (err != nil) { return nil, err }
getUserRiskWeightString := func()string{ getUserRiskWeightString := func()string{
@ -3328,7 +3330,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
userRiskWeightString := getUserRiskWeightString() userRiskWeightString := getUserRiskWeightString()
getUserOddsRatioString := func()string{ getUserOddsRatioString := func()string{
if (userLocusOddsRatioKnown == false){ if (userLocusOddsRatioIsKnown == false){
result := translate("Unknown") result := translate("Unknown")
return result return result
} }
@ -3345,22 +3347,25 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
// -error // -error
getOffspringDiseaseLocusInfo := func()(bool, int, bool, string, error){ getOffspringDiseaseLocusInfo := func()(bool, int, bool, string, error){
if (userLocusInfoIsKnown == false || myLocusInfoIsKnown == false){ if (anyOffspringLociTested == false){
return false, 0, false, "", nil return false, 0, false, "", nil
} }
userLocusBase1, userLocusBase2, semicolonExists := strings.Cut(userLocusBasePair, ";") offspringLocusInfoObject, exists := offspringLociInfoMap[locusIdentifier]
if (semicolonExists == false){ if (exists == false){
return false, 0, false, "", errors.New("Database corrupt: Contains profile with invalid " + locusName + " value: " + userLocusBasePair) return false, 0, false, "", nil
} }
offspringLocusRiskWeight, offspringOddsRatioIsKnown, offspringOddsRatio, unknownOddsRatiosWeightSum, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBase1, myLocusBase2, userLocusBase1, userLocusBase2) offspringLocusRiskWeight := offspringLocusInfoObject.OffspringAverageRiskWeight
if (err != nil) { return false, 0, false, "", err } offspringOddsRatioIsKnown := offspringLocusInfoObject.OffspringOddsRatioIsKnown
if (offspringOddsRatioIsKnown == false){ if (offspringOddsRatioIsKnown == false){
return true, offspringLocusRiskWeight, false, "", nil return true, offspringLocusRiskWeight, false, "", nil
} }
offspringOddsRatio := offspringLocusInfoObject.OffspringAverageOddsRatio
unknownOddsRatiosWeightSum := offspringLocusInfoObject.OffspringAverageUnknownOddsRatiosWeightSum
getOddsRatioFormatted := func()string{ getOddsRatioFormatted := func()string{
offspringOddsRatioString := helpers.ConvertFloat64ToStringRounded(offspringOddsRatio, 2) offspringOddsRatioString := helpers.ConvertFloat64ToStringRounded(offspringOddsRatio, 2)
@ -3414,7 +3419,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
userOddsRatioLabel := getBoldLabelCentered(userOddsRatioString) userOddsRatioLabel := getBoldLabelCentered(userOddsRatioString)
offspringOddsRatioLabel := getBoldLabelCentered(offspringOddsRatioString) offspringOddsRatioLabel := getBoldLabelCentered(offspringOddsRatioString)
locusInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ locusInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifier, currentPage) setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifierHex, currentPage)
}) })
locusNameColumn.Add(locusNameLabel) locusNameColumn.Add(locusNameLabel)
@ -3460,7 +3465,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
return return
} }
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), diseaseNameRow, lociTestedRow, widget.NewSeparator(), diseaseLociGrid) page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), lociTestedRow, widget.NewSeparator(), diseaseLociGrid)
setPageContent(page, window) setPageContent(page, window)
} }
@ -3554,6 +3559,39 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton) viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton)
// We construct the user's trait locus values map
// Map Structure: Locus rsID -> locusValue.LocusValue
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
traitLociList := traitObject.LociList
for _, rsID := range traitLociList{
rsIDString := helpers.ConvertInt64ToString(rsID)
userLocusValueAttributeName := "LocusValue_rs" + rsIDString
userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
if (err != nil) { return nil, err }
if (userLocusBasePairIsKnown == false){
continue
}
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";")
if (semicolonFound == false){
return nil, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + " value: " + userLocusBasePair)
}
userLocusValue := locusValue.LocusValue{
Base1Value: userLocusBase1,
Base2Value: userLocusBase2,
//TODO: Share LocusIsPhased information in user profiles and retrieve it into this value
LocusIsPhased: false,
}
userTraitLocusValuesMap[rsID] = userLocusValue
}
//Outputs: //Outputs:
// -bool: At least 1 rule is known // -bool: At least 1 rule is known
// -map[string]int: Outcome name -> Outcome score // -map[string]int: Outcome name -> Outcome score
@ -3566,49 +3604,9 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
for _, traitRuleObject := range traitRulesList{ for _, traitRuleObject := range traitRulesList{
// Outputs: ruleLociList := traitRuleObject.LociList
// -bool: Status is known
// -bool: User passes rule
// -error
getUserPassesRuleStatus := func()(bool, bool, error){
ruleLociList := traitRuleObject.LociList userRuleStatusIsKnown, userPassesRule, err := createGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
allRuleLociKnown := true
for _, ruleLocusObject := range ruleLociList{
locusRSID := ruleLocusObject.LocusRSID
locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
userLocusValueAttributeName := "LocusValue_rs" + locusRSIDString
userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
if (err != nil) { return false, false, err }
if (userLocusBasePairIsKnown == false){
// We know rule is not passed
// We keep searching to see if ruleIsPassed status is No or Unknown
allRuleLociKnown = false
continue
}
ruleLocusBasePairsList := ruleLocusObject.BasePairsList
userPassesRuleLocus := slices.Contains(ruleLocusBasePairsList, userLocusBasePair)
if (userPassesRuleLocus == false){
// We know the rule is not passed
return true, false, nil
}
}
if (allRuleLociKnown == false){
// Rule status is unknown. Any loci which we knew must have passed.
return false, false, nil
}
// Rule is passed
return true, true, nil
}
userRuleStatusIsKnown, userPassesRule, err := getUserPassesRuleStatus()
if (err != nil) { return false, nil, 0, err } if (err != nil) { return false, nil, 0, err }
if (userRuleStatusIsKnown == false){ if (userRuleStatusIsKnown == false){
continue continue
@ -3660,39 +3658,6 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier) myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier)
if (err != nil) { return false, nil, 0, err } if (err != nil) { return false, nil, 0, err }
// We construct the user's trait locus values map
// Map Structure: Locus rsID -> locusValue.LocusValue
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
traitLociList := traitObject.LociList
for _, rsID := range traitLociList{
rsIDString := helpers.ConvertInt64ToString(rsID)
userLocusValueAttributeName := "LocusValue_rs" + rsIDString
userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
if (err != nil) { return false, nil, 0, err }
if (userLocusBasePairIsKnown == false){
continue
}
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";")
if (semicolonFound == false){
return false, nil, 0, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + " value: " + userLocusBasePair)
}
userLocusValue := locusValue.LocusValue{
Base1Value: userLocusBase1,
Base2Value: userLocusBase2,
//TODO: Share LocusIsPhased information in user profiles and retrieve it into this value
LocusIsPhased: false,
}
userTraitLocusValuesMap[rsID] = userLocusValue
}
anyRuleTested, offspringNumberOfRulesTested, _, offspringAverageOutcomeScoresMap, err := createGeneticAnalysis.GetOffspringTraitInfo(traitObject, myTraitLocusValuesMap, userTraitLocusValuesMap) anyRuleTested, offspringNumberOfRulesTested, _, offspringAverageOutcomeScoresMap, err := createGeneticAnalysis.GetOffspringTraitInfo(traitObject, myTraitLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, nil, 0, err } if (err != nil) { return false, nil, 0, err }
if (anyRuleTested == false){ if (anyRuleTested == false){
@ -3944,50 +3909,6 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
return return
} }
//Outputs:
// -bool: Status is known
// -bool: User passes rule
// -error
getUserPassesRuleBool := func(ruleLociList []traits.RuleLocus)(bool, bool, error){
if (anyUserTraitLocusValueExists == false){
return false, false, nil
}
allRuleLociKnown := true
for _, ruleLocusObject := range ruleLociList{
locusRSID := ruleLocusObject.LocusRSID
userLocusValue, userLocusValueIsKnown := userTraitLocusValuesMap[locusRSID]
if (userLocusValueIsKnown == false){
// We know rule is not passed
// We keep searching to see if ruleIsPassed status is No or Unknown
allRuleLociKnown = false
continue
}
userLocusBase1Value := userLocusValue.Base1Value
userLocusBase2Value := userLocusValue.Base2Value
userLocusBasePair := userLocusBase1Value + ";" + userLocusBase2Value
ruleLocusBasePairsList := ruleLocusObject.BasePairsList
userPassesRuleLocus := slices.Contains(ruleLocusBasePairsList, userLocusBasePair)
if (userPassesRuleLocus == false){
// We know the rule is not passed
return true, false, nil
}
}
if (allRuleLociKnown == false){
// We don't know if the user passes the rule. Any loci which we knew must have passed.
return false, false, nil
}
return true, true, nil
}
//Outputs: //Outputs:
// -bool: Any offspring probability of passing rule is known // -bool: Any offspring probability of passing rule is known
// -map[[3]byte]int: Offspring probability of passing rules map // -map[[3]byte]int: Offspring probability of passing rules map
@ -4025,6 +3946,10 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
return numberOfRulesTested, nil return numberOfRulesTested, nil
} }
if (anyUserTraitLocusValueExists == false){
return 0, nil
}
numberOfRulesTested := 0 numberOfRulesTested := 0
@ -4032,7 +3957,7 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
ruleLociList := ruleObject.LociList ruleLociList := ruleObject.LociList
ruleStatusIsKnown, _, err := getUserPassesRuleBool(ruleLociList) ruleStatusIsKnown, _, err := createGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return 0, err } if (err != nil) { return 0, err }
if (ruleStatusIsKnown == true){ if (ruleStatusIsKnown == true){
numberOfRulesTested += 1 numberOfRulesTested += 1
@ -4125,7 +4050,7 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
getUserPassesRuleString := func()(string, error){ getUserPassesRuleString := func()(string, error){
userRuleStatusIsKnown, userPassesRule, err := getUserPassesRuleBool(ruleLociList) userRuleStatusIsKnown, userPassesRule, err := createGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return "", err } if (err != nil) { return "", err }
if (userRuleStatusIsKnown == false){ if (userRuleStatusIsKnown == false){
@ -4300,7 +4225,7 @@ func setViewMateProfilePage_Diet(window fyne.Window, getAnyUserProfileAttributeF
getRatingLabel := func()*fyne.Container{ getRatingLabel := func()*fyne.Container{
if (ratingExists == false){ if (ratingExists == false){
result := getBoldItalicLabelCentered(translate("Unknown")) result := getBoldItalicLabelCentered(translate("No Response"))
return result return result
} }

View file

@ -5,7 +5,7 @@
package createCharts package createCharts
import "seekia/internal/profiles/userStatistics" import "seekia/internal/statisticsDatum"
import goChart "github.com/wcharczuk/go-chart/v2" import goChart "github.com/wcharczuk/go-chart/v2"
import "github.com/wcharczuk/go-chart/v2/drawing" import "github.com/wcharczuk/go-chart/v2/drawing"
@ -15,7 +15,7 @@ import "errors"
// Inputs: // Inputs:
// -string: Chart title // -string: Chart title
// -[]userStatistics.StatisticsItem: Statistics items list // -[]statisticsDatum.StatisticsDatum: Statistics datums list
// -func(float64)(string, error): formatYAxisValuesFunction // -func(float64)(string, error): formatYAxisValuesFunction
// -This will take values such as 1000000 and turn them to "1 million" // -This will take values such as 1000000 and turn them to "1 million"
// -bool: Y-axis units exist // -bool: Y-axis units exist
@ -23,38 +23,38 @@ import "errors"
// Outputs: // Outputs:
// -image.Image // -image.Image
// -error // -error
func CreateBarChart(chartTitle string, chartStatisticsItemsList []userStatistics.StatisticsItem, formatYAxisValuesFunction func(float64)(string, error), yAxisUnitsProvided bool, yAxisUnits string)(image.Image, error){ func CreateBarChart(chartTitle string, chartStatisticsDatumsList []statisticsDatum.StatisticsDatum, formatYAxisValuesFunction func(float64)(string, error), yAxisUnitsProvided bool, yAxisUnits string)(image.Image, error){
if (len(chartStatisticsItemsList) == 0) { if (len(chartStatisticsDatumsList) == 0) {
return nil, errors.New("CreateBarChart called with empty chartStatisticsItemsList") return nil, errors.New("CreateBarChart called with empty chartStatisticsDatumsList")
} }
chartItemsList := make([]goChart.Value, 0, len(chartStatisticsItemsList)) chartDatumsList := make([]goChart.Value, 0, len(chartStatisticsDatumsList))
for _, statisticsItem := range chartStatisticsItemsList{ for _, statisticsDatum := range chartStatisticsDatumsList{
itemLabel := statisticsItem.LabelFormatted datumLabel := statisticsDatum.LabelFormatted
itemValue := statisticsItem.Value datumValue := statisticsDatum.Value
// We make sure this function does not error // We make sure this function does not error
_, err := formatYAxisValuesFunction(itemValue) _, err := formatYAxisValuesFunction(datumValue)
if (err != nil){ if (err != nil){
return nil, errors.New("Invalid chartStatisticsItemsList: Item value is invalid. Reason: " + err.Error()) return nil, errors.New("Invalid chartStatisticsDatumsList: Datum value is invalid. Reason: " + err.Error())
} }
newChartValue := goChart.Value{ newChartValue := goChart.Value{
Label: itemLabel, Label: datumLabel,
Value: itemValue, Value: datumValue,
} }
chartItemsList = append(chartItemsList, newChartValue) chartDatumsList = append(chartDatumsList, newChartValue)
} }
if (len(chartItemsList) == 1){ if (len(chartDatumsList) == 1){
// This package cannot create bar charts with only 1 item // This package cannot create bar charts with only 1 datum
// Thus, we must add an empty item // Thus, we must add an empty datum
newChartValue := goChart.Value{ newChartValue := goChart.Value{
Style: goChart.Style{ Style: goChart.Style{
@ -65,7 +65,7 @@ func CreateBarChart(chartTitle string, chartStatisticsItemsList []userStatistics
Value: .001, Value: .001,
} }
chartItemsList = append(chartItemsList, newChartValue) chartDatumsList = append(chartDatumsList, newChartValue)
} }
chartStyleObject := goChart.Style{ chartStyleObject := goChart.Style{
@ -110,7 +110,7 @@ func CreateBarChart(chartTitle string, chartStatisticsItemsList []userStatistics
Background: chartStyleObject, Background: chartStyleObject,
Height: 500, Height: 500,
BarWidth: 60, BarWidth: 60,
Bars: chartItemsList, Bars: chartDatumsList,
YAxis: yAxisObject, YAxis: yAxisObject,
} }
@ -123,28 +123,28 @@ func CreateBarChart(chartTitle string, chartStatisticsItemsList []userStatistics
return goImage, nil return goImage, nil
} }
func CreateDonutChart(chartTitle string, chartStatisticsItemsList []userStatistics.StatisticsItem)(image.Image, error){ func CreateDonutChart(chartTitle string, chartStatisticsDatumsList []statisticsDatum.StatisticsDatum)(image.Image, error){
if (len(chartStatisticsItemsList) == 0) { if (len(chartStatisticsDatumsList) == 0) {
return nil, errors.New("CreateDonutChart called with empty chartStatisticsItemsList") return nil, errors.New("CreateDonutChart called with empty chartStatisticsDatumsList")
} }
chartItemsList := make([]goChart.Value, 0, len(chartStatisticsItemsList)) chartDatumsList := make([]goChart.Value, 0, len(chartStatisticsDatumsList))
for _, statisticsItem := range chartStatisticsItemsList{ for _, statisticsDatum := range chartStatisticsDatumsList{
itemLabel := statisticsItem.LabelFormatted datumLabel := statisticsDatum.LabelFormatted
// Value is always a number representing the percentage of the donut // Value is always a number representing the percentage of the donut
itemValue := statisticsItem.Value datumValue := statisticsDatum.Value
newChartValue := goChart.Value{ newChartValue := goChart.Value{
Label: itemLabel, Label: datumLabel,
Value: itemValue, Value: datumValue,
} }
chartItemsList = append(chartItemsList, newChartValue) chartDatumsList = append(chartDatumsList, newChartValue)
} }
chartStyleObject := goChart.Style{ chartStyleObject := goChart.Style{
@ -166,10 +166,10 @@ func CreateDonutChart(chartTitle string, chartStatisticsItemsList []userStatisti
TitleStyle: titleStyleObject, TitleStyle: titleStyleObject,
Background: chartStyleObject, Background: chartStyleObject,
Height: 500, Height: 500,
Values: chartItemsList, Values: chartDatumsList,
} }
if (len(chartItemsList) == 1){ if (len(chartDatumsList) == 1){
// Default is transparent, we need to add color // Default is transparent, we need to add color
sliceStyleObject := goChart.Style{ sliceStyleObject := goChart.Style{

View file

@ -98,6 +98,11 @@ type PersonGenomePolygenicDiseaseInfo struct{
// This should be len(LociInfoList) // This should be len(LociInfoList)
NumberOfLociTested int NumberOfLociTested int
// This map contains the locus values for the genome for this trait
// If an locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value
LocusValuesMap map[int64]locusValue.LocusValue
// This is total risk score for this disease for the person's genome // This is total risk score for this disease for the person's genome
// This is a number between 1-10 // This is a number between 1-10
RiskScore int RiskScore int
@ -110,11 +115,6 @@ type PersonGenomePolygenicDiseaseInfo struct{
type PersonGenomePolygenicDiseaseLocusInfo struct{ type PersonGenomePolygenicDiseaseLocusInfo struct{
// The person's genome locus base pair value for this variant's locus
// Example: "G", "C"
LocusBase1 string
LocusBase2 string
// This is the risk weight that this person's genome has for this variant // 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 // A higher risk weight means more risk of getting the disease
RiskWeight int RiskWeight int
@ -141,10 +141,10 @@ type PersonTraitInfo struct{
type PersonGenomeTraitInfo struct{ type PersonGenomeTraitInfo struct{
// This should be len(RulesList) // This should be len(GenomePassesRulesMap)
NumberOfRulesTested int NumberOfRulesTested int
// This map contains the locus values for the genome // This map contains the locus values for the genome for this trait
// If an locus's entry doesn't exist, its value is unknown // If an locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value // Map Structure: Locus rsID -> Locus Value
LocusValuesMap map[int64]locusValue.LocusValue LocusValuesMap map[int64]locusValue.LocusValue
@ -262,33 +262,38 @@ type OffspringGenomePairPolygenicDiseaseInfo struct{
// This should be len(DiseaseLociList) // This should be len(DiseaseLociList)
NumberOfLociTested int NumberOfLociTested int
// A number between 1-10 representing the offspring's risk // A number between 1-10 representing the offspring's average risk score
// 1 == lowest risk, 10 == highest risk // 1 == lowest risk, 10 == highest risk
OffspringRiskScore int OffspringAverageRiskScore int
// A map of the offspring's locus information // A map of the offspring's locus information
// Map Structure: Locus Identifier -> OffspringPolygenicDiseaseLocusInfo // Map Structure: Locus Identifier -> OffspringPolygenicDiseaseLocusInfo
LociInfoMap map[[3]byte]OffspringPolygenicDiseaseLocusInfo LociInfoMap map[[3]byte]OffspringPolygenicDiseaseLocusInfo
// This is a list of prospective offspring risk scores
// This is useful for plotting on a graph to understand the standard deviation of risk
SampleOffspringRiskScoresList []int
} }
type OffspringPolygenicDiseaseLocusInfo struct{ type OffspringPolygenicDiseaseLocusInfo struct{
// This is the offspring's risk weight for this locus value // This is the offspring's average risk weight for this locus value
// A higher weight means a higher risk of the disease // A higher weight means a higher risk of the disease
OffspringRiskWeight int OffspringAverageRiskWeight int
// This is true if any of the 100 prospective offspring had a known odds ratio for this locus
OffspringOddsRatioIsKnown bool OffspringOddsRatioIsKnown bool
// This value represent's the offspring's average odds ratio for the disease locus // 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 // A value <1 denotes a lesser risk, a value >1 denotes an increased risk
OffspringOddsRatio float64 OffspringAverageOddsRatio float64
// This is the sum of weights for the loci which have no odds ratios // 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 // 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 lower
// If the sum is >0, we say the ratio is probably higher // If the sum is >0, we say the ratio is probably higher
OffspringUnknownOddsRatiosWeightSum int OffspringAverageUnknownOddsRatiosWeightSum int
} }

View file

@ -482,118 +482,119 @@ func GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisOb
//Outputs: //Outputs:
// -bool: Polygenic Disease Risk Score known // -bool: Polygenic Disease Risk Score known (any loci values exist)
// -int: Disease risk score // -int: Person Disease risk score
// -string: Disease risk score formatted (has "/10" suffix) // -string: Person Disease risk score formatted (has "/10" suffix)
// -map[int]locusValue.LocusValue: Person locus values map
// -int: Number of loci tested // -int: Number of loci tested
// -bool: Conflict exists // -bool: Conflict exists
// -error // -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, string, map[int64]locusValue.LocusValue, int, bool, error){
personPolygenicDiseasesMap := personAnalysisObject.PolygenicDiseasesMap personPolygenicDiseasesMap := personAnalysisObject.PolygenicDiseasesMap
personPolygenicDiseaseInfo, exists := personPolygenicDiseasesMap[diseaseName] personPolygenicDiseaseInfo, exists := personPolygenicDiseasesMap[diseaseName]
if (exists == false){ if (exists == false){
return false, 0, "", 0, false, nil return false, 0, "", nil, 0, false, nil
} }
personPolygenicDiseaseInfoMap := personPolygenicDiseaseInfo.PolygenicDiseaseInfoMap personPolygenicDiseaseInfoMap := personPolygenicDiseaseInfo.PolygenicDiseaseInfoMap
genomePolygenicDiseaseInfo, exists := personPolygenicDiseaseInfoMap[genomeIdentifier] genomePolygenicDiseaseInfo, exists := personPolygenicDiseaseInfoMap[genomeIdentifier]
if (exists == false){ if (exists == false){
return false, 0, "", 0, false, nil return false, 0, "", nil, 0, false, nil
} }
conflictExists := personPolygenicDiseaseInfo.ConflictExists conflictExists := personPolygenicDiseaseInfo.ConflictExists
personDiseaseRiskScore := genomePolygenicDiseaseInfo.RiskScore personDiseaseRiskScore := genomePolygenicDiseaseInfo.RiskScore
numberOfLociTested := genomePolygenicDiseaseInfo.NumberOfLociTested
personDiseaseRiskScoreString := helpers.ConvertIntToString(personDiseaseRiskScore) personDiseaseRiskScoreString := helpers.ConvertIntToString(personDiseaseRiskScore)
personDiseaseRiskScoreFormatted := personDiseaseRiskScoreString + "/10" personDiseaseRiskScoreFormatted := personDiseaseRiskScoreString + "/10"
return true, personDiseaseRiskScore, personDiseaseRiskScoreFormatted, numberOfLociTested, conflictExists, nil personLocusValuesMap := genomePolygenicDiseaseInfo.LocusValuesMap
numberOfLociTested := genomePolygenicDiseaseInfo.NumberOfLociTested
return true, personDiseaseRiskScore, personDiseaseRiskScoreFormatted, personLocusValuesMap, numberOfLociTested, conflictExists, nil
} }
//Outputs: //Outputs:
// -bool: Offspring Disease Risk Score known // -bool: Offspring Disease Risk Score known
// -int: Disease risk score // -int: Offspring average disease risk score
// -string: Disease risk score formatted (has "/10" suffix) // -string: Offspring Disease average risk score formatted (has "/10" suffix)
// -[]int: Sample Offspring Risk Scores List
// -int: Number of loci tested // -int: Number of loci tested
// -bool: Conflict exists // -bool: Conflict exists
// -error // -error
func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, string, int, bool, error){ func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, string, []int, int, bool, error){
couplePolygenicDiseasesMap := coupleAnalysisObject.PolygenicDiseasesMap couplePolygenicDiseasesMap := coupleAnalysisObject.PolygenicDiseasesMap
couplePolygenicDiseaseInfo, exists := couplePolygenicDiseasesMap[diseaseName] couplePolygenicDiseaseInfo, exists := couplePolygenicDiseasesMap[diseaseName]
if (exists == false){ if (exists == false){
return false, 0, "", 0, false, nil return false, 0, "", nil, 0, false, nil
} }
polygenicDiseaseInfoMap := couplePolygenicDiseaseInfo.PolygenicDiseaseInfoMap polygenicDiseaseInfoMap := couplePolygenicDiseaseInfo.PolygenicDiseaseInfoMap
genomePairPolygenicDiseaseInfo, exists := polygenicDiseaseInfoMap[genomePairIdentifier] genomePairPolygenicDiseaseInfo, exists := polygenicDiseaseInfoMap[genomePairIdentifier]
if (exists == false){ if (exists == false){
return false, 0, "", 0, false, nil return false, 0, "", nil, 0, false, nil
} }
conflictExists := couplePolygenicDiseaseInfo.ConflictExists conflictExists := couplePolygenicDiseaseInfo.ConflictExists
numberOfLociTested := genomePairPolygenicDiseaseInfo.NumberOfLociTested numberOfLociTested := genomePairPolygenicDiseaseInfo.NumberOfLociTested
offspringRiskScore := genomePairPolygenicDiseaseInfo.OffspringRiskScore offspringAverageRiskScore := genomePairPolygenicDiseaseInfo.OffspringAverageRiskScore
offspringRiskScoreString := helpers.ConvertIntToString(offspringRiskScore) offspringAverageRiskScoreString := helpers.ConvertIntToString(offspringAverageRiskScore)
offspringRiskScoreFormatted := offspringRiskScoreString + "/10" offspringAverageRiskScoreFormatted := offspringAverageRiskScoreString + "/10"
return true, offspringRiskScore, offspringRiskScoreFormatted, numberOfLociTested, conflictExists, nil sampleOffspringRiskScoresList := genomePairPolygenicDiseaseInfo.SampleOffspringRiskScoresList
return true, offspringAverageRiskScore, offspringAverageRiskScoreFormatted, sampleOffspringRiskScoresList, numberOfLociTested, conflictExists, nil
} }
//Outputs: //Outputs:
// -bool: Risk Weight and base pair known // -bool: Risk Weight and base pair known
// -int: Locus risk weight // -int: Locus risk weight
// -string: Locus base 1
// -string: Locus base 2
// -bool: Locus odds ratio known // -bool: Locus odds ratio known
// -float64: Locus odds ratio // -float64: Locus odds ratio
// -string: Locus odds ratio formatted (with x suffix) // -string: Locus odds ratio formatted (with x suffix)
// -error // -error
func GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalyisObject geneticAnalysis.PersonAnalysis, diseaseName string, locusIdentifier [3]byte, genomeIdentifier [16]byte)(bool, int, string, string, bool, float64, string, error){ func GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalyisObject geneticAnalysis.PersonAnalysis, diseaseName string, locusIdentifier [3]byte, genomeIdentifier [16]byte)(bool, int, bool, float64, string, error){
personPolygenicDiseasesMap := personAnalyisObject.PolygenicDiseasesMap personPolygenicDiseasesMap := personAnalyisObject.PolygenicDiseasesMap
personPolygenicDiseaseMap, exists := personPolygenicDiseasesMap[diseaseName] personPolygenicDiseaseMap, exists := personPolygenicDiseasesMap[diseaseName]
if (exists == false){ if (exists == false){
return false, 0, "", "", false, 0, "", nil return false, 0, false, 0, "", nil
} }
personPolygenicDiseaseInfoMap := personPolygenicDiseaseMap.PolygenicDiseaseInfoMap personPolygenicDiseaseInfoMap := personPolygenicDiseaseMap.PolygenicDiseaseInfoMap
personGenomePolygenicDiseaseInfo, exists := personPolygenicDiseaseInfoMap[genomeIdentifier] personGenomePolygenicDiseaseInfo, exists := personPolygenicDiseaseInfoMap[genomeIdentifier]
if (exists == false){ if (exists == false){
return false, 0, "", "", false, 0, "", nil return false, 0, false, 0, "", nil
} }
genomeLociInfoMap := personGenomePolygenicDiseaseInfo.LociInfoMap genomeLociInfoMap := personGenomePolygenicDiseaseInfo.LociInfoMap
locusInfoObject, exists := genomeLociInfoMap[locusIdentifier] locusInfoObject, exists := genomeLociInfoMap[locusIdentifier]
if (exists == false){ if (exists == false){
return false, 0, "", "", false, 0, "", nil return false, 0, false, 0, "", nil
} }
locusRiskWeight := locusInfoObject.RiskWeight locusRiskWeight := locusInfoObject.RiskWeight
locusBase1 := locusInfoObject.LocusBase1
locusBase2 := locusInfoObject.LocusBase2
locusOddsRatioIsKnown := locusInfoObject.OddsRatioIsKnown locusOddsRatioIsKnown := locusInfoObject.OddsRatioIsKnown
if (locusOddsRatioIsKnown == false){ if (locusOddsRatioIsKnown == false){
return true, locusRiskWeight, locusBase1, locusBase2, false, 0, "", nil return true, locusRiskWeight, false, 0, "", nil
} }
locusOddsRatio := locusInfoObject.OddsRatio locusOddsRatio := locusInfoObject.OddsRatio
@ -602,7 +603,7 @@ func GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalyisObject g
locusOddsRatioFormatted := genomeLocusOddsRatioString + "x" locusOddsRatioFormatted := genomeLocusOddsRatioString + "x"
return true, locusRiskWeight, locusBase1, locusBase2, true, locusOddsRatio, locusOddsRatioFormatted, nil return true, locusRiskWeight, true, locusOddsRatio, locusOddsRatioFormatted, nil
} }
//Outputs: //Outputs:
@ -635,40 +636,40 @@ func GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObje
return false, 0, false, 0, "", nil return false, 0, false, 0, "", nil
} }
offspringRiskWeight := locusInfoObject.OffspringRiskWeight offspringAverageRiskWeight := locusInfoObject.OffspringAverageRiskWeight
offspringOddsRatioIsKnown := locusInfoObject.OffspringOddsRatioIsKnown offspringOddsRatioIsKnown := locusInfoObject.OffspringOddsRatioIsKnown
if (offspringOddsRatioIsKnown == false){ if (offspringOddsRatioIsKnown == false){
return true, offspringRiskWeight, false, 0, "", nil return true, offspringAverageRiskWeight, false, 0, "", nil
} }
offspringOddsRatio := locusInfoObject.OffspringOddsRatio offspringAverageOddsRatio := locusInfoObject.OffspringAverageOddsRatio
getOddsRatioFormatted := func()string{ getOddsRatioFormatted := func()string{
offspringUnknownOddsRatiosWeightSum := locusInfoObject.OffspringUnknownOddsRatiosWeightSum offspringAverageUnknownOddsRatiosWeightSum := locusInfoObject.OffspringAverageUnknownOddsRatiosWeightSum
offspringOddsRatioString := helpers.ConvertFloat64ToStringRounded(offspringOddsRatio, 2) offspringAverageOddsRatioString := helpers.ConvertFloat64ToStringRounded(offspringAverageOddsRatio, 2)
if (offspringUnknownOddsRatiosWeightSum == 0){ if (offspringAverageUnknownOddsRatiosWeightSum == 0){
result := offspringOddsRatioString + "x" result := offspringAverageOddsRatioString + "x"
return result return result
} }
if (offspringUnknownOddsRatiosWeightSum < 0){ if (offspringAverageUnknownOddsRatiosWeightSum < 0){
result := "<" + offspringOddsRatioString + "x" result := "<" + offspringAverageOddsRatioString + "x"
return result return result
} }
// offspringUnknownOddsRatiosWeightSum > 0 // offspringAverageUnknownOddsRatiosWeightSum > 0
result := offspringOddsRatioString + "x+" result := offspringAverageOddsRatioString + "x+"
return result return result
} }
oddsRatioFormatted := getOddsRatioFormatted() oddsRatioFormatted := getOddsRatioFormatted()
return true, offspringRiskWeight, true, offspringOddsRatio, oddsRatioFormatted, nil return true, offspringAverageRiskWeight, true, offspringAverageOddsRatio, oddsRatioFormatted, nil
} }
//Outputs: //Outputs:
@ -858,7 +859,7 @@ func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnal
for _, genomeIdentifier := range allGenomeIdentifiersList{ for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier) _, _, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier)
if (err != nil) { return err } if (err != nil) { return err }
} }
@ -873,7 +874,7 @@ func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnal
for _, genomeIdentifier := range allGenomeIdentifiersList{ for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, _, _, _, _, _, err := GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier) _, _, _, _, _, err := GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier)
if (err != nil) { return err } if (err != nil) { return err }
} }
} }
@ -970,7 +971,7 @@ func VerifyCoupleGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnal
for _, genomePairIdentifier := range allGenomePairIdentifiersList{ for _, genomePairIdentifier := range allGenomePairIdentifiersList{
_, _, _, _, _, err := GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier) _, _, _, _, _, _, err := GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier)
if (err != nil) { return err } if (err != nil) { return err }
} }

View file

@ -19,6 +19,7 @@ import "seekia/internal/desires/myMateDesires"
import "seekia/internal/encoding" import "seekia/internal/encoding"
import "seekia/internal/genetics/companyAnalysis" import "seekia/internal/genetics/companyAnalysis"
import "seekia/internal/genetics/createGeneticAnalysis" import "seekia/internal/genetics/createGeneticAnalysis"
import "seekia/internal/genetics/locusValue"
import "seekia/internal/genetics/myChosenAnalysis" import "seekia/internal/genetics/myChosenAnalysis"
import "seekia/internal/genetics/readGeneticAnalysis" import "seekia/internal/genetics/readGeneticAnalysis"
import "seekia/internal/helpers" import "seekia/internal/helpers"
@ -677,12 +678,10 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
for _, diseaseObject := range polygenicDiseaseObjectsList{ for _, diseaseObject := range polygenicDiseaseObjectsList{
diseaseLociList := diseaseObject.LociList // Map Structure: Locus rsID -> Locus Value
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
userRiskWeightSum := 0 diseaseLociList := diseaseObject.LociList
userMinimumPossibleRiskWeightSum := 0
userMaximumPossibleRiskWeightSum := 0
userNumberOfLociTested := 0
for _, locusObject := range diseaseLociList{ for _, locusObject := range diseaseLociList{
@ -690,11 +689,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
locusRiskWeightsMap := locusObject.RiskWeightsMap locusValueAttributeName := "LocusValue_rs" + locusRSIDString
locusMinimumRiskWeight := locusObject.MinimumRiskWeight
locusMaximumRiskWeight := locusObject.MaximumRiskWeight
locusValueAttributeName := "LocusValue_rs" + locusRSIDString
userLocusBasePairExists, _, userLocusBasePair, err := getProfileAttributesFunction(locusValueAttributeName) userLocusBasePairExists, _, userLocusBasePair, err := getProfileAttributesFunction(locusValueAttributeName)
if (err != nil) { return false, 0, "", err } if (err != nil) { return false, 0, "", err }
@ -702,30 +697,30 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
continue continue
} }
userNumberOfLociTested += 1 userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";")
if (semicolonFound == false){
userMinimumPossibleRiskWeightSum += locusMinimumRiskWeight return false, 0, "", errors.New("Database corrupt: Contains profile with invalid " + locusValueAttributeName + " value: " + userLocusBasePair)
userMaximumPossibleRiskWeightSum += locusMaximumRiskWeight
userLocusRiskWeight, exists := locusRiskWeightsMap[userLocusBasePair]
if (exists == false){
// We do not know the risk weight for this base pair
// We treat this as a 0 risk weight
} else {
userRiskWeightSum += userLocusRiskWeight
} }
userLocusValue := locusValue.LocusValue{
Base1Value: userLocusBase1,
Base2Value: userLocusBase2,
//TODO: Share LocusIsPhased information in user profiles and retrieve it into this value
LocusIsPhased: false,
}
userDiseaseLocusValuesMap[locusRSID] = userLocusValue
} }
if (userNumberOfLociTested == 0){ anyLocusTested, userDiseaseRiskScore, _, _, err := createGeneticAnalysis.GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, userDiseaseLocusValuesMap, true)
if (err != nil) { return false, 0, "", err }
if (anyLocusTested == false){
continue continue
} }
numberOfDiseasesTested += 1 numberOfDiseasesTested += 1
userDiseaseRiskScore, err := helpers.ScaleNumberProportionally(true, userRiskWeightSum, userMinimumPossibleRiskWeightSum, userMaximumPossibleRiskWeightSum, 0, 100) userRiskScoreFraction := float64(userDiseaseRiskScore)/float64(10)
if (err != nil) { return false, 0, "", err }
userRiskScoreFraction := float64(userDiseaseRiskScore)/float64(100)
allDiseasesAverageRiskScoreNumerator += userRiskScoreFraction allDiseasesAverageRiskScoreNumerator += userRiskScoreFraction
} }
@ -787,35 +782,20 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
diseaseName := diseaseObject.DiseaseName diseaseName := diseaseObject.DiseaseName
diseaseLociList := diseaseObject.LociList diseaseLociList := diseaseObject.LociList
offspringRiskWeightSum := 0 _, _, _, myDiseaseLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, myGenomeIdentifier)
offspringMinimumPossibleRiskWeightSum := 0 if (err != nil) { return false, 0, "", err }
offspringMaximumPossibleRiskWeightSum := 0
offspringNumberOfLociTested := 0 // Map Structure: rsID -> Locus Value
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, locusObject := range diseaseLociList{ for _, locusObject := range diseaseLociList{
locusIdentifierHex := locusObject.LocusIdentifier
locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex)
if (err != nil) { return false, 0, "", err }
locusRSID := locusObject.LocusRSID locusRSID := locusObject.LocusRSID
locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
locusRiskWeightsMap := locusObject.RiskWeightsMap
locusOddsRatiosMap := locusObject.OddsRatiosMap
locusMinimumRiskWeight := locusObject.MinimumRiskWeight
locusMaximumRiskWeight := locusObject.MaximumRiskWeight
locusValueAttributeName := "LocusValue_rs" + locusRSIDString locusValueAttributeName := "LocusValue_rs" + locusRSIDString
myLocusInfoIsKnown, _, myLocusBase1, myLocusBase2, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, locusIdentifier, myGenomeIdentifier)
if (err != nil) { return false, 0, "", err }
if (myLocusInfoIsKnown == false){
continue
}
userLocusBasePairExists, _, userLocusBasePair, err := getProfileAttributesFunction(locusValueAttributeName) userLocusBasePairExists, _, userLocusBasePair, err := getProfileAttributesFunction(locusValueAttributeName)
if (err != nil) { return false, 0, "", err } if (err != nil) { return false, 0, "", err }
if (userLocusBasePairExists == false){ if (userLocusBasePairExists == false){
@ -827,27 +807,25 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
return false, 0, "", errors.New("GetAnyProfileAttributeIncludingCalculated called with profile containing invalid " + locusValueAttributeName + ": " + userLocusBasePair) return false, 0, "", errors.New("GetAnyProfileAttributeIncludingCalculated called with profile containing invalid " + locusValueAttributeName + ": " + userLocusBasePair)
} }
offspringLocusRiskWeight, _, _, _, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBase1, myLocusBase2, userLocusBase1, userLocusBase2) newLocusValue := locusValue.LocusValue{
if (err != nil) { return false, 0, "", err } Base1Value: userLocusBase1,
Base2Value: userLocusBase2,
offspringNumberOfLociTested += 1 //TODO: Share locusIsPhased information in user profiles are put it here
LocusIsPhased: false,
offspringMinimumPossibleRiskWeightSum += locusMinimumRiskWeight }
offspringMaximumPossibleRiskWeightSum += locusMaximumRiskWeight
userDiseaseLocusValuesMap[locusRSID] = newLocusValue
offspringRiskWeightSum += offspringLocusRiskWeight
} }
if (offspringNumberOfLociTested == 0){ anyLocusValuesTested, offspringAverageRiskScore, _, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseInfo_Fast(diseaseLociList, myDiseaseLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) { return false, 0, "", err }
if (anyLocusValuesTested == false){
continue continue
} }
numberOfDiseasesTested += 1 numberOfDiseasesTested += 1
offspringRiskScore, err := helpers.ScaleNumberProportionally(true, offspringRiskWeightSum, offspringMinimumPossibleRiskWeightSum, offspringMaximumPossibleRiskWeightSum, 0, 100) offspringRiskScoreFraction := float64(offspringAverageRiskScore)/float64(10)
if (err != nil) { return false, 0, "", err }
offspringRiskScoreFraction := float64(offspringRiskScore)/float64(100)
allDiseasesAverageRiskScoreNumerator += offspringRiskScoreFraction allDiseasesAverageRiskScoreNumerator += offspringRiskScoreFraction
} }

View file

@ -205,26 +205,19 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{
continue continue
} }
diseaseLociList := diseaseObject.LociList _, _, _, myDiseaseLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, genomeIdentifierToShare)
if (err != nil) { return err }
for _, locusObject := range diseaseLociList{ for rsID, locusValueObject := range myDiseaseLocusValuesMap{
locusIdentifierHex := locusObject.LocusIdentifier rsIDString := helpers.ConvertInt64ToString(rsID)
locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex) locusBase1 := locusValueObject.Base1Value
if (err != nil) { return err } locusBase2 := locusValueObject.Base2Value
locusValueKnown, _, locusBase1, locusBase2, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifierToShare) basePairValue := locusBase1 + ";" + locusBase2
if (err != nil) { return err }
if (locusValueKnown == false){
continue
}
locusRSID := locusObject.LocusRSID profileMap["LocusValue_rs" + rsIDString] = basePairValue
locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
profileMap["LocusValue_rs" + locusRSIDString] = locusBase1 + ";" + locusBase2
} }
} }

View file

@ -14,6 +14,7 @@ import "seekia/internal/profiles/calculatedAttributes"
import "seekia/internal/profiles/profileStorage" import "seekia/internal/profiles/profileStorage"
import "seekia/internal/profiles/attributeDisplay" import "seekia/internal/profiles/attributeDisplay"
import "seekia/internal/translation" import "seekia/internal/translation"
import "seekia/internal/statisticsDatum"
import "slices" import "slices"
import "strings" import "strings"
@ -21,61 +22,27 @@ import "errors"
import "math" import "math"
type StatisticsItem struct{
// The label for the statistics item
// For a bar chart, this represents the name of an X axis bar.
// For a donut chart, this represents the name of a donut slice
// Example: "Man", "100-200"
// This will never be translated, unless it is the Unknown/No Response item, in which case
// the label will be "Unknown"/"No Response" translated to the user's current app language
Label string
// This is the formatted, human readable version of the label
// This will be translated into the application language
// Sometimes, the LabelFormatted will be identical to Label
// This does not include units (Example: " days", " users")
// Example:
// -"1000000-2000000" -> "1 million-2 million"
LabelFormatted string
// The value corresponding to the label
// For a bar chart, this represents the Y axis value for a bar.
// For a donut chart, this represents the value (size) of one of the donut slices
// This will never be translated
// For example, the value could be 500 if 500 men responded Yes.
Value float64
// This is the formatted version of the value
// This does not include units (Example: " days", " users")
// Examples:
// -5 -> "5/10"
// -1500000000000 -> "1.5 trillion"
ValueFormatted string
}
//Outputs: //Outputs:
// -int: Number of users analyzed in statistics // -int: Number of users analyzed in statistics
// -[]StatisticsItem: Statistics items list (sorted, not grouped) // -[]statisticsDatum.StatisticsDatum: Statistics datums list (sorted, not grouped)
// -bool: Grouping performed // -bool: Grouping performed
// -[]StatisticsItem: Grouped items list // -[]statisticsDatum.StatisticsDatum: Grouped datums list
// -func(float64)(string, error): Function to format y axis values // -func(float64)(string, error): Function to format y axis values
// -This is used because the values must be passed to the chart code as pure floats, but they must be formatted after to be human readable // -This is used because the values must be passed to the chart code as pure floats, but they must be formatted after to be human readable
// -Example: "1000000" -> "1 million" // -Example: "1000000" -> "1 million"
// -error // -error
func GetUserStatisticsItemsLists_BarChart(identityType string, func GetUserStatisticsDatumsLists_BarChart(identityType string,
networkType byte, networkType byte,
xAxisAttribute string, xAxisAttribute string,
xAxisIsNumerical bool, xAxisIsNumerical bool,
formatXAxisValuesFunction func(string)(string, error), formatXAxisValuesFunction func(string)(string, error),
xAxisUnknownLabel string, xAxisUnknownLabel string,
yAxisAttribute string)(int, []StatisticsItem, bool, []StatisticsItem, func(float64)(string, error), error){ yAxisAttribute string)(int, []statisticsDatum.StatisticsDatum, bool, []statisticsDatum.StatisticsDatum, func(float64)(string, error), error){
isValid := helpers.VerifyNetworkType(networkType) isValid := helpers.VerifyNetworkType(networkType)
if (isValid == false){ if (isValid == false){
networkTypeString := helpers.ConvertByteToString(networkType) networkTypeString := helpers.ConvertByteToString(networkType)
return 0, nil, false, nil, nil, errors.New("GetUserStatisticsItemsLists_BarChart called with invalid networkType: " + networkTypeString) return 0, nil, false, nil, nil, errors.New("GetUserStatisticsDatumsLists_BarChart called with invalid networkType: " + networkTypeString)
} }
getYAxisRoundingPrecision := func()int{ getYAxisRoundingPrecision := func()int{
@ -95,16 +62,16 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
//Outputs: //Outputs:
// -int: Total analyzed users // -int: Total analyzed users
// -[]StatisticsItem: Statistics items list // -[]statisticsDatum.StatisticsDatum: Statistics datums list
// -map[string]int: Response Counts map (X axis attribute response -> Number of y axis responses) // -map[string]int: Response Counts map (X axis attribute response -> Number of y axis responses)
// -bool: yAxisIsAverage // -bool: yAxisIsAverage
// -map[string]float64: Response Sums map (X axis attribute response -> all y axis responses summed) (If yAxisIsAverage == true) // -map[string]float64: Response Sums map (X axis attribute response -> all y axis responses summed) (If yAxisIsAverage == true)
// -func(float64)(string, error): Function to format statistics values // -func(float64)(string, error): Function to format statistics values
// -bool: Include a No Response/Unknown value item // -bool: Include a No Response/Unknown value datum
// -This is only needed if at least 1 user did not respond to the X axis attribute on their profile. // -This is only needed if at least 1 user did not respond to the X axis attribute on their profile.
// -StatisticsItem: The unknown value item. // -statisticsDatum.StatisticsDatum: The unknown value datum.
// -error // -error
getStatisticsItemsList := func()(int, []StatisticsItem, map[string]int, bool, map[string]float64, func(float64)(string, error), bool, StatisticsItem, error){ getStatisticsDatumsList := func()(int, []statisticsDatum.StatisticsDatum, map[string]int, bool, map[string]float64, func(float64)(string, error), bool, statisticsDatum.StatisticsDatum, error){
//TODO: Add "Probability Of ..." //TODO: Add "Probability Of ..."
// This will allow viewing of choice attribute probabilities // This will allow viewing of choice attribute probabilities
@ -113,8 +80,8 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
if (yAxisAttribute == "Number Of Users"){ if (yAxisAttribute == "Number Of Users"){
totalAnalyzedUsers, statisticsItemsList, responseCountsMap, numberOfUnknownValueUsers, err := getProfileAttributeCountStatisticsItemsList(identityType, networkType, xAxisAttribute, formatXAxisValuesFunction) totalAnalyzedUsers, statisticsDatumsList, responseCountsMap, numberOfUnknownValueUsers, err := getProfileAttributeCountStatisticsDatumsList(identityType, networkType, xAxisAttribute, formatXAxisValuesFunction)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
formatValuesFunction := func(input float64)(string, error){ formatValuesFunction := func(input float64)(string, error){
@ -126,23 +93,23 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
} }
if (numberOfUnknownValueUsers == 0){ if (numberOfUnknownValueUsers == 0){
return totalAnalyzedUsers, statisticsItemsList, responseCountsMap, false, nil, formatValuesFunction, false, StatisticsItem{}, nil return totalAnalyzedUsers, statisticsDatumsList, responseCountsMap, false, nil, formatValuesFunction, false, statisticsDatum.StatisticsDatum{}, nil
} }
unknownValueFormatted := helpers.ConvertIntToString(numberOfUnknownValueUsers) unknownValueFormatted := helpers.ConvertIntToString(numberOfUnknownValueUsers)
unknownItem := StatisticsItem{ unknownDatum := statisticsDatum.StatisticsDatum{
Label: xAxisUnknownLabel, Label: xAxisUnknownLabel,
LabelFormatted: xAxisUnknownLabel, LabelFormatted: xAxisUnknownLabel,
Value: float64(numberOfUnknownValueUsers), Value: float64(numberOfUnknownValueUsers),
ValueFormatted: unknownValueFormatted, ValueFormatted: unknownValueFormatted,
} }
return totalAnalyzedUsers, statisticsItemsList, responseCountsMap, false, nil, formatValuesFunction, true, unknownItem, nil return totalAnalyzedUsers, statisticsDatumsList, responseCountsMap, false, nil, formatValuesFunction, true, unknownDatum, nil
} }
userIdentityHashesToAnalyzeList, err := getUserIdentityHashesToAnalyzeList(identityType) userIdentityHashesToAnalyzeList, err := getUserIdentityHashesToAnalyzeList(identityType)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
yAxisIsAverage := strings.HasPrefix(yAxisAttribute, "Average ") yAxisIsAverage := strings.HasPrefix(yAxisAttribute, "Average ")
if (yAxisIsAverage == true){ if (yAxisIsAverage == true){
@ -152,69 +119,72 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
getAttributeToGetAverageFor := func()string{ getAttributeToGetAverageFor := func()string{
attributeTitle := strings.TrimPrefix(yAxisAttribute, "Average ") attributeTitle := strings.TrimPrefix(yAxisAttribute, "Average ")
if (attributeTitle == "Wealth"){ switch attributeTitle{
return "WealthInGold"
} case "Wealth":{
if (attributeTitle == "23andMe Neanderthal Variants"){ return "WealthInGold"
return "23andMe_NeanderthalVariants" }
} case "23andMe Neanderthal Variants":{
if (attributeTitle == "Body Fat"){ return "23andMe_NeanderthalVariants"
return "BodyFat" }
} case "Body Fat":{
if (attributeTitle == "Body Muscle"){ return "BodyFat"
return "BodyMuscle" }
} case "Body Muscle":{
if (attributeTitle == "Fruit Rating"){ return "BodyMuscle"
return "FruitRating" }
} case "Fruit Rating":{
if (attributeTitle == "Vegetables Rating"){ return "FruitRating"
return "VegetablesRating" }
} case "Vegetables Rating":{
if (attributeTitle == "Nuts Rating"){ return "VegetablesRating"
return "NutsRating" }
} case "Nuts Rating":{
if (attributeTitle == "Grains Rating"){ return "NutsRating"
return "GrainsRating" }
} case "Grains Rating":{
if (attributeTitle == "Dairy Rating"){ return "GrainsRating"
return "DairyRating" }
} case "Dairy Rating":{
if (attributeTitle == "Seafood Rating"){ return "DairyRating"
return "SeafoodRating" }
} case "Seafood Rating":{
if (attributeTitle == "Beef Rating"){ return "SeafoodRating"
return "BeefRating" }
} case "Beef Rating":{
if (attributeTitle == "Pork Rating"){ return "BeefRating"
return "PorkRating" }
} case "Pork Rating":{
if (attributeTitle == "Poultry Rating"){ return "PorkRating"
return "PoultryRating" }
} case "Poultry Rating":{
if (attributeTitle == "Eggs Rating"){ return "PoultryRating"
return "EggsRating" }
} case "Eggs Rating":{
if (attributeTitle == "Beans Rating"){ return "EggsRating"
return "BeansRating" }
} case "Beans Rating":{
if (attributeTitle == "Alcohol Frequency"){ return "BeansRating"
return "AlcoholFrequency" }
} case "Alcohol Frequency":{
if (attributeTitle == "Tobacco Frequency"){ return "AlcoholFrequency"
return "TobaccoFrequency" }
} case "Tobacco Frequency":{
if (attributeTitle == "Cannabis Frequency"){ return "TobaccoFrequency"
return "CannabisFrequency" }
} case "Cannabis Frequency":{
if (attributeTitle == "Pets Rating"){ return "CannabisFrequency"
return "PetsRating" }
} case "Pets Rating":{
if (attributeTitle == "Dogs Rating"){ return "PetsRating"
return "DogsRating" }
} case "Dogs Rating":{
if (attributeTitle == "Cats Rating"){ return "DogsRating"
return "CatsRating" }
case "Cats Rating":{
return "CatsRating"
}
} }
return attributeTitle return attributeTitle
@ -240,19 +210,19 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
for _, userIdentityHash := range userIdentityHashesToAnalyzeList{ for _, userIdentityHash := range userIdentityHashesToAnalyzeList{
profileFound, getAnyUserAttributeValueFunction, err := getRetrieveAnyAttributeFromUserNewestProfileFunction(userIdentityHash, networkType) profileFound, getAnyUserAttributeValueFunction, err := getRetrieveAnyAttributeFromUserNewestProfileFunction(userIdentityHash, networkType)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
if (profileFound == false){ if (profileFound == false){
continue continue
} }
userIsDisabled, _, _, err := getAnyUserAttributeValueFunction("Disabled") userIsDisabled, _, _, err := getAnyUserAttributeValueFunction("Disabled")
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
if (userIsDisabled == true){ if (userIsDisabled == true){
continue continue
} }
attributeExists, _, userAttributeToGetAverageForValue, err := getAnyUserAttributeValueFunction(attributeToGetAverageFor) attributeExists, _, userAttributeToGetAverageForValue, err := getAnyUserAttributeValueFunction(attributeToGetAverageFor)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
if (attributeExists == false){ if (attributeExists == false){
// This user did not respond to the attribute we are getting the average for // This user did not respond to the attribute we are getting the average for
// We will not add them to the statistics maps // We will not add them to the statistics maps
@ -263,11 +233,11 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
userAttributeToGetAverageForValueFloat64, err := helpers.ConvertStringToFloat64(userAttributeToGetAverageForValue) userAttributeToGetAverageForValueFloat64, err := helpers.ConvertStringToFloat64(userAttributeToGetAverageForValue)
if (err != nil) { if (err != nil) {
return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, errors.New("Database corrupt: Contains invalid " + userAttributeToGetAverageForValue + " value: " + userAttributeToGetAverageForValue) return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, errors.New("Database corrupt: Contains invalid " + userAttributeToGetAverageForValue + " value: " + userAttributeToGetAverageForValue)
} }
attributeFound, _, userXAxisAttributeValue, err := getAnyUserAttributeValueFunction(xAxisAttribute) attributeFound, _, userXAxisAttributeValue, err := getAnyUserAttributeValueFunction(xAxisAttribute)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
if (attributeFound == false){ if (attributeFound == false){
// This user did not respond to the X axis attribute // This user did not respond to the X axis attribute
// We calculate the average for users who do not respond and put it in its own category // We calculate the average for users who do not respond and put it in its own category
@ -283,18 +253,18 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
} }
_, _, formatYAxisValuesFunction, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeToGetAverageFor) _, _, formatYAxisValuesFunction, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeToGetAverageFor)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
statisticsItemsList := make([]StatisticsItem, 0, len(responseCountsMap)) statisticsDatumsList := make([]statisticsDatum.StatisticsDatum, 0, len(responseCountsMap))
for attributeResponse, responsesCount := range responseCountsMap{ for attributeResponse, responsesCount := range responseCountsMap{
attributeResponseFormatted, err := formatXAxisValuesFunction(attributeResponse) attributeResponseFormatted, err := formatXAxisValuesFunction(attributeResponse)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
allResponsesSum, exists := responseSumsMap[attributeResponse] allResponsesSum, exists := responseSumsMap[attributeResponse]
if (exists == false){ if (exists == false){
return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, errors.New("Response sums map missing attribute value") return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, errors.New("Response sums map missing attribute value")
} }
averageValue := allResponsesSum/float64(responsesCount) averageValue := allResponsesSum/float64(responsesCount)
@ -302,16 +272,16 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
averageValueString := helpers.ConvertFloat64ToStringRounded(averageValue, yAxisRoundingPrecision) averageValueString := helpers.ConvertFloat64ToStringRounded(averageValue, yAxisRoundingPrecision)
averageValueFormatted, err := formatYAxisValuesFunction(averageValueString) averageValueFormatted, err := formatYAxisValuesFunction(averageValueString)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
newStatisticsItem := StatisticsItem{ newStatisticsDatum := statisticsDatum.StatisticsDatum{
Label: attributeResponse, Label: attributeResponse,
LabelFormatted: attributeResponseFormatted, LabelFormatted: attributeResponseFormatted,
Value: averageValue, Value: averageValue,
ValueFormatted: averageValueFormatted, ValueFormatted: averageValueFormatted,
} }
statisticsItemsList = append(statisticsItemsList, newStatisticsItem) statisticsDatumsList = append(statisticsDatumsList, newStatisticsDatum)
} }
// We use this function to format values after grouping, if grouping is needed // We use this function to format values after grouping, if grouping is needed
@ -326,111 +296,111 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
} }
if (numberOfUsersWithUnknownXAxisValue == 0){ if (numberOfUsersWithUnknownXAxisValue == 0){
return totalAnalyzedUsers, statisticsItemsList, responseCountsMap, true, responseSumsMap, formatValuesFunction, false, StatisticsItem{}, nil return totalAnalyzedUsers, statisticsDatumsList, responseCountsMap, true, responseSumsMap, formatValuesFunction, false, statisticsDatum.StatisticsDatum{}, nil
} }
unknownResponsesAverage := usersWithUnknownXAxisValueYAxisValuesSum/float64(numberOfUsersWithUnknownXAxisValue) unknownResponsesAverage := usersWithUnknownXAxisValueYAxisValuesSum/float64(numberOfUsersWithUnknownXAxisValue)
unknownResponsesValueFormatted, err := formatValuesFunction(unknownResponsesAverage) unknownResponsesValueFormatted, err := formatValuesFunction(unknownResponsesAverage)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err } if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
// This item represents the average value for the yAxisAttribute for users who did not respond. // This datum represents the average value for the yAxisAttribute for users who did not respond.
// For example, if the xAxisAttribute is Height, and the yAxisAttribute is AverageWealth, this value // For example, if the xAxisAttribute is Height, and the yAxisAttribute is AverageWealth, this value
// will represent the average wealth for users who did not provide Height on their profile. // will represent the average wealth for users who did not provide Height on their profile.
unknownStatisticsItem := StatisticsItem{ unknownStatisticsDatum := statisticsDatum.StatisticsDatum{
Label: xAxisUnknownLabel, Label: xAxisUnknownLabel,
LabelFormatted: xAxisUnknownLabel, LabelFormatted: xAxisUnknownLabel,
Value: unknownResponsesAverage, Value: unknownResponsesAverage,
ValueFormatted: unknownResponsesValueFormatted, ValueFormatted: unknownResponsesValueFormatted,
} }
return totalAnalyzedUsers, statisticsItemsList, responseCountsMap, true, responseSumsMap, formatValuesFunction, true, unknownStatisticsItem, nil return totalAnalyzedUsers, statisticsDatumsList, responseCountsMap, true, responseSumsMap, formatValuesFunction, true, unknownStatisticsDatum, nil
} }
return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, errors.New("Invalid y-axis attribute: " + yAxisAttribute) return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, errors.New("Invalid y-axis attribute: " + yAxisAttribute)
} }
totalAnalyzedUsers, statisticsItemsList, responseCountsMap, yAxisIsAverage, responseSumsMap, formatValuesFunction, includeUnknownItem, unknownValueItem, err := getStatisticsItemsList() totalAnalyzedUsers, statisticsDatumsList, responseCountsMap, yAxisIsAverage, responseSumsMap, formatValuesFunction, includeUnknownDatum, unknownValueDatum, err := getStatisticsDatumsList()
if (err != nil) { return 0, nil, false, nil, nil, err } if (err != nil) { return 0, nil, false, nil, nil, err }
sortStatisticsItemsList(statisticsItemsList, xAxisIsNumerical) sortStatisticsDatumsList(statisticsDatumsList, xAxisIsNumerical)
// We now see if we need to group the items in the list together // We now see if we need to group the datums in the list together
// We do this if there are more than 10 categories // We do this if there are more than 10 categories
if (len(statisticsItemsList) <= 10){ if (len(statisticsDatumsList) <= 10){
// No grouping needed. We are done. // No grouping needed. We are done.
if (includeUnknownItem == true){ if (includeUnknownDatum == true){
statisticsItemsList = append(statisticsItemsList, unknownValueItem) statisticsDatumsList = append(statisticsDatumsList, unknownValueDatum)
} }
return totalAnalyzedUsers, statisticsItemsList, false, nil, formatValuesFunction, nil return totalAnalyzedUsers, statisticsDatumsList, false, nil, formatValuesFunction, nil
} }
groupedStatisticsItemsList, err := getStatisticsItemsListGrouped(10, statisticsItemsList, xAxisIsNumerical, responseCountsMap, yAxisIsAverage, responseSumsMap, formatValuesFunction) groupedStatisticsDatumsList, err := getStatisticsDatumsListGrouped(10, statisticsDatumsList, xAxisIsNumerical, responseCountsMap, yAxisIsAverage, responseSumsMap, formatValuesFunction)
if (err != nil) { return 0, nil, false, nil, nil, err } if (err != nil) { return 0, nil, false, nil, nil, err }
if (includeUnknownItem == true){ if (includeUnknownDatum == true){
statisticsItemsList = append(statisticsItemsList, unknownValueItem) statisticsDatumsList = append(statisticsDatumsList, unknownValueDatum)
groupedStatisticsItemsList = append(groupedStatisticsItemsList, unknownValueItem) groupedStatisticsDatumsList = append(groupedStatisticsDatumsList, unknownValueDatum)
} }
return totalAnalyzedUsers, statisticsItemsList, true, groupedStatisticsItemsList, formatValuesFunction, nil return totalAnalyzedUsers, statisticsDatumsList, true, groupedStatisticsDatumsList, formatValuesFunction, nil
} }
//Outputs: //Outputs:
// -int: Number of users analyzed in statistics // -int: Number of users analyzed in statistics
// -[]StatisticsItem: Statistics items list (sorted, not grouped) // -[]statisticsDatum.StatisticsDatum: Statistics datums list (sorted, not grouped)
// -bool: Grouping performed // -bool: Grouping performed
// -[]StatisticsItem: Grouped items list // -[]statisticsDatum.StatisticsDatum: Grouped datums list
// -error // -error
func GetUserStatisticsItemsLists_DonutChart(identityType string, func GetUserStatisticsDatumsLists_DonutChart(identityType string,
networkType byte, networkType byte,
attributeToAnalyze string, attributeToAnalyze string,
attributeIsNumerical bool, attributeIsNumerical bool,
formatAttributeLabelsFunction func(string)(string, error), formatAttributeLabelsFunction func(string)(string, error),
unknownLabelTranslated string)(int, []StatisticsItem, bool, []StatisticsItem, error){ unknownLabelTranslated string)(int, []statisticsDatum.StatisticsDatum, bool, []statisticsDatum.StatisticsDatum, error){
isValid := helpers.VerifyNetworkType(networkType) isValid := helpers.VerifyNetworkType(networkType)
if (isValid == false){ if (isValid == false){
networkTypeString := helpers.ConvertByteToString(networkType) networkTypeString := helpers.ConvertByteToString(networkType)
return 0, nil, false, nil, errors.New("GetUserStatisticsItemsLists_DonutChart called with invalid networkType: " + networkTypeString) return 0, nil, false, nil, errors.New("GetUserStatisticsDatumsLists_DonutChart called with invalid networkType: " + networkTypeString)
} }
totalAnalyzedUsers, statisticsItemsList, attributeCountsMap, numberOfUnknownResponders, err := getProfileAttributeCountStatisticsItemsList(identityType, networkType, attributeToAnalyze, formatAttributeLabelsFunction) totalAnalyzedUsers, statisticsDatumsList, attributeCountsMap, numberOfUnknownResponders, err := getProfileAttributeCountStatisticsDatumsList(identityType, networkType, attributeToAnalyze, formatAttributeLabelsFunction)
if (err != nil) { return 0, nil, false, nil, err } if (err != nil) { return 0, nil, false, nil, err }
sortStatisticsItemsList(statisticsItemsList, attributeIsNumerical) sortStatisticsDatumsList(statisticsDatumsList, attributeIsNumerical)
getUnknownValueItem := func()StatisticsItem{ getUnknownValueDatum := func()statisticsDatum.StatisticsDatum{
numberOfUnknownRespondersString := helpers.ConvertIntToString(numberOfUnknownResponders) numberOfUnknownRespondersString := helpers.ConvertIntToString(numberOfUnknownResponders)
unknownValueItem := StatisticsItem{ unknownValueDatum := statisticsDatum.StatisticsDatum{
Label: unknownLabelTranslated, Label: unknownLabelTranslated,
LabelFormatted: unknownLabelTranslated, LabelFormatted: unknownLabelTranslated,
Value: float64(numberOfUnknownResponders), Value: float64(numberOfUnknownResponders),
ValueFormatted: numberOfUnknownRespondersString, ValueFormatted: numberOfUnknownRespondersString,
} }
return unknownValueItem return unknownValueDatum
} }
if (len(statisticsItemsList) <= 8){ if (len(statisticsDatumsList) <= 8){
// No grouping needed. // No grouping needed.
if (numberOfUnknownResponders != 0){ if (numberOfUnknownResponders != 0){
unknownValueItem := getUnknownValueItem() unknownValueDatum := getUnknownValueDatum()
statisticsItemsList = append(statisticsItemsList, unknownValueItem) statisticsDatumsList = append(statisticsDatumsList, unknownValueDatum)
} }
return totalAnalyzedUsers, statisticsItemsList, false, nil, nil return totalAnalyzedUsers, statisticsDatumsList, false, nil, nil
} }
formatValuesFunction := func(input float64)(string, error){ formatValuesFunction := func(input float64)(string, error){
@ -443,35 +413,35 @@ func GetUserStatisticsItemsLists_DonutChart(identityType string,
return result, nil return result, nil
} }
groupedStatisticsItemsList, err := getStatisticsItemsListGrouped(8, statisticsItemsList, attributeIsNumerical, attributeCountsMap, false, nil, formatValuesFunction) groupedStatisticsDatumsList, err := getStatisticsDatumsListGrouped(8, statisticsDatumsList, attributeIsNumerical, attributeCountsMap, false, nil, formatValuesFunction)
if (err != nil) { return 0, nil, false, nil, err } if (err != nil) { return 0, nil, false, nil, err }
if (numberOfUnknownResponders != 0){ if (numberOfUnknownResponders != 0){
unknownValueItem := getUnknownValueItem() unknownValueDatum := getUnknownValueDatum()
statisticsItemsList = append(statisticsItemsList, unknownValueItem) statisticsDatumsList = append(statisticsDatumsList, unknownValueDatum)
groupedStatisticsItemsList = append(groupedStatisticsItemsList, unknownValueItem) groupedStatisticsDatumsList = append(groupedStatisticsDatumsList, unknownValueDatum)
} }
return totalAnalyzedUsers, statisticsItemsList, true, groupedStatisticsItemsList, nil return totalAnalyzedUsers, statisticsDatumsList, true, groupedStatisticsDatumsList, nil
} }
// This function will return a statistics items list of the following format: // This function will return a statistics datums list of the following format:
// "Label": Attribute name (Example: "Male") // "Label": Attribute name (Example: "Male")
// "Value": The number of users who responded with the attribute (in this example: "Male") // "Value": The number of users who responded with the attribute (in this example: "Male")
// //
// All users of provided identityType who are not disabled will be analyzed // All users of provided identityType who are not disabled will be analyzed
// -int: Number of analyzed users // -int: Number of analyzed users
// -[]StatisticsItem: Statistics items list (not sorted or grouped) // -[]statisticsDatum.StatisticsDatum: Statistics datums list (not sorted or grouped)
// -map[string]int: Response counts map (Response -> Number of responders) // -map[string]int: Response counts map (Response -> Number of responders)
// -int: Number of No Response/Unknown value responders // -int: Number of No Response/Unknown value responders
// -error // -error
func getProfileAttributeCountStatisticsItemsList(identityType string, func getProfileAttributeCountStatisticsDatumsList(identityType string,
networkType byte, networkType byte,
attributeName string, attributeName string,
formatLabelsFunction func(string)(string, error))(int, []StatisticsItem, map[string]int, int, error){ formatLabelsFunction func(string)(string, error))(int, []statisticsDatum.StatisticsDatum, map[string]int, int, error){
userIdentityHashesToAnalyzeList, err := getUserIdentityHashesToAnalyzeList(identityType) userIdentityHashesToAnalyzeList, err := getUserIdentityHashesToAnalyzeList(identityType)
if (err != nil) { return 0, nil, nil, 0, err } if (err != nil) { return 0, nil, nil, 0, err }
@ -509,7 +479,7 @@ func getProfileAttributeCountStatisticsItemsList(identityType string,
responseCountsMap[attributeValue] += 1 responseCountsMap[attributeValue] += 1
} }
statisticsItemsList := make([]StatisticsItem, 0, len(responseCountsMap)) statisticsDatumsList := make([]statisticsDatum.StatisticsDatum, 0, len(responseCountsMap))
for attributeResponse, numberOfUsers := range responseCountsMap{ for attributeResponse, numberOfUsers := range responseCountsMap{
@ -518,88 +488,88 @@ func getProfileAttributeCountStatisticsItemsList(identityType string,
attributeNumberOfUsersString := helpers.ConvertIntToString(numberOfUsers) attributeNumberOfUsersString := helpers.ConvertIntToString(numberOfUsers)
newStatisticsItem := StatisticsItem{ newStatisticsDatum := statisticsDatum.StatisticsDatum{
Label: attributeResponse, Label: attributeResponse,
LabelFormatted: attributeResponseFormatted, LabelFormatted: attributeResponseFormatted,
Value: float64(numberOfUsers), Value: float64(numberOfUsers),
ValueFormatted: attributeNumberOfUsersString, ValueFormatted: attributeNumberOfUsersString,
} }
statisticsItemsList = append(statisticsItemsList, newStatisticsItem) statisticsDatumsList = append(statisticsDatumsList, newStatisticsDatum)
} }
return totalAnalyzedUsers, statisticsItemsList, responseCountsMap, numberOfUnknownValueUsers, nil return totalAnalyzedUsers, statisticsDatumsList, responseCountsMap, numberOfUnknownValueUsers, nil
} }
func sortStatisticsItemsList(inputStatisticsItemsList []StatisticsItem, labelIsNumerical bool){ func sortStatisticsDatumsList(inputStatisticsDatumsList []statisticsDatum.StatisticsDatum, labelIsNumerical bool){
if (len(inputStatisticsItemsList) <= 1){ if (len(inputStatisticsDatumsList) <= 1){
return return
} }
if (labelIsNumerical == true){ if (labelIsNumerical == true){
// We sort the items by label values in ascending order // We sort the datums by label values in ascending order
// Example: Bar chart columns are ages, in order of youngest to oldest // Example: Bar chart columns are ages, in order of youngest to oldest
compareItemsFunction := func(itemA StatisticsItem, itemB StatisticsItem)int{ compareDatumsFunction := func(datumA statisticsDatum.StatisticsDatum, datumB statisticsDatum.StatisticsDatum)int{
itemALabel := itemA.Label datumALabel := datumA.Label
itemBLabel := itemB.Label datumBLabel := datumB.Label
itemAFloat64, err := helpers.ConvertStringToFloat64(itemALabel) datumAFloat64, err := helpers.ConvertStringToFloat64(datumALabel)
if (err != nil) { if (err != nil) {
panic("Invalid statistics item: Item Label is not float: " + itemALabel) panic("Invalid statistics datum: Datum Label is not float: " + datumALabel)
} }
itemBFloat64, err := helpers.ConvertStringToFloat64(itemBLabel) datumBFloat64, err := helpers.ConvertStringToFloat64(datumBLabel)
if (err != nil) { if (err != nil) {
panic("Invalid statistics item: Item Label is not float: " + itemBLabel) panic("Invalid statistics datum: Datum Label is not float: " + datumBLabel)
} }
if (itemAFloat64 == itemBFloat64){ if (datumAFloat64 == datumBFloat64){
return 0 return 0
} }
if (itemAFloat64 < itemBFloat64){ if (datumAFloat64 < datumBFloat64){
return -1 return -1
} }
return 1 return 1
} }
slices.SortFunc(inputStatisticsItemsList, compareItemsFunction) slices.SortFunc(inputStatisticsDatumsList, compareDatumsFunction)
return return
} }
// We sort the items by their values in descending order // We sort the datums by their values in descending order
compareItemsFunction := func(itemA StatisticsItem, itemB StatisticsItem)int{ compareDatumsFunction := func(datum1 statisticsDatum.StatisticsDatum, datum2 statisticsDatum.StatisticsDatum)int{
itemAValue := itemA.Value datum1Value := datum1.Value
itemBValue := itemB.Value datum2Value := datum2.Value
if (itemAValue == itemBValue){ if (datum1Value == datum2Value){
return 0 return 0
} }
if (itemAValue < itemBValue){ if (datum1Value < datum2Value){
return 1 return 1
} }
return -1 return -1
} }
slices.SortFunc(inputStatisticsItemsList, compareItemsFunction) slices.SortFunc(inputStatisticsDatumsList, compareDatumsFunction)
} }
// This function will group a statistics items list. // This function will group a statistics datums list.
// It will group Labels and their values to fit into a specified number of groups // It will group Labels and their values to fit into a specified number of groups
// Example: "1","2","3","4" -> "1-2", "3-4" // Example: "1","2","3","4" -> "1-2", "3-4"
//Inputs: //Inputs:
// -int: Maximum groups to create // -int: Maximum groups to create
// -[]StatisticsItem: Statistics items list to group // -[]statisticsDatum.StatisticsDatum: Statistics datums list to group
// -bool: Label is numerical // -bool: Label is numerical
// -If it is, we will group labels into groups of numbers. // -If it is, we will group labels into groups of numbers.
// -Otherwise, we will group all categories after the first maximumGroupsToCreate into a group called Other // -Otherwise, we will group all categories after the first maximumGroupsToCreate into a group called Other
@ -612,27 +582,27 @@ func sortStatisticsItemsList(inputStatisticsItemsList []StatisticsItem, labelIsN
// -Response Sums map (X-axis attribute response -> all Y-axis responses summed) (If yAxisIsAverage == true) // -Response Sums map (X-axis attribute response -> all Y-axis responses summed) (If yAxisIsAverage == true)
// -func(float64)(string, error): This is the function we use to format the values // -func(float64)(string, error): This is the function we use to format the values
//Outputs: //Outputs:
// -[]StatisticsItem: Grouped statistics items list // -[]statisticsDatum.StatisticsDatum: Grouped statistics datums list
// -error // -error
func getStatisticsItemsListGrouped(maximumGroupsToCreate int, func getStatisticsDatumsListGrouped(maximumGroupsToCreate int,
inputStatisticsItemsList []StatisticsItem, inputStatisticsDatumsList []statisticsDatum.StatisticsDatum,
labelIsNumerical bool, labelIsNumerical bool,
responseCountsMap map[string]int, responseCountsMap map[string]int,
valueIsAverage bool, valueIsAverage bool,
responseSumsMap map[string]float64, responseSumsMap map[string]float64,
formatValuesFunction func(float64)(string, error))([]StatisticsItem, error){ formatValuesFunction func(float64)(string, error))([]statisticsDatum.StatisticsDatum, error){
if (len(inputStatisticsItemsList) <= maximumGroupsToCreate){ if (len(inputStatisticsDatumsList) <= maximumGroupsToCreate){
return nil, errors.New("maximumGroupsToCreate is <= length of input statistics items list") return nil, errors.New("maximumGroupsToCreate is <= length of input statistics datums list")
} }
// We deep copy the statistics items list to retain the sorted version // We deep copy the statistics datums list to retain the sorted version
// We need to retain both versions because the user can view the raw or grouped data in the GUI // We need to retain both versions because the user can view the raw or grouped data in the GUI
statisticsItemsList := slices.Clone(inputStatisticsItemsList) statisticsDatumsList := slices.Clone(inputStatisticsDatumsList)
// We use this function to get the new value for a group of labels // We use this function to get the new value for a group of labels
getGroupValue := func(itemsToCombineList []StatisticsItem)(float64, error){ getGroupValue := func(datumsToCombineList []statisticsDatum.StatisticsDatum)(float64, error){
// This will count the total number of users who responded with the responses within this group // This will count the total number of users who responded with the responses within this group
// Example: Labels are "Blue", "Green", this variable will store the number of users who responded with either Blue or Green // Example: Labels are "Blue", "Green", this variable will store the number of users who responded with either Blue or Green
@ -642,22 +612,22 @@ func getStatisticsItemsListGrouped(maximumGroupsToCreate int,
// We only need to add to this sum if valueIsAverage == true // We only need to add to this sum if valueIsAverage == true
allReponsesSummed := float64(0) allReponsesSummed := float64(0)
for _, statisticsItem := range itemsToCombineList{ for _, statisticsDatum := range datumsToCombineList{
itemLabel := statisticsItem.Label datumLabel := statisticsDatum.Label
responderCount, exists := responseCountsMap[itemLabel] responderCount, exists := responseCountsMap[datumLabel]
if (exists == false){ if (exists == false){
return 0, errors.New("responseCountsMap missing label: " + itemLabel) return 0, errors.New("responseCountsMap missing label: " + datumLabel)
} }
totalRespondersCount += float64(responderCount) totalRespondersCount += float64(responderCount)
if (valueIsAverage == true){ if (valueIsAverage == true){
yAxisAttributeResponsesSum, exists := responseSumsMap[itemLabel] yAxisAttributeResponsesSum, exists := responseSumsMap[datumLabel]
if (exists == false){ if (exists == false){
return 0, errors.New("responseSumsMap missing label: " + itemLabel) return 0, errors.New("responseSumsMap missing label: " + datumLabel)
} }
allReponsesSummed += yAxisAttributeResponsesSum allReponsesSummed += yAxisAttributeResponsesSum
} }
@ -670,9 +640,9 @@ func getStatisticsItemsListGrouped(maximumGroupsToCreate int,
// The value is an average // The value is an average
// We need to find the average for all of the user responses for the labels in the input list // We need to find the average for all of the user responses for the labels in the input list
// The Values in the inputStatisticsItemsList are averages // The Values in the inputStatisticsDatumsList are averages
// We can't average out the averages, because that will not give us the true average // We can't average out the averages, because that will not give us the true average
// We have to use the original sums for all group items and average them // We have to use the original sums for all group datums and average them
if (totalRespondersCount == 0){ if (totalRespondersCount == 0){
return 0, errors.New("totalRespondersCount is 0.") return 0, errors.New("totalRespondersCount is 0.")
@ -685,82 +655,82 @@ func getStatisticsItemsListGrouped(maximumGroupsToCreate int,
if (labelIsNumerical == true){ if (labelIsNumerical == true){
maximumItemsPerCategory := int(math.Ceil(float64(len(statisticsItemsList))/float64(maximumGroupsToCreate))) maximumDatumsPerCategory := int(math.Ceil(float64(len(statisticsDatumsList))/float64(maximumGroupsToCreate)))
statisticsItemsListSublists, err := helpers.SplitListIntoSublists(statisticsItemsList, maximumItemsPerCategory) statisticsDatumsListSublists, err := helpers.SplitListIntoSublists(statisticsDatumsList, maximumDatumsPerCategory)
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
groupedItemsList := make([]StatisticsItem, 0, len(statisticsItemsListSublists)) groupedDatumsList := make([]statisticsDatum.StatisticsDatum, 0, len(statisticsDatumsListSublists))
for _, groupItemsListSublist := range statisticsItemsListSublists{ for _, groupDatumsListSublist := range statisticsDatumsListSublists{
if (len(groupItemsListSublist) == 1){ if (len(groupDatumsListSublist) == 1){
// Sometimes, a group with 1 item will be created // Sometimes, a group with 1 datum will be created
// This happens if the groups cannot be evenly divided, so there is a remainder of 1. // This happens if the groups cannot be evenly divided, so there is a remainder of 1.
// Example: 10->4 groups = 3, 3, 3, 1. // Example: 10->4 groups = 3, 3, 3, 1.
//TODO: Prevent this from happening so groups always have more than 1 subitem //TODO: Prevent this from happening so groups always have more than 1 subdatum
groupItem := groupItemsListSublist[0] groupDatum := groupDatumsListSublist[0]
groupedItemsList = append(groupedItemsList, groupItem) groupedDatumsList = append(groupedDatumsList, groupDatum)
continue continue
} }
finalIndex := len(groupItemsListSublist)-1 finalIndex := len(groupDatumsListSublist)-1
initialItem := groupItemsListSublist[0] initialDatum := groupDatumsListSublist[0]
finalItem := groupItemsListSublist[finalIndex] finalDatum := groupDatumsListSublist[finalIndex]
initialLabel := initialItem.Label initialLabel := initialDatum.Label
initialLabelFormatted := initialItem.LabelFormatted initialLabelFormatted := initialDatum.LabelFormatted
finalLabel := finalItem.Label finalLabel := finalDatum.Label
finalLabelFormatted := finalItem.LabelFormatted finalLabelFormatted := finalDatum.LabelFormatted
groupValue, err := getGroupValue(groupItemsListSublist) groupValue, err := getGroupValue(groupDatumsListSublist)
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
groupValueFormatted, err := formatValuesFunction(groupValue) groupValueFormatted, err := formatValuesFunction(groupValue)
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
newGroupStatisticsItem := StatisticsItem{ newGroupStatisticsDatum := statisticsDatum.StatisticsDatum{
Label: initialLabel + "-" + finalLabel, Label: initialLabel + "-" + finalLabel,
LabelFormatted: initialLabelFormatted + "-" + finalLabelFormatted, LabelFormatted: initialLabelFormatted + "-" + finalLabelFormatted,
Value: groupValue, Value: groupValue,
ValueFormatted: groupValueFormatted, ValueFormatted: groupValueFormatted,
} }
groupedItemsList = append(groupedItemsList, newGroupStatisticsItem) groupedDatumsList = append(groupedDatumsList, newGroupStatisticsDatum)
} }
return groupedItemsList, nil return groupedDatumsList, nil
} }
// Label is not numerical // Label is not numerical
// We combine all categories after the first maximumGroupsToCreate into a category called Other // We combine all categories after the first maximumGroupsToCreate into a category called Other
itemsToKeep := statisticsItemsList[:maximumGroupsToCreate] datumsToKeep := statisticsDatumsList[:maximumGroupsToCreate]
itemsToCombine := statisticsItemsList[maximumGroupsToCreate:] datumsToCombine := statisticsDatumsList[maximumGroupsToCreate:]
otherTranslated := translation.TranslateTextFromEnglishToMyLanguage("Other") otherTranslated := translation.TranslateTextFromEnglishToMyLanguage("Other")
otherGroupValue, err := getGroupValue(itemsToCombine) otherGroupValue, err := getGroupValue(datumsToCombine)
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
otherGroupValueFormatted, err := formatValuesFunction(otherGroupValue) otherGroupValueFormatted, err := formatValuesFunction(otherGroupValue)
if (err != nil) { return nil, err } if (err != nil) { return nil, err }
otherGroupItem := StatisticsItem{ otherGroupDatum := statisticsDatum.StatisticsDatum{
Label: "Other", Label: "Other",
LabelFormatted: otherTranslated, LabelFormatted: otherTranslated,
Value: otherGroupValue, Value: otherGroupValue,
ValueFormatted: otherGroupValueFormatted, ValueFormatted: otherGroupValueFormatted,
} }
groupedStatisticsItemsList := append(itemsToKeep, otherGroupItem) groupedStatisticsDatumsList := append(datumsToKeep, otherGroupDatum)
return groupedStatisticsItemsList, nil return groupedStatisticsDatumsList, nil
} }

View file

@ -0,0 +1,39 @@
//statisticsDatum implements the StatisticsDatum struct
package statisticsDatum
type StatisticsDatum struct{
// The label for the statistics datum
// For a bar chart, this represents the name of an X axis bar.
// For a donut chart, this represents the name of a donut slice
// Example: "Man", "100-200"
// This will never be translated, unless it is the Unknown/No Response value, in which case
// the label will be "Unknown"/"No Response" translated to the user's current app language
Label string
// This is the formatted, human readable version of the label
// This will be translated into the application language
// Sometimes, the LabelFormatted will be identical to Label
// This does not include units (Example: " days", " users")
// Example:
// -"1000000-2000000" -> "1 million-2 million"
LabelFormatted string
// The value corresponding to the label
// For a bar chart, this represents the Y axis value for a bar.
// For a donut chart, this represents the value (size) of one of the donut slices
// This will never be translated
// For example, the value could be 500 if 500 men responded Yes.
Value float64
// This is the formatted version of the value
// This does not include units (Example: " days", " users")
// Examples:
// -5 -> "5/10"
// -1500000000000 -> "1.5 trillion"
ValueFormatted string
}