seekia/gui/statisticsGui.go

1152 lines
39 KiB
Go
Raw Permalink Normal View History

package gui
// statisticsGui.go implements pages to view statistics about the Seekia network
import "fyne.io/fyne/v2"
import "fyne.io/fyne/v2/canvas"
import "fyne.io/fyne/v2/container"
import "fyne.io/fyne/v2/data/binding"
import "fyne.io/fyne/v2/layout"
import "fyne.io/fyne/v2/theme"
import "fyne.io/fyne/v2/widget"
import "seekia/internal/appMemory"
import "seekia/internal/badgerDatabase"
import "seekia/internal/createCharts"
import "seekia/internal/globalSettings"
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"
import "strings"
import "time"
import "sync"
func setNetworkStatisticsPage(window fyne.Window, previousPage func()){
currentPage := func(){setNetworkStatisticsPage(window, previousPage)}
title := getPageTitleCentered("Network Statistics")
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered(translate("View statistics about the Seekia network."))
mateStatisticsButton := widget.NewButton("Mate Statistics", func(){
setNetworkUserStatisticsPage(window, "Mate", currentPage)
})
hostStatisticsButton := widget.NewButton("Host Statistics", func(){
setNetworkUserStatisticsPage(window, "Host", currentPage)
})
moderatorStatisticsButton := widget.NewButton("Moderator Statistics", func(){
setNetworkUserStatisticsPage(window, "Moderator", currentPage)
})
messageStatisticsButton := widget.NewButton("Message Statistics", func(){
//TODO
showUnderConstructionDialog(window)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, mateStatisticsButton, hostStatisticsButton, moderatorStatisticsButton, messageStatisticsButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), buttonsGrid)
setPageContent(page, window)
}
func setNetworkUserStatisticsPage(window fyne.Window, identityType string, previousPage func()){
if (identityType != "Mate" && identityType != "Host" && identityType != "Moderator"){
setErrorEncounteredPage(window, errors.New("setUserStatisticsPage called with invalid identityType: " + identityType), previousPage)
return
}
setLoadingScreen(window, identityType + " Statistics", "Loading " + identityType + " statistics page...")
currentPage := func(){setNetworkUserStatisticsPage(window, identityType, previousPage)}
title := getPageTitleCentered(identityType + " Statistics")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Below are statistics about " + identityType + " profiles and users.")
description2 := widget.NewLabel("Be aware that these statistics may not represent the entire Seekia network.")
description2HelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setNetworkStatisticsInaccuracyWarningPage(window, currentPage)
})
description2Row := container.NewHBox(layout.NewSpacer(), description2, description2HelpButton, layout.NewSpacer())
profileHashesList, err := badgerDatabase.GetAllProfileHashes(identityType)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfUserProfiles := len(profileHashesList)
profileIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes(identityType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfUserIdentities := len(profileIdentityHashesList)
numberOfUserProfilesString := helpers.ConvertIntToString(numberOfUserProfiles)
numberOfUserProfilesLabel := widget.NewLabel("Number of " + identityType + " Profiles:")
numberOfUserProfilesText := getBoldLabel(numberOfUserProfilesString)
numberOfUserProfilesRow := container.NewHBox(layout.NewSpacer(), numberOfUserProfilesLabel, numberOfUserProfilesText, layout.NewSpacer())
numberOfUserIdentitiesString := helpers.ConvertIntToString(numberOfUserIdentities)
numberOfUserIdentitiesLabel := widget.NewLabel("Number of " + identityType + " Identities:")
numberOfUserIdentitiesText := getBoldLabel(numberOfUserIdentitiesString)
numberOfUserIdentitiesRow := container.NewHBox(layout.NewSpacer(), numberOfUserIdentitiesLabel, numberOfUserIdentitiesText, layout.NewSpacer())
//TODO: add % of users who are disabled.
//TODO: Add more statistics
getIdentityTypeDefaultAttribute := func()string{
if (identityType == "Mate"){
return "Age"
}
if (identityType == "Host"){
return "SeekiaVersion"
}
// identityType == "Moderator"
return "IdentityScore"
}
identityTypeDefaultAttribute := getIdentityTypeDefaultAttribute()
attributeStatisticsButton := getWidgetCentered(widget.NewButtonWithIcon("View Attribute Statistics", theme.VisibilityIcon(), func(){
setViewUserAttributeStatisticsPage_BarChart(window, identityType, identityTypeDefaultAttribute, "Number Of Users", " users", true, false, false, nil, false, nil, nil, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2Row, widget.NewSeparator(), numberOfUserProfilesRow, numberOfUserIdentitiesRow, attributeStatisticsButton)
setPageContent(page, window)
}
func setViewUserAttributeStatisticsPage_BarChart(
window fyne.Window,
identityType string,
xAxisAttributeName string,
yAxisAttribute string,
yAxisUnits string,
showYAxisPercentage bool,
statisticsReady bool,
anyUsersExist bool,
statisticsDatumsList []statisticsDatum.StatisticsDatum,
groupingPerformed bool,
groupedStatisticsDatumsList []statisticsDatum.StatisticsDatum,
chartImage image.Image,
previousPage func()){
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) {
setErrorEncounteredPage(window, err, previousPage)
return
}
appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier)
checkIfPageHasChangedFunction := func()bool{
exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage")
if (exists == true && currentViewedPage == pageIdentifier){
return false
}
return true
}
title := getPageTitleCentered("User Attribute Statistics - Bar Chart")
backButton := getBackButtonCentered(previousPage)
identityTypeLabel := getBoldLabelCentered("Identity Type")
handleIdentityTypeSelectFunction := func(newIdentityType string){
if (newIdentityType == identityType){
return
}
if (newIdentityType == "Mate"){
setViewUserAttributeStatisticsPage_BarChart(window, "Mate", "Age", "Number Of Users", " Users", true, false, false, nil, false, nil, nil, previousPage)
return
}
if (newIdentityType == "Host"){
setViewUserAttributeStatisticsPage_BarChart(window, "Host", "SeekiaVersion", "Number Of Users", " Users", true, false, false, nil, false, nil, nil, previousPage)
return
}
// newIdentityType == "Moderator"
setViewUserAttributeStatisticsPage_BarChart(window, "Moderator", "IdentityScore", "Number Of Users", " Users", true, false, false, nil, false, nil, nil, previousPage)
}
identityTypesList := []string{"Mate", "Host", "Moderator"}
identityTypeSelector := widget.NewSelect(identityTypesList, handleIdentityTypeSelectFunction)
identityTypeSelector.Selected = identityType
identityTypeColumn := container.NewVBox(identityTypeLabel, identityTypeSelector)
chartTypeLabel := getBoldLabelCentered("Chart Type")
chartTypesList := []string{"Bar Chart", "Donut Chart"}
handleChartTypeSelectFunction := func(newChartType string){
if (newChartType == "Bar Chart"){
return
}
// newChartType == "Donut Chart"
setViewUserAttributeStatisticsPage_DonutChart(window, identityType, xAxisAttributeName, false, false, nil, false, nil, nil, previousPage)
}
chartTypeSelector := widget.NewSelect(chartTypesList, handleChartTypeSelectFunction)
chartTypeSelector.Selected = "Bar Chart"
chartTypeColumn := container.NewVBox(chartTypeLabel, chartTypeSelector)
xAxisAttributeTitle, xAxisIsNumerical, formatXAxisValuesFunction, xAxisUnits, unknownXAxisValuesTextTranslated, err := attributeDisplay.GetProfileAttributeDisplayInfo(xAxisAttributeName)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
xAxisLabel := getBoldLabelCentered("X-Axis")
changeXAxisAttributeButton := widget.NewButton(xAxisAttributeTitle, func(){
currentPageWithNewAttributeFunction := func(newXAxisAttributeName string){
setViewUserAttributeStatisticsPage_BarChart(window, identityType, newXAxisAttributeName, yAxisAttribute, yAxisUnits, showYAxisPercentage, false, false, nil, false, nil, nil, previousPage)
}
setViewUserAttributeStatistics_ChooseXAxisAttributePage(window, identityType, currentPageWithNewAttributeFunction, currentPage)
})
xAxisColumn := container.NewVBox(xAxisLabel, changeXAxisAttributeButton)
yAxisLabel := getBoldLabelCentered("Y-Axis")
changeYAxisAttributeButton := widget.NewButton(translate(yAxisAttribute), func(){
currentPageWithNewAttributeFunction := func(newYAxisAttribute string, newYAxisUnits string, showYAxisPercentage bool){
setViewUserAttributeStatisticsPage_BarChart(window, identityType, xAxisAttributeName, newYAxisAttribute, newYAxisUnits, showYAxisPercentage, false, false, nil, false, nil, nil, previousPage)
}
setViewUserAttributeStatistics_ChooseYAxisAttributePage(window, identityType, currentPageWithNewAttributeFunction, currentPage)
})
yAxisColumn := container.NewVBox(yAxisLabel, changeYAxisAttributeButton)
chartSettingsRow := container.NewHBox(layout.NewSpacer(), widget.NewSeparator(), identityTypeColumn, widget.NewSeparator(), chartTypeColumn, widget.NewSeparator(), xAxisColumn, widget.NewSeparator(), yAxisColumn, widget.NewSeparator(), layout.NewSpacer())
if (statisticsReady == true){
if (anyUsersExist == false){
description1 := getBoldLabelCentered("No " + identityType + " profiles exist.")
description2 := getLabelCentered("No statistics are available to show.")
description3 := getLabelCentered("You must wait for profiles to be downloaded.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), chartSettingsRow, widget.NewSeparator(), description1, description2, description3)
setPageContent(page, window)
return
}
viewDataButton := widget.NewButtonWithIcon("View Data", theme.ListIcon(), func(){
setViewUserStatisticsDataPage(window, xAxisAttributeTitle, yAxisAttribute, showYAxisPercentage, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, true, xAxisUnits, yAxisUnits, currentPage)
})
viewFullscreenButton := widget.NewButtonWithIcon("View Fullscreen", theme.ZoomInIcon(), func(){
setViewFullpageImagePage(window, chartImage, currentPage)
})
warningButton := widget.NewButtonWithIcon("Warning", theme.WarningIcon(), func(){
setNetworkStatisticsInaccuracyWarningPage(window, currentPage)
})
chartCompleteButtonsRow := getContainerCentered(container.NewGridWithRows(1, viewDataButton, viewFullscreenButton, warningButton))
header := container.NewVBox(title, backButton, widget.NewSeparator(), chartSettingsRow, widget.NewSeparator(), chartCompleteButtonsRow, widget.NewSeparator())
chartFyneImage := canvas.NewImageFromImage(chartImage)
chartFyneImage.FillMode = canvas.ImageFillContain
page := container.NewBorder(header, nil, nil, nil, chartFyneImage)
setPageContent(page, window)
return
}
loadingTextBinding := binding.NewString()
loadingTextLabel := widget.NewLabelWithData(loadingTextBinding)
loadingTextLabel.TextStyle = getFyneTextStyle_Bold()
loadingTextLabelCentered := container.NewCenter(loadingTextLabel)
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
createChartAndRefreshPageFunction := func(){
var functionCompleteBoolMutex sync.RWMutex
functionCompleteBool := false
updateLoadingBindingFunction := func(){
secondsElapsed := 0
for{
if (secondsElapsed%3 == 0){
loadingTextBinding.Set("Loading Statistics.")
} else if (secondsElapsed%3 == 1){
loadingTextBinding.Set("Loading Statistics..")
} else {
loadingTextBinding.Set("Loading Statistics...")
}
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == true){
return
}
functionCompleteBoolMutex.RLock()
functionIsComplete := functionCompleteBool
functionCompleteBoolMutex.RUnlock()
if (functionIsComplete == true){
return
}
secondsElapsed += 1
time.Sleep(time.Second)
}
}
go updateLoadingBindingFunction()
totalAnalyzedUsers, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, formatYAxisValuesFunction, err := userStatistics.GetUserStatisticsDatumsLists_BarChart(identityType, appNetworkType, xAxisAttributeName, xAxisIsNumerical, formatXAxisValuesFunction, unknownXAxisValuesTextTranslated, yAxisAttribute)
if (err != nil) {
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
functionCompleteBoolMutex.Unlock()
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){
setErrorEncounteredPage(window, err, previousPage)
}
return
}
if (len(statisticsDatumsList) == 0){
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
functionCompleteBoolMutex.Unlock()
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){
setViewUserAttributeStatisticsPage_BarChart(window, identityType, xAxisAttributeName, yAxisAttribute, yAxisUnits, showYAxisPercentage, true, false, nil, false, nil, nil, previousPage)
}
return
}
getChartStatisticsDatumsList := func()[]statisticsDatum.StatisticsDatum{
if (groupingPerformed == false){
return statisticsDatumsList
}
return groupedStatisticsDatumsList
}
chartStatisticsDatumsList := getChartStatisticsDatumsList()
getChartTitle := func()string{
totalAnalyzedUsersString := helpers.ConvertIntToString(totalAnalyzedUsers)
chartTitle := identityType + " Statistics: " + xAxisAttributeTitle
if (xAxisUnits != ""){
xAxisUnitsTrimmed := strings.TrimSpace(xAxisUnits)
chartTitle += " (" + xAxisUnitsTrimmed + ")"
}
chartTitle += " by " + yAxisAttribute
if (yAxisUnits != "" && yAxisUnits != translate(" users")){
yAxisUnitsTrimmed := strings.TrimSpace(yAxisUnits)
chartTitle += " (" + yAxisUnitsTrimmed + ")"
}
chartTitle += " - " + totalAnalyzedUsersString + " Users"
return chartTitle
}
chartTitle := getChartTitle()
newChartImage, err := createCharts.CreateBarChart(chartTitle, chartStatisticsDatumsList, formatYAxisValuesFunction, true, yAxisUnits)
if (err != nil) {
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
functionCompleteBoolMutex.Unlock()
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){
setErrorEncounteredPage(window, err, previousPage)
}
return
}
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
functionCompleteBoolMutex.Unlock()
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){
setViewUserAttributeStatisticsPage_BarChart(window, identityType, xAxisAttributeName, yAxisAttribute, yAxisUnits, showYAxisPercentage, true, true, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, newChartImage, previousPage)
}
}
header := container.NewVBox(title, backButton, widget.NewSeparator(), chartSettingsRow, widget.NewSeparator())
page := container.NewBorder(header, nil, nil, nil, loadingTextLabelCentered)
setPageContent(page, window)
go createChartAndRefreshPageFunction()
}
func setViewUserAttributeStatisticsPage_DonutChart(
window fyne.Window,
identityType string,
attributeName string,
statisticsReady bool,
anyUsersExist bool,
statisticsDatumsList []statisticsDatum.StatisticsDatum,
groupingPerformed bool,
groupedStatisticsDatumsList []statisticsDatum.StatisticsDatum,
chartImage image.Image,
previousPage func()){
currentPage := func(){setViewUserAttributeStatisticsPage_DonutChart(window, identityType, attributeName, statisticsReady, anyUsersExist, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, chartImage, previousPage)}
pageIdentifier, err := helpers.GetNewRandomHexString(16)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier)
checkIfPageHasChangedFunction := func()bool{
exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage")
if (exists == true && currentViewedPage == pageIdentifier){
return false
}
return true
}
title := getPageTitleCentered("User Attribute Statistics - Donut Chart")
backButton := getBackButtonCentered(previousPage)
identityTypeLabel := getBoldLabelCentered("Identity Type")
handleIdentityTypeSelectFunction := func(newIdentityType string){
if (newIdentityType == identityType){
return
}
if (newIdentityType == "Mate"){
setViewUserAttributeStatisticsPage_DonutChart(window, "Mate", "Age", false, false, nil, false, nil, nil, previousPage)
return
}
if (newIdentityType == "Host"){
setViewUserAttributeStatisticsPage_DonutChart(window, "Host", "SeekiaVersion", false, false, nil, false, nil, nil, previousPage)
return
}
// newIdentityType == "Moderator"
setViewUserAttributeStatisticsPage_DonutChart(window, "Moderator", "IdentityScore", false, false, nil, false, nil, nil, previousPage)
}
identityTypesList := []string{"Mate", "Host", "Moderator"}
identityTypeSelector := widget.NewSelect(identityTypesList, handleIdentityTypeSelectFunction)
identityTypeSelector.Selected = identityType
identityTypeColumn := container.NewVBox(identityTypeLabel, identityTypeSelector)
chartTypeLabel := getBoldLabelCentered("Chart Type:")
chartTypesList := []string{"Bar Chart", "Donut Chart"}
handleSelectFunction := func(newType string){
if (newType == "Donut Chart"){
return
}
if (newType == "Bar Chart"){
setViewUserAttributeStatisticsPage_BarChart(window, identityType, attributeName, "Number Of Users", " Users", true, false, false, nil, false, nil, nil, previousPage)
}
}
chartTypeSelector := widget.NewSelect(chartTypesList, handleSelectFunction)
chartTypeSelector.Selected = "Donut Chart"
chartTypeColumn := container.NewVBox(chartTypeLabel, chartTypeSelector)
attributeTitle, attributeIsNumerical, formatAttributeValuesFunction, attributeUnits, unknownValuesTextTranslated, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
attributeLabel := getBoldLabelCentered("Attribute:")
changeAttributeButton := widget.NewButton(attributeTitle, func(){
currentPageWithNewAttributeFunction := func(newAttributeName string){
setViewUserAttributeStatisticsPage_DonutChart(window, identityType, newAttributeName, false, false, nil, false, nil, nil, previousPage)
}
setViewUserAttributeStatistics_ChooseXAxisAttributePage(window, identityType, currentPageWithNewAttributeFunction, currentPage)
})
attributeColumn := container.NewVBox(attributeLabel, changeAttributeButton)
chartSettingsRow := container.NewHBox(layout.NewSpacer(), widget.NewSeparator(), identityTypeColumn, widget.NewSeparator(), chartTypeColumn, widget.NewSeparator(), attributeColumn, widget.NewSeparator(), layout.NewSpacer())
if (statisticsReady == true){
if (anyUsersExist == false){
description1 := getBoldLabelCentered("No " + identityType + " profiles exist.")
description2 := getLabelCentered("No statistics are available to show.")
description3 := getLabelCentered("You must wait for profiles to be downloaded.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), chartSettingsRow, widget.NewSeparator(), description1, description2, description3)
setPageContent(page, window)
return
}
viewDataButton := widget.NewButtonWithIcon("View Data", theme.ListIcon(), func(){
setViewUserStatisticsDataPage(window, attributeTitle, "Number Of Users", true, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, true, attributeUnits, "Users", currentPage)
})
viewFullscreenButton := widget.NewButtonWithIcon("View Fullscreen", theme.ZoomInIcon(), func(){
setViewFullpageImagePage(window, chartImage, currentPage)
})
warningButton := widget.NewButtonWithIcon("Warning", theme.WarningIcon(), func(){
setNetworkStatisticsInaccuracyWarningPage(window, currentPage)
})
chartCompleteButtonsRow := getContainerCentered(container.NewGridWithRows(1, viewDataButton, viewFullscreenButton, warningButton))
header := container.NewVBox(title, backButton, widget.NewSeparator(), chartSettingsRow, widget.NewSeparator(), chartCompleteButtonsRow, widget.NewSeparator())
chartFyneImage := canvas.NewImageFromImage(chartImage)
chartFyneImage.FillMode = canvas.ImageFillContain
page := container.NewBorder(header, nil, nil, nil, chartFyneImage)
setPageContent(page, window)
return
}
loadingTextBinding := binding.NewString()
loadingTextLabel := widget.NewLabelWithData(loadingTextBinding)
loadingTextLabel.TextStyle = getFyneTextStyle_Bold()
loadingTextLabelCentered := container.NewCenter(loadingTextLabel)
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
createChartAndRefreshPageFunction := func(){
var functionCompleteBoolMutex sync.RWMutex
functionCompleteBool := false
updateLoadingBindingFunction := func(){
secondsElapsed := 0
for{
if (secondsElapsed%3 == 0){
loadingTextBinding.Set("Loading Statistics.")
} else if (secondsElapsed%3 == 1){
loadingTextBinding.Set("Loading Statistics..")
} else {
loadingTextBinding.Set("Loading Statistics...")
}
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == true){
return
}
functionCompleteBoolMutex.RLock()
functionIsComplete := functionCompleteBool
functionCompleteBoolMutex.RUnlock()
if (functionIsComplete == true){
return
}
secondsElapsed += 1
time.Sleep(time.Second)
}
}
go updateLoadingBindingFunction()
totalAnalyzedUsers, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, err := userStatistics.GetUserStatisticsDatumsLists_DonutChart(identityType, appNetworkType, attributeName, attributeIsNumerical, formatAttributeValuesFunction, unknownValuesTextTranslated)
if (err != nil) {
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
functionCompleteBoolMutex.Unlock()
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){
setErrorEncounteredPage(window, err, previousPage)
}
return
}
if (len(statisticsDatumsList) == 0){
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
functionCompleteBoolMutex.Unlock()
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){
setViewUserAttributeStatisticsPage_DonutChart(window, identityType, attributeName, true, false, nil, false, nil, nil, previousPage)
}
return
}
getChartStatisticsDatumsList := func()[]statisticsDatum.StatisticsDatum{
if (groupingPerformed == false){
return statisticsDatumsList
}
return groupedStatisticsDatumsList
}
chartStatisticsDatumsList := getChartStatisticsDatumsList()
getChartTitle := func()string{
totalAnalyzedUsersString := helpers.ConvertIntToString(totalAnalyzedUsers)
chartTitle := identityType + " Statistics: " + attributeTitle
if (attributeUnits != ""){
attributeUnitsTrimmed := strings.TrimPrefix(attributeUnits, " ")
chartTitle += " (" + attributeUnitsTrimmed + ")"
}
chartTitle += " - " + totalAnalyzedUsersString + " Users"
return chartTitle
}
chartTitle := getChartTitle()
newChartImage, err := createCharts.CreateDonutChart(chartTitle, chartStatisticsDatumsList)
if (err != nil){
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
functionCompleteBoolMutex.Unlock()
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){
setErrorEncounteredPage(window, err, previousPage)
}
return
}
functionCompleteBoolMutex.Lock()
functionCompleteBool = true
functionCompleteBoolMutex.Unlock()
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == false){
setViewUserAttributeStatisticsPage_DonutChart(window, identityType, attributeName, true, true, statisticsDatumsList, groupingPerformed, groupedStatisticsDatumsList, newChartImage, previousPage)
}
}
header := container.NewVBox(title, backButton, widget.NewSeparator(), chartSettingsRow, widget.NewSeparator())
page := container.NewBorder(header, nil, nil, nil, loadingTextLabelCentered)
setPageContent(page, window)
go createChartAndRefreshPageFunction()
}
//Inputs:
// -fyne.Window
// -string: Profile Type
// -func(attributeName string)
// -func(): Previous page
func setViewUserAttributeStatistics_ChooseXAxisAttributePage(window fyne.Window, profileType string, viewStatisticsPageWithNewAttribute func(string), previousPage func()){
title := getPageTitleCentered(translate("Choose Attribute"))
backButton := getBackButtonCentered(previousPage)
attributesToShowList := make([]string, 0)
addAttributeToListFunction := func(attributeName string){
attributesToShowList = append(attributesToShowList, attributeName)
}
if (profileType == "Mate"){
addAttributeToListFunction("Age")
addAttributeToListFunction("Height")
addAttributeToListFunction("Sex")
addAttributeToListFunction("Sexuality")
addAttributeToListFunction("Distance")
addAttributeToListFunction("WealthInGold")
}
addAttributeToListFunction("ProfileLanguage")
if (profileType == "Mate" || profileType == "Moderator"){
addAttributeToListFunction("HasMessagedMe")
addAttributeToListFunction("IHaveMessaged")
}
if (profileType == "Mate"){
addAttributeToListFunction("HasRejectedMe")
addAttributeToListFunction("IsLiked")
addAttributeToListFunction("IsIgnored")
}
addAttributeToListFunction("IsMyContact")
if (profileType == "Mate"){
addAttributeToListFunction("PrimaryLocationCountry")
addAttributeToListFunction("BodyFat")
addAttributeToListFunction("BodyMuscle")
addAttributeToListFunction("HairColor")
addAttributeToListFunction("HairTexture")
addAttributeToListFunction("EyeColor")
addAttributeToListFunction("SkinColor")
addAttributeToListFunction("RacialSimilarity")
addAttributeToListFunction("EyeColorSimilarity")
addAttributeToListFunction("EyeColorGenesSimilarity")
addAttributeToListFunction("HairColorSimilarity")
addAttributeToListFunction("HairColorGenesSimilarity")
addAttributeToListFunction("SkinColorSimilarity")
addAttributeToListFunction("SkinColorGenesSimilarity")
addAttributeToListFunction("HairTextureSimilarity")
addAttributeToListFunction("HairTextureGenesSimilarity")
addAttributeToListFunction("FacialStructureGenesSimilarity")
addAttributeToListFunction("23andMe_AncestralSimilarity")
addAttributeToListFunction("23andMe_MaternalHaplogroupSimilarity")
addAttributeToListFunction("23andMe_PaternalHaplogroupSimilarity")
addAttributeToListFunction("HasHIV")
addAttributeToListFunction("HasGenitalHerpes")
addAttributeToListFunction("GenderIdentity")
addAttributeToListFunction("23andMe_MaternalHaplogroup")
addAttributeToListFunction("23andMe_PaternalHaplogroup")
addAttributeToListFunction("23andMe_NeanderthalVariants")
addAttributeToListFunction("FruitRating")
addAttributeToListFunction("VegetablesRating")
addAttributeToListFunction("NutsRating")
addAttributeToListFunction("GrainsRating")
addAttributeToListFunction("DairyRating")
addAttributeToListFunction("SeafoodRating")
addAttributeToListFunction("BeefRating")
addAttributeToListFunction("PorkRating")
addAttributeToListFunction("PoultryRating")
addAttributeToListFunction("EggsRating")
addAttributeToListFunction("BeansRating")
addAttributeToListFunction("Fame")
addAttributeToListFunction("AlcoholFrequency")
addAttributeToListFunction("TobaccoFrequency")
addAttributeToListFunction("CannabisFrequency")
addAttributeToListFunction("PetsRating")
addAttributeToListFunction("DogsRating")
addAttributeToListFunction("CatsRating")
addAttributeToListFunction("OffspringProbabilityOfAnyMonogenicDisease")
addAttributeToListFunction("TotalPolygenicDiseaseRiskScore")
addAttributeToListFunction("OffspringTotalPolygenicDiseaseRiskScore")
}
buttonsGrid := container.NewGridWithColumns(1)
for _, attributeName := range attributesToShowList{
attributeTitle, _, _, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
attributeButton := widget.NewButton(attributeTitle, func(){
viewStatisticsPageWithNewAttribute(attributeName)
})
buttonsGrid.Add(attributeButton)
}
buttonsGridCentered := getContainerCentered(buttonsGrid)
page := container.NewVBox(title, backButton, widget.NewSeparator(), buttonsGridCentered)
setPageContent(page, window)
}
//Inputs:
// -fyne.Window
// -string: Profile type
// -func(attributeName string, yAxisUnits string, showYAxisPercentage bool)
// -func(): Previous page
func setViewUserAttributeStatistics_ChooseYAxisAttributePage(window fyne.Window, profileType string, viewStatisticsPageWithNewAttribute func(string, string, bool), previousPage func()){
title := getPageTitleCentered("Choose Y-Axis Attribute")
backButton := getBackButtonCentered(previousPage)
numberOfUsersButton := widget.NewButton("Number Of Users", func(){
viewStatisticsPageWithNewAttribute("Number Of Users", translate(" Users"), true)
})
buttonsGrid := container.NewGridWithColumns(1, numberOfUsersButton)
if (profileType == "Mate"){
averageHeightButton := widget.NewButton("Average Height", func(){
viewStatisticsPageWithNewAttribute("Average Height", translate(" Centimeters"), false)
})
buttonsGrid.Add(averageHeightButton)
averageAgeButton := widget.NewButton("Average Age", func(){
viewStatisticsPageWithNewAttribute("Average Age", translate(" Years Old"), false)
})
buttonsGrid.Add(averageAgeButton)
getAppCurrency := func()(string, error){
exists, appCurrencyCode, err := globalSettings.GetSetting("Currency")
if (err != nil) { return "", err }
if (exists == false){
return "USD", nil
}
return appCurrencyCode, nil
}
appCurrencyCode, err := getAppCurrency()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
averageWealthButton := widget.NewButton("Average Wealth", func(){
wealthUnits := " " + appCurrencyCode
viewStatisticsPageWithNewAttribute("Average Wealth", wealthUnits, false)
})
buttonsGrid.Add(averageWealthButton)
averageNeanderthalVariantsButton := widget.NewButton("Average 23andMe Neanderthal Variants", func(){
viewStatisticsPageWithNewAttribute("Average 23andMe Neanderthal Variants", translate(" Variants"), false)
})
buttonsGrid.Add(averageNeanderthalVariantsButton)
averageBodyFatButton := widget.NewButton("Average Body Fat", func(){
viewStatisticsPageWithNewAttribute("Average Body Fat", "/4", false)
})
buttonsGrid.Add(averageBodyFatButton)
averageBodyMuscleButton := widget.NewButton("Average Body Muscle", func(){
viewStatisticsPageWithNewAttribute("Average Body Muscle", "/4", false)
})
buttonsGrid.Add(averageBodyMuscleButton)
oneToTenValueAttributesList := []string{
"Fruit Rating",
"Vegetables Rating",
"Nuts Rating",
"Grains Rating",
"Dairy Rating",
"Seafood Rating",
"Beef Rating",
"Pork Rating",
"Poultry Rating",
"Eggs Rating",
"Beans Rating",
"Fame",
"Alcohol Frequency",
"Tobacco Frequency",
"Cannabis Frequency",
"Pets Rating",
"Dogs Rating",
"Cats Rating",
}
for _, attributeTitle := range oneToTenValueAttributesList{
averageAttributeTitle := "Average " + attributeTitle
averageAttributeButton := widget.NewButton(averageAttributeTitle, func(){
viewStatisticsPageWithNewAttribute(averageAttributeTitle, "/10", false)
})
buttonsGrid.Add(averageAttributeButton)
}
}
buttonsGridCentered := getContainerCentered(buttonsGrid)
page := container.NewVBox(title, backButton, widget.NewSeparator(), buttonsGridCentered)
setPageContent(page, window)
}
func setViewUserStatisticsDataPage(
window fyne.Window,
attributeTitle string,
rightColumnName string,
showPercentageColumn bool,
statisticsDatumsList []statisticsDatum.StatisticsDatum,
groupingPerformed bool,
groupedStatisticsDatumsList []statisticsDatum.StatisticsDatum,
showGroupedStatistics bool,
attributeColumnUnits string,
rightColumnUnits string,
previousPage func()){
setLoadingScreen(window, "User Statistics Data", "Loading statistics data...")
title := getPageTitleCentered("User Statistics Data")
backButton := getBackButtonCentered(previousPage)
header := container.NewVBox(title, backButton, widget.NewSeparator())
if (groupingPerformed == true){
if (showGroupedStatistics == false){
showDataGroupedButton := getWidgetCentered(widget.NewButton("Show Data Grouped", func(){
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, statisticsDatumsList, true, groupedStatisticsDatumsList, false, attributeColumnUnits, rightColumnUnits, previousPage)
}))
header.Add(showDataRawButton)
}
header.Add(widget.NewSeparator())
}
getStatisticsDatumsListToShow := func()[]statisticsDatum.StatisticsDatum{
if (groupingPerformed == true && showGroupedStatistics == true){
return groupedStatisticsDatumsList
}
return statisticsDatumsList
}
statisticsDatumsToShowList := getStatisticsDatumsListToShow()
getStatisticsDataGrid := func()(*fyne.Container, error){
allValuesSummed := float64(0)
if (showPercentageColumn == true){
for _, datum := range statisticsDatumsToShowList{
datumValue := datum.Value
allValuesSummed += datumValue
}
}
attributeColumnValuesList := make([]string, 0, len(statisticsDatumsToShowList))
rightColumnValuesList := make([]string, 0, len(statisticsDatumsToShowList))
percentageColumnValuesList := make([]string, 0, len(statisticsDatumsToShowList))
for _, datum := range statisticsDatumsToShowList{
// Each datum is represents a row in the data grid.
datumLabelFormatted := datum.LabelFormatted
datumValueFormatted := datum.ValueFormatted
attributeColumnValuesList = append(attributeColumnValuesList, datumLabelFormatted)
rightColumnValuesList = append(rightColumnValuesList, datumValueFormatted)
if (showPercentageColumn == true){
datumValue := datum.Value
getValuePercentage := func()float64{
if (allValuesSummed == 0){
return 0
}
valuePercentage := (datumValue/allValuesSummed)*100
return valuePercentage
}
valuePercentage := getValuePercentage()
valuePercentageString := helpers.ConvertFloat64ToStringRounded(valuePercentage, 2)
valuePercentageFormatted := valuePercentageString + "%"
percentageColumnValuesList = append(percentageColumnValuesList, valuePercentageFormatted)
}
}
getAttributeColumnTitle := func()string{
if (attributeColumnUnits != ""){
attributeColumnUnitsTrimmed := strings.TrimSpace(attributeColumnUnits)
attributeColumnTitle := attributeTitle + " (" + attributeColumnUnitsTrimmed + ")"
return attributeColumnTitle
}
return attributeTitle
}
attributeColumnTitle := getAttributeColumnTitle()
getRightColumnTitle := func()string{
if (rightColumnUnits != ""){
rightColumnUnitsTrimmed := strings.TrimSpace(rightColumnUnits)
rightColumnTitle := rightColumnName + " (" + rightColumnUnitsTrimmed + ")"
return rightColumnTitle
}
return rightColumnName
}
rightColumnTitle := getRightColumnTitle()
if (showGroupedStatistics == false){
// Raw statistics must be rendered using a widget list
// Otherwise, rendering thousands of rows is too difficult for fyne
headerText := attributeColumnTitle + " - " + rightColumnTitle
if (showPercentageColumn == true){
headerText += " - Percentage"
}
headerLabel := getBoldLabelCentered(headerText)
rowValuesList := make([]string, 0, len(attributeColumnValuesList))
for index, attributeColumnValue := range attributeColumnValuesList{
rightColumnValue := rightColumnValuesList[index]
if (showPercentageColumn == false){
rowValue := attributeColumnValue + " - " + rightColumnValue
rowValuesList = append(rowValuesList, rowValue)
} else {
percentageColumnValue := percentageColumnValuesList[index]
rowValue := attributeColumnValue + " - " + rightColumnValue + " - " + percentageColumnValue
rowValuesList = append(rowValuesList, rowValue)
}
}
onClickedFunction := func(_ int){}
widgetList, err := getFyneWidgetListFromStringList(rowValuesList, onClickedFunction)
if (err != nil) { return nil, err }
statisticsDataGrid := container.NewBorder(headerLabel, nil, nil, nil, widgetList)
return statisticsDataGrid, nil
}
attributeColumnHeader := getItalicLabelCentered(attributeColumnTitle)
rightColumnHeader := getItalicLabelCentered(rightColumnTitle)
percentageHeader := getItalicLabelCentered("Percentage")
attributeColumn := container.NewVBox(attributeColumnHeader, widget.NewSeparator())
rightColumn := container.NewVBox(rightColumnHeader, widget.NewSeparator())
percentageColumn := container.NewVBox(percentageHeader, widget.NewSeparator())
for index, attributeColumnValue := range attributeColumnValuesList{
rightColumnValue := rightColumnValuesList[index]
attributeColumnLabel := getBoldLabelCentered(attributeColumnValue)
rightColumnLabel := getBoldLabelCentered(rightColumnValue)
attributeColumn.Add(attributeColumnLabel)
rightColumn.Add(rightColumnLabel)
if (showPercentageColumn == true){
percentageColumnValue := percentageColumnValuesList[index]
valuePercentageLabel := getBoldLabelCentered(percentageColumnValue)
percentageColumn.Add(valuePercentageLabel)
}
}
if (showPercentageColumn == true){
statisticsDataGrid := container.NewHBox(layout.NewSpacer(), attributeColumn, rightColumn, percentageColumn, layout.NewSpacer())
return statisticsDataGrid, nil
}
statisticsDataGrid := container.NewHBox(layout.NewSpacer(), attributeColumn, rightColumn, layout.NewSpacer())
return statisticsDataGrid, nil
}
statisticsDataGrid, err := getStatisticsDataGrid()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewBorder(header, nil, nil, nil, statisticsDataGrid)
setPageContent(page, window)
}