2024-04-11 15:51:56 +02:00
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"
2024-06-07 02:04:13 +02:00
import "seekia/internal/statisticsDatum"
2024-04-11 15:51:56 +02:00
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 ,
2024-06-07 02:04:13 +02:00
statisticsDatumsList [ ] statisticsDatum . StatisticsDatum ,
2024-04-11 15:51:56 +02:00
groupingPerformed bool ,
2024-06-07 02:04:13 +02:00
groupedStatisticsDatumsList [ ] statisticsDatum . StatisticsDatum ,
2024-04-11 15:51:56 +02:00
chartImage image . Image ,
previousPage func ( ) ) {
2024-06-07 02:04:13 +02:00
currentPage := func ( ) { setViewUserAttributeStatisticsPage_BarChart ( window , identityType , xAxisAttributeName , yAxisAttribute , yAxisUnits , showYAxisPercentage , statisticsReady , anyUsersExist , statisticsDatumsList , groupingPerformed , groupedStatisticsDatumsList , chartImage , previousPage ) }
2024-04-11 15:51:56 +02:00
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 ( ) {
2024-06-07 02:04:13 +02:00
setViewUserStatisticsDataPage ( window , xAxisAttributeTitle , yAxisAttribute , showYAxisPercentage , statisticsDatumsList , groupingPerformed , groupedStatisticsDatumsList , true , xAxisUnits , yAxisUnits , currentPage )
2024-04-11 15:51:56 +02:00
} )
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 ( )
2024-06-07 02:04:13 +02:00
totalAnalyzedUsers , statisticsDatumsList , groupingPerformed , groupedStatisticsDatumsList , formatYAxisValuesFunction , err := userStatistics . GetUserStatisticsDatumsLists_BarChart ( identityType , appNetworkType , xAxisAttributeName , xAxisIsNumerical , formatXAxisValuesFunction , unknownXAxisValuesTextTranslated , yAxisAttribute )
2024-04-11 15:51:56 +02:00
if ( err != nil ) {
functionCompleteBoolMutex . Lock ( )
functionCompleteBool = true
functionCompleteBoolMutex . Unlock ( )
pageHasChanged := checkIfPageHasChangedFunction ( )
if ( pageHasChanged == false ) {
setErrorEncounteredPage ( window , err , previousPage )
}
return
}
2024-06-07 02:04:13 +02:00
if ( len ( statisticsDatumsList ) == 0 ) {
2024-04-11 15:51:56 +02:00
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
}
2024-06-07 02:04:13 +02:00
getChartStatisticsDatumsList := func ( ) [ ] statisticsDatum . StatisticsDatum {
2024-04-11 15:51:56 +02:00
if ( groupingPerformed == false ) {
2024-06-07 02:04:13 +02:00
return statisticsDatumsList
2024-04-11 15:51:56 +02:00
}
2024-06-07 02:04:13 +02:00
return groupedStatisticsDatumsList
2024-04-11 15:51:56 +02:00
}
2024-06-07 02:04:13 +02:00
chartStatisticsDatumsList := getChartStatisticsDatumsList ( )
2024-04-11 15:51:56 +02:00
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 ( )
2024-06-07 02:04:13 +02:00
newChartImage , err := createCharts . CreateBarChart ( chartTitle , chartStatisticsDatumsList , formatYAxisValuesFunction , true , yAxisUnits )
2024-04-11 15:51:56 +02:00
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 ) {
2024-06-07 02:04:13 +02:00
setViewUserAttributeStatisticsPage_BarChart ( window , identityType , xAxisAttributeName , yAxisAttribute , yAxisUnits , showYAxisPercentage , true , true , statisticsDatumsList , groupingPerformed , groupedStatisticsDatumsList , newChartImage , previousPage )
2024-04-11 15:51:56 +02:00
}
}
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 ,
2024-06-07 02:04:13 +02:00
statisticsDatumsList [ ] statisticsDatum . StatisticsDatum ,
2024-04-11 15:51:56 +02:00
groupingPerformed bool ,
2024-06-07 02:04:13 +02:00
groupedStatisticsDatumsList [ ] statisticsDatum . StatisticsDatum ,
2024-04-11 15:51:56 +02:00
chartImage image . Image ,
previousPage func ( ) ) {
2024-06-07 02:04:13 +02:00
currentPage := func ( ) { setViewUserAttributeStatisticsPage_DonutChart ( window , identityType , attributeName , statisticsReady , anyUsersExist , statisticsDatumsList , groupingPerformed , groupedStatisticsDatumsList , chartImage , previousPage ) }
2024-04-11 15:51:56 +02:00
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 ( ) {
2024-06-07 02:04:13 +02:00
setViewUserStatisticsDataPage ( window , attributeTitle , "Number Of Users" , true , statisticsDatumsList , groupingPerformed , groupedStatisticsDatumsList , true , attributeUnits , "Users" , currentPage )
2024-04-11 15:51:56 +02:00
} )
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 ( )
2024-06-07 02:04:13 +02:00
totalAnalyzedUsers , statisticsDatumsList , groupingPerformed , groupedStatisticsDatumsList , err := userStatistics . GetUserStatisticsDatumsLists_DonutChart ( identityType , appNetworkType , attributeName , attributeIsNumerical , formatAttributeValuesFunction , unknownValuesTextTranslated )
2024-04-11 15:51:56 +02:00
if ( err != nil ) {
functionCompleteBoolMutex . Lock ( )
functionCompleteBool = true
functionCompleteBoolMutex . Unlock ( )
pageHasChanged := checkIfPageHasChangedFunction ( )
if ( pageHasChanged == false ) {
setErrorEncounteredPage ( window , err , previousPage )
}
return
}
2024-06-07 02:04:13 +02:00
if ( len ( statisticsDatumsList ) == 0 ) {
2024-04-11 15:51:56 +02:00
functionCompleteBoolMutex . Lock ( )
functionCompleteBool = true
functionCompleteBoolMutex . Unlock ( )
pageHasChanged := checkIfPageHasChangedFunction ( )
if ( pageHasChanged == false ) {
setViewUserAttributeStatisticsPage_DonutChart ( window , identityType , attributeName , true , false , nil , false , nil , nil , previousPage )
}
return
}
2024-06-07 02:04:13 +02:00
getChartStatisticsDatumsList := func ( ) [ ] statisticsDatum . StatisticsDatum {
2024-04-11 15:51:56 +02:00
if ( groupingPerformed == false ) {
2024-06-07 02:04:13 +02:00
return statisticsDatumsList
2024-04-11 15:51:56 +02:00
}
2024-06-07 02:04:13 +02:00
return groupedStatisticsDatumsList
2024-04-11 15:51:56 +02:00
}
2024-06-07 02:04:13 +02:00
chartStatisticsDatumsList := getChartStatisticsDatumsList ( )
2024-04-11 15:51:56 +02:00
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 ( )
2024-06-07 02:04:13 +02:00
newChartImage , err := createCharts . CreateDonutChart ( chartTitle , chartStatisticsDatumsList )
2024-04-11 15:51:56 +02:00
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 ) {
2024-06-07 02:04:13 +02:00
setViewUserAttributeStatisticsPage_DonutChart ( window , identityType , attributeName , true , true , statisticsDatumsList , groupingPerformed , groupedStatisticsDatumsList , newChartImage , previousPage )
2024-04-11 15:51:56 +02:00
}
}
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 ,
2024-06-07 02:04:13 +02:00
statisticsDatumsList [ ] statisticsDatum . StatisticsDatum ,
2024-04-11 15:51:56 +02:00
groupingPerformed bool ,
2024-06-07 02:04:13 +02:00
groupedStatisticsDatumsList [ ] statisticsDatum . StatisticsDatum ,
2024-04-11 15:51:56 +02:00
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 ( ) {
2024-06-07 02:04:13 +02:00
setViewUserStatisticsDataPage ( window , attributeTitle , rightColumnName , showPercentageColumn , statisticsDatumsList , true , groupedStatisticsDatumsList , true , attributeColumnUnits , rightColumnUnits , previousPage )
2024-04-11 15:51:56 +02:00
} ) )
header . Add ( showDataGroupedButton )
} else {
showDataRawButton := getWidgetCentered ( widget . NewButton ( "Show Data Raw" , func ( ) {
2024-06-07 02:04:13 +02:00
setViewUserStatisticsDataPage ( window , attributeTitle , rightColumnName , showPercentageColumn , statisticsDatumsList , true , groupedStatisticsDatumsList , false , attributeColumnUnits , rightColumnUnits , previousPage )
2024-04-11 15:51:56 +02:00
} ) )
header . Add ( showDataRawButton )
}
header . Add ( widget . NewSeparator ( ) )
}
2024-06-07 02:04:13 +02:00
getStatisticsDatumsListToShow := func ( ) [ ] statisticsDatum . StatisticsDatum {
2024-04-11 15:51:56 +02:00
if ( groupingPerformed == true && showGroupedStatistics == true ) {
2024-06-07 02:04:13 +02:00
return groupedStatisticsDatumsList
2024-04-11 15:51:56 +02:00
}
2024-06-07 02:04:13 +02:00
return statisticsDatumsList
2024-04-11 15:51:56 +02:00
}
2024-06-07 02:04:13 +02:00
statisticsDatumsToShowList := getStatisticsDatumsListToShow ( )
2024-04-11 15:51:56 +02:00
getStatisticsDataGrid := func ( ) ( * fyne . Container , error ) {
allValuesSummed := float64 ( 0 )
if ( showPercentageColumn == true ) {
2024-06-07 02:04:13 +02:00
for _ , datum := range statisticsDatumsToShowList {
2024-04-11 15:51:56 +02:00
2024-06-07 02:04:13 +02:00
datumValue := datum . Value
2024-04-11 15:51:56 +02:00
2024-06-07 02:04:13 +02:00
allValuesSummed += datumValue
2024-04-11 15:51:56 +02:00
}
}
2024-06-07 02:04:13 +02:00
attributeColumnValuesList := make ( [ ] string , 0 , len ( statisticsDatumsToShowList ) )
rightColumnValuesList := make ( [ ] string , 0 , len ( statisticsDatumsToShowList ) )
percentageColumnValuesList := make ( [ ] string , 0 , len ( statisticsDatumsToShowList ) )
2024-04-11 15:51:56 +02:00
2024-06-07 02:04:13 +02:00
for _ , datum := range statisticsDatumsToShowList {
2024-04-11 15:51:56 +02:00
2024-06-07 02:04:13 +02:00
// Each datum is represents a row in the data grid.
2024-04-11 15:51:56 +02:00
2024-06-07 02:04:13 +02:00
datumLabelFormatted := datum . LabelFormatted
2024-04-11 15:51:56 +02:00
2024-06-07 02:04:13 +02:00
datumValueFormatted := datum . ValueFormatted
2024-04-11 15:51:56 +02:00
2024-06-07 02:04:13 +02:00
attributeColumnValuesList = append ( attributeColumnValuesList , datumLabelFormatted )
rightColumnValuesList = append ( rightColumnValuesList , datumValueFormatted )
2024-04-11 15:51:56 +02:00
if ( showPercentageColumn == true ) {
2024-06-07 02:04:13 +02:00
datumValue := datum . Value
2024-04-11 15:51:56 +02:00
getValuePercentage := func ( ) float64 {
if ( allValuesSummed == 0 ) {
return 0
}
2024-06-07 02:04:13 +02:00
valuePercentage := ( datumValue / allValuesSummed ) * 100
2024-04-11 15:51:56 +02:00
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 )
}