package gui // desireStatisticsGui.go implements pages to view a user's desire statistics import "fyne.io/fyne/v2" 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/desires/mateDesires" import "seekia/internal/desires/myDesireStatistics" import "seekia/internal/desires/myLocalDesires" import "seekia/internal/helpers" import "seekia/internal/network/appNetworkType/getAppNetworkType" import "time" import "errors" // This page is used to view the statistics of a single desire func setViewMyMateDesireStatisticsPage(window fyne.Window, desireTitle string, desireName string, showChartsButton bool, barOrDonutChart string, attributeName string, previousPage func()){ currentPage := func(){setViewMyMateDesireStatisticsPage(window, desireTitle, desireName, showChartsButton, barOrDonutChart, attributeName, previousPage)} // We use a page identifier to uniquely identify this page and detect if the user is still viewing the page // If the user leaves this page before the getStatistics goroutine is complete, once it completes, the page will not refresh 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("My Desire Statistics - " + desireTitle) backButton := getBackButtonCentered(previousPage) viewChartsFunction := func(){ if (showChartsButton == false){ return } if (barOrDonutChart == "Bar"){ setViewUserAttributeStatisticsPage_BarChart(window, "Mate", attributeName, "Number Of Users", " Users", true, false, false, nil, false, nil, nil, currentPage) return } if (barOrDonutChart == "Donut"){ setViewUserAttributeStatisticsPage_DonutChart(window, "Mate", attributeName, false, false, nil, false, nil, nil, currentPage) return } setErrorEncounteredPage(window, errors.New("setViewMyMateDesireStatisticsPage called with invalid barOrDonutChart: " + barOrDonutChart), currentPage) } //Outputs: // -bool: Desire is disabled // -bool: Filter All is disabled // -bool: Require Response is disabled // -error getDesireIsDisabledStatus := func()(bool, bool, bool, error){ getFilterAllIsEnabledBool := func()(bool, error){ exists, currentFilterAllSetting, err := myLocalDesires.GetDesire(desireName + "_FilterAll") if (err != nil) { return false, err } if (exists == true && currentFilterAllSetting == "Yes"){ return true, nil } return false, nil } filterAllIsEnabled, err := getFilterAllIsEnabledBool() if (err != nil) { return false, false, false, err } if (filterAllIsEnabled == true){ // Desire is enabled return false, false, false, nil } // Most desires allow the user to enable Require Response, which filters user who did not respond allowsRequireResponse, _ := mateDesires.CheckIfDesireAllowsRequireResponse(desireName) if (allowsRequireResponse == false){ // Desire is disabled, no users will be filtered return true, true, false, nil } getRequireResponseIsEnabledBool := func()(bool, error){ exists, currentRequireResponse, err := myLocalDesires.GetDesire(desireName + "_RequireResponse") if (err != nil) { return false, err } if (exists == true && currentRequireResponse == "Yes"){ return true, nil } return false, nil } requireResponseIsEnabled, err := getRequireResponseIsEnabledBool() if (err != nil) { return false, false, false, err } if (requireResponseIsEnabled == true){ // Desire is enabled, because we require a response return false, false, false, nil } // FilterAll and RequireResponse are both disabled, thus the desire is disabled return true, true, true, nil } desireIsDisabled, filterAllIsDisabled, requireResponseIsDisabled, err := getDesireIsDisabledStatus() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (desireIsDisabled == true){ getDisabledOptionsString := func()string{ if (filterAllIsDisabled == true && requireResponseIsDisabled == true){ return "Filter All or Require Response" } // filterAllIsDisabled == true && requireResponseIsDisabled == false return "Filter All" } disabledOptionsString := getDisabledOptionsString() description1 := getBoldLabelCentered("You do not have " + disabledOptionsString + " enabled for this desire.") description2 := getLabelCentered("This means that all users will pass the desire.") description3Label := getLabelCentered("Without " + disabledOptionsString + " enabled, the desire only impacts user match scores.") filterOptionsHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setDesireFilterOptionsExplainerPage(window, currentPage) }) description3Row := container.NewHBox(layout.NewSpacer(), description3Label, filterOptionsHelpButton, layout.NewSpacer()) page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3Row) if (showChartsButton == true){ page.Add(widget.NewSeparator()) description4 := getLabelCentered("Visualize the distribution of user responses on a chart.") page.Add(description4) viewChartButton := getWidgetCentered(widget.NewButtonWithIcon("View Chart", theme.InfoIcon(), viewChartsFunction)) page.Add(viewChartButton) } setPageContent(page, window) return } appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } allIdentitiesStatisticsBinding := binding.NewString() matchStatisticsBinding := binding.NewString() updateBindingsFunction := func(){ statisticsFound := false updateBindingsWithProgressEllipsesFunction := func(){ secondsElapsed := 0 for { if (statisticsFound == true){ return } pageHasChanged := checkIfPageHasChangedFunction() if (pageHasChanged == true){ return } if (secondsElapsed % 3 == 0){ allIdentitiesStatisticsBinding.Set("Loading.") matchStatisticsBinding.Set("Loading.") } else if (secondsElapsed %3 == 1){ allIdentitiesStatisticsBinding.Set("Loading..") matchStatisticsBinding.Set("Loading..") } else { allIdentitiesStatisticsBinding.Set("Loading...") matchStatisticsBinding.Set("Loading...") } time.Sleep(time.Second) secondsElapsed += 1 } } go updateBindingsWithProgressEllipsesFunction() totalNumberOfMateIdentities, numberOfMateIdentitiesWhoPassDesire, percentageOfMateIdentitiesWhoPassDesire, numberOfMatches, numberOfMatchesWhoPassDesire, percentageOfMatchesWhoPassDesire, err := myDesireStatistics.GetMyDesireStatistics(desireName, appNetworkType) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } statisticsFound = true totalNumberOfMateIdentitiesString := helpers.ConvertInt64ToString(totalNumberOfMateIdentities) numberOfMateIdentitiesWhoPassDesireString := helpers.ConvertInt64ToString(numberOfMateIdentitiesWhoPassDesire) percentageOfMateIdentiesWhoPassDesireString := helpers.ConvertFloat64ToStringRounded(percentageOfMateIdentitiesWhoPassDesire, 1) numberOfMatchesString := helpers.ConvertInt64ToString(numberOfMatches) numberOfMatchesWhoPassDesireString := helpers.ConvertInt64ToString(numberOfMatchesWhoPassDesire) percentageOfMatchesWhoPassDesireString := helpers.ConvertFloat64ToStringRounded(percentageOfMatchesWhoPassDesire, 1) allIdentitiesStatisticsString := numberOfMateIdentitiesWhoPassDesireString + "/" + totalNumberOfMateIdentitiesString + " = " + percentageOfMateIdentiesWhoPassDesireString + "%" matchesStatisticsString := numberOfMatchesWhoPassDesireString + "/" + numberOfMatchesString + " = " + percentageOfMatchesWhoPassDesireString + "%" allIdentitiesStatisticsBinding.Set(allIdentitiesStatisticsString) matchStatisticsBinding.Set(matchesStatisticsString) } description1 := getLabelCentered("Below are your " + desireTitle + " desire statistics.") description2 := getLabelCentered("They are the statistics of the users who pass your " + desireTitle + " desires.") description3 := getLabelCentered("My Matches divides by the number of matches you would have without the desire.") allUsersLabel := getBoldLabelCentered("All Users:") allIdentitiesStatisticsLabel := getWidgetCentered(widget.NewLabelWithData(allIdentitiesStatisticsBinding)) myMatchesLabel := getBoldLabelCentered("My Matches:") matchIdentitiesStatisticsLabel := getWidgetCentered(widget.NewLabelWithData(matchStatisticsBinding)) accuracyWarningButton := getWidgetCentered(widget.NewButtonWithIcon("Statistics Warning", theme.WarningIcon(), func(){ setMateDesireStatisticsWarningPage(window, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), allUsersLabel, allIdentitiesStatisticsLabel, myMatchesLabel, matchIdentitiesStatisticsLabel, widget.NewSeparator(), accuracyWarningButton) if (showChartsButton == true){ page.Add(widget.NewSeparator()) description4 := getLabelCentered("Visualize the distribution of users on a chart.") page.Add(description4) viewChartButton := getWidgetCentered(widget.NewButtonWithIcon("View Chart", theme.InfoIcon(), viewChartsFunction)) page.Add(viewChartButton) } setPageContent(page, window) go updateBindingsFunction() } func setViewAllMyDesireStatisticsPage(window fyne.Window, statisticsReady bool, numberOfMateIdentities int64, numberOfMatches int64, statisticsItemsList []myDesireStatistics.DesireStatisticsItem, previousPage func()){ 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 } currentPage := func(){setViewAllMyDesireStatisticsPage(window, statisticsReady, numberOfMateIdentities, numberOfMatches, statisticsItemsList, previousPage)} title := getPageTitleCentered("My Mate Desire Statistics") backButton := getBackButtonCentered(previousPage) if (statisticsReady == false){ appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } loadingTextBinding := binding.NewString() loadingTextBinding.Set("Loading Statistics...") loadingProgressBinding := binding.NewString() loadingTextLabel := widget.NewLabelWithData(loadingTextBinding) loadingTextLabel.TextStyle = getFyneTextStyle_Bold() loadingTextLabelCentered := getWidgetCentered(loadingTextLabel) loadingProgressLabel := getWidgetCentered(widget.NewLabelWithData(loadingProgressBinding)) calculateStatisticsAndRefreshPageFunction := func(){ progressIdentifier, _ := helpers.GetNewRandomHexString(16) statisticsComplete := false updateLoadingBindingFunction := func(){ startTime := time.Now().Unix() for { currentTime := time.Now().Unix() secondsElapsed := currentTime - startTime if (secondsElapsed % 3 == 0){ loadingTextBinding.Set("Loading Statistics.") } else if (secondsElapsed % 3 == 1){ loadingTextBinding.Set("Loading Statistics..") } else { loadingTextBinding.Set("Loading Statistics...") } statusExists, newProgressStatus := appMemory.GetMemoryEntry(progressIdentifier) if (statusExists == true){ loadingProgressBinding.Set(newProgressStatus) } pageHasChanged := checkIfPageHasChangedFunction() if (pageHasChanged == true){ return } if (statisticsComplete == true){ return } time.Sleep(100 * time.Millisecond) } } go updateLoadingBindingFunction() numberOfMateIdentities, numberOfMatches, myDesireStatisticsItemsList, err := myDesireStatistics.GetAllMyDesireStatistics(progressIdentifier, appNetworkType) if (err != nil) { statisticsComplete = true pageHasChanged := checkIfPageHasChangedFunction() if (pageHasChanged == false){ setErrorEncounteredPage(window, err, previousPage) } return } statisticsComplete = true pageHasChanged := checkIfPageHasChangedFunction() if (pageHasChanged == false){ setViewAllMyDesireStatisticsPage(window, true, numberOfMateIdentities, numberOfMatches, myDesireStatisticsItemsList, previousPage) } } page := container.NewVBox(title, backButton, widget.NewSeparator(), loadingTextLabelCentered, loadingProgressLabel) setPageContent(page, window) go calculateStatisticsAndRefreshPageFunction() return } setLoadingScreen(window, "My Mate Desire Statistics", "Loading desire statistics...") getStatisticsSection := func()(*fyne.Container, error){ // These are the desires for which FilterAll and Require Response is disabled // These desires will not filter any matches. They only effect match score disabledDesiresList := make([]string, 0) desireNameLabel := getItalicLabelCentered("Desire Name") allUsersLabel := getItalicLabelCentered("All Users") myMatchesLabel := getItalicLabelCentered("My Matches") desireTitlesColumn := container.NewVBox(desireNameLabel, widget.NewSeparator()) allUsersColumn := container.NewVBox(allUsersLabel, widget.NewSeparator()) myMatchesColumn := container.NewVBox(myMatchesLabel, widget.NewSeparator()) numberOfMateIdentitiesString := helpers.ConvertInt64ToString(numberOfMateIdentities) numberOfMatchesString := helpers.ConvertInt64ToString(numberOfMatches) for _, desireObject := range statisticsItemsList{ desireName := desireObject.DesireName numberOfUsersWhoPassDesire := desireObject.NumberOfUsersWhoPassDesire percentageOfUsersWhoPassDesire := desireObject.PercentageOfUsersWhoPassDesire numberOfDesireExcludedMatches := desireObject.NumberOfDesireExcludedMatches percentageOfDesireExcludedMatchesWhoPassDesire := desireObject.PercentageOfDesireExcludedMatchesWhoPassDesire desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName) if (err != nil){ return nil, err } desireTitleTranslated := translate(desireTitle) checkIfDesireIsDisabled := func()(bool, error){ exists, filterAllStatus, err := myLocalDesires.GetDesire(desireName + "_FilterAll") if (err != nil) { return false, err } if (exists == true && filterAllStatus == "Yes"){ return false, nil } requireResponseAllowed, _ := mateDesires.CheckIfDesireAllowsRequireResponse(desireName) if (requireResponseAllowed == true){ exists, requireResponseStatus, err := myLocalDesires.GetDesire(desireName + "_RequireResponse") if (err != nil) { return false, err } if (exists == true && requireResponseStatus == "Yes"){ return false, nil } } // Desire is disabled // We use 99.9 because floating point is not exact, we really mean 100%. if (percentageOfUsersWhoPassDesire < 99.9){ return false, errors.New("Disabled desire does not have a 100% pass rate: " + desireName) } return true, nil } desireIsDisabled, err := checkIfDesireIsDisabled() if (err != nil) { return nil, err } if (desireIsDisabled == true){ disabledDesiresList = append(disabledDesiresList, desireTitleTranslated) continue } desireTitleLabel := getBoldLabelCentered(desireTitleTranslated) numberOfUsersWhoPassDesireString := helpers.ConvertInt64ToString(numberOfUsersWhoPassDesire) percentageOfUsersWhoPassDesireString := helpers.ConvertFloat64ToStringRounded(percentageOfUsersWhoPassDesire, 1) numberOfDesireExcludedMatchesString := helpers.ConvertInt64ToString(numberOfDesireExcludedMatches) percentageOfDesireExcludedMatchesWhoPassDesireString := helpers.ConvertFloat64ToStringRounded(percentageOfDesireExcludedMatchesWhoPassDesire, 1) allUsersLabel := getBoldLabelCentered(numberOfUsersWhoPassDesireString + "/" + numberOfMateIdentitiesString + " = " + percentageOfUsersWhoPassDesireString + "%") matchesLabel := getBoldLabelCentered(numberOfMatchesString + "/" + numberOfDesireExcludedMatchesString + " = " + percentageOfDesireExcludedMatchesWhoPassDesireString + "%") desireTitlesColumn.Add(desireTitleLabel) allUsersColumn.Add(allUsersLabel) myMatchesColumn.Add(matchesLabel) desireTitlesColumn.Add(widget.NewSeparator()) allUsersColumn.Add(widget.NewSeparator()) myMatchesColumn.Add(widget.NewSeparator()) } numberOfDisabledDesires := len(disabledDesiresList) if (numberOfDisabledDesires == len(statisticsItemsList)){ description1 := getBoldLabelCentered("All of your desires are disabled.") description2 := getLabelCentered("None of your desires have Filter All or Require Response enabled.") description3 := getLabelCentered("All users are your matches.") viewDisabledDesiresButton := getWidgetCentered(widget.NewButtonWithIcon("View Disabled Desires", theme.VisibilityIcon(), func(){ setViewAllMyDisabledDesiresPage(window, disabledDesiresList, currentPage) })) statisticsSection := container.NewVBox(description1, description2, description3, viewDisabledDesiresButton) return statisticsSection, nil } description1 := getLabelCentered("Below are the percentages of users who pass each of your desires.") description2 := getLabelCentered("My Matches divides by the number of matches you would have without the desire.") accuracyWarningButton := getWidgetCentered(widget.NewButtonWithIcon("Statistics Warning", theme.WarningIcon(), func(){ setMateDesireStatisticsWarningPage(window, currentPage) })) statisticsGrid := container.NewHBox(layout.NewSpacer(), desireTitlesColumn, allUsersColumn, myMatchesColumn, layout.NewSpacer()) numberOfDisabledDesiresString := helpers.ConvertIntToString(numberOfDisabledDesires) disabledDesiresDescription1 := getBoldLabelCentered("There are " + numberOfDisabledDesiresString + " desires with Filter All and Require Response disabled.") disabledDesiresDescription2 := getLabelCentered("These desires do not filter any matches.") viewDisabledDesiresButton := getWidgetCentered(widget.NewButtonWithIcon("View Disabled Desires", theme.VisibilityIcon(), func(){ setViewAllMyDisabledDesiresPage(window, disabledDesiresList, currentPage) })) statisticsSection := container.NewVBox(description1, description2, widget.NewSeparator(), accuracyWarningButton, widget.NewSeparator(), statisticsGrid, widget.NewSeparator(), disabledDesiresDescription1, disabledDesiresDescription2, viewDisabledDesiresButton) return statisticsSection, nil } statisticsSection, err := getStatisticsSection() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), statisticsSection) setPageContent(page, window) } func setViewAllMyDisabledDesiresPage(window fyne.Window, disabledDesiresList []string, previousPage func()){ title := getPageTitleCentered("My Mate Desire Statistics") backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered("Disabled Desires") description1 := getLabelCentered("Below are your desires with Filter All and Require Response disabled.") description2 := getLabelCentered("They will not filter any matches.") description3 := getLabelCentered("They will only effect match scores.") disabledDesiresGrid := container.NewGridWithColumns(2) for _, desireTitle := range disabledDesiresList{ desireTitleLabel := getBoldLabelCentered(desireTitle) disabledDesiresGrid.Add(desireTitleLabel) } disabledDesiresGridCentered := getContainerCentered(disabledDesiresGrid) page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), disabledDesiresGridCentered) setPageContent(page, window) }