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/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/createGeneticAnalysis" 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/fundedStatus" 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 := fundedStatus.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, _, profileBroadcastTimeString, err := getAnyUserProfileAttributeFunction("BroadcastTime") if (err != nil){ return nil, err } if (exists == false){ return nil, errors.New("setViewUserProfilePage_ProfileDetails called with profile missing BroadcastTime") } profileBroadcastTime, err := helpers.ConvertBroadcastTimeStringToInt64(profileBroadcastTimeString) if (err != nil){ return nil, errors.New("setViewUserProfilePage_ProfileDetails called with profile with invalid BroadcastTime: " + profileBroadcastTimeString) } broadcastTimeLabel := widget.NewLabel("Creation Time:") broadcastTimeString := helpers.ConvertUnixTimeToTranslatedTime(profileBroadcastTime) broadcastTimeAgoString, err := helpers.ConvertUnixTimeToTimeFromNowTranslated(profileBroadcastTime, true) if (err != nil) { return nil, err } broadcastTimeText := getBoldLabel(broadcastTimeString + " (" + broadcastTimeAgoString + ")") broadcastTimeWarningButton := 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) }) broadcastTimeRow := container.NewHBox(layout.NewSpacer(), broadcastTimeLabel, broadcastTimeText, broadcastTimeWarningButton, 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(), broadcastTimeRow, 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, "Offspring", 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 numberOfTraitLoci := len(traitLociList) numberOfTraitLociString := helpers.ConvertIntToString(numberOfTraitLoci) geneticSimilarityIsKnown, _, attributeValue, err := getAnyUserProfileAttributeFunction(geneticSimilarityAttributeName) if (err != nil) { return err } if (geneticSimilarityIsKnown == false){ unknownLabel := getItalicLabelCentered("Unknown") geneticSimilarityColumn.Add(unknownLabel) numberOfTestedLociText := "0/" + numberOfTraitLociString numberOfTestedLociLabel := getBoldLabelCentered(numberOfTestedLociText) numberOfTestedLociColumn.Add(numberOfTestedLociLabel) } 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 + "/" + numberOfTraitLociString 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()){ 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 } totalNumberOfPolygenicDiseases := len(allPolygenicDiseaseNamesList) 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){ emptyLabelA := widget.NewLabel("") emptyLabelB := widget.NewLabel("") emptyLabelC := widget.NewLabel("") diseaseNameLabel := getItalicLabelCentered("Disease Name") 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(emptyLabelA, emptyLabelB, widget.NewSeparator()) diseaseNameColumn := container.NewVBox(emptyLabelC, diseaseNameLabel, 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, myAnalysisMapList, myGenomeIdentifier, iHaveMultipleGenomes, 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 // -int: User probability of passing a disease variant (0-100) // -int: User number of variants tested // -error getUserDiseaseInfo := func()(bool, int, int, error){ diseaseNameWithUnderscores := strings.ReplaceAll(monogenicDiseaseName, " ", "_") probabilityOfPassingAVariantAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_ProbabilityOfPassingAVariant" numberOfVariantsTestedAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_NumberOfVariantsTested" userDiseaseInfoExists, _, userProbabilityOfPassingDiseaseVariant, err := getAnyUserProfileAttributeFunction(probabilityOfPassingAVariantAttributeName) if (err != nil) { return false, 0, 0, err } if (userDiseaseInfoExists == false){ return false, 0, 0, nil } userProbabilityOfPassingDiseaseVariantInt, err := helpers.ConvertStringToInt(userProbabilityOfPassingDiseaseVariant) if (err != nil) { return false, 0, 0, errors.New("setViewMateProfilePage_MonogenicDiseases called with profile containing invalid probabilityOfPassingAVariant: " + userProbabilityOfPassingDiseaseVariant) } userVariantsTestedExists, _, userNumberOfVariantsTested, err := getAnyUserProfileAttributeFunction(numberOfVariantsTestedAttributeName) if (err != nil) { return false, 0, 0, err } if (userVariantsTestedExists == false){ return 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, 0, 0, errors.New("setViewMateProfilePage_MonogenicDiseases called with profile containing invalid numberOfVariantsTested: " + userNumberOfVariantsTested) } return true, userProbabilityOfPassingDiseaseVariantInt, userNumberOfVariantsTestedInt, nil } userDiseaseInfoExists, 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 } probabilitiesKnown, _, _, probabilityOfPassingADiseaseVariant, _, numberOfVariantsTested, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(myAnalysisMapList, monogenicDiseaseName, myGenomeIdentifier, iHaveMultipleGenomes) if (err != nil) { return false, 0, 0, err } if (probabilitiesKnown == false){ return false, 0, 0, nil } return true, probabilityOfPassingADiseaseVariant, numberOfVariantsTested, nil } myDiseaseInfoExists, myProbabilityOfPassingAVariant, myNumberOfVariantsTested, err := getMyDiseaseInfo() if (err != nil) { return nil, err } //Outputs: // -bool: Disease info known // -int: Offspring probability of having disease (0-100) // -bool: Probability offspring has variant is known // -int: Probability offspring has variant (0-100) // -int: Number of variants tested // -error getOffspringDiseaseInfo := func()(bool, int, bool, int, int, error){ if (userDiseaseInfoExists == false && myDiseaseInfoExists == false){ return false, 0, false, 0, 0, nil } if (userDiseaseInfoExists == true && myDiseaseInfoExists == false){ if (diseaseIsDominantOrRecessive == "Dominant"){ if (userProbabilityOfPassingAVariant == 100){ // We know the offspring will have the disease return true, 100, true, 100, userNumberOfVariantsTested, nil } return false, 0, false, 0, 0, nil } if (userProbabilityOfPassingAVariant == 0){ // We know the offspring will not have the disease return true, 0, false, 0, userNumberOfVariantsTested, nil } return false, 0, false, 0, 0, nil } if (userDiseaseInfoExists == false && myDiseaseInfoExists == true){ if (diseaseIsDominantOrRecessive == "Dominant"){ if (myProbabilityOfPassingAVariant == 100){ // We know the offspring will have the disease return true, 100, true, 100, myNumberOfVariantsTested, nil } return false, 0, false, 0, 0, nil } if (myProbabilityOfPassingAVariant == 0){ // We know the offspring will not have the disease return true, 0, false, 0, myNumberOfVariantsTested, nil } return false, 0, false, 0, 0, nil } probabilityOffspringHasDisease, probabilityOffspringHasVariant, err := createGeneticAnalysis.GetOffspringMonogenicDiseaseProbabilities(diseaseIsDominantOrRecessive, userProbabilityOfPassingAVariant, myProbabilityOfPassingAVariant) if (err != nil) { return false, 0, false, 0, 0, err } offspringNumberOfVariantsTested := userNumberOfVariantsTested + myNumberOfVariantsTested return true, probabilityOffspringHasDisease, true, probabilityOffspringHasVariant, offspringNumberOfVariantsTested, nil } offspringDiseaseInfoIsKnown, offspringProbabilityOfHavingDisease, offspringProbabilityOfHavingAVariantIsKnown, offspringProbabilityOfHavingAVariant, offspringNumberOfVariantsTested, err := getOffspringDiseaseInfo() if (err != nil){ return nil, err } 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 (offspringDiseaseInfoIsKnown == false){ result := translate("Unknown") return result } offspringProbabilityOfHavingDiseaseString := helpers.ConvertIntToString(offspringProbabilityOfHavingDisease) resultFormatted := offspringProbabilityOfHavingDiseaseString + "%" return resultFormatted } offspringProbabilityOfHavingDiseaseString := getOffspringProbabilityOfHavingDiseaseString() getOffspringProbabilityOfHavingAVariantString := func()string{ if (offspringDiseaseInfoIsKnown == false || offspringProbabilityOfHavingAVariantIsKnown == false){ result := translate("Unknown") return result } offspringProbabilityOfHavingAVariantString := helpers.ConvertIntToString(offspringProbabilityOfHavingAVariant) resultFormatted := offspringProbabilityOfHavingAVariantString + "%" return resultFormatted } offspringProbabilityOfHavingAVariantString := getOffspringProbabilityOfHavingAVariantString() getOffspringNumberOfVariantsTestedString := func()string{ totalNumberOfOffspringDiseaseVariantsString := helpers.ConvertIntToString(numberOfDiseaseVariants*2) if (offspringDiseaseInfoIsKnown == false){ result := "0/" + totalNumberOfOffspringDiseaseVariantsString return result } offspringNumberOfVariantsTestedString := helpers.ConvertIntToString(offspringNumberOfVariantsTested) result := offspringNumberOfVariantsTestedString + "/" + totalNumberOfOffspringDiseaseVariantsString return result } viewDiseaseInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ setViewMonogenicDiseaseDetailsPage(window, monogenicDiseaseName, currentPage) }) diseaseNameLabel := getBoldLabelCentered(monogenicDiseaseName) offspringNumberOfVariantsTestedString := getOffspringNumberOfVariantsTestedString() userProbabilityOfPassingAVariantLabel := getBoldLabelCentered(userProbabilityOfPassingAVariantString) userNumberOfVariantsTestedLabel := getBoldLabelCentered(userNumberOfVariantsTestedString) offspringProbabilityOfHavingDiseaseLabel := getBoldLabelCentered(offspringProbabilityOfHavingDiseaseString) offspringProbabilityOfHavingAVariantLabel := getBoldLabelCentered(offspringProbabilityOfHavingAVariantString) offspringNumberOfVariantsTestedLabel := getBoldLabelCentered(offspringNumberOfVariantsTestedString) diseaseInfoButtonsColumn.Add(viewDiseaseInfoButton) diseaseNameColumn.Add(diseaseNameLabel) userProbabilityOfPassingVariantColumn.Add(userProbabilityOfPassingAVariantLabel) userNumberOfVariantsTestedColumn.Add(userNumberOfVariantsTestedLabel) offspringProbabilityOfHavingDiseaseColumn.Add(offspringProbabilityOfHavingDiseaseLabel) offspringProbabilityOfHavingAVariantColumn.Add(offspringProbabilityOfHavingAVariantLabel) offspringNumberOfVariantsTestedColumn.Add(offspringNumberOfVariantsTestedLabel) diseaseInfoButtonsColumn.Add(widget.NewSeparator()) diseaseNameColumn.Add(widget.NewSeparator()) userProbabilityOfPassingVariantColumn.Add(widget.NewSeparator()) userNumberOfVariantsTestedColumn.Add(widget.NewSeparator()) offspringProbabilityOfHavingDiseaseColumn.Add(widget.NewSeparator()) offspringProbabilityOfHavingAVariantColumn.Add(widget.NewSeparator()) offspringNumberOfVariantsTestedColumn.Add(widget.NewSeparator()) } 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) }) 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, 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()){ 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){ emptyLabelA := widget.NewLabel("") diseaseNameLabel := getItalicLabelCentered("Disease Name") emptyLabelB := widget.NewLabel("") userRiskScoreLabel := getItalicLabelCentered("User Risk Score") emptyLabelC := widget.NewLabel("") offspringRiskScoreLabel := getItalicLabelCentered("Offspring Risk Score") userNumberOfLabel := getItalicLabelCentered("User Number Of") lociTestedLabelA := getItalicLabelCentered("Loci Tested") offspringNumberOfLabel := getItalicLabelCentered("Offspring Number Of") lociTestedLabelB := getItalicLabelCentered("Loci Tested") emptyLabelD := widget.NewLabel("") emptyLabelE := widget.NewLabel("") diseaseNameColumn := container.NewVBox(emptyLabelA, diseaseNameLabel, widget.NewSeparator()) userRiskScoreColumn := container.NewVBox(emptyLabelB, userRiskScoreLabel, widget.NewSeparator()) offspringRiskScoreColumn := container.NewVBox(emptyLabelC, offspringRiskScoreLabel, widget.NewSeparator()) userNumberOfLociTestedColumn := container.NewVBox(userNumberOfLabel, lociTestedLabelA, widget.NewSeparator()) offspringNumberOfLociTestedColumn := container.NewVBox(offspringNumberOfLabel, lociTestedLabelB, widget.NewSeparator()) viewDiseaseInfoButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisMapList, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { return nil, err } diseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList() if (err != nil) { return nil, err } for _, diseaseObject := range diseaseObjectsList{ diseaseName := diseaseObject.DiseaseName diseaseLociList := diseaseObject.LociList userRiskWeightSum := 0 userMinimumPossibleRiskWeightSum := 0 userMaximumPossibleRiskWeightSum := 0 userNumberOfLociTested := 0 offspringRiskWeightSum := 0 offspringMinimumPossibleRiskWeightSum := 0 offspringMaximumPossibleRiskWeightSum := 0 offspringNumberOfLociTested := 0 for _, locusObject := range diseaseLociList{ locusIdentifier := locusObject.LocusIdentifier locusRSID := locusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusRiskWeightsMap := locusObject.RiskWeightsMap locusOddsRatiosMap := locusObject.OddsRatiosMap locusMinimumRiskWeight := locusObject.MinimumRiskWeight locusMaximumRiskWeight := locusObject.MaximumRiskWeight locusValueAttributeName := "LocusValue_rs" + locusRSIDString userLocusBasePairExists, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(locusValueAttributeName) if (err != nil) { return nil, err } if (userLocusBasePairExists == true){ userNumberOfLociTested += 1 userMinimumPossibleRiskWeightSum += locusMinimumRiskWeight userMaximumPossibleRiskWeightSum += locusMaximumRiskWeight userLocusRiskWeight, exists := locusRiskWeightsMap[userLocusBasePair] if (exists == false){ // We do not know the risk weight for this base pair // We treat this as a 0 risk weight } else { userRiskWeightSum += userLocusRiskWeight } } //Outputs: // -bool: My locus base pair exists // -string: My locus base pair // -error getMyLocusInfo := func()(bool, string, error){ if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ return false, "", nil } locusInfoKnown, _, locusBasePair, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myAnalysisMapList, diseaseName, locusIdentifier, myGenomeIdentifier) if (err != nil){ return false, "", err } if (locusInfoKnown == false){ return false, "", nil } return true, locusBasePair, nil } myLocusBasePairExists, myLocusBasePair, err := getMyLocusInfo() if (err != nil) { return nil, err } if (userLocusBasePairExists == true && myLocusBasePairExists == true){ offspringLocusRiskWeight, _, _, _, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBasePair, userLocusBasePair) if (err != nil) { return nil, err } offspringNumberOfLociTested += 1 offspringMinimumPossibleRiskWeightSum += locusMinimumRiskWeight offspringMaximumPossibleRiskWeightSum += locusMaximumRiskWeight offspringRiskWeightSum += offspringLocusRiskWeight } } getUserDiseaseRiskScoreString := func()(string, error){ if (userNumberOfLociTested == 0){ result := translate("Unknown") return result, nil } userRiskScore, err := helpers.ScaleNumberProportionally(true, userRiskWeightSum, userMinimumPossibleRiskWeightSum, userMaximumPossibleRiskWeightSum, 0, 10) if (err != nil) { return "", err } userRiskScoreString := helpers.ConvertIntToString(userRiskScore) resultFormatted := userRiskScoreString + "/10" return resultFormatted, nil } userDiseaseRiskScore, err := getUserDiseaseRiskScoreString() if (err != nil) { return nil, err } getOffspringDiseaseRiskScoreString := func()(string, error){ if (offspringNumberOfLociTested == 0){ result := translate("Unknown") return result, nil } offspringRiskScore, err := helpers.ScaleNumberProportionally(true, offspringRiskWeightSum, offspringMinimumPossibleRiskWeightSum, offspringMaximumPossibleRiskWeightSum, 0, 10) if (err != nil) { return "", err } offspringRiskScoreString := helpers.ConvertIntToString(offspringRiskScore) resultFormatted := offspringRiskScoreString + "/10" return resultFormatted, nil } offspringDiseaseRiskScore, err := getOffspringDiseaseRiskScoreString() if (err != nil) { return nil, err } totalNumberOfDiseaseLoci := len(diseaseLociList) totalNumberOfDiseaseLociString := helpers.ConvertIntToString(totalNumberOfDiseaseLoci) userNumberOfLociTestedString := helpers.ConvertIntToString(userNumberOfLociTested) userNumberOfLociTestedFormatted := userNumberOfLociTestedString + "/" + totalNumberOfDiseaseLociString offspringNumberOfLociTestedString := helpers.ConvertIntToString(offspringNumberOfLociTested) offspringNumberOfLociTestedFormatted := offspringNumberOfLociTestedString + "/" + totalNumberOfDiseaseLociString diseaseNameText := getBoldLabelCentered(diseaseName) userRiskScoreLabel := getBoldLabelCentered(userDiseaseRiskScore) offspringRiskScoreLabel := getBoldLabelCentered(offspringDiseaseRiskScore) userNumberOfLociTestedLabel := getBoldLabelCentered(userNumberOfLociTestedFormatted) offspringNumberOfLociTestedLabel := getBoldLabelCentered(offspringNumberOfLociTestedFormatted) viewDiseaseDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ setViewMateProfilePage_PolygenicDiseaseLoci(window, diseaseName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage) }) diseaseNameColumn.Add(diseaseNameText) userRiskScoreColumn.Add(userRiskScoreLabel) offspringRiskScoreColumn.Add(offspringRiskScoreLabel) userNumberOfLociTestedColumn.Add(userNumberOfLociTestedLabel) offspringNumberOfLociTestedColumn.Add(offspringNumberOfLociTestedLabel) viewDiseaseInfoButtonsColumn.Add(viewDiseaseDetailsButton) diseaseNameColumn.Add(widget.NewSeparator()) userRiskScoreColumn.Add(widget.NewSeparator()) offspringRiskScoreColumn.Add(widget.NewSeparator()) userNumberOfLociTestedColumn.Add(widget.NewSeparator()) offspringNumberOfLociTestedColumn.Add(widget.NewSeparator()) viewDiseaseInfoButtonsColumn.Add(widget.NewSeparator()) } userRiskScoreHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPolygenicDiseaseRiskScoreExplainerPage(window, currentPage) }) offspringRiskScoreHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setOffspringPolygenicDiseaseRiskScoreExplainerPage(window, currentPage) }) userNumberOfLociTestedButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) }) offspringNumberOfLociTestedButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) }) userRiskScoreColumn.Add(userRiskScoreHelpButton) offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton) userNumberOfLociTestedColumn.Add(userNumberOfLociTestedButton) offspringNumberOfLociTestedColumn.Add(offspringNumberOfLociTestedButton) if (userOrOffspring == "User"){ diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, userRiskScoreColumn, userNumberOfLociTestedColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer()) return diseaseInfoGrid, nil } diseaseInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseNameColumn, offspringRiskScoreColumn, offspringNumberOfLociTestedColumn, 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_PolygenicDiseaseLoci(window fyne.Window, diseaseName string, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){ setLoadingScreen(window, translate("View Profile - Physical"), "Loading " + userOrOffspring + " Disease Loci Info") if (userOrOffspring != "User" && userOrOffspring != "Offspring"){ setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiseaseLoci called with invalid userOrOffspring: " + userOrOffspring), previousPage) return } currentPage := func(){setViewMateProfilePage_PolygenicDiseaseLoci(window, diseaseName, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)} title := getPageTitleCentered(translate("View Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered(translate(userOrOffspring + " Disease Loci Info")) diseaseNameLabel := widget.NewLabel(translate("Disease Name:")) diseaseNameText := getBoldLabel(translate(diseaseName)) viewDiseaseInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ setViewPolygenicDiseaseDetailsPage(window, diseaseName, currentPage) }) diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, viewDiseaseInfoButton, layout.NewSpacer()) diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } diseaseLocusObjectsList := diseaseObject.LociList numberOfDiseaseLoci := len(diseaseLocusObjectsList) myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisMapList, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } //Outputs: // -bool: User locus Info is known // -int: User locus Risk weight // -string: Locus base pair // -bool: User locus odds ratio known // -string: User locus odds ratio formatted // -error getUserLocusInfo := func(locusRSID int64, locusRiskWeightsMap map[string]int, locusOddsRatiosMap map[string]float64)(bool, int, string, bool, string, error){ locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusValueAttributeName := "LocusValue_rs" + locusRSIDString userLocusBasePairExists, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(locusValueAttributeName) if (err != nil) { return false, 0, "", false, "", err } if (userLocusBasePairExists == false){ return false, 0, "", false, "", nil } userLocusRiskWeight, exists := locusRiskWeightsMap[userLocusBasePair] if (exists == false){ // We do not know the risk weight for this base pair // We treat this as a 0 risk weight return true, 0, userLocusBasePair, false, "", nil } locusOddsRatio, exists := locusOddsRatiosMap[userLocusBasePair] if (exists == false){ return true, userLocusRiskWeight, userLocusBasePair, false, "", nil } locusOddsRatioString := helpers.ConvertFloat64ToStringRounded(locusOddsRatio, 2) locusOddsRatioFormatted := locusOddsRatioString + "x" return true, userLocusRiskWeight, userLocusBasePair, true, locusOddsRatioFormatted, nil } //Outputs: // -bool: My locus Info is known // -string: My locus base pair // -error getMyLocusInfo := func(locusIdentifier string)(bool, string, error){ if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ return false, "", nil } locusInfoKnown, _, locusBasePair, _, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseLocusInfoFromGeneticAnalysis(myAnalysisMapList, diseaseName, locusIdentifier, myGenomeIdentifier) if (err != nil){ return false, "", err } if (locusInfoKnown == false){ return false, "", nil } return true, locusBasePair, nil } getNumberOfLociTested := func()(int, error){ if (userOrOffspring == "Offspring"){ if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ return 0, nil } } numberOfLociTested := 0 for _, locusObject := range diseaseLocusObjectsList{ locusIdentifier := locusObject.LocusIdentifier locusRSID := locusObject.LocusRSID locusRiskWeightsMap := locusObject.RiskWeightsMap locusOddsRatiosMap := locusObject.OddsRatiosMap userLocusInfoIsKnown, _, _, _, _, err := getUserLocusInfo(locusRSID, locusRiskWeightsMap, locusOddsRatiosMap) if (err != nil) { return 0, err } if (userLocusInfoIsKnown == false){ continue } if (userOrOffspring == "Offspring") { myLocusInfoKnown, _, err := getMyLocusInfo(locusIdentifier) if (err != nil) { return 0, err } if (myLocusInfoKnown == false){ continue } } numberOfLociTested += 1 } return numberOfLociTested, nil } numberOfLociTested, err := getNumberOfLociTested() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } numberOfLociTestedString := helpers.ConvertIntToString(numberOfLociTested) totalNumberOfDiseaseLociString := helpers.ConvertIntToString(numberOfDiseaseLoci) lociTestedString := numberOfLociTestedString + "/" + totalNumberOfDiseaseLociString lociTestedLabel := widget.NewLabel("Loci Tested:") lociTestedText := getBoldLabel(lociTestedString) lociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ if (userOrOffspring == "User"){ setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) } else { setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage) } }) lociTestedRow := container.NewHBox(layout.NewSpacer(), lociTestedLabel, lociTestedText, lociTestedHelpButton, layout.NewSpacer()) //TODO: Navigation buttons and multiple pages getDiseaseLociGrid := func()(*fyne.Container, error){ //TODO: Sort loci locusNameLabel := getItalicLabelCentered("Locus Name") userRiskWeightLabel := getItalicLabelCentered("User Risk Weight") offspringRiskWeightLabel := getItalicLabelCentered("Offspring Risk Weight") userOddsRatioLabel := getItalicLabelCentered("User Odds Ratio") offspringOddsRatioLabel := getItalicLabelCentered("Offspring Odds Ratio") emptyLabel := widget.NewLabel("") locusNameColumn := container.NewVBox(locusNameLabel, widget.NewSeparator()) userRiskWeightColumn := container.NewVBox(userRiskWeightLabel, widget.NewSeparator()) offspringRiskWeightColumn := container.NewVBox(offspringRiskWeightLabel, widget.NewSeparator()) userOddsRatioColumn := container.NewVBox(userOddsRatioLabel, widget.NewSeparator()) offspringOddsRatioColumn := container.NewVBox(offspringOddsRatioLabel, widget.NewSeparator()) locusInfoButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) for _, locusObject := range diseaseLocusObjectsList{ locusIdentifier := locusObject.LocusIdentifier locusRSID := locusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusName := "rs" + locusRSIDString locusRiskWeightsMap := locusObject.RiskWeightsMap locusOddsRatiosMap := locusObject.OddsRatiosMap userLocusInfoIsKnown, userLocusRiskWeight, userLocusBasePair, userLocusOddsRatioKnown, userLocusOddsRatioFormatted, err := getUserLocusInfo(locusRSID, locusRiskWeightsMap, locusOddsRatiosMap) if (err != nil) { return nil, err } myLocusInfoIsKnown, myLocusBasePair, err := getMyLocusInfo(locusIdentifier) if (err != nil) { return nil, err } getUserRiskWeightString := func()string{ if (userLocusInfoIsKnown == false){ result := translate("Unknown") return result } userRiskWeightString := helpers.ConvertIntToString(userLocusRiskWeight) return userRiskWeightString } userRiskWeightString := getUserRiskWeightString() getUserOddsRatioString := func()string{ if (userLocusOddsRatioKnown == false){ result := translate("Unknown") return result } return userLocusOddsRatioFormatted } userOddsRatioString := getUserOddsRatioString() //Outputs: // -bool: Offspring disease locus info known // -int: Offspring risk weight // -bool: Offspring odds ratio known // -string: Offspring odds ratio formatted // -error getOffspringDiseaseLocusInfo := func()(bool, int, bool, string, error){ if (userLocusInfoIsKnown == false || myLocusInfoIsKnown == false){ return false, 0, false, "", nil } offspringLocusRiskWeight, offspringOddsRatioIsKnown, offspringOddsRatio, unknownOddsRatiosWeightSum, err := createGeneticAnalysis.GetOffspringPolygenicDiseaseLocusInfo(locusRiskWeightsMap, locusOddsRatiosMap, myLocusBasePair, userLocusBasePair) if (err != nil) { return false, 0, false, "", err } if (offspringOddsRatioIsKnown == false){ return true, offspringLocusRiskWeight, false, "", nil } getOddsRatioFormatted := func()string{ offspringOddsRatioString := helpers.ConvertFloat64ToStringRounded(offspringOddsRatio, 2) if (unknownOddsRatiosWeightSum > 0){ result := offspringOddsRatioString + "x+" return result } if (unknownOddsRatiosWeightSum < 0){ result := "<" + offspringOddsRatioString + "x" return result } result := offspringOddsRatioString + "x" return result } oddsRatioFormatted := getOddsRatioFormatted() return true, offspringLocusRiskWeight, true, oddsRatioFormatted, nil } offspringDiseaseLocusKnown, offspringRiskWeight, offspringOddsRatioKnown, offspringOddsRatioFormatted, err := getOffspringDiseaseLocusInfo() if (err != nil) { return nil, err } getOffspringRiskWeightString := func()string{ if (offspringDiseaseLocusKnown == false){ result := translate("Unknown") return result } offspringRiskWeightString := helpers.ConvertIntToString(offspringRiskWeight) return offspringRiskWeightString } offspringRiskWeightString := getOffspringRiskWeightString() getOffspringOddsRatioString := func()string{ if (offspringDiseaseLocusKnown == false || offspringOddsRatioKnown == false){ result := translate("Unknown") return result } return offspringOddsRatioFormatted } offspringOddsRatioString := getOffspringOddsRatioString() locusNameLabel := getBoldLabelCentered(locusName) userRiskWeightLabel := getBoldLabelCentered(userRiskWeightString) offspringRiskWeightLabel := getBoldLabelCentered(offspringRiskWeightString) userOddsRatioLabel := getBoldLabelCentered(userOddsRatioString) offspringOddsRatioLabel := getBoldLabelCentered(offspringOddsRatioString) locusInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){ setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifier, currentPage) }) locusNameColumn.Add(locusNameLabel) userRiskWeightColumn.Add(userRiskWeightLabel) offspringRiskWeightColumn.Add(offspringRiskWeightLabel) userOddsRatioColumn.Add(userOddsRatioLabel) offspringOddsRatioColumn.Add(offspringOddsRatioLabel) locusInfoButtonsColumn.Add(locusInfoButton) locusNameColumn.Add(widget.NewSeparator()) userRiskWeightColumn.Add(widget.NewSeparator()) offspringRiskWeightColumn.Add(widget.NewSeparator()) userOddsRatioColumn.Add(widget.NewSeparator()) offspringOddsRatioColumn.Add(widget.NewSeparator()) locusInfoButtonsColumn.Add(widget.NewSeparator()) } userRiskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) }) offspringRiskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setOffspringPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) }) userRiskWeightColumn.Add(userRiskWeightHelpButton) offspringRiskWeightColumn.Add(offspringRiskWeightHelpButton) if (userOrOffspring == "User"){ diseaseLociGrid := container.NewHBox(layout.NewSpacer(), locusNameColumn, userRiskWeightColumn, userOddsRatioColumn, locusInfoButtonsColumn, layout.NewSpacer()) return diseaseLociGrid, nil } diseaseLociGrid := container.NewHBox(layout.NewSpacer(), locusNameColumn, offspringRiskWeightColumn, offspringOddsRatioColumn, locusInfoButtonsColumn, layout.NewSpacer()) return diseaseLociGrid, nil } diseaseLociGrid, err := getDiseaseLociGrid() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), diseaseNameRow, lociTestedRow, widget.NewSeparator(), diseaseLociGrid) setPageContent(page, window) } func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){ currentPage := func(){setViewMateProfilePage_GeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)} title := getPageTitleCentered(translate("View Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered(translate("Genetic Traits")) description1 := getLabelCentered("Below is the 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_GeneticTraits(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage) } userOrOffspringSelector := widget.NewSelect([]string{"User", "Offspring"}, handleSelectButton) userOrOffspringSelector.Selected = userOrOffspring userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector) getTraitsInfoGrid := func()(*fyne.Container, error){ emptyLabelA := widget.NewLabel("") traitNameLabel := getItalicLabelCentered("Trait Name") emptyLabelB := widget.NewLabel("") userOutcomeScoresLabel := getItalicLabelCentered("User Outcome Scores") emptyLabelC := widget.NewLabel("") offspringOutcomeScoresLabel := getItalicLabelCentered("Offspring Outcome Scores") numberOfLabelA := getItalicLabelCentered("Number Of") rulesTestedLabelA := getItalicLabelCentered("Rules Tested") numberOfLabelB := getItalicLabelCentered("Number Of") rulesTestedLabelB := getItalicLabelCentered("Rules Tested") emptyLabelD := widget.NewLabel("") emptyLabelE := widget.NewLabel("") traitNameColumn := container.NewVBox(emptyLabelA, traitNameLabel, widget.NewSeparator()) userOutcomeScoresColumn := container.NewVBox(emptyLabelB, userOutcomeScoresLabel, widget.NewSeparator()) offspringOutcomeScoresColumn := container.NewVBox(emptyLabelC, offspringOutcomeScoresLabel, widget.NewSeparator()) userNumberOfRulesTestedColumn := container.NewVBox(numberOfLabelA, rulesTestedLabelA, widget.NewSeparator()) offspringNumberOfRulesTestedColumn := container.NewVBox(numberOfLabelB, rulesTestedLabelB, widget.NewSeparator()) viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator()) myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisMapList, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { return nil, err } traitObjectsList, err := traits.GetTraitObjectsList() if (err != nil) { return nil, err } for _, traitObject := range traitObjectsList{ traitName := traitObject.TraitName traitRulesList := traitObject.RulesList totalNumberOfTraitRules := len(traitRulesList) if (totalNumberOfTraitRules == 0){ // We are not able to analyze these traits yet // We will once we add neural network prediction continue } totalNumberOfTraitRulesString := helpers.ConvertIntToString(totalNumberOfTraitRules) traitOutcomeNamesList := traitObject.OutcomesList // We have to sort outcome names so they always show up in the same order traitOutcomeNamesListSorted := helpers.CopyAndSortStringListToUnicodeOrder(traitOutcomeNamesList) traitNameText := getBoldLabelCentered(translate(traitName)) traitNameColumn.Add(traitNameText) viewTraitDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ setViewMateProfilePage_TraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage) }) viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton) //Outputs: // -bool: At least 1 rule is known // -map[string]int: Outcome name -> Outcome score // -int: Number of rules tested // -error getUserTraitOutcomeScoresMap := func()(bool, map[string]int, int, error){ userTraitOutcomeScoresMap := make(map[string]int) userNumberOfRulesTested := 0 for _, traitRuleObject := range traitRulesList{ // Outputs: // -bool: Status is known // -bool: User passes rule // -error getUserPassesRuleStatus := func()(bool, bool, error){ ruleLociList := traitRuleObject.LociList allRuleLociKnown := true for _, ruleLocusObject := range ruleLociList{ locusRSID := ruleLocusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) userLocusValueAttributeName := "LocusValue_rs" + locusRSIDString userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName) if (err != nil) { return false, false, err } if (userLocusBasePairIsKnown == false){ // We know rule is not passed // We keep searching to see if ruleIsPassed status is No or Unknown allRuleLociKnown = false continue } ruleLocusBasePairsList := ruleLocusObject.BasePairsList userPassesRuleLocus := slices.Contains(ruleLocusBasePairsList, userLocusBasePair) if (userPassesRuleLocus == false){ // We know the rule is not passed return true, false, nil } } if (allRuleLociKnown == false){ // Rule status is unknown. Any loci which we knew must have passed. return false, false, nil } // Rule is passed return true, true, nil } userRuleStatusIsKnown, userPassesRule, err := getUserPassesRuleStatus() if (err != nil) { return false, nil, 0, err } if (userRuleStatusIsKnown == false){ continue } userNumberOfRulesTested += 1 if (userPassesRule == true){ ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap for traitOutcome, pointsEffect := range ruleOutcomePointsMap{ userTraitOutcomeScoresMap[traitOutcome] += pointsEffect } } } if (userNumberOfRulesTested == 0){ return false, nil, 0, nil } traitOutcomesList := traitObject.OutcomesList // We add all outcomes for which there were no points for _, traitOutcome := range traitOutcomesList{ _, exists := userTraitOutcomeScoresMap[traitOutcome] if (exists == false){ userTraitOutcomeScoresMap[traitOutcome] = 0 } } return true, userTraitOutcomeScoresMap, userNumberOfRulesTested, nil } //Outputs: // -bool: At least 1 rule is known // -map[string]float64: Outcome name -> Outcome score // -int: Number of rules tested // -error getOffspringTraitOutcomeScoresMap := func()(bool, map[string]float64, int, error){ offspringTraitOutcomeScoresMap := make(map[string]float64) offspringNumberOfRulesTested := 0 for _, traitRuleObject := range traitRulesList{ if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ // Without my genome person chosen, all offspring rules and outcome scores are unknown return false, nil, 0, nil } ruleIdentifier := traitRuleObject.RuleIdentifier _, _, myRuleLociBasePairsMap, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(myAnalysisMapList, traitName, ruleIdentifier, myGenomeIdentifier) if (err != nil) { return false, nil, 0, err } ruleLociList := traitRuleObject.LociList //Outputs: // -bool: Probability is known // -float64: Offspring probability of passing rule // -error getOffspringProbabilityOfPassingRule := func()(bool, float64, error){ offspringProbabilityOfPassingRule := float64(1) for _, ruleLocusObject := range ruleLociList{ locusIdentifier := ruleLocusObject.LocusIdentifier locusRSID := ruleLocusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) userLocusValueAttributeName := "LocusValue_rs" + locusRSIDString userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName) if (err != nil) { return false, 0, err } if (userLocusBasePairIsKnown == false){ // We must know all rule loci base pairs to determine offspring probability of passing rule return false, 0, nil } myLocusBasePair, myLocusBasePairIsKnown := myRuleLociBasePairsMap[locusIdentifier] if (myLocusBasePairIsKnown == false){ // We must know all rule loci base pairs to determine offspring probability of passing rule return false, 0, nil } locusRequiredBasePairsList := ruleLocusObject.BasePairsList offspringProbabilityOfPassingRuleLocus, err := createGeneticAnalysis.GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, userLocusBasePair, myLocusBasePair) if (err != nil) { return false, 0, err } offspringProbabilityOfPassingRule *= offspringProbabilityOfPassingRuleLocus } return true, offspringProbabilityOfPassingRule, nil } offspringProbabilityOfPassingRuleKnown, offspringProbabilityOfPassingRule, err := getOffspringProbabilityOfPassingRule() if (err != nil) { return false, nil, 0, err } if (offspringProbabilityOfPassingRuleKnown == false){ continue } offspringNumberOfRulesTested += 1 ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap for traitOutcome, pointsEffect := range ruleOutcomePointsMap{ pointsToAdd := float64(pointsEffect) * offspringProbabilityOfPassingRule offspringTraitOutcomeScoresMap[traitOutcome] += pointsToAdd } } if (offspringNumberOfRulesTested == 0){ return false, nil, 0, nil } traitOutcomesList := traitObject.OutcomesList // We add all outcomes for which there were no points for _, traitOutcome := range traitOutcomesList{ _, exists := offspringTraitOutcomeScoresMap[traitOutcome] if (exists == false){ offspringTraitOutcomeScoresMap[traitOutcome] = 0 } } return true, offspringTraitOutcomeScoresMap, offspringNumberOfRulesTested, nil } if (userOrOffspring == "User"){ userTraitOutcomeScoresKnown, userTraitOutcomeScoresMap, userNumberOfRulesTested, err := getUserTraitOutcomeScoresMap() if (err != nil) { return nil, err } numberOfRulesTestedString := helpers.ConvertIntToString(userNumberOfRulesTested) numberOfRulesTestedFormatted := numberOfRulesTestedString + "/" + totalNumberOfTraitRulesString numberOfRulesTestedLabel := getBoldLabelCentered(numberOfRulesTestedFormatted) userNumberOfRulesTestedColumn.Add(numberOfRulesTestedLabel) if (userTraitOutcomeScoresKnown == false){ unknownTranslated := translate("Unknown") unknownLabel := getBoldLabelCentered(unknownTranslated) userOutcomeScoresColumn.Add(unknownLabel) } else { for index, outcomeName := range traitOutcomeNamesListSorted{ outcomeScore, exists := userTraitOutcomeScoresMap[outcomeName] if (exists == false){ return nil, errors.New("Outcome not found in userTraitOutcomeScoresMap.") } outcomeScoreString := helpers.ConvertIntToString(outcomeScore) outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString) userOutcomeScoresColumn.Add(outcomeRow) if (index > 0){ emptyLabelA := widget.NewLabel("") emptyLabelB := widget.NewLabel("") emptyLabelC := widget.NewLabel("") traitNameColumn.Add(emptyLabelA) userNumberOfRulesTestedColumn.Add(emptyLabelB) viewTraitDetailsButtonsColumn.Add(emptyLabelC) } } } } else if (userOrOffspring == "Offspring"){ offspringTraitOutcomeScoresKnown, offspringTraitOutcomeScoresMap, offspringNumberOfRulesTested, err := getOffspringTraitOutcomeScoresMap() if (err != nil) { return nil, err } numberOfRulesTestedString := helpers.ConvertIntToString(offspringNumberOfRulesTested) numberOfRulesTestedFormatted := numberOfRulesTestedString + "/" + totalNumberOfTraitRulesString numberOfRulesTestedLabel := getBoldLabelCentered(numberOfRulesTestedFormatted) offspringNumberOfRulesTestedColumn.Add(numberOfRulesTestedLabel) if (offspringTraitOutcomeScoresKnown == false){ unknownTranslated := translate("Unknown") unknownLabel := getBoldLabelCentered(unknownTranslated) offspringOutcomeScoresColumn.Add(unknownLabel) } else { for index, outcomeName := range traitOutcomeNamesListSorted{ outcomeScore, exists := offspringTraitOutcomeScoresMap[outcomeName] if (exists == false){ return nil, errors.New("Outcome not found in offspringTraitOutcomeScoresMap.") } outcomeScoreString := helpers.ConvertFloat64ToStringRounded(outcomeScore, 2) outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString) offspringOutcomeScoresColumn.Add(outcomeRow) if (index > 0){ emptyLabelA := widget.NewLabel("") emptyLabelB := widget.NewLabel("") emptyLabelC := widget.NewLabel("") traitNameColumn.Add(emptyLabelA) offspringNumberOfRulesTestedColumn.Add(emptyLabelB) viewTraitDetailsButtonsColumn.Add(emptyLabelC) } } } } traitNameColumn.Add(widget.NewSeparator()) userOutcomeScoresColumn.Add(widget.NewSeparator()) offspringOutcomeScoresColumn.Add(widget.NewSeparator()) userNumberOfRulesTestedColumn.Add(widget.NewSeparator()) offspringNumberOfRulesTestedColumn.Add(widget.NewSeparator()) viewTraitDetailsButtonsColumn.Add(widget.NewSeparator()) } userOutcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setTraitOutcomeScoresExplainerPage(window, currentPage) }) offspringOutcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setOffspringTraitOutcomeScoresExplainerPage(window, currentPage) }) userNumberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setTraitNumberOfRulesTestedExplainerPage(window, currentPage) }) offspringNumberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setOffspringTraitNumberOfRulesTestedExplainerPage(window, currentPage) }) userOutcomeScoresColumn.Add(userOutcomeScoresHelpButton) offspringOutcomeScoresColumn.Add(offspringOutcomeScoresHelpButton) userNumberOfRulesTestedColumn.Add(userNumberOfRulesTestedHelpButton) offspringNumberOfRulesTestedColumn.Add(offspringNumberOfRulesTestedHelpButton) if (userOrOffspring == "User"){ traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, userOutcomeScoresColumn, userNumberOfRulesTestedColumn, viewTraitDetailsButtonsColumn, layout.NewSpacer()) return traitsInfoGrid, nil } traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, offspringOutcomeScoresColumn, offspringNumberOfRulesTestedColumn, viewTraitDetailsButtonsColumn, 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_TraitRules(window fyne.Window, traitName string, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){ currentPage := func(){setViewMateProfilePage_TraitRules(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: Status is known // -bool: User passes rule // -error getUserPassesRuleBool := func(ruleIdentifier string, ruleLociList []traits.RuleLocus)(bool, bool, error){ allRuleLociKnown := true for _, ruleLocusObject := range ruleLociList{ locusRSID := ruleLocusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) userLocusValueAttributeName := "LocusValue_rs" + locusRSIDString userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName) if (err != nil) { return false, false, err } if (userLocusBasePairIsKnown == false){ // We know rule is not passed // We keep searching to see if ruleIsPassed status is No or Unknown allRuleLociKnown = false continue } ruleLocusBasePairsList := ruleLocusObject.BasePairsList userPassesRuleLocus := slices.Contains(ruleLocusBasePairsList, userLocusBasePair) if (userPassesRuleLocus == false){ // We know the rule is not passed return true, false, nil } } if (allRuleLociKnown == false){ // We don't know if the user passes the rule. Any loci which we knew must have passed. return false, false, nil } return true, true, nil } myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisMapList, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } //Outputs: // -bool: Probability is known // -int: Probability of passing rule (0-100) // -error getOffspringProbabilityOfPassingRule := func(ruleIdentifier string, ruleLociList []traits.RuleLocus)(bool, int, error){ if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){ // Without my genome person chosen, all offspring rule probabilities are unknown return false, 0, nil } _, _, myRuleLociBasePairsMap, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(myAnalysisMapList, traitName, ruleIdentifier, myGenomeIdentifier) if (err != nil) { return false, 0, err } offspringProbabilityOfPassingRule := float64(1) for _, ruleLocusObject := range ruleLociList{ locusIdentifier := ruleLocusObject.LocusIdentifier locusRSID := ruleLocusObject.LocusRSID locusRSIDString := helpers.ConvertInt64ToString(locusRSID) userLocusValueAttributeName := "LocusValue_rs" + locusRSIDString userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName) if (err != nil) { return false, 0, err } if (userLocusBasePairIsKnown == false){ // We must know all rule loci base pairs to determine offspring probability of passing rule return false, 0, nil } myLocusBasePair, myLocusBasePairIsKnown := myRuleLociBasePairsMap[locusIdentifier] if (myLocusBasePairIsKnown == false){ // We must know all rule loci base pairs to determine offspring probability of passing rule return false, 0, nil } locusRequiredBasePairsList := ruleLocusObject.BasePairsList offspringProbabilityOfPassingRuleLocus, err := createGeneticAnalysis.GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, userLocusBasePair, myLocusBasePair) if (err != nil) { return false, 0, err } offspringProbabilityOfPassingRule *= offspringProbabilityOfPassingRuleLocus } offspringPercentageProbabilityOfPassingRule := int(offspringProbabilityOfPassingRule * 100) return true, offspringPercentageProbabilityOfPassingRule, nil } traitObject, err := traits.GetTraitObject(traitName) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } traitRulesList := traitObject.RulesList totalNumberOfTraitRules := len(traitRulesList) getNumberOfRulesTested := func()(int, error){ numberOfRulesTested := 0 for _, ruleObject := range traitRulesList{ ruleIdentifier := ruleObject.RuleIdentifier ruleLociList := ruleObject.LociList if (userOrOffspring == "User"){ ruleStatusIsKnown, _, err := getUserPassesRuleBool(ruleIdentifier, ruleLociList) if (err != nil) { return 0, err } if (ruleStatusIsKnown == true){ numberOfRulesTested += 1 } } else if (userOrOffspring == "Offspring"){ ruleProbabilityIsKnown, _, err := getOffspringProbabilityOfPassingRule(ruleIdentifier, ruleLociList) if (err != nil) { return 0, err } if (ruleProbabilityIsKnown == true){ numberOfRulesTested += 1 } } } return numberOfRulesTested, nil } numberOfRulesTested, err := getNumberOfRulesTested() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } rulesTestedLabel := widget.NewLabel("Rules Tested:") numberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested) totalNumberOfTraitRulesString := helpers.ConvertIntToString(totalNumberOfTraitRules) numberOfRulesTestedFormatted := numberOfRulesTestedString + "/" + totalNumberOfTraitRulesString numberOfRulesTestedLabel := getBoldLabel(numberOfRulesTestedFormatted) numberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ if (userOrOffspring == "User"){ setTraitNumberOfRulesTestedExplainerPage(window, currentPage) } else { setOffspringTraitNumberOfRulesTestedExplainerPage(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{ ruleIdentifier := ruleObject.RuleIdentifier ruleLociList := ruleObject.LociList getUserPassesRuleString := func()(string, error){ userRuleStatusIsKnown, userPassesRule, err := getUserPassesRuleBool(ruleIdentifier, ruleLociList) 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){ probabilityIsKnown, probabilityOfPassingRule, err := getOffspringProbabilityOfPassingRule(ruleIdentifier, ruleLociList) if (err != nil) { return "", err } 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(){ setViewTraitRuleDetailsPage(window, traitName, ruleIdentifier, currentPage) }) ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifier) 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(){ setTraitRuleOutcomeEffectsExplainerPage(window, currentPage) }) userPassesRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPersonPassesTraitRuleExplainerPage(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_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("Unknown")) 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(subregionADescription string, subregionBDescription string)int{ if (subregionADescription == subregionBDescription){ panic("compareSubregionsFunction called with identical subregion descriptions.") } subregionAPercentage, exists := subregionDescriptionPercentagesMap[subregionADescription] if (exists == false){ panic("subregionPercentagesMap missing subregion during sort.") } subregionBPercentage, exists := subregionDescriptionPercentagesMap[subregionBDescription] if (exists == false){ panic("subregionPercentagesMap missing subregion during sort.") } if (subregionAPercentage == subregionBPercentage){ // We sort subregions in unicode order if (subregionADescription < subregionBDescription){ return -1 } return 1 } if (subregionAPercentage > subregionBPercentage){ return -1 } return 1 } slices.SortFunc(currentRegionSubregionsList, compareSubregionsFunction) treeMap[regionDescription] = currentRegionSubregionsList } // We sort continent regions list by highest to lowest percentage. compareRegionsFunction := func(regionADescription string, regionBDescription string)int{ if (regionADescription == regionBDescription){ panic("compareRegionsFunction called with identical regions.") } regionAPercentage, exists := regionDescriptionPercentagesMap[regionADescription] if (exists == false){ panic("regionPercentagesMap missing subregion during sort.") } regionBPercentage, exists := regionDescriptionPercentagesMap[regionBDescription] if (exists == false){ panic("regionPercentagesMap missing subregion during sort.") } if (regionAPercentage == regionBPercentage){ // We sort regions in unicode order if (regionADescription < regionBDescription){ return -1 } return 1 } if (regionAPercentage > regionBPercentage){ return -1 } return 1 } slices.SortFunc(currentContinentRegionsList, compareRegionsFunction) treeMap[continentDescription] = currentContinentRegionsList } // We sort root list by highest to lowest proportions compareContinentsFunction := func(continentADescription string, continentBDescription string)int{ if (continentADescription == continentBDescription){ panic("compareContinentsFunction called with identical continents.") } continentAPercentage, exists := continentDescriptionPercentagesMap[continentADescription] if (exists == false){ panic("Continent percentage not found when sorting root list.") } continentBPercentage, exists := continentDescriptionPercentagesMap[continentBDescription] if (exists == false){ panic("Continent percentage not found when sorting root list.") } if (continentAPercentage == continentBPercentage){ // We sort continents in unicode order if (continentADescription < continentBDescription){ return -1 } return 1 } if (continentAPercentage > continentBPercentage){ return -1 } return 1 } slices.SortFunc(continentsList, compareContinentsFunction) treeMap[""] = continentsList resultTree := widget.NewTreeWithStrings(treeMap) resultTree.OpenAllBranches() return resultTree, nil }