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
* 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 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*

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
--- | --- | ---
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/profiles/attributeDisplay"
import "seekia/internal/profiles/userStatistics"
import "seekia/internal/statisticsDatum"
import "errors"
import "image"
@ -146,13 +147,13 @@ func setViewUserAttributeStatisticsPage_BarChart(
showYAxisPercentage bool,
statisticsReady bool,
anyUsersExist bool,
statisticsItemsList []userStatistics.StatisticsItem,
statisticsDatumsList []statisticsDatum.StatisticsDatum,
groupingPerformed bool,
groupedStatisticsItemsList []userStatistics.StatisticsItem,
groupedStatisticsDatumsList []statisticsDatum.StatisticsDatum,
chartImage image.Image,
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)
if (err != nil) {
@ -264,7 +265,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
}
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(){
@ -336,7 +337,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
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) {
functionCompleteBoolMutex.Lock()
@ -350,7 +351,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
return
}
if (len(statisticsItemsList) == 0){
if (len(statisticsDatumsList) == 0){
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
@ -363,14 +364,14 @@ func setViewUserAttributeStatisticsPage_BarChart(
return
}
getChartStatisticsItemsList := func()[]userStatistics.StatisticsItem{
getChartStatisticsDatumsList := func()[]statisticsDatum.StatisticsDatum{
if (groupingPerformed == false){
return statisticsItemsList
return statisticsDatumsList
}
return groupedStatisticsItemsList
return groupedStatisticsDatumsList
}
chartStatisticsItemsList := getChartStatisticsItemsList()
chartStatisticsDatumsList := getChartStatisticsDatumsList()
getChartTitle := func()string{
@ -401,7 +402,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
chartTitle := getChartTitle()
newChartImage, err := createCharts.CreateBarChart(chartTitle, chartStatisticsItemsList, formatYAxisValuesFunction, true, yAxisUnits)
newChartImage, err := createCharts.CreateBarChart(chartTitle, chartStatisticsDatumsList, formatYAxisValuesFunction, true, yAxisUnits)
if (err != nil) {
functionCompleteBoolMutex.Lock()
@ -421,7 +422,7 @@ func setViewUserAttributeStatisticsPage_BarChart(
pageHasChanged := checkIfPageHasChangedFunction()
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,
statisticsReady bool,
anyUsersExist bool,
statisticsItemsList []userStatistics.StatisticsItem,
statisticsDatumsList []statisticsDatum.StatisticsDatum,
groupingPerformed bool,
groupedStatisticsItemsList []userStatistics.StatisticsItem,
groupedStatisticsDatumsList []statisticsDatum.StatisticsDatum,
chartImage image.Image,
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)
if (err != nil) {
@ -545,7 +546,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
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(){
@ -616,7 +617,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
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) {
functionCompleteBoolMutex.Lock()
@ -630,7 +631,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
return
}
if (len(statisticsItemsList) == 0){
if (len(statisticsDatumsList) == 0){
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
@ -643,14 +644,14 @@ func setViewUserAttributeStatisticsPage_DonutChart(
return
}
getChartStatisticsItemsList := func()[]userStatistics.StatisticsItem{
getChartStatisticsDatumsList := func()[]statisticsDatum.StatisticsDatum{
if (groupingPerformed == false){
return statisticsItemsList
return statisticsDatumsList
}
return groupedStatisticsItemsList
return groupedStatisticsDatumsList
}
chartStatisticsItemsList := getChartStatisticsItemsList()
chartStatisticsDatumsList := getChartStatisticsDatumsList()
getChartTitle := func()string{
@ -672,7 +673,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
chartTitle := getChartTitle()
newChartImage, err := createCharts.CreateDonutChart(chartTitle, chartStatisticsItemsList)
newChartImage, err := createCharts.CreateDonutChart(chartTitle, chartStatisticsDatumsList)
if (err != nil){
functionCompleteBoolMutex.Lock()
@ -692,7 +693,7 @@ func setViewUserAttributeStatisticsPage_DonutChart(
pageHasChanged := checkIfPageHasChangedFunction()
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,
rightColumnName string,
showPercentageColumn bool,
statisticsItemsList []userStatistics.StatisticsItem,
statisticsDatumsList []statisticsDatum.StatisticsDatum,
groupingPerformed bool,
groupedStatisticsItemsList []userStatistics.StatisticsItem,
groupedStatisticsDatumsList []statisticsDatum.StatisticsDatum,
showGroupedStatistics bool,
attributeColumnUnits string,
rightColumnUnits string,
@ -954,13 +955,13 @@ func setViewUserStatisticsDataPage(
if (showGroupedStatistics == false){
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)
} else {
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)
@ -968,14 +969,14 @@ func setViewUserStatisticsDataPage(
header.Add(widget.NewSeparator())
}
getStatisticsItemsListToShow := func()[]userStatistics.StatisticsItem{
getStatisticsDatumsListToShow := func()[]statisticsDatum.StatisticsDatum{
if (groupingPerformed == true && showGroupedStatistics == true){
return groupedStatisticsItemsList
return groupedStatisticsDatumsList
}
return statisticsItemsList
return statisticsDatumsList
}
statisticsItemsToShowList := getStatisticsItemsListToShow()
statisticsDatumsToShowList := getStatisticsDatumsListToShow()
getStatisticsDataGrid := func()(*fyne.Container, error){
@ -983,38 +984,38 @@ func setViewUserStatisticsDataPage(
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))
rightColumnValuesList := make([]string, 0, len(statisticsItemsToShowList))
percentageColumnValuesList := make([]string, 0, len(statisticsItemsToShowList))
attributeColumnValuesList := make([]string, 0, len(statisticsDatumsToShowList))
rightColumnValuesList := make([]string, 0, len(statisticsDatumsToShowList))
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)
rightColumnValuesList = append(rightColumnValuesList, itemValueFormatted)
attributeColumnValuesList = append(attributeColumnValuesList, datumLabelFormatted)
rightColumnValuesList = append(rightColumnValuesList, datumValueFormatted)
if (showPercentageColumn == true){
itemValue := item.Value
datumValue := datum.Value
getValuePercentage := func()float64{
if (allValuesSummed == 0){
return 0
}
valuePercentage := (itemValue/allValuesSummed)*100
valuePercentage := (datumValue/allValuesSummed)*100
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/theme"
import "fyne.io/fyne/v2/widget"
import "fyne.io/fyne/v2/canvas"
import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases"
import "seekia/resources/geneticReferences/traits"
import "seekia/internal/appMemory"
import "seekia/internal/createCharts"
import "seekia/internal/encoding"
import "seekia/internal/genetics/geneticAnalysis"
import "seekia/internal/genetics/myGenomes"
import "seekia/internal/genetics/myPeople"
import "seekia/internal/genetics/readGeneticAnalysis"
import "seekia/internal/helpers"
import "seekia/internal/statisticsDatum"
import "slices"
import "image"
import "errors"
@ -1006,7 +1010,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso
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 }
getRiskScoreLabelText := func()string{
@ -1116,25 +1120,27 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
})
diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, diseaseNameInfoButton, layout.NewSpacer())
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabel1 := widget.NewLabel("")
emptyLabel2 := widget.NewLabel("")
offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score")
emptyLabelC := widget.NewLabel("")
emptyLabelD := widget.NewLabel("")
emptyLabel3 := widget.NewLabel("")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
viewGenomePairButtonsColumn := container.NewVBox(emptyLabelA, widget.NewSeparator())
pairNameColumn := container.NewVBox(emptyLabelB, widget.NewSeparator())
viewGenomePairButtonsColumn := container.NewVBox(emptyLabel1, widget.NewSeparator())
pairNameColumn := container.NewVBox(emptyLabel2, widget.NewSeparator())
offspringRiskScoreColumn := container.NewVBox(offspringRiskScoreLabel, widget.NewSeparator())
viewLifetimeRiskButtonsColumn := container.NewVBox(emptyLabelC, widget.NewSeparator())
viewOffspringLociButtonsColumn := container.NewVBox(emptyLabelD, widget.NewSeparator())
viewSampleOffspringsChartButtonsColumn := container.NewVBox(emptyLabel3, 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{
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 }
getRiskScoreLabelText := func()string{
@ -1158,6 +1164,10 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
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(){
diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName)
@ -1190,12 +1200,15 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
viewGenomePairButtonsColumn.Add(viewGenomePairButton)
pairNameColumn.Add(genomePairNameLabel)
offspringRiskScoreColumn.Add(offspringRiskScoreLabel)
viewSampleOffspringsChartButtonsColumn.Add(viewSampleOffspringsChartButton)
viewLifetimeRiskButtonsColumn.Add(viewOffspringLifetimeRiskButton)
viewOffspringLociButtonsColumn.Add(viewOffspringLociButton)
viewGenomePairButtonsColumn.Add(widget.NewSeparator())
pairNameColumn.Add(widget.NewSeparator())
offspringRiskScoreColumn.Add(widget.NewSeparator())
viewSampleOffspringsChartButtonsColumn.Add(widget.NewSeparator())
viewLifetimeRiskButtonsColumn.Add(widget.NewSeparator())
viewOffspringLociButtonsColumn.Add(widget.NewSeparator())
return nil
@ -1221,7 +1234,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
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)
@ -1314,7 +1327,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy
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 }
getPersonRiskScoreLabelText := func()string{
@ -1408,7 +1421,7 @@ func setViewCouplePolygenicDiseaseLociPage(window fyne.Window, person1Name strin
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){
setErrorEncounteredPage(window, err, previousPage)
return
@ -1719,6 +1732,133 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
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()){

View file

@ -935,7 +935,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso
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 }
getPersonRiskScoreLabelText := func()string{
@ -1096,7 +1096,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
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 }
getRiskScoreLabelText := func()string{
@ -1418,7 +1418,7 @@ func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnal
return
}
locusRiskWeightIsKnown, genomeLocusRiskWeight, _, _, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier)
locusRiskWeightIsKnown, genomeLocusRiskWeight, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(geneticAnalysisObject, diseaseName, locusIdentifier, genomeIdentifier)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -1484,7 +1484,7 @@ func setViewPersonGenomePolygenicDiseaseLociPage(window fyne.Window, geneticAnal
locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex)
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 }
getGenomeLocusRiskWeightText := func()string{
@ -1635,7 +1635,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
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 }
getGenomeRiskWeightText := func()string{
@ -1654,7 +1654,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
getGenomeOddsRatioText := func()string{
if (genomeOddsRatioKnown == false){
if (genomeOddsRatioIsKnown == false){
result := translate("Unknown")
return result

View file

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

View file

@ -5,7 +5,7 @@
package createCharts
import "seekia/internal/profiles/userStatistics"
import "seekia/internal/statisticsDatum"
import goChart "github.com/wcharczuk/go-chart/v2"
import "github.com/wcharczuk/go-chart/v2/drawing"
@ -15,7 +15,7 @@ import "errors"
// Inputs:
// -string: Chart title
// -[]userStatistics.StatisticsItem: Statistics items list
// -[]statisticsDatum.StatisticsDatum: Statistics datums list
// -func(float64)(string, error): formatYAxisValuesFunction
// -This will take values such as 1000000 and turn them to "1 million"
// -bool: Y-axis units exist
@ -23,38 +23,38 @@ import "errors"
// Outputs:
// -image.Image
// -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) {
return nil, errors.New("CreateBarChart called with empty chartStatisticsItemsList")
if (len(chartStatisticsDatumsList) == 0) {
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
_, err := formatYAxisValuesFunction(itemValue)
_, err := formatYAxisValuesFunction(datumValue)
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{
Label: itemLabel,
Value: itemValue,
Label: datumLabel,
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
// Thus, we must add an empty item
// This package cannot create bar charts with only 1 datum
// Thus, we must add an empty datum
newChartValue := goChart.Value{
Style: goChart.Style{
@ -65,7 +65,7 @@ func CreateBarChart(chartTitle string, chartStatisticsItemsList []userStatistics
Value: .001,
}
chartItemsList = append(chartItemsList, newChartValue)
chartDatumsList = append(chartDatumsList, newChartValue)
}
chartStyleObject := goChart.Style{
@ -110,7 +110,7 @@ func CreateBarChart(chartTitle string, chartStatisticsItemsList []userStatistics
Background: chartStyleObject,
Height: 500,
BarWidth: 60,
Bars: chartItemsList,
Bars: chartDatumsList,
YAxis: yAxisObject,
}
@ -123,28 +123,28 @@ func CreateBarChart(chartTitle string, chartStatisticsItemsList []userStatistics
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
itemValue := statisticsItem.Value
datumValue := statisticsDatum.Value
newChartValue := goChart.Value{
Label: itemLabel,
Value: itemValue,
Label: datumLabel,
Value: datumValue,
}
chartItemsList = append(chartItemsList, newChartValue)
chartDatumsList = append(chartDatumsList, newChartValue)
}
chartStyleObject := goChart.Style{
@ -166,10 +166,10 @@ func CreateDonutChart(chartTitle string, chartStatisticsItemsList []userStatisti
TitleStyle: titleStyleObject,
Background: chartStyleObject,
Height: 500,
Values: chartItemsList,
Values: chartDatumsList,
}
if (len(chartItemsList) == 1){
if (len(chartDatumsList) == 1){
// Default is transparent, we need to add color
sliceStyleObject := goChart.Style{

View file

@ -98,6 +98,11 @@ type PersonGenomePolygenicDiseaseInfo struct{
// This should be len(LociInfoList)
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 a number between 1-10
RiskScore int
@ -110,11 +115,6 @@ type PersonGenomePolygenicDiseaseInfo 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
// A higher risk weight means more risk of getting the disease
RiskWeight int
@ -141,10 +141,10 @@ type PersonTraitInfo struct{
type PersonGenomeTraitInfo struct{
// This should be len(RulesList)
// This should be len(GenomePassesRulesMap)
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
// Map Structure: Locus rsID -> Locus Value
LocusValuesMap map[int64]locusValue.LocusValue
@ -262,33 +262,38 @@ type OffspringGenomePairPolygenicDiseaseInfo struct{
// This should be len(DiseaseLociList)
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
OffspringRiskScore int
OffspringAverageRiskScore int
// A map of the offspring's locus information
// Map Structure: Locus Identifier -> 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{
// 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
OffspringRiskWeight int
OffspringAverageRiskWeight int
// This is true if any of the 100 prospective offspring had a known odds ratio for this locus
OffspringOddsRatioIsKnown bool
// This value represent's the offspring's average odds ratio for the disease locus
// A value <1 denotes a lesser risk, a value >1 denotes an increased risk
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
// If the sum is <0, we say the ratio is probably lower
// If the sum is >0, we say the ratio is probably higher
OffspringUnknownOddsRatiosWeightSum int
OffspringAverageUnknownOddsRatiosWeightSum int
}

View file

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

View file

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

View file

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

View file

@ -14,6 +14,7 @@ import "seekia/internal/profiles/calculatedAttributes"
import "seekia/internal/profiles/profileStorage"
import "seekia/internal/profiles/attributeDisplay"
import "seekia/internal/translation"
import "seekia/internal/statisticsDatum"
import "slices"
import "strings"
@ -21,61 +22,27 @@ import "errors"
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:
// -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
// -[]StatisticsItem: Grouped items list
// -[]statisticsDatum.StatisticsDatum: Grouped datums list
// -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
// -Example: "1000000" -> "1 million"
// -error
func GetUserStatisticsItemsLists_BarChart(identityType string,
func GetUserStatisticsDatumsLists_BarChart(identityType string,
networkType byte,
xAxisAttribute string,
xAxisIsNumerical bool,
formatXAxisValuesFunction func(string)(string, error),
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)
if (isValid == false){
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{
@ -95,16 +62,16 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
//Outputs:
// -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)
// -bool: yAxisIsAverage
// -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
// -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.
// -StatisticsItem: The unknown value item.
// -statisticsDatum.StatisticsDatum: The unknown value datum.
// -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 ..."
// This will allow viewing of choice attribute probabilities
@ -113,8 +80,8 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
if (yAxisAttribute == "Number Of Users"){
totalAnalyzedUsers, statisticsItemsList, responseCountsMap, numberOfUnknownValueUsers, err := getProfileAttributeCountStatisticsItemsList(identityType, networkType, xAxisAttribute, formatXAxisValuesFunction)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, StatisticsItem{}, err }
totalAnalyzedUsers, statisticsDatumsList, responseCountsMap, numberOfUnknownValueUsers, err := getProfileAttributeCountStatisticsDatumsList(identityType, networkType, xAxisAttribute, formatXAxisValuesFunction)
if (err != nil) { return 0, nil, nil, false, nil, nil, false, statisticsDatum.StatisticsDatum{}, err }
formatValuesFunction := func(input float64)(string, error){
@ -126,23 +93,23 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
}
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)
unknownItem := StatisticsItem{
unknownDatum := statisticsDatum.StatisticsDatum{
Label: xAxisUnknownLabel,
LabelFormatted: xAxisUnknownLabel,
Value: float64(numberOfUnknownValueUsers),
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)
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 ")
if (yAxisIsAverage == true){
@ -153,69 +120,72 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
attributeTitle := strings.TrimPrefix(yAxisAttribute, "Average ")
if (attributeTitle == "Wealth"){
switch attributeTitle{
case "Wealth":{
return "WealthInGold"
}
if (attributeTitle == "23andMe Neanderthal Variants"){
case "23andMe Neanderthal Variants":{
return "23andMe_NeanderthalVariants"
}
if (attributeTitle == "Body Fat"){
case "Body Fat":{
return "BodyFat"
}
if (attributeTitle == "Body Muscle"){
case "Body Muscle":{
return "BodyMuscle"
}
if (attributeTitle == "Fruit Rating"){
case "Fruit Rating":{
return "FruitRating"
}
if (attributeTitle == "Vegetables Rating"){
case "Vegetables Rating":{
return "VegetablesRating"
}
if (attributeTitle == "Nuts Rating"){
case "Nuts Rating":{
return "NutsRating"
}
if (attributeTitle == "Grains Rating"){
case "Grains Rating":{
return "GrainsRating"
}
if (attributeTitle == "Dairy Rating"){
case "Dairy Rating":{
return "DairyRating"
}
if (attributeTitle == "Seafood Rating"){
case "Seafood Rating":{
return "SeafoodRating"
}
if (attributeTitle == "Beef Rating"){
case "Beef Rating":{
return "BeefRating"
}
if (attributeTitle == "Pork Rating"){
case "Pork Rating":{
return "PorkRating"
}
if (attributeTitle == "Poultry Rating"){
case "Poultry Rating":{
return "PoultryRating"
}
if (attributeTitle == "Eggs Rating"){
case "Eggs Rating":{
return "EggsRating"
}
if (attributeTitle == "Beans Rating"){
case "Beans Rating":{
return "BeansRating"
}
if (attributeTitle == "Alcohol Frequency"){
case "Alcohol Frequency":{
return "AlcoholFrequency"
}
if (attributeTitle == "Tobacco Frequency"){
case "Tobacco Frequency":{
return "TobaccoFrequency"
}
if (attributeTitle == "Cannabis Frequency"){
case "Cannabis Frequency":{
return "CannabisFrequency"
}
if (attributeTitle == "Pets Rating"){
case "Pets Rating":{
return "PetsRating"
}
if (attributeTitle == "Dogs Rating"){
case "Dogs Rating":{
return "DogsRating"
}
if (attributeTitle == "Cats Rating"){
case "Cats Rating":{
return "CatsRating"
}
}
return attributeTitle
}
@ -240,19 +210,19 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
for _, userIdentityHash := range userIdentityHashesToAnalyzeList{
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){
continue
}
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){
continue
}
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){
// This user did not respond to the attribute we are getting the average for
// We will not add them to the statistics maps
@ -263,11 +233,11 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
userAttributeToGetAverageForValueFloat64, err := helpers.ConvertStringToFloat64(userAttributeToGetAverageForValue)
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)
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){
// 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
@ -283,18 +253,18 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
}
_, _, 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{
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]
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)
@ -302,16 +272,16 @@ func GetUserStatisticsItemsLists_BarChart(identityType string,
averageValueString := helpers.ConvertFloat64ToStringRounded(averageValue, yAxisRoundingPrecision)
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,
LabelFormatted: attributeResponseFormatted,
Value: averageValue,
ValueFormatted: averageValueFormatted,
}
statisticsItemsList = append(statisticsItemsList, newStatisticsItem)
statisticsDatumsList = append(statisticsDatumsList, newStatisticsDatum)
}
// 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){
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)
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
// will represent the average wealth for users who did not provide Height on their profile.
unknownStatisticsItem := StatisticsItem{
unknownStatisticsDatum := statisticsDatum.StatisticsDatum{
Label: xAxisUnknownLabel,
LabelFormatted: xAxisUnknownLabel,
Value: unknownResponsesAverage,
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 }
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
if (len(statisticsItemsList) <= 10){
if (len(statisticsDatumsList) <= 10){
// No grouping needed. We are done.
if (includeUnknownItem == true){
statisticsItemsList = append(statisticsItemsList, unknownValueItem)
if (includeUnknownDatum == true){
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 (includeUnknownItem == true){
statisticsItemsList = append(statisticsItemsList, unknownValueItem)
groupedStatisticsItemsList = append(groupedStatisticsItemsList, unknownValueItem)
if (includeUnknownDatum == true){
statisticsDatumsList = append(statisticsDatumsList, unknownValueDatum)
groupedStatisticsDatumsList = append(groupedStatisticsDatumsList, unknownValueDatum)
}
return totalAnalyzedUsers, statisticsItemsList, true, groupedStatisticsItemsList, formatValuesFunction, nil
return totalAnalyzedUsers, statisticsDatumsList, true, groupedStatisticsDatumsList, formatValuesFunction, nil
}
//Outputs:
// -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
// -[]StatisticsItem: Grouped items list
// -[]statisticsDatum.StatisticsDatum: Grouped datums list
// -error
func GetUserStatisticsItemsLists_DonutChart(identityType string,
func GetUserStatisticsDatumsLists_DonutChart(identityType string,
networkType byte,
attributeToAnalyze string,
attributeIsNumerical bool,
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)
if (isValid == false){
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 }
sortStatisticsItemsList(statisticsItemsList, attributeIsNumerical)
sortStatisticsDatumsList(statisticsDatumsList, attributeIsNumerical)
getUnknownValueItem := func()StatisticsItem{
getUnknownValueDatum := func()statisticsDatum.StatisticsDatum{
numberOfUnknownRespondersString := helpers.ConvertIntToString(numberOfUnknownResponders)
unknownValueItem := StatisticsItem{
unknownValueDatum := statisticsDatum.StatisticsDatum{
Label: unknownLabelTranslated,
LabelFormatted: unknownLabelTranslated,
Value: float64(numberOfUnknownResponders),
ValueFormatted: numberOfUnknownRespondersString,
}
return unknownValueItem
return unknownValueDatum
}
if (len(statisticsItemsList) <= 8){
if (len(statisticsDatumsList) <= 8){
// No grouping needed.
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){
@ -443,35 +413,35 @@ func GetUserStatisticsItemsLists_DonutChart(identityType string,
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 (numberOfUnknownResponders != 0){
unknownValueItem := getUnknownValueItem()
unknownValueDatum := getUnknownValueDatum()
statisticsItemsList = append(statisticsItemsList, unknownValueItem)
groupedStatisticsItemsList = append(groupedStatisticsItemsList, unknownValueItem)
statisticsDatumsList = append(statisticsDatumsList, unknownValueDatum)
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")
// "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
// -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)
// -int: Number of No Response/Unknown value responders
// -error
func getProfileAttributeCountStatisticsItemsList(identityType string,
func getProfileAttributeCountStatisticsDatumsList(identityType string,
networkType byte,
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)
if (err != nil) { return 0, nil, nil, 0, err }
@ -509,7 +479,7 @@ func getProfileAttributeCountStatisticsItemsList(identityType string,
responseCountsMap[attributeValue] += 1
}
statisticsItemsList := make([]StatisticsItem, 0, len(responseCountsMap))
statisticsDatumsList := make([]statisticsDatum.StatisticsDatum, 0, len(responseCountsMap))
for attributeResponse, numberOfUsers := range responseCountsMap{
@ -518,88 +488,88 @@ func getProfileAttributeCountStatisticsItemsList(identityType string,
attributeNumberOfUsersString := helpers.ConvertIntToString(numberOfUsers)
newStatisticsItem := StatisticsItem{
newStatisticsDatum := statisticsDatum.StatisticsDatum{
Label: attributeResponse,
LabelFormatted: attributeResponseFormatted,
Value: float64(numberOfUsers),
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
}
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
compareItemsFunction := func(itemA StatisticsItem, itemB StatisticsItem)int{
compareDatumsFunction := func(datumA statisticsDatum.StatisticsDatum, datumB statisticsDatum.StatisticsDatum)int{
itemALabel := itemA.Label
itemBLabel := itemB.Label
datumALabel := datumA.Label
datumBLabel := datumB.Label
itemAFloat64, err := helpers.ConvertStringToFloat64(itemALabel)
datumAFloat64, err := helpers.ConvertStringToFloat64(datumALabel)
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) {
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
}
if (itemAFloat64 < itemBFloat64){
if (datumAFloat64 < datumBFloat64){
return -1
}
return 1
}
slices.SortFunc(inputStatisticsItemsList, compareItemsFunction)
slices.SortFunc(inputStatisticsDatumsList, compareDatumsFunction)
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
itemBValue := itemB.Value
datum1Value := datum1.Value
datum2Value := datum2.Value
if (itemAValue == itemBValue){
if (datum1Value == datum2Value){
return 0
}
if (itemAValue < itemBValue){
if (datum1Value < datum2Value){
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
// Example: "1","2","3","4" -> "1-2", "3-4"
//Inputs:
// -int: Maximum groups to create
// -[]StatisticsItem: Statistics items list to group
// -[]statisticsDatum.StatisticsDatum: Statistics datums list to group
// -bool: Label is numerical
// -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
@ -612,27 +582,27 @@ func sortStatisticsItemsList(inputStatisticsItemsList []StatisticsItem, labelIsN
// -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
//Outputs:
// -[]StatisticsItem: Grouped statistics items list
// -[]statisticsDatum.StatisticsDatum: Grouped statistics datums list
// -error
func getStatisticsItemsListGrouped(maximumGroupsToCreate int,
inputStatisticsItemsList []StatisticsItem,
func getStatisticsDatumsListGrouped(maximumGroupsToCreate int,
inputStatisticsDatumsList []statisticsDatum.StatisticsDatum,
labelIsNumerical bool,
responseCountsMap map[string]int,
valueIsAverage bool,
responseSumsMap map[string]float64,
formatValuesFunction func(float64)(string, error))([]StatisticsItem, error){
formatValuesFunction func(float64)(string, error))([]statisticsDatum.StatisticsDatum, error){
if (len(inputStatisticsItemsList) <= maximumGroupsToCreate){
return nil, errors.New("maximumGroupsToCreate is <= length of input statistics items list")
if (len(inputStatisticsDatumsList) <= maximumGroupsToCreate){
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
statisticsItemsList := slices.Clone(inputStatisticsItemsList)
statisticsDatumsList := slices.Clone(inputStatisticsDatumsList)
// 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
// 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
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){
return 0, errors.New("responseCountsMap missing label: " + itemLabel)
return 0, errors.New("responseCountsMap missing label: " + datumLabel)
}
totalRespondersCount += float64(responderCount)
if (valueIsAverage == true){
yAxisAttributeResponsesSum, exists := responseSumsMap[itemLabel]
yAxisAttributeResponsesSum, exists := responseSumsMap[datumLabel]
if (exists == false){
return 0, errors.New("responseSumsMap missing label: " + itemLabel)
return 0, errors.New("responseSumsMap missing label: " + datumLabel)
}
allReponsesSummed += yAxisAttributeResponsesSum
}
@ -670,9 +640,9 @@ func getStatisticsItemsListGrouped(maximumGroupsToCreate int,
// The value is an average
// 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 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){
return 0, errors.New("totalRespondersCount is 0.")
@ -685,82 +655,82 @@ func getStatisticsItemsListGrouped(maximumGroupsToCreate int,
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 }
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){
// Sometimes, a group with 1 item will be created
if (len(groupDatumsListSublist) == 1){
// 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.
// 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
}
finalIndex := len(groupItemsListSublist)-1
finalIndex := len(groupDatumsListSublist)-1
initialItem := groupItemsListSublist[0]
finalItem := groupItemsListSublist[finalIndex]
initialDatum := groupDatumsListSublist[0]
finalDatum := groupDatumsListSublist[finalIndex]
initialLabel := initialItem.Label
initialLabelFormatted := initialItem.LabelFormatted
initialLabel := initialDatum.Label
initialLabelFormatted := initialDatum.LabelFormatted
finalLabel := finalItem.Label
finalLabelFormatted := finalItem.LabelFormatted
finalLabel := finalDatum.Label
finalLabelFormatted := finalDatum.LabelFormatted
groupValue, err := getGroupValue(groupItemsListSublist)
groupValue, err := getGroupValue(groupDatumsListSublist)
if (err != nil) { return nil, err }
groupValueFormatted, err := formatValuesFunction(groupValue)
if (err != nil) { return nil, err }
newGroupStatisticsItem := StatisticsItem{
newGroupStatisticsDatum := statisticsDatum.StatisticsDatum{
Label: initialLabel + "-" + finalLabel,
LabelFormatted: initialLabelFormatted + "-" + finalLabelFormatted,
Value: groupValue,
ValueFormatted: groupValueFormatted,
}
groupedItemsList = append(groupedItemsList, newGroupStatisticsItem)
groupedDatumsList = append(groupedDatumsList, newGroupStatisticsDatum)
}
return groupedItemsList, nil
return groupedDatumsList, nil
}
// Label is not numerical
// 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")
otherGroupValue, err := getGroupValue(itemsToCombine)
otherGroupValue, err := getGroupValue(datumsToCombine)
if (err != nil) { return nil, err }
otherGroupValueFormatted, err := formatValuesFunction(otherGroupValue)
if (err != nil) { return nil, err }
otherGroupItem := StatisticsItem{
otherGroupDatum := statisticsDatum.StatisticsDatum{
Label: "Other",
LabelFormatted: otherTranslated,
Value: otherGroupValue,
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
}