seekia/gui/viewProfileGui.go

4636 lines
176 KiB
Go

package gui
// viewProfileGui.go implements pages to view user profiles
import "fyne.io/fyne/v2"
import "fyne.io/fyne/v2/canvas"
import "fyne.io/fyne/v2/container"
import "fyne.io/fyne/v2/dialog"
import "fyne.io/fyne/v2/layout"
import "fyne.io/fyne/v2/theme"
import "fyne.io/fyne/v2/widget"
import "seekia/resources/worldLanguages"
import "seekia/resources/worldLocations"
import "seekia/resources/trainedPredictionModels"
import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases"
import "seekia/resources/geneticReferences/traits"
import "seekia/internal/appMemory"
import "seekia/internal/encoding"
import "seekia/internal/genetics/companyAnalysis"
import "seekia/internal/genetics/createCoupleGeneticAnalysis"
import "seekia/internal/genetics/createPersonGeneticAnalysis"
import "seekia/internal/genetics/locusValue"
import "seekia/internal/genetics/myChosenAnalysis"
import "seekia/internal/genetics/myPeople"
import "seekia/internal/genetics/readGeneticAnalysis"
import "seekia/internal/globalSettings"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/imagery"
import "seekia/internal/mateQuestionnaire"
import "seekia/internal/moderation/moderatorRanking"
import "seekia/internal/moderation/trustedViewableStatus"
import "seekia/internal/moderation/verifiedStickyStatus"
import "seekia/internal/myIdentity"
import "seekia/internal/network/appNetworkType/getAppNetworkType"
import "seekia/internal/network/getFundedStatuses"
import "seekia/internal/network/myBroadcasts"
import "seekia/internal/profiles/attributeDisplay"
import "seekia/internal/profiles/calculatedAttributes"
import "seekia/internal/profiles/myLocalProfiles"
import "seekia/internal/profiles/myProfileExports"
import "seekia/internal/profiles/myProfileStatus"
import "seekia/internal/profiles/profileStorage"
import "seekia/internal/profiles/readProfiles"
import "seekia/internal/profiles/viewableProfiles"
import "slices"
import "strings"
import "image"
import "time"
import "errors"
// This page will show user the difference between local/public and let them choose
func setViewMyLocalOrPublicProfilePage(window fyne.Window, myProfileType string, previousPage func()){
setLoadingScreen(window, "Loading My Profile", "Loading...")
if (myProfileType != "Mate" && myProfileType != "Host" && myProfileType != "Moderator"){
setErrorEncounteredPage(window, errors.New("setViewMyLocalOrPublicProfilePage called with invalid profile type: " + myProfileType), previousPage)
return
}
currentPage := func(){setViewMyLocalOrPublicProfilePage(window, myProfileType, previousPage)}
title := getPageTitleCentered(translate("View My Profile"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("View My " + myProfileType + " Profile")
myIdentityExists, _, err := myIdentity.GetMyIdentityHash(myProfileType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (myIdentityExists == false){
description1 := getBoldLabelCentered("Your " + myProfileType + " identity does not exist.")
description2 := getLabelCentered("Create your " + myProfileType + " identity below.")
createIdentityButton := getWidgetCentered(widget.NewButtonWithIcon("Create Identity", theme.NavigateNextIcon(), func(){
setChooseNewIdentityHashPage(window, myProfileType, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, createIdentityButton)
setPageContent(page, window)
return
}
description1 := getLabelCentered("Choose which profile to view.")
description2 := getLabelCentered("Your Local profile is the profile stored on your computer.")
description3 := getLabelCentered("Your Public profile is the most recent profile that you have broadcasted.")
localIcon, err := getFyneImageIcon("Local")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
publicIcon, err := getFyneImageIcon("Broadcast")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
viewLocalProfileButton := widget.NewButton("Local", func(){
setViewMyProfilePage(window, myProfileType, "Local", currentPage)
})
viewPublicProfileButton := widget.NewButton("Public", func(){
setViewMyProfilePage(window, myProfileType, "Public", currentPage)
})
viewLocalButtonWithIcon := container.NewGridWithColumns(1, localIcon, viewLocalProfileButton)
viewPublicButtonWithIcon := container.NewGridWithColumns(1, publicIcon, viewPublicProfileButton)
buttonsRow := container.NewHBox(layout.NewSpacer(), viewLocalButtonWithIcon, viewPublicButtonWithIcon, layout.NewSpacer())
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), buttonsRow, widget.NewSeparator())
setPageContent(page, window)
}
func setViewMyProfilePage(window fyne.Window, myProfileType string, localOrPublic string, previousPage func()){
if (myProfileType != "Mate" && myProfileType != "Host" && myProfileType != "Moderator"){
setErrorEncounteredPage(window, errors.New("setViewMyProfilePage called with invalid myProfileType: " + myProfileType), previousPage)
return
}
if (localOrPublic != "Local" && localOrPublic != "Public"){
setErrorEncounteredPage(window, errors.New("setViewMyProfilePage called with invalid localOrPublic: " + localOrPublic), previousPage)
return
}
currentPage := func(){setViewMyProfilePage(window, myProfileType, localOrPublic, previousPage)}
title := getPageTitleCentered("View My Profile")
backButton := getBackButtonCentered(previousPage)
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(myProfileType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (myIdentityExists == false){
description1 := getBoldLabelCentered("Your " + myProfileType + " identity does not exist.")
description2 := getLabelCentered("Create your " + myProfileType + " identity below.")
createIdentityButton := getWidgetCentered(widget.NewButtonWithIcon("Create Identity", theme.NavigateNextIcon(), func(){
setChooseNewIdentityHashPage(window, myProfileType, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, createIdentityButton)
setPageContent(page, window)
return
}
if (myProfileType == "Mate"){
myGenomePersonIdentifierExists, myGenomePersonIdentifier, err := myLocalProfiles.GetProfileData("Mate", "GenomePersonIdentifier")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (myGenomePersonIdentifierExists == true){
anyGenomesExist, personAnalysisIsReady, _, err := myPeople.CheckIfPersonAnalysisIsReady(myGenomePersonIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (anyGenomesExist == true && personAnalysisIsReady == false){
description1 := getBoldLabelCentered(translate("Your profile contains a linked genome person."))
description2 := getLabelCentered(translate("You need to perform your genetic analysis."))
description3 := getLabelCentered(translate("Only the information you choose will be shared in your profile."))
performAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Perform Analysis"), theme.NavigateNextIcon(), func(){
setConfirmPerformPersonAnalysisPage(window, myGenomePersonIdentifier, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, performAnalysisButton)
setPageContent(page, window)
return
}
}
}
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
err = myProfileExports.UpdateMyExportedProfile(myProfileType, appNetworkType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
profileFound, profileHash, _, rawProfileMap, err := myProfileExports.GetMyExportedProfile(myProfileType, appNetworkType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (profileFound == false){
setErrorEncounteredPage(window, errors.New("My exported profile not found after exporting."), previousPage)
return
}
getAnyLocalProfileAttributeFunction, err := calculatedAttributes.GetRetrieveAnyProfileAttributeIncludingCalculatedFunction(1, rawProfileMap)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
exists, _, iAmDisabled, err := getAnyLocalProfileAttributeFunction("Disabled")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (exists == true && iAmDisabled == "Yes"){
description1 := getBoldLabelCentered("Your " + myProfileType + " profile is disabled.")
description2 := getLabelCentered("Re-enable it on the Broadcast page.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2)
setPageContent(page, window)
return
}
if (localOrPublic == "Local"){
setViewUserProfilePage(window, true, profileHash, 0, getAnyLocalProfileAttributeFunction, previousPage)
return
}
// localOrPublic == "Public"
identityExists, broadcastProfileExists, publicProfileHash, getAnyPublicProfileAttributeFunction, err := myBroadcasts.GetRetrieveAnyAttributeFromMyBroadcastProfileFunction(myIdentityHash, appNetworkType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (identityExists == false){
setErrorEncounteredPage(window, errors.New("My identity not found after being found already."), previousPage)
return
}
if (broadcastProfileExists == false){
description1 := getBoldLabelCentered("Your public profile does not exist.")
description2 := getLabelCentered("You have not broadcast your profile.")
description3 := getLabelCentered("Broadcast your profile on the Profile - Broadcast page.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3)
setPageContent(page, window)
return
}
myIdentityExists, myProfileIsActive, err := myProfileStatus.GetMyProfileIsActiveStatus(myIdentityHash, appNetworkType)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (myIdentityExists == false){
setErrorEncounteredPage(window, errors.New("My identity not found after being found already."), previousPage)
return
}
if (myProfileIsActive == false){
description1 := getBoldLabelCentered("Your public profile has expired.")
description2 := getLabelCentered("You must broadcast your profile again.")
description3 := getLabelCentered("Broadcast your profile on the Profile - Broadcast page.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3)
setPageContent(page, window)
return
}
setViewUserProfilePage(window, true, publicProfileHash, 0, getAnyPublicProfileAttributeFunction, previousPage)
}
func setViewPeerProfilePageFromIdentityHash(window fyne.Window, peerIdentityHash [16]byte, previousPage func()){
setLoadingScreen(window, "View Profile", "Loading Profile...")
currentPage := func(){setViewPeerProfilePageFromIdentityHash(window, peerIdentityHash, previousPage)}
title := getPageTitleCentered(translate("View Profile"))
backButton := getBackButtonCentered(previousPage)
userIdentityType, err := identity.GetIdentityTypeFromIdentityHash(peerIdentityHash)
if (err != nil) {
peerIdentityHashHex := encoding.EncodeBytesToHexString(peerIdentityHash[:])
setErrorEncounteredPage(window, errors.New("setViewPeerProfilePageFromIdentityHash called with invalid peerIdentityHash: " + peerIdentityHashHex), previousPage)
return
}
// We only allow unknown viewable status profiles to be viewed if the user profile type is host/moderator
getAllowUnknownStatusBool := func()bool{
if (userIdentityType == "Mate"){
return false
}
return true
}
allowUnknownStatusBool := getAllowUnknownStatusBool()
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
viewableProfileExists, profileHash, getAnyAttributeFromViewableProfileFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(peerIdentityHash, appNetworkType, true, allowUnknownStatusBool, true)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (viewableProfileExists == true){
// The user has a viewable profile.
// We display it.
setViewUserProfilePage(window, false, profileHash, 0, getAnyAttributeFromViewableProfileFunction, previousPage)
return
}
// Viewable profile does not exist
// We check to see if any profile exists
//Outputs:
// -bool: Viewable status is known
// -bool: Identity Is Viewable status
// -error
getIdentityIsViewableStickyStatus := func()(bool, bool, error){
// First try verified status, then try trusted if verified is not available
downloadingRequiredReviews, networkParametersExist, stickyConsensusEstablished, identityIsViewableStatus, err := verifiedStickyStatus.GetVerifiedIdentityIsViewableStickyStatus(peerIdentityHash, appNetworkType)
if (err != nil) { return false, false, err }
if (downloadingRequiredReviews == true && networkParametersExist == true && stickyConsensusEstablished == true){
return true, identityIsViewableStatus, nil
}
statusIsKnown, identityIsViewable, _, err := trustedViewableStatus.GetTrustedIdentityIsViewableStatus(peerIdentityHash, appNetworkType)
if (err != nil) { return false, false, err }
if (statusIsKnown == true){
return true, identityIsViewable, nil
}
return false, false, nil
}
identityViewableStatusIsKnown, identityIsViewableStatus, err := getIdentityIsViewableStickyStatus()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
theirNewestProfileExists, newestProfileVersion, newestProfileHash, _, _, newestProfileRawMap, err := profileStorage.GetNewestUserProfile(peerIdentityHash, appNetworkType)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (theirNewestProfileExists == false){
// Their profile does not exist.
downloadProfileFunction := func(){setDownloadMissingUserProfilePage(window, peerIdentityHash, true, true, previousPage, currentPage, previousPage)}
if (identityViewableStatusIsKnown == true && identityIsViewableStatus == false){
description1 := getLabelCentered("This user is banned.")
description2 := getLabelCentered("You do not have their profile downloaded.")
description3 := getLabelCentered("Do you want to try to download their profile?")
downloadButton := getWidgetCentered(widget.NewButtonWithIcon("Download", theme.ConfirmIcon(), downloadProfileFunction))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, downloadButton)
setPageContent(page, window)
return
}
// Identity is not banned. Profile does not exist
// We check if user's identity is known to be expired.
statusIsKnown, identityIsFunded, _, err := getFundedStatuses.GetIdentityIsFundedStatus(peerIdentityHash, appNetworkType)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (statusIsKnown == true && identityIsFunded == false){
// Identity is known to be expired
description1 := getBoldLabelCentered("This user's identity is expired.")
description2 := getLabelCentered("Their profile is expired from the network.")
description3 := getLabelCentered("You can try to update this status to see if the user's identity is funded.")
updateButton := getWidgetCentered(widget.NewButtonWithIcon("Update", theme.ViewRefreshIcon(), func(){
//TODO:
// Use manualDownloads to update the user identity's funded status
showUnderConstructionDialog(window)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, updateButton)
setPageContent(page, window)
return
}
// We will commence profile download without any prompt.
downloadProfileFunction()
return
}
// We know their profile exists, but it is not viewable
// For host/moderator profiles, this must mean that the profile/identity is banned
// For mate profiles, it could also mean that we have not downloaded the profile's sticky viewable status
getAnyNewestProfileAttributeFunction, err := calculatedAttributes.GetRetrieveAnyProfileAttributeIncludingCalculatedFunction(newestProfileVersion, newestProfileRawMap)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
viewNewestProfileFunction := func(){setViewUserProfilePage(window, false, newestProfileHash, 0, getAnyNewestProfileAttributeFunction, previousPage)}
_, newestProfileIsDisabled, err := readProfiles.ReadProfileHashMetadata(newestProfileHash)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (newestProfileIsDisabled == true){
// Their newest profile is disabled.
// The user must be banned.
// We can show it to the user.
viewNewestProfileFunction()
return
}
if (identityViewableStatusIsKnown == true && identityIsViewableStatus == false){
description1 := getBoldLabelCentered("This user is banned.")
description2 := getLabelCentered("Do you want to view their profile anyway?")
viewProfileButton := getWidgetCentered(widget.NewButtonWithIcon("View Profile", theme.VisibilityIcon(), viewNewestProfileFunction))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, viewProfileButton)
setPageContent(page, window)
return
}
if (userIdentityType == "Host" || userIdentityType == "Moderator"){
// Host/moderator profiles are not considered unviewable until they are banned, or their author is banned.
// An unreviewed host/moderator profile is considered viewable.
// We already checked to see if the user's identity is banned
// Therefore, we know that this profile is banned.
description1 := getBoldLabelCentered("This user's profile is banned.")
description2 := getLabelCentered("Do you want to view their profile anyway?")
viewProfileButton := getWidgetCentered(widget.NewButtonWithIcon("View Profile", theme.VisibilityIcon(), viewNewestProfileFunction))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, viewProfileButton)
setPageContent(page, window)
return
}
// userIdentityType == "Mate"
// We know the user's identity is either viewable or unknown
// We check to see if mate profile is viewable
//Outputs:
// -bool: Profile metadata found
// -bool: Viewable Status is known
// -bool: Profile is viewable
// -error
getNewestProfileIsViewableStatus := func()(bool, bool, bool, error){
profileIsDisabled, profileMetadataIsKnown, profileNetworkType, profileIdentityHash, downloadingRequiredReviews, networkParametersExist, stickyConsensusEstablished, profileIsViewableStatus, err := verifiedStickyStatus.GetVerifiedProfileIsViewableStickyStatus(newestProfileHash)
if (err != nil) { return false, false, false, err }
if (profileIsDisabled == true){
return false, false, false, errors.New("GetVerifiedProfileIsViewableStickyStatus returning different profileIsDisabled status than ReadProfileHashMetadata.")
}
if (profileMetadataIsKnown == false){
// Profile metadata must have been deleted after earlier check.
return false, false, false, nil
}
if (profileNetworkType != appNetworkType){
return false, false, false, errors.New("GetVerifiedProfileIsViewableStickyStatus returning different networkType than requested from GetNewestUserProfile.")
}
if (profileIdentityHash != peerIdentityHash){
return false, false, false, errors.New("GetVerifiedProfileIsViewableStickyStatus returning different profileIdentityHash")
}
if (downloadingRequiredReviews == true && networkParametersExist == true && stickyConsensusEstablished == true){
return true, true, profileIsViewableStatus, nil
}
return true, false, false, nil
}
newestProfileMetadataFound, profileViewableStatusIsKnown, profileIsViewableStatus, err := getNewestProfileIsViewableStatus()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (newestProfileMetadataFound == false){
// This should only happen if the profile metadata was deleted after our earlier check
// We show a loading screen so this issue can be identified if it is because of an error
setLoadingScreen(window, "Loading Profile", "Looking for profile...")
time.Sleep(time.Second)
currentPage()
return
}
if (profileViewableStatusIsKnown == true && profileIsViewableStatus == false){
description1 := getBoldLabelCentered("This user's profile is not viewable.")
description2 := getLabelCentered("It may be banned, or not yet approved.")
description3 := getLabelCentered("Do you still want to view it?")
viewProfileButton := getWidgetCentered(widget.NewButtonWithIcon("View Profile", theme.VisibilityIcon(), viewNewestProfileFunction))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, viewProfileButton)
setPageContent(page, window)
return
}
// We know the profile is not considered viewable
// We know the identity is not banned, or its status is unknown
// We know the profile is not known to be unviewable
// Therefore, the profile viewable status must be unknown
description1 := getBoldLabelCentered("This user's profile has an unknown moderation status.")
description2 := getLabelCentered("Do you still want to view their profile?")
description3 := getLabelCentered("If not, you must wait for Seekia to download the profile's moderation status.")
description4 := getLabelCentered("After the download, you will know if the profile is banned or viewable.")
description5 := getLabelCentered("This will happen automatically in the background.")
description6 := getLabelCentered("Refresh the page to check if the status is downloaded.")
refreshButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh", theme.ViewRefreshIcon(), currentPage))
viewProfileButton := widget.NewButtonWithIcon("View Profile", theme.VisibilityIcon(), viewNewestProfileFunction)
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, viewProfileButton, widget.NewSeparator(), description3, description4, description5, description6, refreshButton)
setPageContent(page, window)
}
func setViewUserProfilePage(window fyne.Window, profileIsMine bool, profileHash [28]byte, currentImageIndex int, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()) {
pageIdentifier, err := helpers.GetNewRandomHexString(16)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier)
getTitleText := func()string{
if (profileIsMine == true){
return "Viewing My Profile"
}
return "Viewing Profile"
}
titleText := getTitleText()
setLoadingScreen(window, titleText, "Loading Profile...")
currentPage := func(){setViewUserProfilePage(window, profileIsMine, profileHash, currentImageIndex, getAnyUserProfileAttributeFunction, previousPage)}
userIdentityHashExists, _, userIdentityHashString, err := getAnyUserProfileAttributeFunction("IdentityHash")
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (userIdentityHashExists == false) {
setErrorEncounteredPage(window, errors.New("Failed to get user profile IdentityHash"), previousPage)
return
}
userIdentityHash, userProfileType, err := identity.ReadIdentityHashString(userIdentityHashString)
if (err != nil){
setErrorEncounteredPage(window, errors.New("getAnyUserProfileAttributeFunction returning invalid IdentityHash: " + userIdentityHashString), previousPage)
return
}
title := getPageTitleCentered(titleText)
backButton := getBackButtonCentered(previousPage)
getProfileIsDisabledStatus := func()(bool, error){
exists, _, disabledAttribute, err := getAnyUserProfileAttributeFunction("Disabled")
if (err != nil) { return false, err }
if (exists == true && disabledAttribute == "Yes") {
return true, nil
}
return false, nil
}
userIsDisabled, err := getProfileIsDisabledStatus()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (userIsDisabled == true){
description1 := getBoldLabelCentered("User Is Disabled.")
getDescription2Text := func()string{
if (profileIsMine == true){
result := translate("You have disabled your profile.")
return result
}
result := translate("This user has disabled their profile.")
return result
}
description2Text := getDescription2Text()
description2 := getLabelCentered(description2Text)
theirIdentityHashLabel := getItalicLabelCentered("User Identity Hash:")
theirIdentityHashText := getBoldLabelCentered(userIdentityHashString)
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), theirIdentityHashLabel, theirIdentityHashText)
if (profileIsMine == false){
page.Add(widget.NewSeparator())
actionsButton := getWidgetCentered(widget.NewButtonWithIcon("Actions", theme.ContentRedoIcon(), func(){
setViewPeerActionsPage(window, userIdentityHash, currentPage)
}))
page.Add(actionsButton)
}
setPageContent(page, window)
return
}
getProfileContainer := func()(*fyne.Container, error){
chatButton := widget.NewButtonWithIcon("Chat", theme.MailComposeIcon(), func(){
if (profileIsMine == true){
dialogTitle := translate("Chat Inaccessible")
dialogMessageA := getLabelCentered(translate("You are viewing your own profile."))
dialogMessageB := getLabelCentered(translate("You cannot chat with yourself."))
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
setViewAConversationPage(window, userIdentityHash, true, currentPage)
})
actionsButton := widget.NewButtonWithIcon("Actions", theme.ContentRedoIcon(), func(){
if (profileIsMine == true){
dialogTitle := translate("Peer Actions Inaccessible")
dialogMessageA := getLabelCentered(translate("You are viewing your own profile."))
dialogMessageB := getLabelCentered(translate("You cannot access the peer actions page for your own identity."))
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
setViewPeerActionsPage(window, userIdentityHash, currentPage)
})
topRowLeftButtonsColumn := container.NewGridWithColumns(1, chatButton, actionsButton)
getPhotosColumnWithNavigationButtons := func()(*fyne.Container, error){
imageObjectsList := make([]image.Image, 0)
if (userProfileType == "Mate"){
exists, _, webpPhotosAttribute, err := getAnyUserProfileAttributeFunction("Photos")
if (err != nil) { return nil, err }
if (exists == true){
webpPhotosList := strings.Split(webpPhotosAttribute, "+")
for _, base64Image := range webpPhotosList{
imageObject, err := imagery.ConvertWEBPBase64StringToCroppedDownsizedImageObject(base64Image)
if (err != nil) { return nil, err }
imageObjectsList = append(imageObjectsList, imageObject)
}
}
}
getAvatarEmojiIdentifier := func()(int, error){
exists, _, emojiIdentifier, err := getAnyUserProfileAttributeFunction("Avatar")
if (err != nil){ return 0, err }
if (exists == false){
// This is the avatar for users who have not selected an avatar
return 2929, nil
}
emojiIdentifierInt, err := helpers.ConvertStringToInt(emojiIdentifier)
if (err != nil) {
return 0, errors.New("User profile is malformed: Invalid avatar: " + emojiIdentifier)
}
return emojiIdentifierInt, nil
}
avatarEmojiIdentifier, err := getAvatarEmojiIdentifier()
if (err != nil) { return nil, err }
avatarImage, err := getEmojiImageObject(avatarEmojiIdentifier)
if (err != nil){ return nil, err }
imageObjectsList = append(imageObjectsList, avatarImage)
numberOfImages := len(imageObjectsList)
getImageIndex := func()int{
if (currentImageIndex <= 0){
return 0
}
finalIndex := numberOfImages-1
if (currentImageIndex >= finalIndex){
return finalIndex
}
return currentImageIndex
}
imageIndex := getImageIndex()
currentImage := imageObjectsList[imageIndex]
currentFyneImage := canvas.NewImageFromImage(currentImage)
currentFyneImage.FillMode = canvas.ImageFillContain
currentFyneImage.SetMinSize(getCustomFyneSize(30))
getNavigationWithZoomRow := func()*fyne.Container{
zoomButton := widget.NewButtonWithIcon("", theme.ZoomInIcon(), func(){
setViewFullpageImagesWithNavigationPage(window, imageObjectsList, imageIndex, currentPage)
})
if (numberOfImages == 1){
zoomButtonRow := getWidgetCentered(zoomButton)
return zoomButtonRow
}
getBackButton := func()fyne.Widget{
if (imageIndex <= 0) {
button := widget.NewButton("", nil)
return button
}
button := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func(){
newImageIndex := imageIndex-1
setViewUserProfilePage(window, profileIsMine, profileHash, newImageIndex, getAnyUserProfileAttributeFunction, previousPage)
})
return button
}
getNextButton := func()fyne.Widget{
if (imageIndex >= numberOfImages-1) {
button := widget.NewButton("", nil)
return button
}
newImageIndex := imageIndex+1
button := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func(){
setViewUserProfilePage(window, profileIsMine, profileHash, newImageIndex, getAnyUserProfileAttributeFunction, previousPage)
})
return button
}
backButton := getBackButton()
nextButton := getNextButton()
navigationWithZoomRow := getContainerCentered(container.NewGridWithRows(1, backButton, zoomButton, nextButton))
return navigationWithZoomRow
}
navigationWithZoomRow := getNavigationWithZoomRow()
currentFyneImageBoxed := container.NewHBox(layout.NewSpacer(), currentFyneImage, layout.NewSpacer())
imagesColumn := container.NewVBox(currentFyneImageBoxed, navigationWithZoomRow)
return imagesColumn, nil
}
photosColumnWithNavButtons, err := getPhotosColumnWithNavigationButtons()
if (err != nil) { return nil, err }
profileDetailsButton := widget.NewButtonWithIcon("Details", theme.InfoIcon(), func(){
setViewUserProfilePage_ProfileDetails(window, profileHash, getAnyUserProfileAttributeFunction, currentPage)
})
reportProfileButton := widget.NewButtonWithIcon("Report", theme.WarningIcon(), func(){
if (profileIsMine == true){
dialogTitle := translate("Reporting Inaccessible")
dialogMessageA := getLabelCentered(translate("You are viewing your own profile."))
dialogMessageB := getLabelCentered(translate("You cannot report your own profile."))
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
//TODO
showUnderConstructionDialog(window)
})
topRowRightButtonsColumn := container.NewGridWithColumns(1, profileDetailsButton, reportProfileButton)
topRow := container.NewHBox(layout.NewSpacer(), topRowLeftButtonsColumn, widget.NewSeparator(), photosColumnWithNavButtons, widget.NewSeparator(), topRowRightButtonsColumn, layout.NewSpacer())
identityHashTitle := widget.NewLabel("Identity Hash:")
identityHashLabel := getBoldLabel(userIdentityHashString)
viewIdentityHashButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewIdentityHashPage(window, userIdentityHash, currentPage)
})
identityHashRow := container.NewHBox(layout.NewSpacer(), identityHashTitle, identityHashLabel, viewIdentityHashButton, layout.NewSpacer())
profileContentContainer := container.NewVBox(topRow, widget.NewSeparator(), identityHashRow)
usernameTitle := widget.NewLabel("Username:")
getUsernameLabel := func()(fyne.Widget, error){
exists, _, usernameString, err := getAnyUserProfileAttributeFunction("Username")
if (err != nil) { return nil, err }
if (exists == false){
anonymousLabel := getBoldItalicLabel(translate("Anonymous"))
return anonymousLabel, nil
}
usernameLabel := getBoldLabel(usernameString)
return usernameLabel, nil
}
usernameLabel, err := getUsernameLabel()
if (err != nil) { return nil, err }
usernameRow := container.NewHBox(layout.NewSpacer(), usernameTitle, usernameLabel, layout.NewSpacer())
profileContentContainer.Add(usernameRow)
if (profileIsMine == false && userProfileType == "Mate"){
exists, _, matchScore, err := getAnyUserProfileAttributeFunction("MatchScore")
if (err != nil) { return nil, err }
if (exists == false){
return nil, errors.New("Unable to retrieve mate user match score.")
}
matchScoreTitle := widget.NewLabel("Match Score:")
matchScoreLabel := getBoldLabel(matchScore)
viewMatchScoreDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
// This page describes which desires a user fulfills
setViewUserMatchScoreBreakdownPage(window, userIdentityHash, currentPage)
})
matchScoreRow := container.NewHBox(layout.NewSpacer(), matchScoreTitle, matchScoreLabel, viewMatchScoreDetailsButton, layout.NewSpacer())
profileContentContainer.Add(matchScoreRow)
distanceTranslated, _, formatDistanceFunction, distanceUnits, unavailableDistanceText, err := attributeDisplay.GetProfileAttributeDisplayInfo("Distance")
if (err != nil) { return nil, err }
distanceTitle := getLabelCentered(distanceTranslated + ":")
getDistanceLabel := func()(fyne.Widget, error){
exists, _, distanceKilometersString, err := getAnyUserProfileAttributeFunction("Distance")
if (err != nil) { return nil, err }
if (exists == false){
distanceLabel := getBoldItalicLabel(unavailableDistanceText)
return distanceLabel, nil
}
distanceFormatted, err := formatDistanceFunction(distanceKilometersString)
if (err != nil) { return nil, err }
distanceLabel := getBoldLabel(distanceFormatted + distanceUnits)
return distanceLabel, nil
}
distanceLabel, err := getDistanceLabel()
if (err != nil) { return nil, err }
distanceRow := container.NewHBox(layout.NewSpacer(), distanceTitle, distanceLabel, layout.NewSpacer())
profileContentContainer.Add(distanceRow)
}
if (userProfileType == "Moderator"){
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
if (err != nil) { return nil, err }
rankingKnown, moderatorRank, totalNumberOfModerators, err := moderatorRanking.GetModeratorRanking(userIdentityHash, appNetworkType)
if (err != nil) { return nil, err }
getModeratorRankText := func()string{
if (rankingKnown == false){
result := translate("Unknown")
return result
}
moderatorRankString := helpers.ConvertIntToString(moderatorRank)
totalNumberOfModeratorsString := helpers.ConvertIntToString(totalNumberOfModerators)
moderatorRankText := moderatorRankString + "/" + totalNumberOfModeratorsString
return moderatorRankText
}
moderatorRankText := getModeratorRankText()
moderatorRankTitle := widget.NewLabel(translate("Moderator Rank") + ":")
moderatorRankLabel := getBoldLabel(moderatorRankText)
moderatorRankRow := container.NewHBox(layout.NewSpacer(), moderatorRankTitle, moderatorRankLabel, layout.NewSpacer())
profileContentContainer.Add(moderatorRankRow)
}
if (userProfileType == "Mate"){
ageTitle := widget.NewLabel("Age:")
getAgeLabel := func()(fyne.Widget, error){
exists, _, ageString, err := getAnyUserProfileAttributeFunction("Age")
if (err != nil) { return nil, err }
if (exists == false){
noResponseLabel := getBoldItalicLabel(translate("No Response"))
return noResponseLabel, nil
}
ageLabel := getBoldLabel(ageString)
return ageLabel, nil
}
ageLabel, err := getAgeLabel()
if (err != nil) { return nil, err }
ageRow := container.NewHBox(layout.NewSpacer(), ageTitle, ageLabel, layout.NewSpacer())
profileContentContainer.Add(ageRow)
}
getProfileCategorySelectButtons := func()(*fyne.Container, error){
generalIcon, err := getFyneImageIcon("General")
if (err != nil) { return nil, err }
generalButton := widget.NewButton("General", func(){
setViewUserProfilePage_Category(window, profileIsMine, "General", getAnyUserProfileAttributeFunction, currentPage)
})
generalButtonWithIcon := container.NewGridWithRows(2, generalIcon, generalButton)
if (userProfileType == "Host" || userProfileType == "Moderator"){
categorySelectButtonsRow := getContainerCentered(generalButtonWithIcon)
return categorySelectButtonsRow, nil
}
physicalIcon, err := getFyneImageIcon("Person")
if (err != nil) { return nil, err }
physicalButton := widget.NewButton("Physical", func(){
setViewUserProfilePage_Category(window, profileIsMine, "Physical", getAnyUserProfileAttributeFunction, currentPage)
})
physicalButtonWithIcon := container.NewGridWithRows(2, physicalIcon, physicalButton)
lifestyleIcon, err := getFyneImageIcon("Lifestyle")
if (err != nil) { return nil, err }
lifestyleButton := widget.NewButton("Lifestyle", func(){
setViewUserProfilePage_Category(window, profileIsMine, "Lifestyle", getAnyUserProfileAttributeFunction, currentPage)
})
lifestyleButtonWithIcon := container.NewGridWithRows(2, lifestyleIcon, lifestyleButton)
mentalIcon, err := getFyneImageIcon("Mental")
if (err != nil) { return nil, err }
mentalButton := widget.NewButton("Mental", func(){
setViewUserProfilePage_Category(window, profileIsMine, "Mental", getAnyUserProfileAttributeFunction, currentPage)
})
mentalButtonWithIcon := container.NewGridWithRows(2, mentalIcon, mentalButton)
categoriesRow := getContainerCentered(container.NewGridWithRows(1, generalButtonWithIcon, physicalButtonWithIcon, lifestyleButtonWithIcon, mentalButtonWithIcon))
return categoriesRow, nil
}
selectProfileCategoryButtonsRow, err := getProfileCategorySelectButtons()
if (err != nil) { return nil, err }
profileContentContainer.Add(widget.NewSeparator())
profileContentContainer.Add(selectProfileCategoryButtonsRow)
profileContentContainer.Add(widget.NewSeparator())
return profileContentContainer, nil
}
profileContent, err := getProfileContainer()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), profileContent)
setPageContent(page, window)
}
func setViewUserProfilePage_ProfileDetails(window fyne.Window, profileHash [28]byte, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()) {
currentPage := func(){setViewUserProfilePage_ProfileDetails(window, profileHash, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile Details"))
backButton := getBackButtonCentered(previousPage)
getPageContent := func()(*fyne.Container, error){
profileHashLabel := widget.NewLabel("Profile Hash:")
profileHashHex := encoding.EncodeBytesToHexString(profileHash[:])
profileHashTrimmed, _, err := helpers.TrimAndFlattenString(profileHashHex, 20)
if (err != nil){ return nil, err }
profileHashTrimmedLabel := getBoldLabel(profileHashTrimmed)
viewProfileHashButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewContentHashPage(window, "Profile", profileHash[:], currentPage)
})
profileHashRow := container.NewHBox(layout.NewSpacer(), profileHashLabel, profileHashTrimmedLabel, viewProfileHashButton, layout.NewSpacer())
exists, _, profileCreationTimeString, err := getAnyUserProfileAttributeFunction("CreationTime")
if (err != nil){ return nil, err }
if (exists == false){
return nil, errors.New("setViewUserProfilePage_ProfileDetails called with profile missing CreationTime")
}
profileCreationTime, err := helpers.ConvertCreationTimeStringToInt64(profileCreationTimeString)
if (err != nil){
return nil, errors.New("setViewUserProfilePage_ProfileDetails called with profile with invalid CreationTime: " + profileCreationTimeString)
}
creationTimeLabel := widget.NewLabel("Creation Time:")
creationTimeString := helpers.ConvertUnixTimeToTranslatedTime(profileCreationTime)
creationTimeAgoString, err := helpers.ConvertUnixTimeToTimeFromNowTranslated(profileCreationTime, true)
if (err != nil) { return nil, err }
creationTimeText := getBoldLabel(creationTimeString + " (" + creationTimeAgoString + ")")
creationTimeWarningButton := widget.NewButtonWithIcon("", theme.WarningIcon(), func(){
title := translate("Creation Time Warning")
dialogMessageA := getLabelCentered("Profile creation times are not verified.")
dialogMessageB := getLabelCentered("They can be faked by the profile author.")
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
})
creationTimeRow := container.NewHBox(layout.NewSpacer(), creationTimeLabel, creationTimeText, creationTimeWarningButton, layout.NewSpacer())
exists, profileVersion, profileType, err := getAnyUserProfileAttributeFunction("ProfileType")
if (err != nil) { return nil, err }
if (exists == false){
return nil, errors.New("getAnyUserProfileAttributeFunction called and ProfileType not found")
}
profileTypeLabel := widget.NewLabel("Profile Type:")
profileTypeText := getBoldLabel(profileType)
profileTypeRow := container.NewHBox(layout.NewSpacer(), profileTypeLabel, profileTypeText, layout.NewSpacer())
profileVersionLabel := widget.NewLabel("Profile Version:")
profileVersionString := helpers.ConvertIntToString(profileVersion)
profileVersionText := getBoldLabel(profileVersionString)
profileVersionRow := container.NewHBox(layout.NewSpacer(), profileVersionLabel, profileVersionText, layout.NewSpacer())
//TODO: Add moderation details (if profile/identity is banned)
//TODO: Show when identity/profile will expire, and allow user to send funds to increase the user's identity balance/score
//TODO: Save raw profile button (will save it as a txt file)
pageContent := container.NewVBox(profileHashRow, widget.NewSeparator(), creationTimeRow, widget.NewSeparator(), profileTypeRow, widget.NewSeparator(), profileVersionRow)
return pageContent, nil
}
pageContent, err := getPageContent()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), pageContent)
setPageContent(page, window)
}
func setViewUserProfilePage_Category(window fyne.Window, profileIsMine bool, categoryName string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()) {
if (categoryName != "General" && categoryName != "Physical" && categoryName != "Lifestyle" && categoryName != "Mental"){
setErrorEncounteredPage(window, errors.New("setViewUserProfilePage_Category called with invalid profile category: " + categoryName), previousPage)
return
}
currentPage := func(){setViewUserProfilePage_Category(window, profileIsMine, categoryName, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered("View Profile - " + categoryName)
backButton := getBackButtonCentered(previousPage)
page := container.NewVBox(title, backButton, widget.NewSeparator())
addAttributeRow := func(attributeName string)error{
attributeTitle, _, formatAttributeValue, attributeUnits, missingValueText, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName)
if (err != nil) { return err }
attributeTitleLabel := widget.NewLabel(attributeTitle + ":")
exists, _, attributeValue, err := getAnyUserProfileAttributeFunction(attributeName)
if (err != nil) { return err }
if (exists == false) {
attributeValueLabel := getBoldItalicLabel(missingValueText)
attributeRow := container.NewHBox(layout.NewSpacer(), attributeTitleLabel, attributeValueLabel, layout.NewSpacer())
page.Add(attributeRow)
return nil
}
attributeValueFormatted, err := formatAttributeValue(attributeValue)
if (err != nil) { return err }
attributeValueFormattedWithUnits := attributeValueFormatted + attributeUnits
attributeValueTrimmed, anyChangesOccurred, err := helpers.TrimAndFlattenString(attributeValueFormattedWithUnits, 25)
if (err != nil) { return err }
if (anyChangesOccurred == false){
attributeValueLabel := getBoldLabel(attributeValueFormattedWithUnits)
attributeRow := container.NewHBox(layout.NewSpacer(), attributeTitleLabel, attributeValueLabel, layout.NewSpacer())
page.Add(attributeRow)
return nil
}
attributeValueTrimmedLabel := getBoldLabel(attributeValueTrimmed)
viewAttributeValueButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewTextPage(window, "Viewing " + attributeTitle, attributeValueFormattedWithUnits, false, currentPage)
})
attributeValueRow := container.NewHBox(layout.NewSpacer(), attributeTitleLabel, attributeValueTrimmedLabel, viewAttributeValueButton, layout.NewSpacer())
page.Add(attributeValueRow)
return nil
}
addAttributesFunction := func()error{
if (categoryName == "General"){
exists, _, userProfileType, err := getAnyUserProfileAttributeFunction("ProfileType")
if (err != nil) { return err }
if (exists == false){
return errors.New("getAnyUserProfileAttributeFunction not able to find ProfileType")
}
if (userProfileType == "Mate"){
locationButton := widget.NewButton("Location", func(){
setViewMateProfilePage_Location(window, getAnyUserProfileAttributeFunction, currentPage)
})
questionnaireButton := widget.NewButton("Questionnaire", func(){
setViewMateProfilePage_Questionnaire(window, profileIsMine, getAnyUserProfileAttributeFunction, currentPage)
})
tagsButton := widget.NewButton("Tags", func(){
setViewMateProfilePage_Tags(window, getAnyUserProfileAttributeFunction, currentPage)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, locationButton, questionnaireButton, tagsButton))
page.Add(buttonsGrid)
page.Add(widget.NewSeparator())
}
err = addAttributeRow("Description")
if (err != nil) { return err }
err = addAttributeRow("Username")
if (err != nil) { return err }
if (userProfileType == "Mate"){
err = addAttributeRow("Sexuality")
if (err != nil) { return err }
}
return nil
}
if (categoryName == "Physical"){
racialSimilarityButton := widget.NewButton("Racial Similarity", func(){
setViewMateProfilePage_RacialSimilarity(window, getAnyUserProfileAttributeFunction, currentPage)
})
ancestryCompositionButton := widget.NewButton("Ancestry Composition", func(){
setViewMateProfilePage_AncestryComposition(window, "User", getAnyUserProfileAttributeFunction, currentPage)
})
haplogroupsButton := widget.NewButton("Haplogroups", func(){
setViewMateProfilePage_Haplogroups(window, getAnyUserProfileAttributeFunction, currentPage)
})
neanderthalVariantsButton := widget.NewButton("Neanderthal Variants", func(){
setViewMateProfilePage_NeanderthalVariants(window, getAnyUserProfileAttributeFunction, currentPage)
})
totalDiseaseRiskButton := widget.NewButton("Total Disease Risk", func(){
setViewMateProfilePage_TotalDiseaseRisk(window, getAnyUserProfileAttributeFunction, currentPage)
})
monogenicDiseasesButton := widget.NewButton("Monogenic Diseases", func(){
setViewMateProfilePage_MonogenicDiseases(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
})
polygenicDiseasesButton := widget.NewButton("Polygenic Diseases", func(){
setViewMateProfilePage_PolygenicDiseases(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
})
geneticTraitsButton := widget.NewButton("Genetic Traits", func(){
setViewMateProfilePage_GeneticTraits(window, getAnyUserProfileAttributeFunction, currentPage)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(2, racialSimilarityButton, totalDiseaseRiskButton, ancestryCompositionButton, monogenicDiseasesButton, haplogroupsButton, polygenicDiseasesButton, neanderthalVariantsButton, geneticTraitsButton))
page.Add(buttonsGrid)
page.Add(widget.NewSeparator())
err := addAttributeRow("Age")
if (err != nil) { return err }
err = addAttributeRow("Sex")
if (err != nil) { return err }
err = addAttributeRow("Height")
if (err != nil) { return err }
page.Add(widget.NewSeparator())
err = addAttributeRow("EyeColor")
if (err != nil) { return err }
err = addAttributeRow("SkinColor")
if (err != nil) { return err }
err = addAttributeRow("HairColor")
if (err != nil) { return err }
err = addAttributeRow("HairTexture")
if (err != nil) { return err }
page.Add(widget.NewSeparator())
err = addAttributeRow("BodyFat")
if (err != nil) { return err }
err = addAttributeRow("BodyMuscle")
if (err != nil) { return err }
page.Add(widget.NewSeparator())
err = addAttributeRow("HasHIV")
if (err != nil) { return err }
err = addAttributeRow("HasGenitalHerpes")
if (err != nil) { return err }
return nil
}
if (categoryName == "Lifestyle"){
dietButton := getWidgetCentered(widget.NewButton("Diet", func(){
setViewMateProfilePage_Diet(window, getAnyUserProfileAttributeFunction, currentPage)
}))
page.Add(dietButton)
page.Add(widget.NewSeparator())
err := addAttributeRow("Hobbies")
if (err != nil) { return err }
err = addAttributeRow("WealthInGold")
if (err != nil) { return err }
err = addAttributeRow("Job")
if (err != nil) { return err }
err = addAttributeRow("Fame")
if (err != nil) { return err }
page.Add(widget.NewSeparator())
err = addAttributeRow("AlcoholFrequency")
if (err != nil) { return err }
err = addAttributeRow("TobaccoFrequency")
if (err != nil) { return err }
err = addAttributeRow("CannabisFrequency")
if (err != nil) { return err }
return nil
}
if (categoryName == "Mental"){
languageButton := getWidgetCentered(widget.NewButton("Language", func(){
setViewMateProfilePage_Language(window, getAnyUserProfileAttributeFunction, currentPage)
}))
page.Add(languageButton)
page.Add(widget.NewSeparator())
err := addAttributeRow("Beliefs")
if (err != nil) { return err }
err = addAttributeRow("GenderIdentity")
if (err != nil) { return err }
page.Add(widget.NewSeparator())
err = addAttributeRow("PetsRating")
if (err != nil) { return err }
err = addAttributeRow("DogsRating")
if (err != nil) { return err }
err = addAttributeRow("CatsRating")
if (err != nil) { return err }
return nil
}
return errors.New("setViewUserProfilePage_Category called with invalid profile category: " + categoryName)
}
err := addAttributesFunction()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page.Add(widget.NewSeparator())
setPageContent(page, window)
}
func setViewMateProfilePage_Location(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
setLoadingScreen(window, "View Profile - General", "Loading user location...")
title := getPageTitleCentered("View Profile - General")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Location"))
//Outputs:
// -bool: Location exists
// -*fyne.Container
// -error
getLocationColumn := func(locationRank string)(bool, *fyne.Container, error){
if (locationRank != "Primary" && locationRank != "Secondary"){
return false, nil, errors.New("getLocationColumn called with invalid locationRank: " + locationRank)
}
exists, _, locationLatitudeString, err := getAnyUserProfileAttributeFunction(locationRank + "LocationLatitude")
if (err != nil) { return false, nil, err }
if (exists == false){
return false, nil, nil
}
exists, _, locationLongitudeString, err := getAnyUserProfileAttributeFunction(locationRank + "LocationLongitude")
if (err != nil) { return false, nil, err }
if (exists == false){
return false, nil, errors.New("setViewMateProfilePage_Location called with profile containing " + locationRank + "LocationLatitude but missing " + locationRank + "LocationLongitude")
}
locationLatitudeFloat64, err := helpers.ConvertStringToFloat64(locationLatitudeString)
if (err != nil){
return false, nil, errors.New("setViewMateProfilePage_Location called with profile containing invalid LocationLatitude: " + locationLatitudeString)
}
locationLongitudeFloat64, err := helpers.ConvertStringToFloat64(locationLongitudeString)
if (err != nil){
return false, nil, errors.New("setViewMateProfilePage_Location called with profile containing invalid LocationLongitude: " + locationLongitudeString)
}
locationRankLabel := getBoldLabelCentered(locationRank)
locationCountryExists, _, locationCountryIdentifier, err := getAnyUserProfileAttributeFunction(locationRank + "LocationCountry")
if (err != nil) { return false, nil, err }
getCountryTextLabel := func()(fyne.Widget, error){
if (locationCountryExists == false){
noneLabel := getBoldItalicLabel(translate("None"))
return noneLabel, nil
}
locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
if (err != nil) { return nil, err }
countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(locationCountryIdentifierInt)
if (err != nil){ return nil, err }
countryNamesList := countryObject.NamesList
countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")
countryTextLabel := getBoldLabel(translate(countryDescription))
return countryTextLabel, nil
}
countryTextLabel, err := getCountryTextLabel()
if (err != nil) { return false, nil, err }
countryLabel := getLabelCentered("Country:")
coordinatesLabel := getLabelCentered("Coordinates:")
locationCoordinatesLabel := getBoldLabel(locationLatitudeString + "°, " + locationLongitudeString + "°")
cityLabel := getLabelCentered("City:")
getCityContent := func()(*fyne.Container, error){
// We will either find the exact city or the closest city
cityName, cityState, cityCountryIdentifier, cityDistanceKilometers, err := worldLocations.GetClosestCityFromCoordinates(locationLatitudeFloat64, locationLongitudeFloat64)
if (err != nil) { return nil, err }
if (cityDistanceKilometers == 0){
locationCityFormatted := cityName + ", " + cityState
locationCityLabel := getBoldLabelCentered(locationCityFormatted)
return locationCityLabel, nil
}
currentUnitsExist, currentUnits, err := globalSettings.GetSetting("MetricOrImperial")
if (err != nil){ return nil, err }
getNearbyCityDistanceFormattedString := func()(string, error){
if (currentUnitsExist == true && currentUnits == "Imperial"){
distanceMiles, err := helpers.ConvertKilometersToMiles(cityDistanceKilometers)
if (err != nil) { return "", err }
distanceMilesString := helpers.ConvertFloat64ToStringRounded(distanceMiles, 1)
result := distanceMilesString + " miles"
return result, nil
}
distanceKilometersString := helpers.ConvertFloat64ToStringRounded(cityDistanceKilometers, 1)
result := distanceKilometersString + " kilometers"
return result, nil
}
nearbyCityDistanceFormattedString, err := getNearbyCityDistanceFormattedString()
if (err != nil) { return nil, err }
getNearbyCityNameFormatted := func()(string, error){
if (locationCountryExists == true){
locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
if (err != nil) { return "", err }
if (cityCountryIdentifier == locationCountryIdentifierInt){
result := cityName + ", " + cityState
return result, nil
}
}
countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(cityCountryIdentifier)
if (err != nil){ return "", err }
countryNamesList := countryObject.NamesList
countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")
result := cityName + ", " + cityState + ", " + countryDescription
return result, nil
}
nearbyCityNameFormatted, err := getNearbyCityNameFormatted()
if (err != nil) { return nil, err }
nearbyCityNameFormattedAndTrimmed, _, err := helpers.TrimAndFlattenString(nearbyCityNameFormatted, 40)
if (err != nil) { return nil, err }
locationDistanceLabel := getBoldLabelCentered(nearbyCityDistanceFormattedString + " from")
locationCityNameLabel := getBoldLabelCentered(nearbyCityNameFormattedAndTrimmed)
cityInfoContainer := container.NewVBox(locationDistanceLabel, locationCityNameLabel)
return cityInfoContainer, nil
}
cityContent, err := getCityContent()
if (err != nil){ return false, nil, err }
locationColumn := container.NewVBox(locationRankLabel, widget.NewSeparator(), countryLabel, countryTextLabel, widget.NewSeparator(), coordinatesLabel, locationCoordinatesLabel, widget.NewSeparator(), cityLabel, cityContent)
return true, locationColumn, nil
}
primaryLocationExists, primaryLocationColumn, err := getLocationColumn("Primary")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (primaryLocationExists == false){
noLocationsLabel := getBoldLabelCentered("This user has no locations.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), noLocationsLabel)
setPageContent(page, window)
return
}
locationColumnsGrid := container.NewGridWithRows(1, primaryLocationColumn)
secondaryLocationExists, secondaryLocationColumn, err := getLocationColumn("Secondary")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (secondaryLocationExists == true){
locationColumnsGrid.Add(secondaryLocationColumn)
}
locationColumnsGridCentered := getContainerCentered(locationColumnsGrid)
getDescriptionText := func()string{
if (secondaryLocationExists == false){
result := translate("Below is the user's location.")
return result
}
result := translate("Below are the user's locations.")
return result
}
descriptionText := getDescriptionText()
description := getLabelCentered(descriptionText)
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), locationColumnsGridCentered)
setPageContent(page, window)
}
func setViewMateProfilePage_Questionnaire(window fyne.Window, profileIsMine bool, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
currentPage := func(){setViewMateProfilePage_Questionnaire(window, profileIsMine, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - General"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Questionnaire"))
questionnaireExists, _, questionnaireString, err := getAnyUserProfileAttributeFunction("Questionnaire")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (questionnaireExists == false){
description := getBoldLabelCentered("This user does not have a questionnaire.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description)
setPageContent(page, window)
return
}
description1 := getLabelCentered("This user has a questionnaire.")
description2 := getLabelCentered("You can take their questionnaire and send them your responses.")
questionnaireObject, err := mateQuestionnaire.ReadQuestionnaireString(questionnaireString)
if (err != nil){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_Questionnaire called with profile containing invalid questionnaire."), previousPage)
return
}
numberOfQuestions := len(questionnaireObject)
numberOfQuestionsString := helpers.ConvertIntToString(numberOfQuestions)
numberOfQuestionsLabel := widget.NewLabel("Number of questions:")
numberOfQuestionsText := getBoldLabel(numberOfQuestionsString)
numberOfQuestionsRow := container.NewHBox(layout.NewSpacer(), numberOfQuestionsLabel, numberOfQuestionsText, layout.NewSpacer())
exists, _, userIdentityHashString, err := getAnyUserProfileAttributeFunction("IdentityHash")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (exists == false){
setErrorEncounteredPage(window, errors.New("getAnyUserProfileAttributeFunction failed to get IdentityHash"), previousPage)
return
}
userIdentityHash, _, err := identity.ReadIdentityHashString(userIdentityHashString)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
//TODO: Show if user has already taken the questionnaire
submitQuestionnairePage := func(questionnaireResponse string, prevPage func()){
if (profileIsMine == true){
dialogTitle := translate("Cannot Submit Questionnaire")
dialogMessageA := getLabelCentered(translate("You are viewing your own profile."))
dialogMessageB := getLabelCentered(translate("You cannot submit a response to your own questionnaire."))
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
setSubmitQuestionnairePage(window, userIdentityHash, questionnaireResponse, prevPage, currentPage)
}
takeQuestionnaireButton := getWidgetCentered(widget.NewButtonWithIcon("Take Questionnaire", theme.DocumentCreateIcon(), func(){
emptyMap := make(map[string]string)
setTakeQuestionnairePage(window, questionnaireObject, 0, emptyMap, currentPage, submitQuestionnairePage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, numberOfQuestionsRow, takeQuestionnaireButton)
setPageContent(page, window)
}
func setViewMateProfilePage_Tags(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
title := getPageTitleCentered(translate("View Profile - General"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Tags"))
anyTagsExist, _, tagsAttribute, err := getAnyUserProfileAttributeFunction("Tags")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (anyTagsExist == false){
description := getBoldLabelCentered("This user has no tags.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description)
setPageContent(page, window)
return
}
description := getLabelCentered("Below are the user's tags.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator())
tagsList := strings.Split(tagsAttribute, "+&")
for _, tagName := range tagsList{
tagLabel := getBoldLabelCentered(tagName)
page.Add(tagLabel)
}
setPageContent(page, window)
}
func setViewMateProfilePage_Haplogroups(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Haplogroups"))
description1 := getLabelCentered(translate("Below are the user's haplogroups."))
description2 := getLabelCentered("These are reported by 23andMe. More companies will be added.")
userMaternalHaplogroupExists, _, userMaternalHaplogroup_23andMe, err := getAnyUserProfileAttributeFunction("23andMe_MaternalHaplogroup")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
maternalHaplogroupLabel := widget.NewLabel("Maternal Haplogroup:")
getMaternalHalogroupText := func()fyne.Widget{
if (userMaternalHaplogroupExists == false){
noResponseLabel := getBoldItalicLabel("No Response")
return noResponseLabel
}
maternalHaplogroupLabel := getBoldLabel(userMaternalHaplogroup_23andMe)
return maternalHaplogroupLabel
}
maternalHaplogroupText := getMaternalHalogroupText()
userMaternalHaplogroupRow := container.NewHBox(layout.NewSpacer(), maternalHaplogroupLabel, maternalHaplogroupText, layout.NewSpacer())
userPaternalHaplogroupExists, _, userPaternalHaplogroup_23andMe, err := getAnyUserProfileAttributeFunction("23andMe_PaternalHaplogroup")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
paternalHaplogroupLabel := widget.NewLabel("Paternal Haplogroup:")
getPaternalHalogroupText := func()fyne.Widget{
if (userPaternalHaplogroupExists == false){
noResponseLabel := getBoldItalicLabel("No Response")
return noResponseLabel
}
paternalHaplogroupLabel := getBoldLabel(userPaternalHaplogroup_23andMe)
return paternalHaplogroupLabel
}
paternalHaplogroupText := getPaternalHalogroupText()
userPaternalHaplogroupRow := container.NewHBox(layout.NewSpacer(), paternalHaplogroupLabel, paternalHaplogroupText, layout.NewSpacer())
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), userMaternalHaplogroupRow, userPaternalHaplogroupRow)
//Outputs:
// -bool: Offspring maternal haplogroup is known
// -string: Offspring maternal haplogroup
// -bool: Offspring paternal haplogroup is known
// -string: Offspring paternal haplogroup is known
// -error
getOffspringHaplogroups := func()(bool, string, bool, string, error){
isKnown, _, isSameSex, err := getAnyUserProfileAttributeFunction("IsSameSex")
if (err != nil){ return false, "", false, "", err }
if (isKnown == true && isSameSex == "Yes"){
return false, "", false, "", nil
}
mySexExists, mySex, err := myLocalProfiles.GetProfileData("Mate", "Sex")
if (err != nil) { return false, "", false, "", err }
userSexExists, _, userSex, err := getAnyUserProfileAttributeFunction("Sex")
if (err != nil) { return false, "", false, "", err }
if (mySexExists == false && userSexExists == false){
return false, "", false, "", nil
}
//Outputs:
// -bool: Haplogroup is known
// -string: Offspring maternal haplogroup
// -error
getOffspringMaternalHaplogroup := func()(bool, string, error){
if (mySexExists == true){
if (mySex == "Female" || mySex == "Intersex Female"){
exists, myMaternalHaplogroup, err := myLocalProfiles.GetProfileData("Mate", "23andMe_MaternalHaplogroup")
if (err != nil){ return false, "", err }
if (exists == false){
return false, "", nil
}
return true, myMaternalHaplogroup, nil
}
}
if (userSexExists == true){
if (userSex == "Female" || userSex == "Intersex Female"){
if (userMaternalHaplogroupExists == true){
return true, userMaternalHaplogroup_23andMe, nil
}
}
}
return false, "", nil
}
offspringMaternalHaplogroupIsKnown, offspringMaternalHaplogroup, err := getOffspringMaternalHaplogroup()
if (err != nil) { return false, "", false, "", err }
//Outputs:
// -bool: Haplogroup is known
// -string: Offspring paternal haplogroup
// -error
getOffspringPaternalHaplogroup := func()(bool, string, error){
if (mySexExists == true){
if (mySex == "Male" || mySex == "Intersex Male"){
exists, myPaternalHaplogroup, err := myLocalProfiles.GetProfileData("Mate", "23andMe_PaternalHaplogroup")
if (err != nil){ return false, "", err }
if (exists == false){
return false, "", nil
}
return true, myPaternalHaplogroup, nil
}
}
if (userSexExists == true){
if (userSex == "Male" || userSex == "Intersex Male"){
if (userPaternalHaplogroupExists == true){
return true, userPaternalHaplogroup_23andMe, nil
}
}
}
return false, "", nil
}
offspringPaternalHaplogroupIsKnown, offspringPaternalHaplogroup, err := getOffspringPaternalHaplogroup()
if (err != nil) { return false, "", false, "", err }
return offspringMaternalHaplogroupIsKnown, offspringMaternalHaplogroup, offspringPaternalHaplogroupIsKnown, offspringPaternalHaplogroup, nil
}
offspringMaternalHaplogroupIsKnown, offspringMaternalHaplogroup, offspringPaternalHaplogroupIsKnown, offspringPaternalHaplogroup, err := getOffspringHaplogroups()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (offspringMaternalHaplogroupIsKnown == true || offspringPaternalHaplogroupIsKnown == true){
page.Add(widget.NewSeparator())
offspringDescription := getLabelCentered("Below are the haplogroups of an offspring created from you and the user.")
page.Add(offspringDescription)
page.Add(widget.NewSeparator())
offspringMaternalHaplogroupLabel := widget.NewLabel("Offspring Maternal Haplogroup:")
getOffspringMaternalHaplogroupText := func()fyne.Widget{
if (offspringMaternalHaplogroupIsKnown == false){
unknownLabel := getBoldItalicLabel("Unknown")
return unknownLabel
}
haplogroupLabel := getBoldLabel(offspringMaternalHaplogroup)
return haplogroupLabel
}
offspringMaternalHaplogroupText := getOffspringMaternalHaplogroupText()
offspringMaternalHaplogroupRow := container.NewHBox(layout.NewSpacer(), offspringMaternalHaplogroupLabel, offspringMaternalHaplogroupText, layout.NewSpacer())
page.Add(offspringMaternalHaplogroupRow)
offspringPaternalHaplogroupLabel := widget.NewLabel("Offspring Paternal Haplogroup:")
getOffspringPaternalHaplogroupText := func()fyne.Widget{
if (offspringPaternalHaplogroupIsKnown == false){
unknownLabel := getBoldItalicLabel("Unknown")
return unknownLabel
}
haplogroupLabel := getBoldLabel(offspringPaternalHaplogroup)
return haplogroupLabel
}
offspringPaternalHaplogroupText := getOffspringPaternalHaplogroupText()
offspringPaternalHaplogroupRow := container.NewHBox(layout.NewSpacer(), offspringPaternalHaplogroupLabel, offspringPaternalHaplogroupText, layout.NewSpacer())
page.Add(offspringPaternalHaplogroupRow)
}
setPageContent(page, window)
}
func setViewMateProfilePage_NeanderthalVariants(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Neanderthal Variants"))
description1 := getLabelCentered("Below is the user's neanderthal variant count.")
description2 := getLabelCentered("The information comes from 23andMe. More companies will be added.")
userResponseExists, _, userNeanderthalVariants, err := getAnyUserProfileAttributeFunction("23andMe_NeanderthalVariants")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
getUserNeanderthalVariantsLabel := func()fyne.Widget{
if (userResponseExists == false){
noResponseLabel := getBoldItalicLabel(translate("No Response"))
return noResponseLabel
}
userNeanderthalVariantsLabel := getBoldLabel(userNeanderthalVariants + " variants")
return userNeanderthalVariantsLabel
}
userNeanderthalVariantsLabel := getUserNeanderthalVariantsLabel()
neanderthalVariantsLabel := widget.NewLabel("Neanderthal Variants:")
neanderthalVariantsRow := container.NewHBox(layout.NewSpacer(), neanderthalVariantsLabel, userNeanderthalVariantsLabel, layout.NewSpacer())
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), neanderthalVariantsRow)
isSameSexIsKnown, _, isSameSex, err := getAnyUserProfileAttributeFunction("IsSameSex")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (isSameSexIsKnown == true && isSameSex == "Yes"){
setPageContent(page, window)
return
}
offspringNeanderthalVariantsKnown, _, offspringNeanderthalVariants, err := getAnyUserProfileAttributeFunction("23andMe_OffspringNeanderthalVariants")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (offspringNeanderthalVariantsKnown == false){
setPageContent(page, window)
return
}
page.Add(widget.NewSeparator())
offspringDescription := getLabelCentered("Below is the estimated neanderthal variant count of an offspring created from you and this user.")
page.Add(offspringDescription)
offspringNeanderthalVariantsLabel := widget.NewLabel("Offspring Neanderthal Variants:")
offspringNeanderthalVariantsText := getBoldLabel(offspringNeanderthalVariants + " variants")
offspringNeanderthalVariantsRow := container.NewHBox(layout.NewSpacer(), offspringNeanderthalVariantsLabel, offspringNeanderthalVariantsText, layout.NewSpacer())
page.Add(offspringNeanderthalVariantsRow)
setPageContent(page, window)
}
func setViewMateProfilePage_RacialSimilarity(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Racial Similarity"))
description := getLabelCentered("This page describes the racial similarity between you and the user.")
//TODO: Add help pages to explain results
racialSimilarityTitle := widget.NewLabel("Racial Similarity:")
getRacialSimilarityText := func()(string, error){
similarityIsKnown, _, racialSimilarity, err := getAnyUserProfileAttributeFunction("RacialSimilarity")
if (err != nil) { return "", err }
if (similarityIsKnown == false){
result := translate("Unknown")
return result, nil
}
return racialSimilarity, nil
}
racialSimilarityText, err := getRacialSimilarityText()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
racialSimilarityLabel := getBoldLabel(racialSimilarityText)
racialSimilarityHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
racialSimilarityRow := container.NewHBox(layout.NewSpacer(), racialSimilarityTitle, racialSimilarityLabel, racialSimilarityHelpButton, layout.NewSpacer())
getRacialSimilarityBreakdownGrid_Traits := func()(*fyne.Container, error){
//TODO: A button for each trait to see what the our and the user's trait value is
traitNameHeader := getItalicLabelCentered("Trait Name")
traitSimilarityHeader := getItalicLabelCentered("Trait Similarity")
geneticSimilarityHeader := getItalicLabelCentered("Genetic Similarity")
numberOfTestedLociHeader := getItalicLabelCentered("Number Of Tested Loci")
traitNamesColumn := container.NewVBox(traitNameHeader, widget.NewSeparator())
traitSimilarityColumn := container.NewVBox(traitSimilarityHeader, widget.NewSeparator())
geneticSimilarityColumn := container.NewVBox(geneticSimilarityHeader, widget.NewSeparator())
numberOfTestedLociColumn := container.NewVBox(numberOfTestedLociHeader, widget.NewSeparator())
addSimilarityRow := func(traitName string, traitSimilarityAttributeName string, geneticSimilarityAttributeName string, numberOfSimilarAllelesAttributeName string)error{
traitTitleLabel := getBoldLabelCentered(translate(traitName))
traitNamesColumn.Add(traitTitleLabel)
if (traitName == "Facial Structure"){
// This trait's similarity cannot be calculated yet
// This feature is planned to be added - it will compare the faces in user profile photos.
dashLabel := getItalicLabelCentered("-")
traitSimilarityColumn.Add(dashLabel)
} else {
traitSimilarityIsKnown, _, attributeValue, err := getAnyUserProfileAttributeFunction(traitSimilarityAttributeName)
if (err != nil) { return err }
if (traitSimilarityIsKnown == false){
unknownLabel := getItalicLabelCentered("Unknown")
traitSimilarityColumn.Add(unknownLabel)
} else {
similarityFormatted := attributeValue + "%"
similarityLabel := getBoldLabelCentered(similarityFormatted)
traitSimilarityColumn.Add(similarityLabel)
}
}
// We figure out how many loci exist for this trait
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return err }
traitLociList := traitObject.LociList
quantityOfTraitLoci := len(traitLociList)
quantityOfTraitLociString := helpers.ConvertIntToString(quantityOfTraitLoci)
geneticSimilarityIsKnown, _, attributeValue, err := getAnyUserProfileAttributeFunction(geneticSimilarityAttributeName)
if (err != nil) { return err }
if (geneticSimilarityIsKnown == false){
unknownLabel := getItalicLabelCentered("Unknown")
geneticSimilarityColumn.Add(unknownLabel)
quantityOfTestedLociText := "0/" + quantityOfTraitLociString
quantityOfTestedLociLabel := getBoldLabelCentered(quantityOfTestedLociText)
numberOfTestedLociColumn.Add(quantityOfTestedLociLabel)
} else {
similarityFormatted := attributeValue + "%"
similarityLabel := getBoldLabelCentered(similarityFormatted)
geneticSimilarityColumn.Add(similarityLabel)
attributeIsKnown, _, numberOfSimilarAllelesValue, err := getAnyUserProfileAttributeFunction(numberOfSimilarAllelesAttributeName)
if (err != nil) { return err }
if (attributeIsKnown == false){
return errors.New("User profile contains " + geneticSimilarityAttributeName + " but is missing " + numberOfSimilarAllelesAttributeName)
}
// numberOfSimilarAllelesValue is represented by (number of shared alleles)/(number of tested alleles)
// Example: "52/60"
// We extract the denominator
_, numberOfTestedAllelesString, delimeterFound := strings.Cut(numberOfSimilarAllelesValue, "/")
if (delimeterFound == false){
return errors.New("User profile contains invalid " + numberOfSimilarAllelesAttributeName + ": " + numberOfSimilarAllelesValue)
}
numberOfTestedAlleles, err := helpers.ConvertStringToInt(numberOfTestedAllelesString)
if (err != nil){
return errors.New("User profile contains invalid " + numberOfSimilarAllelesAttributeName + ": " + numberOfSimilarAllelesValue)
}
// Each locus has 2 alleles (this will change once we include sex chromosome locations)
numberOfTestedLoci := numberOfTestedAlleles/2
numberOfTestedLociString := helpers.ConvertIntToString(numberOfTestedLoci)
numberOfTestedLociLabelText := numberOfTestedLociString + "/" + quantityOfTraitLociString
numberOfTestedLociLabel := getBoldLabelCentered(numberOfTestedLociLabelText)
numberOfTestedLociColumn.Add(numberOfTestedLociLabel)
}
traitNamesColumn.Add(widget.NewSeparator())
traitSimilarityColumn.Add(widget.NewSeparator())
geneticSimilarityColumn.Add(widget.NewSeparator())
numberOfTestedLociColumn.Add(widget.NewSeparator())
return nil
}
err = addSimilarityRow("Eye Color", "EyeColorSimilarity", "EyeColorGenesSimilarity", "EyeColorGenesSimilarity_NumberOfSimilarAlleles")
if (err != nil) { return nil, err }
err = addSimilarityRow("Hair Color", "HairColorSimilarity", "HairColorGenesSimilarity", "HairColorGenesSimilarity_NumberOfSimilarAlleles")
if (err != nil) { return nil, err }
err = addSimilarityRow("Skin Color", "SkinColorSimilarity", "SkinColorGenesSimilarity", "SkinColorGenesSimilarity_NumberOfSimilarAlleles")
if (err != nil) { return nil, err }
err = addSimilarityRow("Hair Texture", "HairTextureSimilarity", "HairTextureGenesSimilarity", "HairTextureGenesSimilarity_NumberOfSimilarAlleles")
if (err != nil) { return nil, err }
err = addSimilarityRow("Facial Structure", "", "FacialStructureGenesSimilarity", "FacialStructureGenesSimilarity_NumberOfSimilarAlleles")
if (err != nil) { return nil, err }
similarityGrid := container.NewHBox(layout.NewSpacer(), traitNamesColumn, traitSimilarityColumn, geneticSimilarityColumn, numberOfTestedLociColumn, layout.NewSpacer())
return similarityGrid, nil
}
racialSimilarityBreakdownGrid_Traits, err := getRacialSimilarityBreakdownGrid_Traits()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
getRacialSimilarityBreakdownGrid_GeneticAttributes := func()(*fyne.Container, error){
attributeTitleHeader := getItalicLabelCentered("Genetic Attribute Name")
twentyThreeAndMeHeader := getItalicLabelCentered("23andMe")
attributeTitleColumn := container.NewVBox(attributeTitleHeader, widget.NewSeparator())
similarityColumn_23andMe := container.NewVBox(twentyThreeAndMeHeader, widget.NewSeparator())
addSimilarityRow := func(attributeTitle string, attributeName_23andMe string)error{
attributeTitleLabel := getBoldLabelCentered(translate(attributeTitle))
attributeTitleColumn.Add(attributeTitleLabel)
similarityIsKnown, _, attributeValue, err := getAnyUserProfileAttributeFunction(attributeName_23andMe)
if (err != nil) { return err }
if (similarityIsKnown == false){
unknownLabel := getItalicLabelCentered("Unknown")
similarityColumn_23andMe.Add(unknownLabel)
} else {
similarityFormatted := attributeValue + "%"
similarityLabel := getBoldLabelCentered(similarityFormatted)
similarityColumn_23andMe.Add(similarityLabel)
}
attributeTitleColumn.Add(widget.NewSeparator())
similarityColumn_23andMe.Add(widget.NewSeparator())
return nil
}
err = addSimilarityRow("Ancestral Similarity", "23andMe_AncestralSimilarity")
if (err != nil) { return nil, err }
err = addSimilarityRow("Maternal Haplogroup Similarity", "23andMe_MaternalHaplogroupSimilarity")
if (err != nil) { return nil, err }
err = addSimilarityRow("Paternal Haplogroup Similarity", "23andMe_PaternalHaplogroupSimilarity")
if (err != nil) { return nil, err }
//TODO: Add neanderthal variants similarity?
similarityGrid := container.NewHBox(layout.NewSpacer(), attributeTitleColumn, similarityColumn_23andMe, layout.NewSpacer())
return similarityGrid, nil
}
racialSimilarityBreakdownGrid_GeneticAttributes, err := getRacialSimilarityBreakdownGrid_GeneticAttributes()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), racialSimilarityRow, widget.NewSeparator(), racialSimilarityBreakdownGrid_Traits, widget.NewSeparator(), racialSimilarityBreakdownGrid_GeneticAttributes)
setPageContent(page, window)
}
func setViewMateProfilePage_AncestryComposition(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
if (userOrOffspring != "User" && userOrOffspring != "Offspring"){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_AncestryComposition called with invalid userOrOffspring: " + userOrOffspring), previousPage)
return
}
currentPage := func(){setViewMateProfilePage_AncestryComposition(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Ancestry Composition"))
description1 := getLabelCentered("Below is the ancestry location composition for this user.")
description2 := getLabelCentered("The information is provided by 23andMe. More companies will be added.")
userAncestryCompositionExists, _, userAncestryCompositionAttribute, err := getAnyUserProfileAttributeFunction("23andMe_AncestryComposition")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (userAncestryCompositionExists == false){
noAncestryComposition := getBoldLabelCentered("This user has no ancestry composition.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), noAncestryComposition)
setPageContent(page, window)
return
}
description3Label := getLabelCentered("Choose if you want to view the composition of the user or the offspring between you and the user.")
offspringAncestryCompositionHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringAncestryCompositionExplainerPage(window, currentPage)
})
description3Row := container.NewHBox(layout.NewSpacer(), description3Label, offspringAncestryCompositionHelpButton, layout.NewSpacer())
handleSelectFunction := func(newUserOrOffspring string){
if (newUserOrOffspring == userOrOffspring){
return
}
setViewMateProfilePage_AncestryComposition(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
}
userOrOffspringList := []string{"User", "Offspring"}
userOrOffspringSelector := widget.NewSelect(userOrOffspringList, handleSelectFunction)
userOrOffspringSelector.Selected = userOrOffspring
userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector)
header := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), description3Row, userOrOffspringSelectorCentered, widget.NewSeparator())
if (userOrOffspring == "User"){
attributeIsValid, continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap, err := companyAnalysis.ReadAncestryCompositionAttribute_23andMe(true, userAncestryCompositionAttribute)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (attributeIsValid == false){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_AncestryComposition called with getAnyUserProfileAttributeFunction returning invalid userAncestryCompositionAttribute"), previousPage)
return
}
userCompositionDisplay, err := get23andMeAncestryCompositionDisplay(continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewBorder(header, nil, nil, nil, userCompositionDisplay)
setPageContent(page, window)
return
}
//userOrOffspring == "Offspring"
myAncestryCompositionExists, myAncestryCompositionAttribute, err := myLocalProfiles.GetProfileData("Mate", "23andMe_AncestryComposition")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (myAncestryCompositionExists == false){
description4 := getBoldLabelCentered("Your ancestry location composition does not exist.")
description5 := getLabelCentered("Add your ancestry composition to view your offspring's ancestry composition.")
description6 := getLabelCentered("You can do this on the Build Profile - Physical - Ancestry Composition page.")
page := container.NewVBox(header, description4, description5, description6)
setPageContent(page, window)
return
}
offspringContinentPercentagesMap, offspringRegionPercentagesMap, offspringSubregionPercentagesMap, err := companyAnalysis.GetOffspringAncestryComposition_23andMe(myAncestryCompositionAttribute, userAncestryCompositionAttribute)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
offspringAncestryCompositionDisplay, err := get23andMeAncestryCompositionDisplay(offspringContinentPercentagesMap, offspringRegionPercentagesMap, offspringSubregionPercentagesMap)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewBorder(header, nil, nil, nil, offspringAncestryCompositionDisplay)
setPageContent(page, window)
}
// This is a page to view the total monogenic disease risk for a user's offspring
func setViewMateProfilePage_TotalDiseaseRisk(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...")
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Total Disease Risk"))
description1 := getLabelCentered("This page describes the total disease risk for this user.")
description2 := getLabelCentered("You must link your genome person in the Build Profile menu to see offspring disease information.")
offspringProbabilityOfAnyMonogenicDiseaseTitle := widget.NewLabel("Offspring Probability Of Any Monogenic Disease:")
getOffspringProbabilityOfAnyMonogenicDiseaseLabel := func()(fyne.Widget, error){
probabilityIsKnown, _, offspringProbabilityOfAnyMonogenicDisease, err := getAnyUserProfileAttributeFunction("OffspringProbabilityOfAnyMonogenicDisease")
if (err != nil) { return nil, err }
if (probabilityIsKnown == false){
unknownLabel := getItalicLabel(translate("Unknown"))
return unknownLabel, nil
}
probabilityFormatted := offspringProbabilityOfAnyMonogenicDisease + "%"
probabilityLabel := getBoldLabel(probabilityFormatted)
return probabilityLabel, nil
}
offspringProbabilityOfAnyMonogenicDiseaseLabel, err := getOffspringProbabilityOfAnyMonogenicDiseaseLabel()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
offspringProbabilityOfAnyMonogenicDiseaseRow := container.NewHBox(layout.NewSpacer(), offspringProbabilityOfAnyMonogenicDiseaseTitle, offspringProbabilityOfAnyMonogenicDiseaseLabel, layout.NewSpacer())
getNumberOfOffspringMonogenicDiseasesTested := func()(int, error){
attributeIsKnown, _, totalNumberOfMonogenicDiseasesTestedString, err := getAnyUserProfileAttributeFunction("OffspringProbabilityOfAnyMonogenicDisease_NumberOfDiseasesTested")
if (err != nil) { return 0, err }
if (attributeIsKnown == false){
return 0, nil
}
totalNumberOfMonogenicDiseasesTested, err := helpers.ConvertStringToInt(totalNumberOfMonogenicDiseasesTestedString)
if (err != nil){
return 0, errors.New("getAnyUserProfileAttributeFunction returning invalid OffspringProbabilityOfAnyMonogenicDisease_NumberOfDiseasesTested value: " + totalNumberOfMonogenicDiseasesTestedString)
}
return totalNumberOfMonogenicDiseasesTested, nil
}
numberOfOffspringMonogenicDiseasesTested, err := getNumberOfOffspringMonogenicDiseasesTested()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
allMonogenicDiseaseNamesList, err := monogenicDiseases.GetMonogenicDiseaseNamesList()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
totalNumberOfMonogenicDiseases := len(allMonogenicDiseaseNamesList)
totalNumberOfMonogenicDiseasesString := helpers.ConvertIntToString(totalNumberOfMonogenicDiseases)
numberOfOffspringMonogenicDiseasesTestedString := helpers.ConvertIntToString(numberOfOffspringMonogenicDiseasesTested)
numberOfOffspringMonogenicDiseasesTestedLabelText := numberOfOffspringMonogenicDiseasesTestedString + "/" + totalNumberOfMonogenicDiseasesString
numberOfOffspringMonogenicDiseasesTestedTitle := widget.NewLabel("Number Of Offspring Monogenic Diseases Tested:")
numberOfOffspringMonogenicDiseasesTestedLabel := getBoldLabel(numberOfOffspringMonogenicDiseasesTestedLabelText)
numberOfOffspringMonogenicDiseasesTestedRow := container.NewHBox(layout.NewSpacer(), numberOfOffspringMonogenicDiseasesTestedTitle, numberOfOffspringMonogenicDiseasesTestedLabel, layout.NewSpacer())
userTotalPolygenicDiseaseRiskScoreTitle := widget.NewLabel("User Total Polygenic Disease Risk Score:")
getUserTotalPolygenicDiseaseRiskScoreLabel := func()(fyne.Widget, error){
riskScoreIsKnown, _, userTotalPolygenicDiseaseRiskScore, err := getAnyUserProfileAttributeFunction("TotalPolygenicDiseaseRiskScore")
if (err != nil) { return nil, err }
if (riskScoreIsKnown == false){
unknownLabel := getItalicLabel(translate("Unknown"))
return unknownLabel, nil
}
riskScoreFormatted := userTotalPolygenicDiseaseRiskScore + "/100"
riskScoreLabel := getBoldLabel(riskScoreFormatted)
return riskScoreLabel, nil
}
userTotalPolygenicDiseaseRiskScoreLabel, err := getUserTotalPolygenicDiseaseRiskScoreLabel()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
userTotalPolygenicDiseaseRiskScoreRow := container.NewHBox(layout.NewSpacer(), userTotalPolygenicDiseaseRiskScoreTitle, userTotalPolygenicDiseaseRiskScoreLabel, layout.NewSpacer())
getNumberOfUserPolygenicDiseasesTested := func()(int, error){
attributeIsKnown, _, totalNumberOfPolygenicDiseasesTestedString, err := getAnyUserProfileAttributeFunction("TotalPolygenicDiseaseRiskScore_NumberOfDiseasesTested")
if (err != nil) { return 0, err }
if (attributeIsKnown == false){
return 0, nil
}
totalNumberOfPolygenicDiseasesTested, err := helpers.ConvertStringToInt(totalNumberOfPolygenicDiseasesTestedString)
if (err != nil){
return 0, errors.New("getAnyUserProfileAttributeFunction returning invalid TotalPolygenicDiseaseRiskScore_NumberOfDiseasesTested value: " + totalNumberOfPolygenicDiseasesTestedString)
}
return totalNumberOfPolygenicDiseasesTested, nil
}
numberOfUserPolygenicDiseasesTested, err := getNumberOfUserPolygenicDiseasesTested()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
allPolygenicDiseaseNamesList, err := polygenicDiseases.GetPolygenicDiseaseNamesList()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
// We count up every disease that has a prediction model
totalNumberOfPolygenicDiseases := 0
for _, diseaseName := range allPolygenicDiseaseNamesList{
predictionModelExists := trainedPredictionModels.CheckIfAttributeNeuralNetworkExists(diseaseName)
if (predictionModelExists == true){
totalNumberOfPolygenicDiseases += 1
}
}
totalNumberOfPolygenicDiseasesString := helpers.ConvertIntToString(totalNumberOfPolygenicDiseases)
numberOfUserPolygenicDiseasesTestedString := helpers.ConvertIntToString(numberOfUserPolygenicDiseasesTested)
numberOfUserPolygenicDiseasesTestedLabelText := numberOfUserPolygenicDiseasesTestedString + "/" + totalNumberOfPolygenicDiseasesString
numberOfUserPolygenicDiseasesTestedTitle := widget.NewLabel("Number Of User Polygenic Diseases Tested:")
numberOfUserPolygenicDiseasesTestedLabel := getBoldLabel(numberOfUserPolygenicDiseasesTestedLabelText)
numberOfUserPolygenicDiseasesTestedRow := container.NewHBox(layout.NewSpacer(), numberOfUserPolygenicDiseasesTestedTitle, numberOfUserPolygenicDiseasesTestedLabel, layout.NewSpacer())
offspringTotalPolygenicDiseaseRiskScoreTitle := widget.NewLabel("Offspring Total Polygenic Disease Risk Score:")
getOffspringTotalPolygenicDiseaseRiskScoreLabel := func()(fyne.Widget, error){
riskScoreIsKnown, _, offspringTotalPolygenicDiseaseRiskScore, err := getAnyUserProfileAttributeFunction("OffspringTotalPolygenicDiseaseRiskScore")
if (err != nil) { return nil, err }
if (riskScoreIsKnown == false){
unknownLabel := getItalicLabel(translate("Unknown"))
return unknownLabel, nil
}
riskScoreFormatted := offspringTotalPolygenicDiseaseRiskScore + "/100"
riskScoreLabel := getBoldLabel(riskScoreFormatted)
return riskScoreLabel, nil
}
offspringTotalPolygenicDiseaseRiskScoreLabel, err := getOffspringTotalPolygenicDiseaseRiskScoreLabel()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
offspringTotalPolygenicDiseaseRiskScoreRow := container.NewHBox(layout.NewSpacer(), offspringTotalPolygenicDiseaseRiskScoreTitle, offspringTotalPolygenicDiseaseRiskScoreLabel, layout.NewSpacer())
getNumberOfOffspringPolygenicDiseasesTested := func()(int, error){
attributeIsKnown, _, totalNumberOfPolygenicDiseasesTestedString, err := getAnyUserProfileAttributeFunction("OffspringTotalPolygenicDiseaseRiskScore_NumberOfDiseasesTested")
if (err != nil) { return 0, err }
if (attributeIsKnown == false){
return 0, nil
}
totalNumberOfPolygenicDiseasesTested, err := helpers.ConvertStringToInt(totalNumberOfPolygenicDiseasesTestedString)
if (err != nil){
return 0, errors.New("getAnyUserProfileAttributeFunction returning invalid OffspringTotalPolygenicDiseaseRiskScore_NumberOfDiseasesTested value: " + totalNumberOfPolygenicDiseasesTestedString)
}
return totalNumberOfPolygenicDiseasesTested, nil
}
numberOfOffspringPolygenicDiseasesTested, err := getNumberOfOffspringPolygenicDiseasesTested()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfOffspringPolygenicDiseasesTestedString := helpers.ConvertIntToString(numberOfOffspringPolygenicDiseasesTested)
numberOfOffspringPolygenicDiseasesTestedLabelText := numberOfOffspringPolygenicDiseasesTestedString + "/" + totalNumberOfPolygenicDiseasesString
numberOfOffspringPolygenicDiseasesTestedTitle := widget.NewLabel("Number Of Offspring Polygenic Diseases Tested:")
numberOfOffspringPolygenicDiseasesTestedLabel := getBoldLabel(numberOfOffspringPolygenicDiseasesTestedLabelText)
numberOfOffspringPolygenicDiseasesTestedRow := container.NewHBox(layout.NewSpacer(), numberOfOffspringPolygenicDiseasesTestedTitle, numberOfOffspringPolygenicDiseasesTestedLabel, layout.NewSpacer())
//TODO: Add help buttons
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), offspringProbabilityOfAnyMonogenicDiseaseRow, numberOfOffspringMonogenicDiseasesTestedRow, widget.NewSeparator(), userTotalPolygenicDiseaseRiskScoreRow, numberOfUserPolygenicDiseasesTestedRow, widget.NewSeparator(), offspringTotalPolygenicDiseaseRiskScoreRow, numberOfOffspringPolygenicDiseasesTestedRow)
setPageContent(page, window)
}
func setViewMateProfilePage_MonogenicDiseases(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
currentPage := func(){setViewMateProfilePage_MonogenicDiseases(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Monogenic Diseases"))
description1 := getLabelCentered("Below is the monogenic disease analysis for this user.")
description2 := getLabelCentered("You can choose to view the analysis of the user or an offspring between you and the user.")
description3 := getLabelCentered("You must link your genome person in the Build Profile menu to see full offspring information.")
handleSelectButton := func(newUserOrOffspring string){
if (userOrOffspring == newUserOrOffspring){
return
}
setViewMateProfilePage_MonogenicDiseases(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
}
userOrOffspringSelector := widget.NewSelect([]string{"User", "Offspring"}, handleSelectButton)
userOrOffspringSelector.Selected = userOrOffspring
userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector)
//TODO: Sort to show highest risk diseases first. All other diseases should be in normal order
getDiseasesInfoGrid := func()(*fyne.Container, error){
emptyLabel1 := widget.NewLabel("")
emptyLabel2 := widget.NewLabel("")
emptyLabel3 := widget.NewLabel("")
diseaseNameLabel := getItalicLabelCentered("Disease Name")
emptyLabel4 := widget.NewLabel("")
userHasDiseaseLabel := getItalicLabelCentered("User Has Disease")
userProbabilityOfLabelA := getItalicLabelCentered("User Probability Of")
passingVariantLabel := getItalicLabelCentered("Passing Variant")
userNumberOfLabel := getItalicLabelCentered("User Number Of")
variantsTestedLabelA := getItalicLabelCentered("Variants Tested")
offspringProbabilityOfLabelA := getItalicLabelCentered("Offspring Probability Of")
havingDiseaseLabel := getItalicLabelCentered("Having Disease")
offspringProbabilityOfLabelB := getItalicLabelCentered("Offspring Probability Of")
havingVariantLabel := getItalicLabelCentered("Having Variant")
offspringNumberOfLabel := getItalicLabelCentered("Offspring Number Of")
variantsTestedLabelB := getItalicLabelCentered("Variants Tested")
diseaseInfoButtonsColumn := container.NewVBox(emptyLabel1, emptyLabel2, widget.NewSeparator())
diseaseNameColumn := container.NewVBox(emptyLabel3, diseaseNameLabel, widget.NewSeparator())
userHasDiseaseColumn := container.NewVBox(emptyLabel4, userHasDiseaseLabel, widget.NewSeparator())
userProbabilityOfPassingVariantColumn := container.NewVBox(userProbabilityOfLabelA, passingVariantLabel, widget.NewSeparator())
userNumberOfVariantsTestedColumn := container.NewVBox(userNumberOfLabel, variantsTestedLabelA, widget.NewSeparator())
offspringProbabilityOfHavingDiseaseColumn := container.NewVBox(offspringProbabilityOfLabelA, havingDiseaseLabel, widget.NewSeparator())
offspringProbabilityOfHavingAVariantColumn := container.NewVBox(offspringProbabilityOfLabelB, havingVariantLabel, widget.NewSeparator())
offspringNumberOfVariantsTestedColumn := container.NewVBox(offspringNumberOfLabel, variantsTestedLabelB, widget.NewSeparator())
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err }
monogenicDiseaseObjectsList, err := monogenicDiseases.GetMonogenicDiseaseObjectsList()
if (err != nil) { return nil, err }
for _, diseaseObject := range monogenicDiseaseObjectsList{
monogenicDiseaseName := diseaseObject.DiseaseName
diseaseIsDominantOrRecessive := diseaseObject.DominantOrRecessive
diseaseVariantsList := diseaseObject.VariantsList
numberOfDiseaseVariants := len(diseaseVariantsList)
//Outputs:
// -bool: User disease info exists
// -bool: User has the disease
// -int: User probability of passing a disease variant (0-100)
// -int: User number of variants tested
// -error
getUserDiseaseInfo := func()(bool, bool, int, int, error){
diseaseNameWithUnderscores := strings.ReplaceAll(monogenicDiseaseName, " ", "_")
probabilityOfPassingAVariantAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_ProbabilityOfPassingAVariant"
numberOfVariantsTestedAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_NumberOfVariantsTested"
userDiseaseInfoExists, _, userProbabilityOfPassingDiseaseVariantString, err := getAnyUserProfileAttributeFunction(probabilityOfPassingAVariantAttributeName)
if (err != nil) { return false, false, 0, 0, err }
if (userDiseaseInfoExists == false){
return false, false, 0, 0, nil
}
userProbabilityOfPassingDiseaseVariant, err := helpers.ConvertStringToInt(userProbabilityOfPassingDiseaseVariantString)
if (err != nil) {
return false, false, 0, 0, errors.New("setViewMateProfilePage_MonogenicDiseases called with profile containing invalid probabilityOfPassingAVariant: " + userProbabilityOfPassingDiseaseVariantString)
}
getUserHasDiseaseBool := func()bool{
if (diseaseIsDominantOrRecessive == "Dominant"){
if (userProbabilityOfPassingDiseaseVariant != 0){
return true
}
return false
}
// diseaseIsDominantOrRecessive == "Recessive"
if (userProbabilityOfPassingDiseaseVariant == 100){
return true
}
return false
}
userHasDisease := getUserHasDiseaseBool()
userVariantsTestedExists, _, userNumberOfVariantsTested, err := getAnyUserProfileAttributeFunction(numberOfVariantsTestedAttributeName)
if (err != nil) { return false, false, 0, 0, err }
if (userVariantsTestedExists == false){
return false, false, 0, 0, errors.New("setViewMateProfilePage_MonogenicDiseases called with user profile containing probabilityOfPassingAVariant but not numberOfVariantsTested")
}
userNumberOfVariantsTestedInt, err := helpers.ConvertStringToInt(userNumberOfVariantsTested)
if (err != nil) {
return false, false, 0, 0, errors.New("setViewMateProfilePage_MonogenicDiseases called with profile containing invalid numberOfVariantsTested: " + userNumberOfVariantsTested)
}
return true, userHasDisease, userProbabilityOfPassingDiseaseVariant, userNumberOfVariantsTestedInt, nil
}
userDiseaseInfoExists, userHasDisease, userProbabilityOfPassingAVariant, userNumberOfVariantsTested, err := getUserDiseaseInfo()
if (err != nil) { return nil, err }
//Outputs:
// -bool: My disease info exists
// -int: My probability of passing a disease variant
// -int: Number of variants tested
// -error
getMyDiseaseInfo := func()(bool, int, int, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
return false, 0, 0, nil
}
diseaseInfoIsKnown, _, probabilityOfPassingADiseaseVariant, _, numberOfVariantsTested, _, _, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(myAnalysisObject, monogenicDiseaseName, myGenomeIdentifier)
if (err != nil) { return false, 0, 0, err }
if (diseaseInfoIsKnown == false){
return false, 0, 0, nil
}
return true, probabilityOfPassingADiseaseVariant, numberOfVariantsTested, nil
}
myDiseaseInfoExists, myProbabilityOfPassingAVariant, myNumberOfVariantsTested, err := getMyDiseaseInfo()
if (err != nil) { return nil, err }
probabilityOffspringHasDiseaseIsKnown, probabilityOffspringHasDisease, probabilityOffspringHasVariantIsKnown, probabilityOffspringHasVariant, err := createCoupleGeneticAnalysis.GetOffspringMonogenicDiseaseProbabilities(diseaseIsDominantOrRecessive, userDiseaseInfoExists, userProbabilityOfPassingAVariant, myDiseaseInfoExists, myProbabilityOfPassingAVariant)
if (err != nil) { return nil, err }
getUserHasDiseaseString := func()string{
if (userDiseaseInfoExists == false){
return "Unknown"
}
userHasDiseaseString := helpers.ConvertBoolToYesOrNoString(userHasDisease)
return userHasDiseaseString
}
userHasDiseaseString := getUserHasDiseaseString()
getUserProbabilityOfPassingAVariantString := func()string{
if (userDiseaseInfoExists == false){
return "Unknown"
}
userProbabilityOfPassingAVariantString := helpers.ConvertIntToString(userProbabilityOfPassingAVariant)
resultFormatted := userProbabilityOfPassingAVariantString + "%"
return resultFormatted
}
userProbabilityOfPassingAVariantString := getUserProbabilityOfPassingAVariantString()
getUserNumberOfVariantsTestedString := func()string{
numberOfDiseaseVariantsString := helpers.ConvertIntToString(numberOfDiseaseVariants)
if (userDiseaseInfoExists == false){
result := "0/" + numberOfDiseaseVariantsString
return result
}
userNumberOfVariantsTestedString := helpers.ConvertIntToString(userNumberOfVariantsTested)
resultFormatted := userNumberOfVariantsTestedString + "/" + numberOfDiseaseVariantsString
return resultFormatted
}
userNumberOfVariantsTestedString := getUserNumberOfVariantsTestedString()
getOffspringProbabilityOfHavingDiseaseString := func()string{
if (probabilityOffspringHasDiseaseIsKnown == false){
result := translate("Unknown")
return result
}
offspringProbabilityOfHavingDiseaseString := helpers.ConvertIntToString(probabilityOffspringHasDisease)
resultFormatted := offspringProbabilityOfHavingDiseaseString + "%"
return resultFormatted
}
offspringProbabilityOfHavingDiseaseString := getOffspringProbabilityOfHavingDiseaseString()
getOffspringProbabilityOfHavingAVariantFormatted := func()string{
if (probabilityOffspringHasVariantIsKnown == false){
result := translate("Unknown")
return result
}
offspringProbabilityOfHavingAVariantString := helpers.ConvertIntToString(probabilityOffspringHasVariant)
resultFormatted := offspringProbabilityOfHavingAVariantString + "%"
return resultFormatted
}
offspringProbabilityOfHavingAVariantFormatted := getOffspringProbabilityOfHavingAVariantFormatted()
totalNumberOfOffspringDiseaseVariantsString := helpers.ConvertIntToString(numberOfDiseaseVariants*2)
offspringNumberOfVariantsTested := userNumberOfVariantsTested + myNumberOfVariantsTested
offspringNumberOfVariantsTestedString := helpers.ConvertIntToString(offspringNumberOfVariantsTested)
offspringNumberOfVariantsTestedFormatted := offspringNumberOfVariantsTestedString + "/" + totalNumberOfOffspringDiseaseVariantsString
viewDiseaseInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewMonogenicDiseaseDetailsPage(window, monogenicDiseaseName, currentPage)
})
diseaseNameLabel := getBoldLabelCentered(monogenicDiseaseName)
userHasDiseaseLabel := getBoldLabelCentered(userHasDiseaseString)
userProbabilityOfPassingAVariantLabel := getBoldLabelCentered(userProbabilityOfPassingAVariantString)
userNumberOfVariantsTestedLabel := getBoldLabelCentered(userNumberOfVariantsTestedString)
offspringProbabilityOfHavingDiseaseLabel := getBoldLabelCentered(offspringProbabilityOfHavingDiseaseString)
offspringProbabilityOfHavingAVariantLabel := getBoldLabelCentered(offspringProbabilityOfHavingAVariantFormatted)
offspringNumberOfVariantsTestedLabel := getBoldLabelCentered(offspringNumberOfVariantsTestedFormatted)
diseaseInfoButtonsColumn.Add(viewDiseaseInfoButton)
diseaseNameColumn.Add(diseaseNameLabel)
userHasDiseaseColumn.Add(userHasDiseaseLabel)
userProbabilityOfPassingVariantColumn.Add(userProbabilityOfPassingAVariantLabel)
userNumberOfVariantsTestedColumn.Add(userNumberOfVariantsTestedLabel)
offspringProbabilityOfHavingDiseaseColumn.Add(offspringProbabilityOfHavingDiseaseLabel)
offspringProbabilityOfHavingAVariantColumn.Add(offspringProbabilityOfHavingAVariantLabel)
offspringNumberOfVariantsTestedColumn.Add(offspringNumberOfVariantsTestedLabel)
diseaseInfoButtonsColumn.Add(widget.NewSeparator())
diseaseNameColumn.Add(widget.NewSeparator())
userHasDiseaseColumn.Add(widget.NewSeparator())
userProbabilityOfPassingVariantColumn.Add(widget.NewSeparator())
userNumberOfVariantsTestedColumn.Add(widget.NewSeparator())
offspringProbabilityOfHavingDiseaseColumn.Add(widget.NewSeparator())
offspringProbabilityOfHavingAVariantColumn.Add(widget.NewSeparator())
offspringNumberOfVariantsTestedColumn.Add(widget.NewSeparator())
}
userHasDiseaseHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setPersonHasMonogenicDiseaseExplainerPage(window, currentPage)
})
probabilityOfPassingVariantHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setPersonProbabilityOfPassingVariantExplainerPage(window, currentPage)
})
numberOfVariantsTestedHelpButtonA := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setNumberOfTestedVariantsExplainerPage(window, currentPage)
})
numberOfVariantsTestedHelpButtonB := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setNumberOfTestedVariantsExplainerPage(window, currentPage)
})
probabilityOfHavingDiseaseHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringProbabilityOfHavingMonogenicDiseaseExplainerPage(window, currentPage)
})
probabilityOfHavingAVariantHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringProbabilityOfHavingVariantExplainerPage(window, currentPage)
})
userHasDiseaseColumn.Add(userHasDiseaseHelpButton)
userProbabilityOfPassingVariantColumn.Add(probabilityOfPassingVariantHelpButton)
userNumberOfVariantsTestedColumn.Add(numberOfVariantsTestedHelpButtonA)
offspringProbabilityOfHavingDiseaseColumn.Add(probabilityOfHavingDiseaseHelpButton)
offspringProbabilityOfHavingAVariantColumn.Add(probabilityOfHavingAVariantHelpButton)
offspringNumberOfVariantsTestedColumn.Add(numberOfVariantsTestedHelpButtonB)
if (userOrOffspring == "User"){
diseasesInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseInfoButtonsColumn, diseaseNameColumn, userHasDiseaseColumn, userProbabilityOfPassingVariantColumn, userNumberOfVariantsTestedColumn, layout.NewSpacer())
return diseasesInfoGrid, nil
}
diseasesInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseInfoButtonsColumn, diseaseNameColumn, offspringProbabilityOfHavingDiseaseColumn, offspringProbabilityOfHavingAVariantColumn, offspringNumberOfVariantsTestedColumn, layout.NewSpacer())
return diseasesInfoGrid, nil
}
diseasesInfoGrid, err := getDiseasesInfoGrid()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), userOrOffspringSelectorCentered, widget.NewSeparator(), diseasesInfoGrid)
setPageContent(page, window)
}
func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
if (userOrOffspring == "Offspring"){
setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...")
}
currentPage := func(){setViewMateProfilePage_PolygenicDiseases(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Polygenic Diseases"))
description1 := getLabelCentered("Below is the polygenic disease risk analysis for this user.")
description2 := getLabelCentered("You can choose to view the analysis of the user or an offspring between you and the user.")
description3 := getLabelCentered("You must link your genome person in the Build Profile menu to see offspring information.")
handleSelectButton := func(newUserOrOffspring string){
if (userOrOffspring == newUserOrOffspring){
return
}
setViewMateProfilePage_PolygenicDiseases(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
}
userOrOffspringSelector := widget.NewSelect([]string{"User", "Offspring"}, handleSelectButton)
userOrOffspringSelector.Selected = userOrOffspring
userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector)
getDiseaseInfoGrid := func()(*fyne.Container, error){
diseaseNameLabel := getItalicLabelCentered("Disease Name")
userRiskScoreLabel := getItalicLabelCentered("User Risk Score")
offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score")
confidenceRangeLabel := getItalicLabelCentered("Confidence Range")
emptyLabel1 := widget.NewLabel("")
emptyLabel2 := widget.NewLabel("")
diseaseNameColumn := container.NewVBox(diseaseNameLabel, widget.NewSeparator())
userRiskScoreColumn := container.NewVBox(userRiskScoreLabel, widget.NewSeparator())
offspringRiskScoreColumn := container.NewVBox(offspringRiskScoreLabel, widget.NewSeparator())
confidenceRangeColumn := container.NewVBox(confidenceRangeLabel, widget.NewSeparator())
viewSampleOffspringsChartButtonsColumn := container.NewVBox(emptyLabel1, widget.NewSeparator())
viewDiseaseInfoButtonsColumn := container.NewVBox(emptyLabel2, widget.NewSeparator())
//Outputs:
// -map[int64]locusValue.LocusValue
// -error
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
if (userOrOffspring == "User"){
// We don't need to retrieve our locus values map
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err }
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil) { return nil, err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
}
return myGenomeLocusValuesMap, nil
}
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
if (err != nil) { return nil, err }
getConfidenceRangeLabel := func(analysisExists bool, predictionConfidenceRangesMap map[int]float64)(fyne.Widget, error){
if (analysisExists == false){
result := widget.NewLabel("Unknown")
return result, nil
}
// This is a list of the percentage accuracies in the map
// For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be
// accurate within that range
confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap)
// We sort the list so the percentage is always the same upon refreshing the page
slices.Sort(confidenceRangePercentagesList)
closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80)
if (err != nil) { return nil, err }
closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage]
if (exists == false){
return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.")
}
closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2)
closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage)
confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)"
confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted)
return confidenceRangeLabel, nil
}
diseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList()
if (err != nil) { return nil, err }
for _, diseaseObject := range diseaseObjectsList{
diseaseName := diseaseObject.DiseaseName
diseaseLociList := diseaseObject.LociList
predictionModelExists := trainedPredictionModels.CheckIfAttributeNeuralNetworkExists(diseaseName)
if (predictionModelExists == false){
// Prediction is not possible for this disease
continue
}
diseaseNameLabel := getBoldLabelCentered(diseaseName)
userDiseaseLocusValuesMap, err := calculatedAttributes.GetUserGenomeLocusValuesMapFromProfile(diseaseLociList, getAnyUserProfileAttributeFunction)
if (err != nil) { return nil, err }
if (userOrOffspring == "User"){
neuralNetworkExists, analysisExists, userDiseaseRiskScore, predictionConfidenceRangesMap, _, _, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseAnalysis(diseaseObject, userDiseaseLocusValuesMap, true)
if (err != nil) { return nil, err }
if (neuralNetworkExists == false){
return nil, errors.New("GetPersonGenomePolygenicDiseaseAnalysis claims model doesn't exist when we already checked.")
}
getUserDiseaseRiskScoreLabel := func()fyne.Widget{
if (analysisExists == false){
result := widget.NewLabel(translate("Unknown"))
return result
}
userRiskScoreString := helpers.ConvertIntToString(userDiseaseRiskScore)
riskScoreFormatted := userRiskScoreString + "/10"
riskScoreLabel := getBoldLabel(riskScoreFormatted)
return riskScoreLabel
}
userDiseaseRiskScoreLabel := getUserDiseaseRiskScoreLabel()
userDiseaseRiskScoreLabelCentered := getWidgetCentered(userDiseaseRiskScoreLabel)
userRiskScoreColumn.Add(userDiseaseRiskScoreLabelCentered)
confidenceRangeLabel, err := getConfidenceRangeLabel(analysisExists, predictionConfidenceRangesMap)
if (err != nil) { return nil, err }
confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel)
confidenceRangeColumn.Add(confidenceRangeLabelCentered)
} else if (userOrOffspring == "Offspring"){
neuralNetworkExists, anyOffspringLociKnown, offspringDiseaseRiskScore, predictionConfidenceRangesMap, offspringSampleRiskScoresList, offspringQuantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseAnalysis(diseaseObject, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) { return nil, err }
if (neuralNetworkExists == false){
return nil, errors.New("GetOffspringPolygenicDiseaseAnalysis claims that neural network doesn't exist when we already checked.")
}
getOffspringDiseaseRiskScoreLabel := func()fyne.Widget{
if (anyOffspringLociKnown == false){
result := widget.NewLabel(translate("Unknown"))
return result
}
offspringRiskScoreString := helpers.ConvertIntToString(offspringDiseaseRiskScore)
riskScoreFormatted := offspringRiskScoreString + "/10"
riskScoreLabel := getBoldLabel(riskScoreFormatted)
return riskScoreLabel
}
offspringDiseaseRiskScoreLabel := getOffspringDiseaseRiskScoreLabel()
offspringDiseaseRiskScoreLabelCentered := getWidgetCentered(offspringDiseaseRiskScoreLabel)
offspringRiskScoreColumn.Add(offspringDiseaseRiskScoreLabelCentered)
confidenceRangeLabel, err := getConfidenceRangeLabel(anyOffspringLociKnown, predictionConfidenceRangesMap)
if (err != nil) { return nil, err }
confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel)
confidenceRangeColumn.Add(confidenceRangeLabelCentered)
viewSampleOffspringsChartButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window, diseaseName, offspringSampleRiskScoresList, offspringQuantityOfLociKnown, currentPage)
})
viewSampleOffspringsChartButtonsColumn.Add(viewSampleOffspringsChartButton)
}
viewDiseaseDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
diseaseNameColumn.Add(diseaseNameLabel)
viewDiseaseInfoButtonsColumn.Add(viewDiseaseDetailsButton)
diseaseNameColumn.Add(widget.NewSeparator())
userRiskScoreColumn.Add(widget.NewSeparator())
offspringRiskScoreColumn.Add(widget.NewSeparator())
confidenceRangeColumn.Add(widget.NewSeparator())
viewSampleOffspringsChartButtonsColumn.Add(widget.NewSeparator())
viewDiseaseInfoButtonsColumn.Add(widget.NewSeparator())
}
userRiskScoreHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setPolygenicDiseaseRiskScoreExplainerPage(window, currentPage)
})
userRiskScoreColumn.Add(userRiskScoreHelpButton)
offspringRiskScoreHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringPolygenicDiseaseRiskScoreExplainerPage(window, currentPage)
})
offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton)
confidenceRangeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
showUnderConstructionDialog(window)
//TODO
})
confidenceRangeColumn.Add(confidenceRangeHelpButton)
if (userOrOffspring == "User"){
diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, userRiskScoreColumn, confidenceRangeColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer())
return diseaseInfoGrid, nil
}
diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, offspringRiskScoreColumn, confidenceRangeColumn, viewSampleOffspringsChartButtonsColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer())
return diseaseInfoGrid, nil
}
diseaseInfoGrid, err := getDiseaseInfoGrid()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), userOrOffspringSelectorCentered, widget.NewSeparator(), diseaseInfoGrid)
setPageContent(page, window)
}
func setViewMateProfilePage_GeneticTraits(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
currentPage := func(){setViewMateProfilePage_GeneticTraits(window, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Genetic Traits"))
description1 := getLabelCentered("Choose if you want to view Discrete traits or Numeric traits.")
description2 := getLabelCentered("Discrete traits are traits which have discrete outcomes, such as Eye Color")
description3 := getLabelCentered("Numeric traits are traits with numeric outcomes, such as Height.")
discreteTraitsButton := widget.NewButton("Discrete Traits", func(){
setViewMateProfilePage_DiscreteGeneticTraits(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
})
numericTraitsButton := widget.NewButton("Numeric Traits", func(){
setViewMateProfilePage_NumericGeneticTraits(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, discreteTraitsButton, numericTraitsButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), buttonsGrid)
setPageContent(page, window)
}
func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
if (userOrOffspring != "User" && userOrOffspring != "Offspring"){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiscreteGeneticTraits called with invalid userOrOffspring: " + userOrOffspring), previousPage)
return
}
if (userOrOffspring == "Offspring"){
setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...")
}
currentPage := func(){setViewMateProfilePage_DiscreteGeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Discrete Genetic Traits"))
description1 := getLabelCentered("Below is the discrete genetic trait analysis for this user.")
description2 := getLabelCentered("You can choose to view the analysis of the user or an offspring between you and the user.")
description3 := getLabelCentered("You must link your genome person in the Build Profile menu to see offspring information.")
handleSelectButton := func(newUserOrOffspring string){
if (userOrOffspring == newUserOrOffspring){
return
}
setViewMateProfilePage_DiscreteGeneticTraits(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
}
userOrOffspringSelector := widget.NewSelect([]string{"User", "Offspring"}, handleSelectButton)
userOrOffspringSelector.Selected = userOrOffspring
userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector)
getTraitsInfoGrid := func()(*fyne.Container, error){
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err }
//Outputs:
// -map[int64]locusValue.LocusValue
// -error
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil) { return nil, err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
}
return myGenomeLocusValuesMap, nil
}
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
if (err != nil) { return nil, err }
emptyLabel1 := widget.NewLabel("")
traitNameLabel := getItalicLabelCentered("Trait Name")
emptyLabel2 := widget.NewLabel("")
userPredictedOutcomeTitle := getItalicLabelCentered("User Predicted Outcome")
emptyLabel3 := widget.NewLabel("")
offspringOutcomeProbabilitiesLabel := getItalicLabelCentered("Offspring Outcome Probabilities")
quantityOfLabel1 := getItalicLabelCentered("Quantity Of")
lociKnownLabel := getItalicLabelCentered("Loci Known")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator())
userPredictedOutcomeColumn := container.NewVBox(emptyLabel2, userPredictedOutcomeTitle, widget.NewSeparator())
offspringOutcomeProbabilitiesColumn := container.NewVBox(emptyLabel3, offspringOutcomeProbabilitiesLabel, widget.NewSeparator())
quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel1, lociKnownLabel, widget.NewSeparator())
viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
traitObjectsList, err := traits.GetTraitObjectsList()
if (err != nil) { return nil, err }
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
continue
}
traitRulesList := traitObject.RulesList
totalNumberOfTraitRules := len(traitRulesList)
traitLociList := traitObject.LociList
numberOfTraitLoci := len(traitLociList)
if (totalNumberOfTraitRules == 0 && numberOfTraitLoci == 0){
// We are not able to analyze these traits yet
continue
}
traitNeuralNetworkExists := trainedPredictionModels.CheckIfAttributeNeuralNetworkExists(traitName)
if (traitNeuralNetworkExists == false && totalNumberOfTraitRules == 0){
// We are not able to analyze these traits yet
continue
}
traitNameText := getBoldLabelCentered(translate(traitName))
traitNameColumn.Add(traitNameText)
viewTraitDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
if (traitNeuralNetworkExists == true){
//TODO
showUnderConstructionDialog(window)
} else {
setViewMateProfilePage_DiscreteTraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage)
}
})
viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton)
// We construct the user's trait locus values map
userTraitLocusValuesMap, err := calculatedAttributes.GetUserGenomeLocusValuesMapFromProfile(traitLociList, getAnyUserProfileAttributeFunction)
if (err != nil) { return nil, err }
if (userOrOffspring == "User"){
if (traitNeuralNetworkExists == true){
traitNeuralNetworkExists, anyLocusValuesAreKnown, predictedOutcome, _, quantityOfLociKnown, _, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return nil, err }
if (traitNeuralNetworkExists == false){
return nil, errors.New("GetGenomeTraitAnalysis_NeuralNetwork claims neural network doesn't exist for trait, but we already checked.")
}
if (anyLocusValuesAreKnown == false){
unknownLabel := getItalicLabelCentered(translate("Unknown"))
userPredictedOutcomeColumn.Add(unknownLabel)
} else {
predictedOutcomeLabel := getBoldLabelCentered(predictedOutcome)
userPredictedOutcomeColumn.Add(predictedOutcomeLabel)
}
totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci)
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
} else {
// We use the rules-based analysis
anyRulesExist, _, quantityOfLociKnown_Rules, _, predictedOutcomeIsKnown, predictedOutcome, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_Rules(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return nil, err }
if (anyRulesExist == false){
return nil, errors.New("GetGenomeTraitAnalysis_Rules claims no rules exist when we already checked.")
}
if (predictedOutcomeIsKnown == false){
unknownLabel := getItalicLabelCentered("Unknown")
userPredictedOutcomeColumn.Add(unknownLabel)
} else {
predictedOutcomeLabel := getBoldLabelCentered(predictedOutcome)
userPredictedOutcomeColumn.Add(predictedOutcomeLabel)
}
traitLociList_Rules := traitObject.LociList_Rules
totalQuantityOfLoci := len(traitLociList_Rules)
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown_Rules)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
}
} else {
// userOrOffspring == "Offspring"
if (traitNeuralNetworkExists == true){
neuralNetworkExists, anyLociKnown, outcomeProbabilitiesMap, _, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitAnalysis_NeuralNetwork(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap)
if (err != nil) { return nil, err }
if (neuralNetworkExists == false){
return nil, errors.New("GetOffspringDiscreteTraitAnalysis_NeuralNetwork claiming that neural network doesn't exist when we already checked.")
}
totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci)
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
if (anyLociKnown == false){
unknownLabel := getItalicLabelCentered("Unknown")
offspringOutcomeProbabilitiesColumn.Add(unknownLabel)
} else {
outcomesList := helpers.GetListOfMapKeys(outcomeProbabilitiesMap)
// We sort the outcomes in alphabetical order so they show up the same way each time
slices.Sort(outcomesList)
quantityOfAddedItems := 0
for _, outcomeName := range outcomesList{
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
return nil, errors.New("GetListOfMapKeys returning element which doesn't exist in map.")
}
if (outcomeProbability == 0){
continue
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
outcomeProbabilityLabelText := outcomeName + ": " + outcomeProbabilityString + "%"
outcomeProbabilityLabel := getBoldLabelCentered(outcomeProbabilityLabelText)
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilityLabel)
quantityOfAddedItems += 1
if (quantityOfAddedItems > 1){
// We add whitespace for the other columns
traitNameColumn.Add(widget.NewLabel(""))
quantityOfLociKnownColumn.Add(widget.NewLabel(""))
viewTraitDetailsButtonsColumn.Add(widget.NewLabel(""))
}
}
}
} else {
// We use the rules-based analysis
anyRulesExist, rulesAnalysisExists, _, offspringQuantityOfLociKnown, _, outcomeProbabilitiesMap, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitAnalysis_Rules(traitObject, myGenomeLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return nil, err }
if (anyRulesExist == false){
return nil, errors.New("GetOffspringDiscreteTraitAnalysis_Rules claiming that no rules exist when we already checked.")
}
lociList_Rules := traitObject.LociList_Rules
totalQuantityOfLoci := len(lociList_Rules)
quantityOfLociKnownString := helpers.ConvertIntToString(offspringQuantityOfLociKnown)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
if (rulesAnalysisExists == false){
unknownLabel := getItalicLabelCentered(translate("Unknown"))
offspringOutcomeProbabilitiesColumn.Add(unknownLabel)
} else {
outcomesList := helpers.GetListOfMapKeys(outcomeProbabilitiesMap)
// We sort the outcomes in alphabetical order so they show up the same way each time
slices.Sort(outcomesList)
quantityOfAddedItems := 0
for _, outcomeName := range outcomesList{
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
return nil, errors.New("GetListOfMapKeys returning element which doesn't exist in map.")
}
if (outcomeProbability == 0){
continue
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
outcomeProbabilityLabelText := outcomeName + ": " + outcomeProbabilityString + "%"
outcomeProbabilityLabel := getBoldLabelCentered(outcomeProbabilityLabelText)
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilityLabel)
quantityOfAddedItems += 1
if (quantityOfAddedItems > 1){
// We add whitespace for the other columns
traitNameColumn.Add(widget.NewLabel(""))
quantityOfLociKnownColumn.Add(widget.NewLabel(""))
viewTraitDetailsButtonsColumn.Add(widget.NewLabel(""))
}
}
}
}
}
traitNameColumn.Add(widget.NewSeparator())
userPredictedOutcomeColumn.Add(widget.NewSeparator())
offspringOutcomeProbabilitiesColumn.Add(widget.NewSeparator())
quantityOfLociKnownColumn.Add(widget.NewSeparator())
viewTraitDetailsButtonsColumn.Add(widget.NewSeparator())
}
if (userOrOffspring == "User"){
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
userPredictedOutcomeColumn.Add(predictedOutcomeHelpButton)
} else {
// userOrOffspring == "Offspring"
outcomeProbabilitiesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilitiesHelpButton)
}
quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton)
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn)
if (userOrOffspring == "User"){
traitsInfoGrid.Add(userPredictedOutcomeColumn)
} else {
// userOrOffspring == "Offspring"
traitsInfoGrid.Add(offspringOutcomeProbabilitiesColumn)
}
traitsInfoGrid.Add(quantityOfLociKnownColumn)
traitsInfoGrid.Add(viewTraitDetailsButtonsColumn)
traitsInfoGrid.Add(layout.NewSpacer())
return traitsInfoGrid, nil
}
traitsInfoGrid, err := getTraitsInfoGrid()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), userOrOffspringSelectorCentered, widget.NewSeparator(), traitsInfoGrid)
setPageContent(page, window)
}
func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName string, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
currentPage := func(){setViewMateProfilePage_DiscreteTraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered("View Profile - Physical")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(userOrOffspring + " Trait Rules")
traitNameLabel := widget.NewLabel("Trait Name:")
traitNameText := getBoldLabel(traitName)
viewTraitInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewTraitDetailsPage(window, traitName, currentPage)
})
traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, viewTraitInfoButton, layout.NewSpacer())
//Outputs:
// -bool: Any trait locus value exists for this myself
// -map[int64]locusValue.LocusValue: My locus values map
// -error
getMyGenomeLocusValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return false, nil, err }
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
// Without my genome person chosen, all offspring rule probabilities are unknown
return false, nil, nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil){ return false, nil, err }
myGenomeMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which does not contain genome matching myGenomeIdentifier")
}
return true, myGenomeMap, nil
}
anyMyLocusValuesExist, myLocusValuesMap, err := getMyGenomeLocusValuesMap()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
traitLociList_Rules := traitObject.LociList_Rules
traitRulesList := traitObject.RulesList
if (len(traitRulesList) == 0){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiscreteTraitRules called with trait which has no rules."), previousPage)
return
}
//Outputs:
// -bool: Any trait locus value exists for this user
// -map[int64]locusValue.LocusValue: User locus values map
// -error
getUserTraitLocusValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
// We construct the user's trait locus values map
userTraitLocusValuesMap, err := calculatedAttributes.GetUserGenomeLocusValuesMapFromProfile(traitLociList_Rules, getAnyUserProfileAttributeFunction)
if (err != nil) { return false, nil, err }
if (len(userTraitLocusValuesMap) == 0){
return false, nil, nil
}
return true, userTraitLocusValuesMap, nil
}
anyUserTraitLocusValueExists, userTraitLocusValuesMap, err := getUserTraitLocusValuesMap()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
//Outputs:
// -bool: Any offspring probability of passing rule is known
// -map[[3]byte]int: Offspring probability of passing rules map
// Map Structure: Rule Identifier -> Probability offspring will pass rule (0-100%)
// -error
getOffspringProbabilityOfPassingRulesMap := func()(bool, map[[3]byte]int, error){
if (anyMyLocusValuesExist == false || anyUserTraitLocusValueExists == false){
return false, nil, nil
}
anyRulesExist, anyOffspringRulesTested, _, _, offspringProbabilityOfPassingRulesMap, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitAnalysis_Rules(traitObject, myLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, nil, err }
if (anyRulesExist == false){
return false, nil, errors.New("GetOffspringDiscreteTraitAnalysis_Rules claiming no trait rules exist when we already checked.")
}
if (anyOffspringRulesTested == false){
return false, nil, nil
}
return true, offspringProbabilityOfPassingRulesMap, nil
}
anyOffspringProbabilityOfPassingRuleIsKnown, offspringProbabilityOfPassingRulesMap, err := getOffspringProbabilityOfPassingRulesMap()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
getNumberOfRulesTested := func()(int, error){
if (userOrOffspring == "Offspring"){
if (anyOffspringProbabilityOfPassingRuleIsKnown == false){
return 0, nil
}
numberOfRulesTested := len(offspringProbabilityOfPassingRulesMap)
return numberOfRulesTested, nil
}
if (anyUserTraitLocusValueExists == false){
return 0, nil
}
numberOfRulesTested := 0
for _, ruleObject := range traitRulesList{
ruleLociList := ruleObject.LociList
ruleStatusIsKnown, _, err := createPersonGeneticAnalysis.GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return 0, err }
if (ruleStatusIsKnown == true){
numberOfRulesTested += 1
}
}
return numberOfRulesTested, nil
}
numberOfRulesTested, err := getNumberOfRulesTested()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
rulesTestedLabel := widget.NewLabel("Rules Tested:")
totalNumberOfTraitRules := len(traitRulesList)
numberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested)
totalNumberOfTraitRulesString := helpers.ConvertIntToString(totalNumberOfTraitRules)
numberOfRulesTestedFormatted := numberOfRulesTestedString + "/" + totalNumberOfTraitRulesString
numberOfRulesTestedLabel := getBoldLabel(numberOfRulesTestedFormatted)
numberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
if (userOrOffspring == "User"){
setDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
} else {
setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
}
})
numberOfRulesTestedRow := container.NewHBox(layout.NewSpacer(), rulesTestedLabel, numberOfRulesTestedLabel, numberOfRulesTestedHelpButton, layout.NewSpacer())
getRulesGrid := func()(*fyne.Container, error){
//TODO: Sort results
viewRuleInfoButtonsColumn := container.NewVBox()
ruleIdentifierColumn := container.NewVBox()
ruleEffectsColumn := container.NewVBox()
userPassesRuleColumn := container.NewVBox()
offspringProbabilityOfPassingRuleColumn := container.NewVBox()
if (userOrOffspring == "Offspring"){
// We need this because the header row is 2 rows tall when userOrOffspring == "Offspring"
// Otherwise, it is 1 row tall
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
emptyLabelD := widget.NewLabel("")
viewRuleInfoButtonsColumn.Add(emptyLabelA)
ruleIdentifierColumn.Add(emptyLabelB)
ruleEffectsColumn.Add(emptyLabelC)
userPassesRuleColumn.Add(emptyLabelD)
}
offspringProbabilityOfTitle := getItalicLabelCentered("Offspring Probability Of")
offspringProbabilityOfPassingRuleColumn.Add(offspringProbabilityOfTitle)
emptyLabelE := widget.NewLabel("")
ruleIdentifierTitle := getItalicLabelCentered("Rule Identifier")
ruleEffectsTitle := getItalicLabelCentered("Rule Effects")
userPassesRuleTitle := getItalicLabelCentered("User Passes Rule")
passingRuleTitle := getItalicLabelCentered("Passing Rule")
viewRuleInfoButtonsColumn.Add(emptyLabelE)
ruleIdentifierColumn.Add(ruleIdentifierTitle)
ruleEffectsColumn.Add(ruleEffectsTitle)
userPassesRuleColumn.Add(userPassesRuleTitle)
offspringProbabilityOfPassingRuleColumn.Add(passingRuleTitle)
viewRuleInfoButtonsColumn.Add(widget.NewSeparator())
ruleIdentifierColumn.Add(widget.NewSeparator())
ruleEffectsColumn.Add(widget.NewSeparator())
userPassesRuleColumn.Add(widget.NewSeparator())
offspringProbabilityOfPassingRuleColumn.Add(widget.NewSeparator())
for _, ruleObject := range traitRulesList{
ruleIdentifierHex := ruleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return nil, err }
ruleLociList := ruleObject.LociList
getUserPassesRuleString := func()(string, error){
userRuleStatusIsKnown, userPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return "", err }
if (userRuleStatusIsKnown == false){
result := translate("Unknown")
return result, nil
}
userPassesRuleString := helpers.ConvertBoolToYesOrNoString(userPassesRule)
userPassesRuleTranslated := translate(userPassesRuleString)
return userPassesRuleTranslated, nil
}
userPassesRuleString, err := getUserPassesRuleString()
if (err != nil) { return nil, err }
getOffspringProbabilityOfPassingRuleString := func()(string, error){
if (anyOffspringProbabilityOfPassingRuleIsKnown == false){
result := translate("Unknown")
return result, nil
}
probabilityOfPassingRule, probabilityIsKnown := offspringProbabilityOfPassingRulesMap[ruleIdentifier]
if (probabilityIsKnown == false){
result := translate("Unknown")
return result, nil
}
ruleProbabilityString := helpers.ConvertIntToString(probabilityOfPassingRule)
ruleProbabilityFormatted := ruleProbabilityString + "%"
return ruleProbabilityFormatted, nil
}
offspringProbabilityOfPassingRuleString, err := getOffspringProbabilityOfPassingRuleString()
if (err != nil) { return nil, err }
// We add all of the columns except for the rule effects column
// We do this because the rule effects column may be multiple rows tall
viewRuleInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
})
ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifierHex)
userPassesRuleLabel := getBoldLabelCentered(userPassesRuleString)
offspringProbabilityOfPassingRuleLabel := getBoldLabelCentered(offspringProbabilityOfPassingRuleString)
viewRuleInfoButtonsColumn.Add(viewRuleInfoButton)
ruleIdentifierColumn.Add(ruleIdentifierLabel)
userPassesRuleColumn.Add(userPassesRuleLabel)
offspringProbabilityOfPassingRuleColumn.Add(offspringProbabilityOfPassingRuleLabel)
traitOutcomesList := traitObject.OutcomesList
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
// We have to sort the outcome names so they always show up in the same order
outcomeNamesListSorted := helpers.CopyAndSortStringListToUnicodeOrder(traitOutcomesList)
addedOutcomes := 0
for _, outcomeName := range outcomeNamesListSorted{
outcomeChange, exists := ruleOutcomePointsMap[outcomeName]
if (exists == false){
// This rule does not effect this outcome. Skip
continue
}
getOutcomeEffectString := func()string{
outcomeChangeString := helpers.ConvertIntToString(outcomeChange)
if (outcomeChange < 0){
return outcomeChangeString
}
outcomeEffect := "+" + outcomeChangeString
return outcomeEffect
}
outcomeEffect := getOutcomeEffectString()
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeEffect)
ruleEffectsColumn.Add(outcomeRow)
if (addedOutcomes > 0){
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
emptyLabelD := widget.NewLabel("")
viewRuleInfoButtonsColumn.Add(emptyLabelA)
ruleIdentifierColumn.Add(emptyLabelB)
userPassesRuleColumn.Add(emptyLabelC)
offspringProbabilityOfPassingRuleColumn.Add(emptyLabelD)
}
addedOutcomes += 1
}
viewRuleInfoButtonsColumn.Add(widget.NewSeparator())
ruleIdentifierColumn.Add(widget.NewSeparator())
ruleEffectsColumn.Add(widget.NewSeparator())
userPassesRuleColumn.Add(widget.NewSeparator())
offspringProbabilityOfPassingRuleColumn.Add(widget.NewSeparator())
}
ruleEffectsHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
})
userPassesRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setPersonPassesDiscreteTraitRuleExplainerPage(window, currentPage)
})
offspringProbabilityOfPassingRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringProbabilityOfPassingTraitRuleExplainerPage(window, currentPage)
})
ruleEffectsColumn.Add(ruleEffectsHelpButton)
userPassesRuleColumn.Add(userPassesRuleHelpButton)
offspringProbabilityOfPassingRuleColumn.Add(offspringProbabilityOfPassingRuleHelpButton)
if (userOrOffspring == "User"){
rulesGrid := container.NewHBox(layout.NewSpacer(), viewRuleInfoButtonsColumn, ruleIdentifierColumn, ruleEffectsColumn, userPassesRuleColumn, layout.NewSpacer())
return rulesGrid, nil
}
rulesGrid := container.NewHBox(layout.NewSpacer(), viewRuleInfoButtonsColumn, ruleIdentifierColumn, ruleEffectsColumn, offspringProbabilityOfPassingRuleColumn, layout.NewSpacer())
return rulesGrid, nil
}
rulesGrid, err := getRulesGrid()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), traitNameRow, numberOfRulesTestedRow, widget.NewSeparator(), rulesGrid)
setPageContent(page, window)
}
func setViewMateProfilePage_NumericGeneticTraits(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
if (userOrOffspring != "User" && userOrOffspring != "Offspring"){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_NumericGeneticTraits called with invalid userOrOffspring: " + userOrOffspring), previousPage)
return
}
if (userOrOffspring == "Offspring"){
setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...")
}
currentPage := func(){setViewMateProfilePage_NumericGeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Numeric Genetic Traits"))
description1 := getLabelCentered("Below is the numeric genetic trait analysis for this user.")
description2 := getLabelCentered("You can choose to view the analysis of the user or an offspring between you and the user.")
description3 := getLabelCentered("You must link your genome person in the Build Profile menu to see offspring information.")
handleSelectButton := func(newUserOrOffspring string){
if (userOrOffspring == newUserOrOffspring){
return
}
setViewMateProfilePage_NumericGeneticTraits(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
}
userOrOffspringSelector := widget.NewSelect([]string{"User", "Offspring"}, handleSelectButton)
userOrOffspringSelector.Selected = userOrOffspring
userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector)
getTraitsInfoGrid := func()(*fyne.Container, error){
//Outputs:
// -map[int64]locusValue.LocusValue
// -error
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
if (userOrOffspring == "User"){
// We don't need to retrieve our locus values map
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err }
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil) { return nil, err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
}
return myGenomeLocusValuesMap, nil
}
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
if (err != nil) { return nil, err }
traitNameLabel := getItalicLabelCentered("Trait Name")
userPredictedOutcomeTitle := getItalicLabelCentered("User Predicted Outcome")
offspringPredictedOutcomeTitle := getItalicLabelCentered("Offspring Predicted Outcome")
confidenceRangeTitle := getItalicLabelCentered("Confidence Range")
emptyLabel1 := widget.NewLabel("")
emptyLabel2 := widget.NewLabel("")
traitNameColumn := container.NewVBox(traitNameLabel, widget.NewSeparator())
userPredictedOutcomeColumn := container.NewVBox(userPredictedOutcomeTitle, widget.NewSeparator())
offspringPredictedOutcomeColumn := container.NewVBox(offspringPredictedOutcomeTitle, widget.NewSeparator())
predictionConfidenceRangeColumn := container.NewVBox(confidenceRangeTitle, widget.NewSeparator())
viewSampleOffspringsButtonsColumn := container.NewVBox(emptyLabel1, widget.NewSeparator())
viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabel2, widget.NewSeparator())
getConfidenceRangeLabel := func(traitObject traits.Trait, analysisExists bool, predictionConfidenceRangesMap map[int]float64)(fyne.Widget, error){
if (analysisExists == false){
result := widget.NewLabel("Unknown")
return result, nil
}
// This is a list of the percentage accuracies in the map
// For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be
// accurate within that range
confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap)
// We sort the list so the percentage is always the same upon refreshing the page
slices.Sort(confidenceRangePercentagesList)
closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80)
if (err != nil) { return nil, err }
closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage]
if (exists == false){
return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.")
}
outcomeFormatter := traitObject.NumericValueFormatter
closestConfidenceDistanceString, err := outcomeFormatter(closestToEightyPercentageConfidenceDistance, false)
if (err != nil) { return nil, err }
closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage)
confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "%)"
confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted)
return confidenceRangeLabel, nil
}
traitObjectsList, err := traits.GetTraitObjectsList()
if (err != nil) { return nil, err }
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Numeric"){
continue
}
traitLociList := traitObject.LociList
numberOfTraitLoci := len(traitLociList)
if (numberOfTraitLoci == 0){
// We are not able to analyze these traits
continue
}
traitNeuralNetworkExists := trainedPredictionModels.CheckIfAttributeNeuralNetworkExists(traitName)
if (traitNeuralNetworkExists == false){
// We are not able to analyze these traits yet
continue
}
traitNameText := getBoldLabelCentered(translate(traitName))
// We construct the user's trait locus values map
userTraitLocusValuesMap, err := calculatedAttributes.GetUserGenomeLocusValuesMapFromProfile(traitLociList, getAnyUserProfileAttributeFunction)
if (err != nil) { return nil, err }
getPredictedOutcomeLabel := func(analysisExists bool, predictedOutcome float64)(fyne.Widget, error){
if (analysisExists == false){
unknownLabel := widget.NewLabel("Unknown")
return unknownLabel, nil
}
outcomeFormatter := traitObject.NumericValueFormatter
predictedOutcomeFormatted, err := outcomeFormatter(predictedOutcome, true)
if (err != nil) { return nil, err }
predictedOutcomeLabel := getBoldLabel(predictedOutcomeFormatted)
return predictedOutcomeLabel, nil
}
if (userOrOffspring == "User"){
traitNeuralNetworkExists, anyLocusValuesAreKnown, predictedOutcome, predictionAccuracyRangesMap, _, _, err := createPersonGeneticAnalysis.GetGenomeNumericTraitAnalysis(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return nil, err }
if (traitNeuralNetworkExists == false){
return nil, errors.New("GetGenomeNumericTraitAnalysis claims neural network doesn't exist for trait, but we already checked.")
}
predictedOutcomeLabel, err := getPredictedOutcomeLabel(anyLocusValuesAreKnown, predictedOutcome)
if (err != nil) { return nil, err }
predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel)
userPredictedOutcomeColumn.Add(predictedOutcomeLabelCentered)
confidenceRangeLabel, err := getConfidenceRangeLabel(traitObject, anyLocusValuesAreKnown, predictionAccuracyRangesMap)
if (err != nil) { return nil, err }
confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel)
predictionConfidenceRangeColumn.Add(confidenceRangeLabelCentered)
} else {
// userOrOffspring == "Offspring"
neuralNetworkExists, anyLociKnown, predictedOutcome, predictionAccuracyRangesMap, sampleOffspringOutcomesList, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringNumericTraitAnalysis(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap)
if (err != nil) { return nil, err }
if (neuralNetworkExists == false){
return nil, errors.New("GetOffspringNumericTraitAnalysis claiming that neural network doesn't exist when we already checked.")
}
predictedOutcomeLabel, err := getPredictedOutcomeLabel(anyLociKnown, predictedOutcome)
if (err != nil) { return nil, err }
predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel)
offspringPredictedOutcomeColumn.Add(predictedOutcomeLabelCentered)
confidenceRangeLabel, err := getConfidenceRangeLabel(traitObject, anyLociKnown, predictionAccuracyRangesMap)
if (err != nil) { return nil, err }
confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel)
predictionConfidenceRangeColumn.Add(confidenceRangeLabelCentered)
viewSampleOffspringsButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewNumericTraitSampleOffspringOutcomesChart(window, traitName, sampleOffspringOutcomesList, quantityOfLociKnown, currentPage)
})
viewSampleOffspringsButtonsColumn.Add(viewSampleOffspringsButton)
}
viewTraitDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
traitNameColumn.Add(traitNameText)
viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton)
traitNameColumn.Add(widget.NewSeparator())
userPredictedOutcomeColumn.Add(widget.NewSeparator())
offspringPredictedOutcomeColumn.Add(widget.NewSeparator())
predictionConfidenceRangeColumn.Add(widget.NewSeparator())
viewSampleOffspringsButtonsColumn.Add(widget.NewSeparator())
viewTraitDetailsButtonsColumn.Add(widget.NewSeparator())
}
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
predictionConfidenceRangeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
predictionConfidenceRangeColumn.Add(predictionConfidenceRangeHelpButton)
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn)
if (userOrOffspring == "User"){
userPredictedOutcomeColumn.Add(predictedOutcomeHelpButton)
traitsInfoGrid.Add(userPredictedOutcomeColumn)
} else {
// userOrOffspring == "Offspring"
offspringPredictedOutcomeColumn.Add(predictedOutcomeHelpButton)
traitsInfoGrid.Add(offspringPredictedOutcomeColumn)
}
traitsInfoGrid.Add(predictionConfidenceRangeColumn)
if (userOrOffspring == "Offspring"){
traitsInfoGrid.Add(viewSampleOffspringsButtonsColumn)
}
traitsInfoGrid.Add(viewTraitDetailsButtonsColumn)
traitsInfoGrid.Add(layout.NewSpacer())
return traitsInfoGrid, nil
}
traitsInfoGrid, err := getTraitsInfoGrid()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), userOrOffspringSelectorCentered, widget.NewSeparator(), traitsInfoGrid)
setPageContent(page, window)
}
func setViewMateProfilePage_Diet(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
title := getPageTitleCentered(translate("View Profile - Lifestyle"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Diet")
description := getLabelCentered("Below are the user's food ratings.")
foodNameTitle := getItalicLabelCentered("Food Name")
ratingTitle := getItalicLabelCentered("Rating")
foodNameColumn := container.NewVBox(foodNameTitle, widget.NewSeparator())
foodRatingColumn := container.NewVBox(ratingTitle, widget.NewSeparator())
foodsList := []string{"Fruit", "Vegetables", "Nuts", "Grains", "Dairy", "Seafood", "Beef", "Pork", "Poultry", "Eggs", "Beans"}
for _, foodName := range foodsList{
foodAttributeName := foodName + "Rating"
ratingExists, _, foodRating, err := getAnyUserProfileAttributeFunction(foodAttributeName)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
foodNameLabel := getBoldLabelCentered(foodName)
getRatingLabel := func()*fyne.Container{
if (ratingExists == false){
result := getBoldItalicLabelCentered(translate("No Response"))
return result
}
result := getBoldLabelCentered(foodRating + "/10")
return result
}
ratingLabel := getRatingLabel()
foodNameColumn.Add(foodNameLabel)
foodRatingColumn.Add(ratingLabel)
foodNameColumn.Add(widget.NewSeparator())
foodRatingColumn.Add(widget.NewSeparator())
}
foodsGrid := container.NewHBox(layout.NewSpacer(), foodNameColumn, foodRatingColumn, layout.NewSpacer())
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), foodsGrid)
setPageContent(page, window)
}
func setViewMateProfilePage_Language(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
title := getPageTitleCentered(translate("View Profile - Mental"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Language"))
description := getLabelCentered("Below describes the language(s) the user can speak.")
userLanguagesExist, _, userLanguageAttribute, err := getAnyUserProfileAttributeFunction("Language")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (userLanguagesExist == false){
noLanguagesExistLabel := getBoldLabelCentered("This user's profile has no languages listed.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), noLanguagesExistLabel)
setPageContent(page, window)
return
}
getLanguagesGrid := func()(*fyne.Container, error){
//TODO: Sort by fluency
languageTitle := getItalicLabelCentered("Language")
fluencyTitle := getItalicLabelCentered("Fluency")
languageNameColumn := container.NewVBox(languageTitle, widget.NewSeparator())
languageFluencyColumn := container.NewVBox(fluencyTitle, widget.NewSeparator())
languageItemsList := strings.Split(userLanguageAttribute, "+&")
worldLanguageObjectsMap, err := worldLanguages.GetWorldLanguageObjectsMap()
if (err != nil) { return nil, err }
for _, languageItem := range languageItemsList{
languageName, languageRating, delimiterFound := strings.Cut(languageItem, "$")
if (delimiterFound == false){
return nil, errors.New("setViewMateProfilePage_Language called with profile containing invalid language attribute item: " + languageItem)
}
getLanguageNameFormatted := func()string{
// We only translate if language name is canonical
_, languageIsCanonical := worldLanguageObjectsMap[languageName]
if (languageIsCanonical == false){
return languageName
}
languageNameTranslated := translate(languageName)
return languageNameTranslated
}
languageNameFormatted := getLanguageNameFormatted()
languageFluencyFormatted := languageRating + "/5"
languageNameLabel := getBoldLabelCentered(languageNameFormatted)
languageFluencyLabel := getBoldLabelCentered(languageFluencyFormatted)
languageNameColumn.Add(languageNameLabel)
languageFluencyColumn.Add(languageFluencyLabel)
languageNameColumn.Add(widget.NewSeparator())
languageFluencyColumn.Add(widget.NewSeparator())
}
languagesGrid := container.NewHBox(layout.NewSpacer(), languageNameColumn, languageFluencyColumn, layout.NewSpacer())
return languagesGrid, nil
}
languagesGrid, err := getLanguagesGrid()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), languagesGrid)
setPageContent(page, window)
}
// We use this to view an ancestry composition for moderation/comparing profile changes
func setViewUser23andMeAncestryCompositionPage(window fyne.Window, inputAncestryCompositionAttribute string, previousPage func()){
title := getPageTitleCentered("Viewing 23andMe Ancestry Composition")
backButton := getBackButtonCentered(previousPage)
attributeIsValid, continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap, err := companyAnalysis.ReadAncestryCompositionAttribute_23andMe(true, inputAncestryCompositionAttribute)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (attributeIsValid == false){
setErrorEncounteredPage(window, errors.New("setViewUser23andMeAncestryCompositionPage called with invalid inputAncestryCompositionAttribute"), previousPage)
return
}
userCompositionDisplay, err := get23andMeAncestryCompositionDisplay(continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
header := container.NewVBox(title, backButton, widget.NewSeparator())
page := container.NewBorder(header, nil, nil, nil, userCompositionDisplay)
setPageContent(page, window)
}
func get23andMeAncestryCompositionDisplay(inputContinentPercentagesMap map[string]float64, inputRegionPercentagesMap map[string]float64, inputSubregionPercentagesMap map[string]float64)(fyne.Widget, error){
mapsAreValid, continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap, err := companyAnalysis.AddMissingParentsToAncestryCompositionMaps_23andMe(inputContinentPercentagesMap, inputRegionPercentagesMap, inputSubregionPercentagesMap)
if (err != nil){ return nil, err }
if (mapsAreValid == false){
return nil, errors.New("get23andMeAncestryCompositionDisplay called with invalid ancestry location maps.")
}
// This map is used to create a fyne tree widget
// Each item maps to a list of child items
treeMap := make(map[string][]string)
continentsList := make([]string, 0, len(continentPercentagesMap))
// Map Structure: Continent Description -> Continent percentage
continentDescriptionPercentagesMap := make(map[string]float64)
for continentName, continentPercentage := range continentPercentagesMap{
continentPercentageString := helpers.ConvertFloat64ToStringRounded(continentPercentage, 1)
continentDescription := continentName + " - " + continentPercentageString + "%"
continentDescriptionPercentagesMap[continentDescription] = continentPercentage
continentsList = append(continentsList, continentDescription)
currentContinentRegionsList := make([]string, 0)
allContinentRegionsList, err := companyAnalysis.GetAncestryContinentRegionsList_23andMe(continentName)
if (err != nil) {
return nil, errors.New("continentPercentagesMap contains invalid continent.")
}
// Map Structure: Region Description -> Region percentage
regionDescriptionPercentagesMap := make(map[string]float64)
for regionName, regionPercentage := range regionPercentagesMap{
regionIsRelevant := slices.Contains(allContinentRegionsList, regionName)
if (regionIsRelevant == false){
continue
}
regionPercentageString := helpers.ConvertFloat64ToStringRounded(regionPercentage, 1)
regionDescription := regionName + " - " + regionPercentageString + "%"
regionDescriptionPercentagesMap[regionDescription] = regionPercentage
currentContinentRegionsList = append(currentContinentRegionsList, regionDescription)
allRegionSubregionsList, err := companyAnalysis.GetAncestryRegionSubregionsList_23andMe(continentName, regionName)
if (err != nil){ return nil, err }
currentRegionSubregionsList := make([]string, 0, len(subregionPercentagesMap))
// Map Structure: Subregion description -> Subregion percentage
subregionDescriptionPercentagesMap := make(map[string]float64)
for subregionName, subregionPercentage := range subregionPercentagesMap{
subregionIsRelevant := slices.Contains(allRegionSubregionsList, subregionName)
if (subregionIsRelevant == false){
continue
}
subregionPercentageString := helpers.ConvertFloat64ToStringRounded(subregionPercentage, 1)
subregionDescription := subregionName + " - " + subregionPercentageString + "%"
subregionDescriptionPercentagesMap[subregionDescription] = subregionPercentage
currentRegionSubregionsList = append(currentRegionSubregionsList, subregionDescription)
}
// We sort region subregions list
// We sort them from highest to lowest in percentage.
compareSubregionsFunction := func(subregion1Description string, subregion2Description string)int{
if (subregion1Description == subregion2Description){
panic("compareSubregionsFunction called with identical subregion descriptions.")
}
subregion1Percentage, exists := subregionDescriptionPercentagesMap[subregion1Description]
if (exists == false){
panic("subregionPercentagesMap missing subregion during sort.")
}
subregion2Percentage, exists := subregionDescriptionPercentagesMap[subregion2Description]
if (exists == false){
panic("subregionPercentagesMap missing subregion during sort.")
}
if (subregion1Percentage == subregion2Percentage){
// We sort subregions in unicode order
if (subregion1Description < subregion2Description){
return -1
}
return 1
}
if (subregion1Percentage > subregion2Percentage){
return -1
}
return 1
}
slices.SortFunc(currentRegionSubregionsList, compareSubregionsFunction)
treeMap[regionDescription] = currentRegionSubregionsList
}
// We sort continent regions list by highest to lowest percentage.
compareRegionsFunction := func(region1Description string, region2Description string)int{
if (region1Description == region2Description){
panic("compareRegionsFunction called with identical regions.")
}
region1Percentage, exists := regionDescriptionPercentagesMap[region1Description]
if (exists == false){
panic("regionPercentagesMap missing subregion during sort.")
}
region2Percentage, exists := regionDescriptionPercentagesMap[region2Description]
if (exists == false){
panic("regionPercentagesMap missing subregion during sort.")
}
if (region1Percentage == region2Percentage){
// We sort regions in unicode order
if (region1Description < region2Description){
return -1
}
return 1
}
if (region1Percentage > region2Percentage){
return -1
}
return 1
}
slices.SortFunc(currentContinentRegionsList, compareRegionsFunction)
treeMap[continentDescription] = currentContinentRegionsList
}
// We sort root list by highest to lowest proportions
compareContinentsFunction := func(continent1Description string, continent2Description string)int{
if (continent1Description == continent2Description){
panic("compareContinentsFunction called with identical continents.")
}
continent1Percentage, exists := continentDescriptionPercentagesMap[continent1Description]
if (exists == false){
panic("Continent percentage not found when sorting root list.")
}
continent2Percentage, exists := continentDescriptionPercentagesMap[continent2Description]
if (exists == false){
panic("Continent percentage not found when sorting root list.")
}
if (continent1Percentage == continent2Percentage){
// We sort continents in unicode order
if (continent1Description < continent2Description){
return -1
}
return 1
}
if (continent1Percentage > continent2Percentage){
return -1
}
return 1
}
slices.SortFunc(continentsList, compareContinentsFunction)
treeMap[""] = continentsList
resultTree := widget.NewTreeWithStrings(treeMap)
resultTree.OpenAllBranches()
return resultTree, nil
}