5130 lines
194 KiB
Go
5130 lines
194 KiB
Go
package gui
|
|
|
|
// viewProfileGui.go implements pages to view user profiles
|
|
|
|
import "fyne.io/fyne/v2"
|
|
import "fyne.io/fyne/v2/canvas"
|
|
import "fyne.io/fyne/v2/container"
|
|
import "fyne.io/fyne/v2/dialog"
|
|
import "fyne.io/fyne/v2/layout"
|
|
import "fyne.io/fyne/v2/theme"
|
|
import "fyne.io/fyne/v2/widget"
|
|
|
|
import "seekia/resources/worldLanguages"
|
|
import "seekia/resources/worldLocations"
|
|
import "seekia/resources/geneticPredictionModels"
|
|
import "seekia/resources/geneticReferences/monogenicDiseases"
|
|
import "seekia/resources/geneticReferences/polygenicDiseases"
|
|
import "seekia/resources/geneticReferences/traits"
|
|
|
|
import "seekia/internal/appMemory"
|
|
import "seekia/internal/encoding"
|
|
import "seekia/internal/genetics/companyAnalysis"
|
|
import "seekia/internal/genetics/createCoupleGeneticAnalysis"
|
|
import "seekia/internal/genetics/createPersonGeneticAnalysis"
|
|
import "seekia/internal/genetics/locusValue"
|
|
import "seekia/internal/genetics/myChosenAnalysis"
|
|
import "seekia/internal/genetics/myPeople"
|
|
import "seekia/internal/genetics/readGeneticAnalysis"
|
|
import "seekia/internal/globalSettings"
|
|
import "seekia/internal/helpers"
|
|
import "seekia/internal/identity"
|
|
import "seekia/internal/imagery"
|
|
import "seekia/internal/mateQuestionnaire"
|
|
import "seekia/internal/moderation/moderatorRanking"
|
|
import "seekia/internal/moderation/trustedViewableStatus"
|
|
import "seekia/internal/moderation/verifiedStickyStatus"
|
|
import "seekia/internal/myIdentity"
|
|
import "seekia/internal/network/appNetworkType/getAppNetworkType"
|
|
import "seekia/internal/network/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, _, profileCreationTimeString, err := getAnyUserProfileAttributeFunction("CreationTime")
|
|
if (err != nil){ return nil, err }
|
|
if (exists == false){
|
|
return nil, errors.New("setViewUserProfilePage_ProfileDetails called with profile missing CreationTime")
|
|
}
|
|
|
|
profileCreationTime, err := helpers.ConvertCreationTimeStringToInt64(profileCreationTimeString)
|
|
if (err != nil){
|
|
return nil, errors.New("setViewUserProfilePage_ProfileDetails called with profile with invalid CreationTime: " + profileCreationTimeString)
|
|
}
|
|
|
|
creationTimeLabel := widget.NewLabel("Creation Time:")
|
|
|
|
creationTimeString := helpers.ConvertUnixTimeToTranslatedTime(profileCreationTime)
|
|
|
|
creationTimeAgoString, err := helpers.ConvertUnixTimeToTimeFromNowTranslated(profileCreationTime, true)
|
|
if (err != nil) { return nil, err }
|
|
|
|
creationTimeText := getBoldLabel(creationTimeString + " (" + creationTimeAgoString + ")")
|
|
|
|
creationTimeWarningButton := widget.NewButtonWithIcon("", theme.WarningIcon(), func(){
|
|
title := translate("Creation Time Warning")
|
|
dialogMessageA := getLabelCentered("Profile creation times are not verified.")
|
|
dialogMessageB := getLabelCentered("They can be faked by the profile author.")
|
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
|
})
|
|
|
|
creationTimeRow := container.NewHBox(layout.NewSpacer(), creationTimeLabel, creationTimeText, creationTimeWarningButton, layout.NewSpacer())
|
|
|
|
exists, profileVersion, profileType, err := getAnyUserProfileAttributeFunction("ProfileType")
|
|
if (err != nil) { return nil, err }
|
|
if (exists == false){
|
|
return nil, errors.New("getAnyUserProfileAttributeFunction called and ProfileType not found")
|
|
}
|
|
profileTypeLabel := widget.NewLabel("Profile Type:")
|
|
profileTypeText := getBoldLabel(profileType)
|
|
profileTypeRow := container.NewHBox(layout.NewSpacer(), profileTypeLabel, profileTypeText, layout.NewSpacer())
|
|
|
|
profileVersionLabel := widget.NewLabel("Profile Version:")
|
|
profileVersionString := helpers.ConvertIntToString(profileVersion)
|
|
profileVersionText := getBoldLabel(profileVersionString)
|
|
profileVersionRow := container.NewHBox(layout.NewSpacer(), profileVersionLabel, profileVersionText, layout.NewSpacer())
|
|
|
|
//TODO: Add moderation details (if profile/identity is banned)
|
|
|
|
//TODO: Show when identity/profile will expire, and allow user to send funds to increase the user's identity balance/score
|
|
|
|
//TODO: Save raw profile button (will save it as a txt file)
|
|
|
|
pageContent := container.NewVBox(profileHashRow, widget.NewSeparator(), creationTimeRow, widget.NewSeparator(), profileTypeRow, widget.NewSeparator(), profileVersionRow)
|
|
|
|
return pageContent, nil
|
|
}
|
|
|
|
pageContent, err := getPageContent()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), pageContent)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
|
|
func setViewUserProfilePage_Category(window fyne.Window, profileIsMine bool, categoryName string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()) {
|
|
|
|
if (categoryName != "General" && categoryName != "Physical" && categoryName != "Lifestyle" && categoryName != "Mental"){
|
|
setErrorEncounteredPage(window, errors.New("setViewUserProfilePage_Category called with invalid profile category: " + categoryName), previousPage)
|
|
return
|
|
}
|
|
|
|
currentPage := func(){setViewUserProfilePage_Category(window, profileIsMine, categoryName, getAnyUserProfileAttributeFunction, previousPage)}
|
|
|
|
title := getPageTitleCentered("View Profile - " + categoryName)
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator())
|
|
|
|
addAttributeRow := func(attributeName string)error{
|
|
|
|
attributeTitle, _, formatAttributeValue, attributeUnits, missingValueText, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName)
|
|
if (err != nil) { return err }
|
|
|
|
attributeTitleLabel := widget.NewLabel(attributeTitle + ":")
|
|
|
|
exists, _, attributeValue, err := getAnyUserProfileAttributeFunction(attributeName)
|
|
if (err != nil) { return err }
|
|
if (exists == false) {
|
|
|
|
attributeValueLabel := getBoldItalicLabel(missingValueText)
|
|
attributeRow := container.NewHBox(layout.NewSpacer(), attributeTitleLabel, attributeValueLabel, layout.NewSpacer())
|
|
page.Add(attributeRow)
|
|
|
|
return nil
|
|
}
|
|
|
|
attributeValueFormatted, err := formatAttributeValue(attributeValue)
|
|
if (err != nil) { return err }
|
|
|
|
attributeValueFormattedWithUnits := attributeValueFormatted + attributeUnits
|
|
|
|
attributeValueTrimmed, anyChangesOccurred, err := helpers.TrimAndFlattenString(attributeValueFormattedWithUnits, 25)
|
|
if (err != nil) { return err }
|
|
if (anyChangesOccurred == false){
|
|
|
|
attributeValueLabel := getBoldLabel(attributeValueFormattedWithUnits)
|
|
|
|
attributeRow := container.NewHBox(layout.NewSpacer(), attributeTitleLabel, attributeValueLabel, layout.NewSpacer())
|
|
page.Add(attributeRow)
|
|
|
|
return nil
|
|
}
|
|
|
|
attributeValueTrimmedLabel := getBoldLabel(attributeValueTrimmed)
|
|
|
|
viewAttributeValueButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
|
setViewTextPage(window, "Viewing " + attributeTitle, attributeValueFormattedWithUnits, false, currentPage)
|
|
})
|
|
|
|
attributeValueRow := container.NewHBox(layout.NewSpacer(), attributeTitleLabel, attributeValueTrimmedLabel, viewAttributeValueButton, layout.NewSpacer())
|
|
page.Add(attributeValueRow)
|
|
|
|
return nil
|
|
}
|
|
|
|
addAttributesFunction := func()error{
|
|
|
|
if (categoryName == "General"){
|
|
|
|
exists, _, userProfileType, err := getAnyUserProfileAttributeFunction("ProfileType")
|
|
if (err != nil) { return err }
|
|
if (exists == false){
|
|
return errors.New("getAnyUserProfileAttributeFunction not able to find ProfileType")
|
|
}
|
|
|
|
if (userProfileType == "Mate"){
|
|
|
|
locationButton := widget.NewButton("Location", func(){
|
|
setViewMateProfilePage_Location(window, getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
questionnaireButton := widget.NewButton("Questionnaire", func(){
|
|
setViewMateProfilePage_Questionnaire(window, profileIsMine, getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
tagsButton := widget.NewButton("Tags", func(){
|
|
setViewMateProfilePage_Tags(window, getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, locationButton, questionnaireButton, tagsButton))
|
|
page.Add(buttonsGrid)
|
|
|
|
page.Add(widget.NewSeparator())
|
|
}
|
|
|
|
err = addAttributeRow("Description")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("Username")
|
|
if (err != nil) { return err }
|
|
|
|
if (userProfileType == "Mate"){
|
|
|
|
err = addAttributeRow("Sexuality")
|
|
if (err != nil) { return err }
|
|
}
|
|
|
|
return nil
|
|
}
|
|
if (categoryName == "Physical"){
|
|
|
|
racialSimilarityButton := widget.NewButton("Racial Similarity", func(){
|
|
setViewMateProfilePage_RacialSimilarity(window, getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
ancestryCompositionButton := widget.NewButton("Ancestry Composition", func(){
|
|
setViewMateProfilePage_AncestryComposition(window, "User", getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
haplogroupsButton := widget.NewButton("Haplogroups", func(){
|
|
setViewMateProfilePage_Haplogroups(window, getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
neanderthalVariantsButton := widget.NewButton("Neanderthal Variants", func(){
|
|
setViewMateProfilePage_NeanderthalVariants(window, getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
totalDiseaseRiskButton := widget.NewButton("Total Disease Risk", func(){
|
|
setViewMateProfilePage_TotalDiseaseRisk(window, getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
monogenicDiseasesButton := widget.NewButton("Monogenic Diseases", func(){
|
|
setViewMateProfilePage_MonogenicDiseases(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
polygenicDiseasesButton := widget.NewButton("Polygenic Diseases", func(){
|
|
setViewMateProfilePage_PolygenicDiseases(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
geneticTraitsButton := widget.NewButton("Genetic Traits", func(){
|
|
setViewMateProfilePage_GeneticTraits(window, getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
buttonsGrid := getContainerCentered(container.NewGridWithColumns(2, racialSimilarityButton, totalDiseaseRiskButton, ancestryCompositionButton, monogenicDiseasesButton, haplogroupsButton, polygenicDiseasesButton, neanderthalVariantsButton, geneticTraitsButton))
|
|
page.Add(buttonsGrid)
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
err := addAttributeRow("Age")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("Sex")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("Height")
|
|
if (err != nil) { return err }
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
err = addAttributeRow("EyeColor")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("SkinColor")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("HairColor")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("HairTexture")
|
|
if (err != nil) { return err }
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
err = addAttributeRow("BodyFat")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("BodyMuscle")
|
|
if (err != nil) { return err }
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
err = addAttributeRow("HasHIV")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("HasGenitalHerpes")
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
if (categoryName == "Lifestyle"){
|
|
|
|
dietButton := getWidgetCentered(widget.NewButton("Diet", func(){
|
|
setViewMateProfilePage_Diet(window, getAnyUserProfileAttributeFunction, currentPage)
|
|
}))
|
|
page.Add(dietButton)
|
|
page.Add(widget.NewSeparator())
|
|
|
|
err := addAttributeRow("Hobbies")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("WealthInGold")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("Job")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("Fame")
|
|
if (err != nil) { return err }
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
err = addAttributeRow("AlcoholFrequency")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("TobaccoFrequency")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("CannabisFrequency")
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
if (categoryName == "Mental"){
|
|
|
|
languageButton := getWidgetCentered(widget.NewButton("Language", func(){
|
|
setViewMateProfilePage_Language(window, getAnyUserProfileAttributeFunction, currentPage)
|
|
}))
|
|
page.Add(languageButton)
|
|
page.Add(widget.NewSeparator())
|
|
|
|
err := addAttributeRow("Beliefs")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("GenderIdentity")
|
|
if (err != nil) { return err }
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
err = addAttributeRow("PetsRating")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("DogsRating")
|
|
if (err != nil) { return err }
|
|
|
|
err = addAttributeRow("CatsRating")
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
return errors.New("setViewUserProfilePage_Category called with invalid profile category: " + categoryName)
|
|
}
|
|
|
|
err := addAttributesFunction()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_Location(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
setLoadingScreen(window, "View Profile - General", "Loading user location...")
|
|
|
|
title := getPageTitleCentered("View Profile - General")
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Location"))
|
|
|
|
//Outputs:
|
|
// -bool: Location exists
|
|
// -*fyne.Container
|
|
// -error
|
|
getLocationColumn := func(locationRank string)(bool, *fyne.Container, error){
|
|
|
|
if (locationRank != "Primary" && locationRank != "Secondary"){
|
|
return false, nil, errors.New("getLocationColumn called with invalid locationRank: " + locationRank)
|
|
}
|
|
|
|
exists, _, locationLatitudeString, err := getAnyUserProfileAttributeFunction(locationRank + "LocationLatitude")
|
|
if (err != nil) { return false, nil, err }
|
|
if (exists == false){
|
|
return false, nil, nil
|
|
}
|
|
|
|
exists, _, locationLongitudeString, err := getAnyUserProfileAttributeFunction(locationRank + "LocationLongitude")
|
|
if (err != nil) { return false, nil, err }
|
|
if (exists == false){
|
|
return false, nil, errors.New("setViewMateProfilePage_Location called with profile containing " + locationRank + "LocationLatitude but missing " + locationRank + "LocationLongitude")
|
|
}
|
|
|
|
locationLatitudeFloat64, err := helpers.ConvertStringToFloat64(locationLatitudeString)
|
|
if (err != nil){
|
|
return false, nil, errors.New("setViewMateProfilePage_Location called with profile containing invalid LocationLatitude: " + locationLatitudeString)
|
|
}
|
|
|
|
locationLongitudeFloat64, err := helpers.ConvertStringToFloat64(locationLongitudeString)
|
|
if (err != nil){
|
|
return false, nil, errors.New("setViewMateProfilePage_Location called with profile containing invalid LocationLongitude: " + locationLongitudeString)
|
|
}
|
|
|
|
locationRankLabel := getBoldLabelCentered(locationRank)
|
|
|
|
locationCountryExists, _, locationCountryIdentifier, err := getAnyUserProfileAttributeFunction(locationRank + "LocationCountry")
|
|
if (err != nil) { return false, nil, err }
|
|
|
|
getCountryTextLabel := func()(fyne.Widget, error){
|
|
|
|
if (locationCountryExists == false){
|
|
noneLabel := getBoldItalicLabel(translate("None"))
|
|
return noneLabel, nil
|
|
}
|
|
|
|
locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
|
|
if (err != nil) { return nil, err }
|
|
|
|
countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(locationCountryIdentifierInt)
|
|
if (err != nil){ return nil, err }
|
|
|
|
countryNamesList := countryObject.NamesList
|
|
|
|
countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")
|
|
|
|
countryTextLabel := getBoldLabel(translate(countryDescription))
|
|
return countryTextLabel, nil
|
|
}
|
|
|
|
countryTextLabel, err := getCountryTextLabel()
|
|
if (err != nil) { return false, nil, err }
|
|
|
|
countryLabel := getLabelCentered("Country:")
|
|
|
|
coordinatesLabel := getLabelCentered("Coordinates:")
|
|
|
|
locationCoordinatesLabel := getBoldLabel(locationLatitudeString + "°, " + locationLongitudeString + "°")
|
|
|
|
cityLabel := getLabelCentered("City:")
|
|
|
|
getCityContent := func()(*fyne.Container, error){
|
|
|
|
// We will either find the exact city or the closest city
|
|
|
|
cityName, cityState, cityCountryIdentifier, cityDistanceKilometers, err := worldLocations.GetClosestCityFromCoordinates(locationLatitudeFloat64, locationLongitudeFloat64)
|
|
if (err != nil) { return nil, err }
|
|
|
|
if (cityDistanceKilometers == 0){
|
|
|
|
locationCityFormatted := cityName + ", " + cityState
|
|
|
|
locationCityLabel := getBoldLabelCentered(locationCityFormatted)
|
|
|
|
return locationCityLabel, nil
|
|
}
|
|
|
|
currentUnitsExist, currentUnits, err := globalSettings.GetSetting("MetricOrImperial")
|
|
if (err != nil){ return nil, err }
|
|
|
|
getNearbyCityDistanceFormattedString := func()(string, error){
|
|
|
|
if (currentUnitsExist == true && currentUnits == "Imperial"){
|
|
|
|
distanceMiles, err := helpers.ConvertKilometersToMiles(cityDistanceKilometers)
|
|
if (err != nil) { return "", err }
|
|
|
|
distanceMilesString := helpers.ConvertFloat64ToStringRounded(distanceMiles, 1)
|
|
result := distanceMilesString + " miles"
|
|
return result, nil
|
|
}
|
|
|
|
distanceKilometersString := helpers.ConvertFloat64ToStringRounded(cityDistanceKilometers, 1)
|
|
|
|
result := distanceKilometersString + " kilometers"
|
|
|
|
return result, nil
|
|
}
|
|
|
|
nearbyCityDistanceFormattedString, err := getNearbyCityDistanceFormattedString()
|
|
if (err != nil) { return nil, err }
|
|
|
|
getNearbyCityNameFormatted := func()(string, error){
|
|
|
|
if (locationCountryExists == true){
|
|
|
|
locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
|
|
if (err != nil) { return "", err }
|
|
|
|
if (cityCountryIdentifier == locationCountryIdentifierInt){
|
|
result := cityName + ", " + cityState
|
|
return result, nil
|
|
}
|
|
}
|
|
|
|
countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(cityCountryIdentifier)
|
|
if (err != nil){ return "", err }
|
|
|
|
countryNamesList := countryObject.NamesList
|
|
|
|
countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")
|
|
|
|
result := cityName + ", " + cityState + ", " + countryDescription
|
|
return result, nil
|
|
}
|
|
|
|
nearbyCityNameFormatted, err := getNearbyCityNameFormatted()
|
|
if (err != nil) { return nil, err }
|
|
|
|
nearbyCityNameFormattedAndTrimmed, _, err := helpers.TrimAndFlattenString(nearbyCityNameFormatted, 40)
|
|
if (err != nil) { return nil, err }
|
|
|
|
locationDistanceLabel := getBoldLabelCentered(nearbyCityDistanceFormattedString + " from")
|
|
|
|
locationCityNameLabel := getBoldLabelCentered(nearbyCityNameFormattedAndTrimmed)
|
|
|
|
cityInfoContainer := container.NewVBox(locationDistanceLabel, locationCityNameLabel)
|
|
|
|
return cityInfoContainer, nil
|
|
}
|
|
|
|
cityContent, err := getCityContent()
|
|
if (err != nil){ return false, nil, err }
|
|
|
|
locationColumn := container.NewVBox(locationRankLabel, widget.NewSeparator(), countryLabel, countryTextLabel, widget.NewSeparator(), coordinatesLabel, locationCoordinatesLabel, widget.NewSeparator(), cityLabel, cityContent)
|
|
|
|
return true, locationColumn, nil
|
|
}
|
|
|
|
primaryLocationExists, primaryLocationColumn, err := getLocationColumn("Primary")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (primaryLocationExists == false){
|
|
|
|
noLocationsLabel := getBoldLabelCentered("This user has no locations.")
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), noLocationsLabel)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
locationColumnsGrid := container.NewGridWithRows(1, primaryLocationColumn)
|
|
|
|
secondaryLocationExists, secondaryLocationColumn, err := getLocationColumn("Secondary")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (secondaryLocationExists == true){
|
|
|
|
locationColumnsGrid.Add(secondaryLocationColumn)
|
|
}
|
|
locationColumnsGridCentered := getContainerCentered(locationColumnsGrid)
|
|
|
|
getDescriptionText := func()string{
|
|
if (secondaryLocationExists == false){
|
|
result := translate("Below is the user's location.")
|
|
return result
|
|
}
|
|
|
|
result := translate("Below are the user's locations.")
|
|
return result
|
|
}
|
|
|
|
descriptionText := getDescriptionText()
|
|
|
|
description := getLabelCentered(descriptionText)
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), locationColumnsGridCentered)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_Questionnaire(window fyne.Window, profileIsMine bool, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
currentPage := func(){setViewMateProfilePage_Questionnaire(window, profileIsMine, getAnyUserProfileAttributeFunction, previousPage)}
|
|
|
|
title := getPageTitleCentered(translate("View Profile - General"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Questionnaire"))
|
|
|
|
questionnaireExists, _, questionnaireString, err := getAnyUserProfileAttributeFunction("Questionnaire")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (questionnaireExists == false){
|
|
description := getBoldLabelCentered("This user does not have a questionnaire.")
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
description1 := getLabelCentered("This user has a questionnaire.")
|
|
description2 := getLabelCentered("You can take their questionnaire and send them your responses.")
|
|
|
|
questionnaireObject, err := mateQuestionnaire.ReadQuestionnaireString(questionnaireString)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_Questionnaire called with profile containing invalid questionnaire."), previousPage)
|
|
return
|
|
}
|
|
|
|
numberOfQuestions := len(questionnaireObject)
|
|
numberOfQuestionsString := helpers.ConvertIntToString(numberOfQuestions)
|
|
|
|
numberOfQuestionsLabel := widget.NewLabel("Number of questions:")
|
|
numberOfQuestionsText := getBoldLabel(numberOfQuestionsString)
|
|
|
|
numberOfQuestionsRow := container.NewHBox(layout.NewSpacer(), numberOfQuestionsLabel, numberOfQuestionsText, layout.NewSpacer())
|
|
|
|
exists, _, userIdentityHashString, err := getAnyUserProfileAttributeFunction("IdentityHash")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (exists == false){
|
|
setErrorEncounteredPage(window, errors.New("getAnyUserProfileAttributeFunction failed to get IdentityHash"), previousPage)
|
|
return
|
|
}
|
|
|
|
userIdentityHash, _, err := identity.ReadIdentityHashString(userIdentityHashString)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
//TODO: Show if user has already taken the questionnaire
|
|
|
|
submitQuestionnairePage := func(questionnaireResponse string, prevPage func()){
|
|
if (profileIsMine == true){
|
|
|
|
dialogTitle := translate("Cannot Submit Questionnaire")
|
|
dialogMessageA := getLabelCentered(translate("You are viewing your own profile."))
|
|
dialogMessageB := getLabelCentered(translate("You cannot submit a response to your own questionnaire."))
|
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
|
return
|
|
}
|
|
setSubmitQuestionnairePage(window, userIdentityHash, questionnaireResponse, prevPage, currentPage)
|
|
}
|
|
|
|
takeQuestionnaireButton := getWidgetCentered(widget.NewButtonWithIcon("Take Questionnaire", theme.DocumentCreateIcon(), func(){
|
|
emptyMap := make(map[string]string)
|
|
setTakeQuestionnairePage(window, questionnaireObject, 0, emptyMap, currentPage, submitQuestionnairePage)
|
|
}))
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, numberOfQuestionsRow, takeQuestionnaireButton)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_Tags(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
title := getPageTitleCentered(translate("View Profile - General"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Tags"))
|
|
|
|
anyTagsExist, _, tagsAttribute, err := getAnyUserProfileAttributeFunction("Tags")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (anyTagsExist == false){
|
|
|
|
description := getBoldLabelCentered("This user has no tags.")
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
description := getLabelCentered("Below are the user's tags.")
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator())
|
|
|
|
tagsList := strings.Split(tagsAttribute, "+&")
|
|
|
|
for _, tagName := range tagsList{
|
|
|
|
tagLabel := getBoldLabelCentered(tagName)
|
|
|
|
page.Add(tagLabel)
|
|
}
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_Haplogroups(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
title := getPageTitleCentered(translate("View Profile - Physical"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Haplogroups"))
|
|
|
|
description1 := getLabelCentered(translate("Below are the user's haplogroups."))
|
|
description2 := getLabelCentered("These are reported by 23andMe. More companies will be added.")
|
|
|
|
userMaternalHaplogroupExists, _, userMaternalHaplogroup_23andMe, err := getAnyUserProfileAttributeFunction("23andMe_MaternalHaplogroup")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
maternalHaplogroupLabel := widget.NewLabel("Maternal Haplogroup:")
|
|
|
|
getMaternalHalogroupText := func()fyne.Widget{
|
|
|
|
if (userMaternalHaplogroupExists == false){
|
|
noResponseLabel := getBoldItalicLabel("No Response")
|
|
return noResponseLabel
|
|
}
|
|
|
|
maternalHaplogroupLabel := getBoldLabel(userMaternalHaplogroup_23andMe)
|
|
|
|
return maternalHaplogroupLabel
|
|
}
|
|
|
|
maternalHaplogroupText := getMaternalHalogroupText()
|
|
|
|
userMaternalHaplogroupRow := container.NewHBox(layout.NewSpacer(), maternalHaplogroupLabel, maternalHaplogroupText, layout.NewSpacer())
|
|
|
|
userPaternalHaplogroupExists, _, userPaternalHaplogroup_23andMe, err := getAnyUserProfileAttributeFunction("23andMe_PaternalHaplogroup")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
paternalHaplogroupLabel := widget.NewLabel("Paternal Haplogroup:")
|
|
|
|
getPaternalHalogroupText := func()fyne.Widget{
|
|
|
|
if (userPaternalHaplogroupExists == false){
|
|
noResponseLabel := getBoldItalicLabel("No Response")
|
|
return noResponseLabel
|
|
}
|
|
|
|
paternalHaplogroupLabel := getBoldLabel(userPaternalHaplogroup_23andMe)
|
|
|
|
return paternalHaplogroupLabel
|
|
}
|
|
|
|
paternalHaplogroupText := getPaternalHalogroupText()
|
|
|
|
userPaternalHaplogroupRow := container.NewHBox(layout.NewSpacer(), paternalHaplogroupLabel, paternalHaplogroupText, layout.NewSpacer())
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), userMaternalHaplogroupRow, userPaternalHaplogroupRow)
|
|
|
|
//Outputs:
|
|
// -bool: Offspring maternal haplogroup is known
|
|
// -string: Offspring maternal haplogroup
|
|
// -bool: Offspring paternal haplogroup is known
|
|
// -string: Offspring paternal haplogroup is known
|
|
// -error
|
|
getOffspringHaplogroups := func()(bool, string, bool, string, error){
|
|
|
|
isKnown, _, isSameSex, err := getAnyUserProfileAttributeFunction("IsSameSex")
|
|
if (err != nil){ return false, "", false, "", err }
|
|
if (isKnown == true && isSameSex == "Yes"){
|
|
return false, "", false, "", nil
|
|
}
|
|
|
|
mySexExists, mySex, err := myLocalProfiles.GetProfileData("Mate", "Sex")
|
|
if (err != nil) { return false, "", false, "", err }
|
|
|
|
userSexExists, _, userSex, err := getAnyUserProfileAttributeFunction("Sex")
|
|
if (err != nil) { return false, "", false, "", err }
|
|
|
|
if (mySexExists == false && userSexExists == false){
|
|
return false, "", false, "", nil
|
|
}
|
|
|
|
//Outputs:
|
|
// -bool: Haplogroup is known
|
|
// -string: Offspring maternal haplogroup
|
|
// -error
|
|
getOffspringMaternalHaplogroup := func()(bool, string, error){
|
|
|
|
if (mySexExists == true){
|
|
if (mySex == "Female" || mySex == "Intersex Female"){
|
|
|
|
exists, myMaternalHaplogroup, err := myLocalProfiles.GetProfileData("Mate", "23andMe_MaternalHaplogroup")
|
|
if (err != nil){ return false, "", err }
|
|
if (exists == false){
|
|
return false, "", nil
|
|
}
|
|
return true, myMaternalHaplogroup, nil
|
|
}
|
|
}
|
|
if (userSexExists == true){
|
|
if (userSex == "Female" || userSex == "Intersex Female"){
|
|
if (userMaternalHaplogroupExists == true){
|
|
return true, userMaternalHaplogroup_23andMe, nil
|
|
}
|
|
}
|
|
}
|
|
return false, "", nil
|
|
}
|
|
|
|
offspringMaternalHaplogroupIsKnown, offspringMaternalHaplogroup, err := getOffspringMaternalHaplogroup()
|
|
if (err != nil) { return false, "", false, "", err }
|
|
|
|
//Outputs:
|
|
// -bool: Haplogroup is known
|
|
// -string: Offspring paternal haplogroup
|
|
// -error
|
|
getOffspringPaternalHaplogroup := func()(bool, string, error){
|
|
if (mySexExists == true){
|
|
if (mySex == "Male" || mySex == "Intersex Male"){
|
|
|
|
exists, myPaternalHaplogroup, err := myLocalProfiles.GetProfileData("Mate", "23andMe_PaternalHaplogroup")
|
|
if (err != nil){ return false, "", err }
|
|
if (exists == false){
|
|
return false, "", nil
|
|
}
|
|
return true, myPaternalHaplogroup, nil
|
|
}
|
|
}
|
|
if (userSexExists == true){
|
|
|
|
if (userSex == "Male" || userSex == "Intersex Male"){
|
|
if (userPaternalHaplogroupExists == true){
|
|
return true, userPaternalHaplogroup_23andMe, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return false, "", nil
|
|
}
|
|
|
|
offspringPaternalHaplogroupIsKnown, offspringPaternalHaplogroup, err := getOffspringPaternalHaplogroup()
|
|
if (err != nil) { return false, "", false, "", err }
|
|
|
|
return offspringMaternalHaplogroupIsKnown, offspringMaternalHaplogroup, offspringPaternalHaplogroupIsKnown, offspringPaternalHaplogroup, nil
|
|
}
|
|
|
|
offspringMaternalHaplogroupIsKnown, offspringMaternalHaplogroup, offspringPaternalHaplogroupIsKnown, offspringPaternalHaplogroup, err := getOffspringHaplogroups()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
if (offspringMaternalHaplogroupIsKnown == true || offspringPaternalHaplogroupIsKnown == true){
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
offspringDescription := getLabelCentered("Below are the haplogroups of an offspring created from you and the user.")
|
|
page.Add(offspringDescription)
|
|
page.Add(widget.NewSeparator())
|
|
|
|
offspringMaternalHaplogroupLabel := widget.NewLabel("Offspring Maternal Haplogroup:")
|
|
|
|
getOffspringMaternalHaplogroupText := func()fyne.Widget{
|
|
|
|
if (offspringMaternalHaplogroupIsKnown == false){
|
|
unknownLabel := getBoldItalicLabel("Unknown")
|
|
return unknownLabel
|
|
}
|
|
haplogroupLabel := getBoldLabel(offspringMaternalHaplogroup)
|
|
return haplogroupLabel
|
|
}
|
|
|
|
offspringMaternalHaplogroupText := getOffspringMaternalHaplogroupText()
|
|
|
|
offspringMaternalHaplogroupRow := container.NewHBox(layout.NewSpacer(), offspringMaternalHaplogroupLabel, offspringMaternalHaplogroupText, layout.NewSpacer())
|
|
page.Add(offspringMaternalHaplogroupRow)
|
|
|
|
offspringPaternalHaplogroupLabel := widget.NewLabel("Offspring Paternal Haplogroup:")
|
|
|
|
getOffspringPaternalHaplogroupText := func()fyne.Widget{
|
|
|
|
if (offspringPaternalHaplogroupIsKnown == false){
|
|
unknownLabel := getBoldItalicLabel("Unknown")
|
|
return unknownLabel
|
|
}
|
|
haplogroupLabel := getBoldLabel(offspringPaternalHaplogroup)
|
|
return haplogroupLabel
|
|
}
|
|
|
|
offspringPaternalHaplogroupText := getOffspringPaternalHaplogroupText()
|
|
|
|
offspringPaternalHaplogroupRow := container.NewHBox(layout.NewSpacer(), offspringPaternalHaplogroupLabel, offspringPaternalHaplogroupText, layout.NewSpacer())
|
|
|
|
page.Add(offspringPaternalHaplogroupRow)
|
|
}
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_NeanderthalVariants(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
title := getPageTitleCentered(translate("View Profile - Physical"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Neanderthal Variants"))
|
|
|
|
description1 := getLabelCentered("Below is the user's neanderthal variant count.")
|
|
description2 := getLabelCentered("The information comes from 23andMe. More companies will be added.")
|
|
|
|
userResponseExists, _, userNeanderthalVariants, err := getAnyUserProfileAttributeFunction("23andMe_NeanderthalVariants")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
getUserNeanderthalVariantsLabel := func()fyne.Widget{
|
|
|
|
if (userResponseExists == false){
|
|
noResponseLabel := getBoldItalicLabel(translate("No Response"))
|
|
|
|
return noResponseLabel
|
|
}
|
|
|
|
userNeanderthalVariantsLabel := getBoldLabel(userNeanderthalVariants + " variants")
|
|
return userNeanderthalVariantsLabel
|
|
}
|
|
|
|
userNeanderthalVariantsLabel := getUserNeanderthalVariantsLabel()
|
|
|
|
neanderthalVariantsLabel := widget.NewLabel("Neanderthal Variants:")
|
|
|
|
neanderthalVariantsRow := container.NewHBox(layout.NewSpacer(), neanderthalVariantsLabel, userNeanderthalVariantsLabel, layout.NewSpacer())
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), neanderthalVariantsRow)
|
|
|
|
isSameSexIsKnown, _, isSameSex, err := getAnyUserProfileAttributeFunction("IsSameSex")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (isSameSexIsKnown == true && isSameSex == "Yes"){
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
offspringNeanderthalVariantsKnown, _, offspringNeanderthalVariants, err := getAnyUserProfileAttributeFunction("23andMe_OffspringNeanderthalVariants")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
if (offspringNeanderthalVariantsKnown == false){
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
page.Add(widget.NewSeparator())
|
|
offspringDescription := getLabelCentered("Below is the estimated neanderthal variant count of an offspring created from you and this user.")
|
|
page.Add(offspringDescription)
|
|
|
|
offspringNeanderthalVariantsLabel := widget.NewLabel("Offspring Neanderthal Variants:")
|
|
offspringNeanderthalVariantsText := getBoldLabel(offspringNeanderthalVariants + " variants")
|
|
|
|
offspringNeanderthalVariantsRow := container.NewHBox(layout.NewSpacer(), offspringNeanderthalVariantsLabel, offspringNeanderthalVariantsText, layout.NewSpacer())
|
|
page.Add(offspringNeanderthalVariantsRow)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_RacialSimilarity(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
title := getPageTitleCentered(translate("View Profile - Physical"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Racial Similarity"))
|
|
|
|
description := getLabelCentered("This page describes the racial similarity between you and the user.")
|
|
|
|
//TODO: Add help pages to explain results
|
|
|
|
racialSimilarityTitle := widget.NewLabel("Racial Similarity:")
|
|
|
|
getRacialSimilarityText := func()(string, error){
|
|
|
|
similarityIsKnown, _, racialSimilarity, err := getAnyUserProfileAttributeFunction("RacialSimilarity")
|
|
if (err != nil) { return "", err }
|
|
if (similarityIsKnown == false){
|
|
|
|
result := translate("Unknown")
|
|
return result, nil
|
|
}
|
|
|
|
return racialSimilarity, nil
|
|
}
|
|
|
|
racialSimilarityText, err := getRacialSimilarityText()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
racialSimilarityLabel := getBoldLabel(racialSimilarityText)
|
|
|
|
racialSimilarityHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
racialSimilarityRow := container.NewHBox(layout.NewSpacer(), racialSimilarityTitle, racialSimilarityLabel, racialSimilarityHelpButton, layout.NewSpacer())
|
|
|
|
getRacialSimilarityBreakdownGrid_Traits := func()(*fyne.Container, error){
|
|
|
|
//TODO: A button for each trait to see what the our and the user's trait value is
|
|
|
|
traitNameHeader := getItalicLabelCentered("Trait Name")
|
|
traitSimilarityHeader := getItalicLabelCentered("Trait Similarity")
|
|
geneticSimilarityHeader := getItalicLabelCentered("Genetic Similarity")
|
|
numberOfTestedLociHeader := getItalicLabelCentered("Number Of Tested Loci")
|
|
|
|
traitNamesColumn := container.NewVBox(traitNameHeader, widget.NewSeparator())
|
|
traitSimilarityColumn := container.NewVBox(traitSimilarityHeader, widget.NewSeparator())
|
|
geneticSimilarityColumn := container.NewVBox(geneticSimilarityHeader, widget.NewSeparator())
|
|
numberOfTestedLociColumn := container.NewVBox(numberOfTestedLociHeader, widget.NewSeparator())
|
|
|
|
addSimilarityRow := func(traitName string, traitSimilarityAttributeName string, geneticSimilarityAttributeName string, numberOfSimilarAllelesAttributeName string)error{
|
|
|
|
traitTitleLabel := getBoldLabelCentered(translate(traitName))
|
|
|
|
traitNamesColumn.Add(traitTitleLabel)
|
|
|
|
if (traitName == "Facial Structure"){
|
|
|
|
// This trait's similarity cannot be calculated yet
|
|
// This feature is planned to be added - it will compare the faces in user profile photos.
|
|
|
|
dashLabel := getItalicLabelCentered("-")
|
|
|
|
traitSimilarityColumn.Add(dashLabel)
|
|
|
|
} else {
|
|
|
|
traitSimilarityIsKnown, _, attributeValue, err := getAnyUserProfileAttributeFunction(traitSimilarityAttributeName)
|
|
if (err != nil) { return err }
|
|
if (traitSimilarityIsKnown == false){
|
|
|
|
unknownLabel := getItalicLabelCentered("Unknown")
|
|
|
|
traitSimilarityColumn.Add(unknownLabel)
|
|
} else {
|
|
|
|
similarityFormatted := attributeValue + "%"
|
|
|
|
similarityLabel := getBoldLabelCentered(similarityFormatted)
|
|
|
|
traitSimilarityColumn.Add(similarityLabel)
|
|
}
|
|
}
|
|
|
|
// We figure out how many loci exist for this trait
|
|
|
|
traitObject, err := traits.GetTraitObject(traitName)
|
|
if (err != nil) { return err }
|
|
|
|
traitLociList := traitObject.LociList
|
|
quantityOfTraitLoci := len(traitLociList)
|
|
|
|
quantityOfTraitLociString := helpers.ConvertIntToString(quantityOfTraitLoci)
|
|
|
|
geneticSimilarityIsKnown, _, attributeValue, err := getAnyUserProfileAttributeFunction(geneticSimilarityAttributeName)
|
|
if (err != nil) { return err }
|
|
if (geneticSimilarityIsKnown == false){
|
|
unknownLabel := getItalicLabelCentered("Unknown")
|
|
|
|
geneticSimilarityColumn.Add(unknownLabel)
|
|
|
|
quantityOfTestedLociText := "0/" + quantityOfTraitLociString
|
|
|
|
quantityOfTestedLociLabel := getBoldLabelCentered(quantityOfTestedLociText)
|
|
|
|
numberOfTestedLociColumn.Add(quantityOfTestedLociLabel)
|
|
} else {
|
|
|
|
similarityFormatted := attributeValue + "%"
|
|
|
|
similarityLabel := getBoldLabelCentered(similarityFormatted)
|
|
|
|
geneticSimilarityColumn.Add(similarityLabel)
|
|
|
|
attributeIsKnown, _, numberOfSimilarAllelesValue, err := getAnyUserProfileAttributeFunction(numberOfSimilarAllelesAttributeName)
|
|
if (err != nil) { return err }
|
|
if (attributeIsKnown == false){
|
|
return errors.New("User profile contains " + geneticSimilarityAttributeName + " but is missing " + numberOfSimilarAllelesAttributeName)
|
|
}
|
|
|
|
// numberOfSimilarAllelesValue is represented by (number of shared alleles)/(number of tested alleles)
|
|
// Example: "52/60"
|
|
|
|
// We extract the denominator
|
|
|
|
_, numberOfTestedAllelesString, delimeterFound := strings.Cut(numberOfSimilarAllelesValue, "/")
|
|
if (delimeterFound == false){
|
|
return errors.New("User profile contains invalid " + numberOfSimilarAllelesAttributeName + ": " + numberOfSimilarAllelesValue)
|
|
}
|
|
|
|
numberOfTestedAlleles, err := helpers.ConvertStringToInt(numberOfTestedAllelesString)
|
|
if (err != nil){
|
|
return errors.New("User profile contains invalid " + numberOfSimilarAllelesAttributeName + ": " + numberOfSimilarAllelesValue)
|
|
}
|
|
|
|
// Each locus has 2 alleles (this will change once we include sex chromosome locations)
|
|
numberOfTestedLoci := numberOfTestedAlleles/2
|
|
|
|
numberOfTestedLociString := helpers.ConvertIntToString(numberOfTestedLoci)
|
|
|
|
numberOfTestedLociLabelText := numberOfTestedLociString + "/" + quantityOfTraitLociString
|
|
|
|
numberOfTestedLociLabel := getBoldLabelCentered(numberOfTestedLociLabelText)
|
|
|
|
numberOfTestedLociColumn.Add(numberOfTestedLociLabel)
|
|
}
|
|
|
|
traitNamesColumn.Add(widget.NewSeparator())
|
|
traitSimilarityColumn.Add(widget.NewSeparator())
|
|
geneticSimilarityColumn.Add(widget.NewSeparator())
|
|
numberOfTestedLociColumn.Add(widget.NewSeparator())
|
|
|
|
return nil
|
|
}
|
|
|
|
err = addSimilarityRow("Eye Color", "EyeColorSimilarity", "EyeColorGenesSimilarity", "EyeColorGenesSimilarity_NumberOfSimilarAlleles")
|
|
if (err != nil) { return nil, err }
|
|
|
|
err = addSimilarityRow("Hair Color", "HairColorSimilarity", "HairColorGenesSimilarity", "HairColorGenesSimilarity_NumberOfSimilarAlleles")
|
|
if (err != nil) { return nil, err }
|
|
|
|
err = addSimilarityRow("Skin Color", "SkinColorSimilarity", "SkinColorGenesSimilarity", "SkinColorGenesSimilarity_NumberOfSimilarAlleles")
|
|
if (err != nil) { return nil, err }
|
|
|
|
err = addSimilarityRow("Hair Texture", "HairTextureSimilarity", "HairTextureGenesSimilarity", "HairTextureGenesSimilarity_NumberOfSimilarAlleles")
|
|
if (err != nil) { return nil, err }
|
|
|
|
err = addSimilarityRow("Facial Structure", "", "FacialStructureGenesSimilarity", "FacialStructureGenesSimilarity_NumberOfSimilarAlleles")
|
|
if (err != nil) { return nil, err }
|
|
|
|
similarityGrid := container.NewHBox(layout.NewSpacer(), traitNamesColumn, traitSimilarityColumn, geneticSimilarityColumn, numberOfTestedLociColumn, layout.NewSpacer())
|
|
|
|
return similarityGrid, nil
|
|
}
|
|
|
|
racialSimilarityBreakdownGrid_Traits, err := getRacialSimilarityBreakdownGrid_Traits()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
getRacialSimilarityBreakdownGrid_GeneticAttributes := func()(*fyne.Container, error){
|
|
|
|
attributeTitleHeader := getItalicLabelCentered("Genetic Attribute Name")
|
|
twentyThreeAndMeHeader := getItalicLabelCentered("23andMe")
|
|
|
|
attributeTitleColumn := container.NewVBox(attributeTitleHeader, widget.NewSeparator())
|
|
similarityColumn_23andMe := container.NewVBox(twentyThreeAndMeHeader, widget.NewSeparator())
|
|
|
|
addSimilarityRow := func(attributeTitle string, attributeName_23andMe string)error{
|
|
|
|
attributeTitleLabel := getBoldLabelCentered(translate(attributeTitle))
|
|
|
|
attributeTitleColumn.Add(attributeTitleLabel)
|
|
|
|
similarityIsKnown, _, attributeValue, err := getAnyUserProfileAttributeFunction(attributeName_23andMe)
|
|
if (err != nil) { return err }
|
|
if (similarityIsKnown == false){
|
|
unknownLabel := getItalicLabelCentered("Unknown")
|
|
|
|
similarityColumn_23andMe.Add(unknownLabel)
|
|
} else {
|
|
|
|
similarityFormatted := attributeValue + "%"
|
|
|
|
similarityLabel := getBoldLabelCentered(similarityFormatted)
|
|
|
|
similarityColumn_23andMe.Add(similarityLabel)
|
|
}
|
|
|
|
attributeTitleColumn.Add(widget.NewSeparator())
|
|
similarityColumn_23andMe.Add(widget.NewSeparator())
|
|
|
|
return nil
|
|
}
|
|
|
|
err = addSimilarityRow("Ancestral Similarity", "23andMe_AncestralSimilarity")
|
|
if (err != nil) { return nil, err }
|
|
|
|
err = addSimilarityRow("Maternal Haplogroup Similarity", "23andMe_MaternalHaplogroupSimilarity")
|
|
if (err != nil) { return nil, err }
|
|
|
|
err = addSimilarityRow("Paternal Haplogroup Similarity", "23andMe_PaternalHaplogroupSimilarity")
|
|
if (err != nil) { return nil, err }
|
|
|
|
//TODO: Add neanderthal variants similarity?
|
|
|
|
similarityGrid := container.NewHBox(layout.NewSpacer(), attributeTitleColumn, similarityColumn_23andMe, layout.NewSpacer())
|
|
|
|
return similarityGrid, nil
|
|
}
|
|
|
|
racialSimilarityBreakdownGrid_GeneticAttributes, err := getRacialSimilarityBreakdownGrid_GeneticAttributes()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), racialSimilarityRow, widget.NewSeparator(), racialSimilarityBreakdownGrid_Traits, widget.NewSeparator(), racialSimilarityBreakdownGrid_GeneticAttributes)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_AncestryComposition(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
if (userOrOffspring != "User" && userOrOffspring != "Offspring"){
|
|
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_AncestryComposition called with invalid userOrOffspring: " + userOrOffspring), previousPage)
|
|
return
|
|
}
|
|
|
|
currentPage := func(){setViewMateProfilePage_AncestryComposition(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
|
|
|
|
title := getPageTitleCentered(translate("View Profile - Physical"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Ancestry Composition"))
|
|
|
|
description1 := getLabelCentered("Below is the ancestry location composition for this user.")
|
|
description2 := getLabelCentered("The information is provided by 23andMe. More companies will be added.")
|
|
|
|
userAncestryCompositionExists, _, userAncestryCompositionAttribute, err := getAnyUserProfileAttributeFunction("23andMe_AncestryComposition")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (userAncestryCompositionExists == false){
|
|
noAncestryComposition := getBoldLabelCentered("This user has no ancestry composition.")
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), noAncestryComposition)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
description3Label := getLabelCentered("Choose if you want to view the composition of the user or the offspring between you and the user.")
|
|
offspringAncestryCompositionHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setOffspringAncestryCompositionExplainerPage(window, currentPage)
|
|
})
|
|
|
|
description3Row := container.NewHBox(layout.NewSpacer(), description3Label, offspringAncestryCompositionHelpButton, layout.NewSpacer())
|
|
|
|
handleSelectFunction := func(newUserOrOffspring string){
|
|
if (newUserOrOffspring == userOrOffspring){
|
|
return
|
|
}
|
|
setViewMateProfilePage_AncestryComposition(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
|
|
}
|
|
|
|
userOrOffspringList := []string{"User", "Offspring"}
|
|
|
|
userOrOffspringSelector := widget.NewSelect(userOrOffspringList, handleSelectFunction)
|
|
|
|
userOrOffspringSelector.Selected = userOrOffspring
|
|
|
|
userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector)
|
|
|
|
header := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), description3Row, userOrOffspringSelectorCentered, widget.NewSeparator())
|
|
|
|
if (userOrOffspring == "User"){
|
|
|
|
attributeIsValid, continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap, err := companyAnalysis.ReadAncestryCompositionAttribute_23andMe(true, userAncestryCompositionAttribute)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (attributeIsValid == false){
|
|
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_AncestryComposition called with getAnyUserProfileAttributeFunction returning invalid userAncestryCompositionAttribute"), previousPage)
|
|
return
|
|
}
|
|
|
|
userCompositionDisplay, err := get23andMeAncestryCompositionDisplay(continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewBorder(header, nil, nil, nil, userCompositionDisplay)
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
//userOrOffspring == "Offspring"
|
|
|
|
myAncestryCompositionExists, myAncestryCompositionAttribute, err := myLocalProfiles.GetProfileData("Mate", "23andMe_AncestryComposition")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (myAncestryCompositionExists == false){
|
|
|
|
description4 := getBoldLabelCentered("Your ancestry location composition does not exist.")
|
|
description5 := getLabelCentered("Add your ancestry composition to view your offspring's ancestry composition.")
|
|
description6 := getLabelCentered("You can do this on the Build Profile - Physical - Ancestry Composition page.")
|
|
|
|
page := container.NewVBox(header, description4, description5, description6)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
offspringContinentPercentagesMap, offspringRegionPercentagesMap, offspringSubregionPercentagesMap, err := companyAnalysis.GetOffspringAncestryComposition_23andMe(myAncestryCompositionAttribute, userAncestryCompositionAttribute)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
offspringAncestryCompositionDisplay, err := get23andMeAncestryCompositionDisplay(offspringContinentPercentagesMap, offspringRegionPercentagesMap, offspringSubregionPercentagesMap)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewBorder(header, nil, nil, nil, offspringAncestryCompositionDisplay)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
|
|
// This is a page to view the total monogenic disease risk for a user's offspring
|
|
func setViewMateProfilePage_TotalDiseaseRisk(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
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){
|
|
|
|
emptyLabel1 := widget.NewLabel("")
|
|
emptyLabel2 := widget.NewLabel("")
|
|
|
|
emptyLabel3 := widget.NewLabel("")
|
|
diseaseNameLabel := getItalicLabelCentered("Disease Name")
|
|
|
|
emptyLabel4 := widget.NewLabel("")
|
|
userHasDiseaseLabel := getItalicLabelCentered("User Has Disease")
|
|
|
|
userProbabilityOfLabelA := getItalicLabelCentered("User Probability Of")
|
|
passingVariantLabel := getItalicLabelCentered("Passing Variant")
|
|
|
|
userNumberOfLabel := getItalicLabelCentered("User Number Of")
|
|
variantsTestedLabelA := getItalicLabelCentered("Variants Tested")
|
|
|
|
offspringProbabilityOfLabelA := getItalicLabelCentered("Offspring Probability Of")
|
|
havingDiseaseLabel := getItalicLabelCentered("Having Disease")
|
|
|
|
offspringProbabilityOfLabelB := getItalicLabelCentered("Offspring Probability Of")
|
|
havingVariantLabel := getItalicLabelCentered("Having Variant")
|
|
|
|
offspringNumberOfLabel := getItalicLabelCentered("Offspring Number Of")
|
|
variantsTestedLabelB := getItalicLabelCentered("Variants Tested")
|
|
|
|
diseaseInfoButtonsColumn := container.NewVBox(emptyLabel1, emptyLabel2, widget.NewSeparator())
|
|
diseaseNameColumn := container.NewVBox(emptyLabel3, diseaseNameLabel, widget.NewSeparator())
|
|
userHasDiseaseColumn := container.NewVBox(emptyLabel4, userHasDiseaseLabel, widget.NewSeparator())
|
|
userProbabilityOfPassingVariantColumn := container.NewVBox(userProbabilityOfLabelA, passingVariantLabel, widget.NewSeparator())
|
|
userNumberOfVariantsTestedColumn := container.NewVBox(userNumberOfLabel, variantsTestedLabelA, widget.NewSeparator())
|
|
offspringProbabilityOfHavingDiseaseColumn := container.NewVBox(offspringProbabilityOfLabelA, havingDiseaseLabel, widget.NewSeparator())
|
|
offspringProbabilityOfHavingAVariantColumn := container.NewVBox(offspringProbabilityOfLabelB, havingVariantLabel, widget.NewSeparator())
|
|
offspringNumberOfVariantsTestedColumn := container.NewVBox(offspringNumberOfLabel, variantsTestedLabelB, widget.NewSeparator())
|
|
|
|
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
|
|
if (err != nil) { return nil, err }
|
|
|
|
monogenicDiseaseObjectsList, err := monogenicDiseases.GetMonogenicDiseaseObjectsList()
|
|
if (err != nil) { return nil, err }
|
|
|
|
for _, diseaseObject := range monogenicDiseaseObjectsList{
|
|
|
|
monogenicDiseaseName := diseaseObject.DiseaseName
|
|
diseaseIsDominantOrRecessive := diseaseObject.DominantOrRecessive
|
|
|
|
diseaseVariantsList := diseaseObject.VariantsList
|
|
numberOfDiseaseVariants := len(diseaseVariantsList)
|
|
|
|
//Outputs:
|
|
// -bool: User disease info exists
|
|
// -bool: User has the disease
|
|
// -int: User probability of passing a disease variant (0-100)
|
|
// -int: User number of variants tested
|
|
// -error
|
|
getUserDiseaseInfo := func()(bool, bool, int, int, error){
|
|
|
|
diseaseNameWithUnderscores := strings.ReplaceAll(monogenicDiseaseName, " ", "_")
|
|
|
|
probabilityOfPassingAVariantAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_ProbabilityOfPassingAVariant"
|
|
numberOfVariantsTestedAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_NumberOfVariantsTested"
|
|
|
|
userDiseaseInfoExists, _, userProbabilityOfPassingDiseaseVariantString, err := getAnyUserProfileAttributeFunction(probabilityOfPassingAVariantAttributeName)
|
|
if (err != nil) { return false, false, 0, 0, err }
|
|
if (userDiseaseInfoExists == false){
|
|
return false, false, 0, 0, nil
|
|
}
|
|
|
|
userProbabilityOfPassingDiseaseVariant, err := helpers.ConvertStringToInt(userProbabilityOfPassingDiseaseVariantString)
|
|
if (err != nil) {
|
|
return false, false, 0, 0, errors.New("setViewMateProfilePage_MonogenicDiseases called with profile containing invalid probabilityOfPassingAVariant: " + userProbabilityOfPassingDiseaseVariantString)
|
|
}
|
|
|
|
getUserHasDiseaseBool := func()bool{
|
|
|
|
if (diseaseIsDominantOrRecessive == "Dominant"){
|
|
if (userProbabilityOfPassingDiseaseVariant != 0){
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
// diseaseIsDominantOrRecessive == "Recessive"
|
|
if (userProbabilityOfPassingDiseaseVariant == 100){
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
userHasDisease := getUserHasDiseaseBool()
|
|
|
|
userVariantsTestedExists, _, userNumberOfVariantsTested, err := getAnyUserProfileAttributeFunction(numberOfVariantsTestedAttributeName)
|
|
if (err != nil) { return false, false, 0, 0, err }
|
|
if (userVariantsTestedExists == false){
|
|
return false, false, 0, 0, errors.New("setViewMateProfilePage_MonogenicDiseases called with user profile containing probabilityOfPassingAVariant but not numberOfVariantsTested")
|
|
}
|
|
|
|
userNumberOfVariantsTestedInt, err := helpers.ConvertStringToInt(userNumberOfVariantsTested)
|
|
if (err != nil) {
|
|
return false, false, 0, 0, errors.New("setViewMateProfilePage_MonogenicDiseases called with profile containing invalid numberOfVariantsTested: " + userNumberOfVariantsTested)
|
|
}
|
|
|
|
return true, userHasDisease, userProbabilityOfPassingDiseaseVariant, userNumberOfVariantsTestedInt, nil
|
|
}
|
|
|
|
userDiseaseInfoExists, userHasDisease, userProbabilityOfPassingAVariant, userNumberOfVariantsTested, err := getUserDiseaseInfo()
|
|
if (err != nil) { return nil, err }
|
|
|
|
//Outputs:
|
|
// -bool: My disease info exists
|
|
// -int: My probability of passing a disease variant
|
|
// -int: Number of variants tested
|
|
// -error
|
|
getMyDiseaseInfo := func()(bool, int, int, error){
|
|
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
|
|
return false, 0, 0, nil
|
|
}
|
|
|
|
diseaseInfoIsKnown, _, probabilityOfPassingADiseaseVariant, _, numberOfVariantsTested, _, _, _, err := readGeneticAnalysis.GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(myAnalysisObject, monogenicDiseaseName, myGenomeIdentifier)
|
|
if (err != nil) { return false, 0, 0, err }
|
|
if (diseaseInfoIsKnown == false){
|
|
return false, 0, 0, nil
|
|
}
|
|
|
|
return true, probabilityOfPassingADiseaseVariant, numberOfVariantsTested, nil
|
|
}
|
|
|
|
myDiseaseInfoExists, myProbabilityOfPassingAVariant, myNumberOfVariantsTested, err := getMyDiseaseInfo()
|
|
if (err != nil) { return nil, err }
|
|
|
|
probabilityOffspringHasDiseaseIsKnown, probabilityOffspringHasDisease, probabilityOffspringHasVariantIsKnown, probabilityOffspringHasVariant, err := createCoupleGeneticAnalysis.GetOffspringMonogenicDiseaseProbabilities(diseaseIsDominantOrRecessive, userDiseaseInfoExists, userProbabilityOfPassingAVariant, myDiseaseInfoExists, myProbabilityOfPassingAVariant)
|
|
if (err != nil) { return nil, err }
|
|
|
|
getUserHasDiseaseString := func()string{
|
|
if (userDiseaseInfoExists == false){
|
|
return "Unknown"
|
|
}
|
|
|
|
userHasDiseaseString := helpers.ConvertBoolToYesOrNoString(userHasDisease)
|
|
|
|
return userHasDiseaseString
|
|
}
|
|
|
|
userHasDiseaseString := getUserHasDiseaseString()
|
|
|
|
getUserProbabilityOfPassingAVariantString := func()string{
|
|
if (userDiseaseInfoExists == false){
|
|
return "Unknown"
|
|
}
|
|
userProbabilityOfPassingAVariantString := helpers.ConvertIntToString(userProbabilityOfPassingAVariant)
|
|
resultFormatted := userProbabilityOfPassingAVariantString + "%"
|
|
return resultFormatted
|
|
}
|
|
|
|
userProbabilityOfPassingAVariantString := getUserProbabilityOfPassingAVariantString()
|
|
|
|
getUserNumberOfVariantsTestedString := func()string{
|
|
numberOfDiseaseVariantsString := helpers.ConvertIntToString(numberOfDiseaseVariants)
|
|
if (userDiseaseInfoExists == false){
|
|
result := "0/" + numberOfDiseaseVariantsString
|
|
return result
|
|
}
|
|
userNumberOfVariantsTestedString := helpers.ConvertIntToString(userNumberOfVariantsTested)
|
|
|
|
resultFormatted := userNumberOfVariantsTestedString + "/" + numberOfDiseaseVariantsString
|
|
return resultFormatted
|
|
}
|
|
|
|
userNumberOfVariantsTestedString := getUserNumberOfVariantsTestedString()
|
|
|
|
getOffspringProbabilityOfHavingDiseaseString := func()string{
|
|
|
|
if (probabilityOffspringHasDiseaseIsKnown == false){
|
|
result := translate("Unknown")
|
|
return result
|
|
}
|
|
offspringProbabilityOfHavingDiseaseString := helpers.ConvertIntToString(probabilityOffspringHasDisease)
|
|
resultFormatted := offspringProbabilityOfHavingDiseaseString + "%"
|
|
return resultFormatted
|
|
}
|
|
|
|
offspringProbabilityOfHavingDiseaseString := getOffspringProbabilityOfHavingDiseaseString()
|
|
|
|
getOffspringProbabilityOfHavingAVariantFormatted := func()string{
|
|
|
|
if (probabilityOffspringHasVariantIsKnown == false){
|
|
result := translate("Unknown")
|
|
return result
|
|
}
|
|
offspringProbabilityOfHavingAVariantString := helpers.ConvertIntToString(probabilityOffspringHasVariant)
|
|
resultFormatted := offspringProbabilityOfHavingAVariantString + "%"
|
|
|
|
return resultFormatted
|
|
}
|
|
|
|
offspringProbabilityOfHavingAVariantFormatted := getOffspringProbabilityOfHavingAVariantFormatted()
|
|
|
|
totalNumberOfOffspringDiseaseVariantsString := helpers.ConvertIntToString(numberOfDiseaseVariants*2)
|
|
|
|
offspringNumberOfVariantsTested := userNumberOfVariantsTested + myNumberOfVariantsTested
|
|
|
|
offspringNumberOfVariantsTestedString := helpers.ConvertIntToString(offspringNumberOfVariantsTested)
|
|
offspringNumberOfVariantsTestedFormatted := offspringNumberOfVariantsTestedString + "/" + totalNumberOfOffspringDiseaseVariantsString
|
|
|
|
viewDiseaseInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
|
|
setViewMonogenicDiseaseDetailsPage(window, monogenicDiseaseName, currentPage)
|
|
})
|
|
|
|
diseaseNameLabel := getBoldLabelCentered(monogenicDiseaseName)
|
|
|
|
userHasDiseaseLabel := getBoldLabelCentered(userHasDiseaseString)
|
|
userProbabilityOfPassingAVariantLabel := getBoldLabelCentered(userProbabilityOfPassingAVariantString)
|
|
userNumberOfVariantsTestedLabel := getBoldLabelCentered(userNumberOfVariantsTestedString)
|
|
|
|
offspringProbabilityOfHavingDiseaseLabel := getBoldLabelCentered(offspringProbabilityOfHavingDiseaseString)
|
|
offspringProbabilityOfHavingAVariantLabel := getBoldLabelCentered(offspringProbabilityOfHavingAVariantFormatted)
|
|
offspringNumberOfVariantsTestedLabel := getBoldLabelCentered(offspringNumberOfVariantsTestedFormatted)
|
|
|
|
diseaseInfoButtonsColumn.Add(viewDiseaseInfoButton)
|
|
diseaseNameColumn.Add(diseaseNameLabel)
|
|
userHasDiseaseColumn.Add(userHasDiseaseLabel)
|
|
userProbabilityOfPassingVariantColumn.Add(userProbabilityOfPassingAVariantLabel)
|
|
userNumberOfVariantsTestedColumn.Add(userNumberOfVariantsTestedLabel)
|
|
offspringProbabilityOfHavingDiseaseColumn.Add(offspringProbabilityOfHavingDiseaseLabel)
|
|
offspringProbabilityOfHavingAVariantColumn.Add(offspringProbabilityOfHavingAVariantLabel)
|
|
offspringNumberOfVariantsTestedColumn.Add(offspringNumberOfVariantsTestedLabel)
|
|
|
|
diseaseInfoButtonsColumn.Add(widget.NewSeparator())
|
|
diseaseNameColumn.Add(widget.NewSeparator())
|
|
userHasDiseaseColumn.Add(widget.NewSeparator())
|
|
userProbabilityOfPassingVariantColumn.Add(widget.NewSeparator())
|
|
userNumberOfVariantsTestedColumn.Add(widget.NewSeparator())
|
|
offspringProbabilityOfHavingDiseaseColumn.Add(widget.NewSeparator())
|
|
offspringProbabilityOfHavingAVariantColumn.Add(widget.NewSeparator())
|
|
offspringNumberOfVariantsTestedColumn.Add(widget.NewSeparator())
|
|
}
|
|
|
|
userHasDiseaseHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setPersonHasMonogenicDiseaseExplainerPage(window, currentPage)
|
|
})
|
|
probabilityOfPassingVariantHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setPersonProbabilityOfPassingVariantExplainerPage(window, currentPage)
|
|
})
|
|
numberOfVariantsTestedHelpButtonA := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setNumberOfTestedVariantsExplainerPage(window, currentPage)
|
|
})
|
|
numberOfVariantsTestedHelpButtonB := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setNumberOfTestedVariantsExplainerPage(window, currentPage)
|
|
})
|
|
probabilityOfHavingDiseaseHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setOffspringProbabilityOfHavingMonogenicDiseaseExplainerPage(window, currentPage)
|
|
})
|
|
probabilityOfHavingAVariantHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setOffspringProbabilityOfHavingVariantExplainerPage(window, currentPage)
|
|
})
|
|
|
|
userHasDiseaseColumn.Add(userHasDiseaseHelpButton)
|
|
userProbabilityOfPassingVariantColumn.Add(probabilityOfPassingVariantHelpButton)
|
|
userNumberOfVariantsTestedColumn.Add(numberOfVariantsTestedHelpButtonA)
|
|
offspringProbabilityOfHavingDiseaseColumn.Add(probabilityOfHavingDiseaseHelpButton)
|
|
offspringProbabilityOfHavingAVariantColumn.Add(probabilityOfHavingAVariantHelpButton)
|
|
offspringNumberOfVariantsTestedColumn.Add(numberOfVariantsTestedHelpButtonB)
|
|
|
|
if (userOrOffspring == "User"){
|
|
|
|
diseasesInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseInfoButtonsColumn, diseaseNameColumn, userHasDiseaseColumn, userProbabilityOfPassingVariantColumn, userNumberOfVariantsTestedColumn, layout.NewSpacer())
|
|
|
|
return diseasesInfoGrid, nil
|
|
}
|
|
|
|
diseasesInfoGrid := container.NewHBox(layout.NewSpacer(), diseaseInfoButtonsColumn, diseaseNameColumn, offspringProbabilityOfHavingDiseaseColumn, offspringProbabilityOfHavingAVariantColumn, offspringNumberOfVariantsTestedColumn, layout.NewSpacer())
|
|
|
|
return diseasesInfoGrid, nil
|
|
}
|
|
|
|
diseasesInfoGrid, err := getDiseasesInfoGrid()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), userOrOffspringSelectorCentered, widget.NewSeparator(), diseasesInfoGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
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){
|
|
|
|
emptyLabel1 := widget.NewLabel("")
|
|
diseaseNameLabel := getItalicLabelCentered("Disease Name")
|
|
|
|
emptyLabel2 := widget.NewLabel("")
|
|
userRiskScoreLabel := getItalicLabelCentered("User Risk Score")
|
|
|
|
emptyLabel3 := 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")
|
|
|
|
emptyLabel4 := widget.NewLabel("")
|
|
emptyLabel5 := widget.NewLabel("")
|
|
|
|
emptyLabel6 := widget.NewLabel("")
|
|
emptyLabel7 := widget.NewLabel("")
|
|
|
|
diseaseNameColumn := container.NewVBox(emptyLabel1, diseaseNameLabel, widget.NewSeparator())
|
|
userRiskScoreColumn := container.NewVBox(emptyLabel2, userRiskScoreLabel, widget.NewSeparator())
|
|
offspringRiskScoreColumn := container.NewVBox(emptyLabel3, offspringRiskScoreLabel, widget.NewSeparator())
|
|
userNumberOfLociTestedColumn := container.NewVBox(userNumberOfLabel, lociTestedLabelA, widget.NewSeparator())
|
|
offspringNumberOfLociTestedColumn := container.NewVBox(offspringNumberOfLabel, lociTestedLabelB, widget.NewSeparator())
|
|
viewSampleOffspringsChartButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
|
|
viewDiseaseInfoButtonsColumn := container.NewVBox(emptyLabel6, emptyLabel7, widget.NewSeparator())
|
|
|
|
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, 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
|
|
|
|
//Outputs:
|
|
// -map[int64]locusValue.LocusValue
|
|
// -error
|
|
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
|
|
|
|
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
|
|
emptyMap := make(map[int64]locusValue.LocusValue)
|
|
return emptyMap, nil
|
|
}
|
|
|
|
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
|
|
if (err != nil) { return nil, err }
|
|
|
|
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
|
|
if (exists == false){
|
|
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
|
|
}
|
|
|
|
return myGenomeLocusValuesMap, nil
|
|
}
|
|
|
|
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
|
|
if (err != nil) { return nil, err }
|
|
|
|
// Map Structure: Locus rsID -> Locus Value
|
|
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
|
|
|
|
for _, locusObject := range diseaseLociList{
|
|
|
|
locusRSID := locusObject.LocusRSID
|
|
|
|
locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
|
|
|
|
locusValueAttributeName := "LocusValue_rs" + locusRSIDString
|
|
|
|
userLocusValueExists, _, userLocusValue, err := getAnyUserProfileAttributeFunction(locusValueAttributeName)
|
|
if (err != nil) { return nil, err }
|
|
if (userLocusValueExists == false){
|
|
continue
|
|
}
|
|
|
|
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusValue, ";")
|
|
if (semicolonFound == false){
|
|
return nil, errors.New("Database corrupt: Contains profile with invalid " + locusValueAttributeName + " value: " + userLocusValue)
|
|
}
|
|
|
|
userLocusIsPhasedAttributeName := "LocusIsPhased_rs" + locusRSIDString
|
|
|
|
userLocusIsPhasedExists, _, userLocusIsPhasedString, err := getAnyUserProfileAttributeFunction(userLocusIsPhasedAttributeName)
|
|
if (err != nil) { return nil, err }
|
|
if (userLocusIsPhasedExists == false){
|
|
return nil, errors.New("Database corrupt: Contains profile with locusValue but not locusIsPhased status for locus: " + locusRSIDString)
|
|
}
|
|
|
|
userLocusIsPhased, err := helpers.ConvertYesOrNoStringToBool(userLocusIsPhasedString)
|
|
if (err != nil) { return nil, err }
|
|
|
|
userLocusValueObject := locusValue.LocusValue{
|
|
Base1Value: userLocusBase1,
|
|
Base2Value: userLocusBase2,
|
|
LocusIsPhased: userLocusIsPhased,
|
|
}
|
|
|
|
userDiseaseLocusValuesMap[locusRSID] = userLocusValueObject
|
|
}
|
|
|
|
userDiseaseInfoIsKnown, userDiseaseRiskScore, userNumberOfLociTested, _, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, userDiseaseLocusValuesMap, true)
|
|
if (err != nil) { return nil, err }
|
|
|
|
getUserDiseaseRiskScoreString := func()(string, error){
|
|
|
|
if (userDiseaseInfoIsKnown == false){
|
|
result := translate("Unknown")
|
|
|
|
return result, nil
|
|
}
|
|
|
|
userRiskScoreString := helpers.ConvertIntToString(userDiseaseRiskScore)
|
|
resultFormatted := userRiskScoreString + "/10"
|
|
|
|
return resultFormatted, nil
|
|
}
|
|
|
|
userDiseaseRiskScoreString, err := getUserDiseaseRiskScoreString()
|
|
if (err != nil) { return nil, err }
|
|
|
|
anyOffspringLociTested, offspringDiseaseRiskScore, offspringNumberOfLociTested, _, offspringSampleRiskScoresList, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLociList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
|
|
if (err != nil) { return nil, err }
|
|
|
|
getOffspringDiseaseRiskScoreFormatted := func()(string, error){
|
|
|
|
if (anyOffspringLociTested == false){
|
|
result := translate("Unknown")
|
|
|
|
return result, nil
|
|
}
|
|
|
|
offspringRiskScoreString := helpers.ConvertIntToString(offspringDiseaseRiskScore)
|
|
resultFormatted := offspringRiskScoreString + "/10"
|
|
return resultFormatted, nil
|
|
}
|
|
|
|
offspringDiseaseRiskScoreFormatted, err := getOffspringDiseaseRiskScoreFormatted()
|
|
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(userDiseaseRiskScoreString)
|
|
offspringRiskScoreLabel := getBoldLabelCentered(offspringDiseaseRiskScoreFormatted)
|
|
userNumberOfLociTestedLabel := getBoldLabelCentered(userNumberOfLociTestedFormatted)
|
|
offspringNumberOfLociTestedLabel := getBoldLabelCentered(offspringNumberOfLociTestedFormatted)
|
|
|
|
viewSampleOffspringsChartButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
|
|
|
|
setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window, diseaseName, offspringSampleRiskScoresList, offspringNumberOfLociTested, currentPage)
|
|
})
|
|
|
|
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)
|
|
viewSampleOffspringsChartButtonsColumn.Add(viewSampleOffspringsChartButton)
|
|
viewDiseaseInfoButtonsColumn.Add(viewDiseaseDetailsButton)
|
|
|
|
diseaseNameColumn.Add(widget.NewSeparator())
|
|
userRiskScoreColumn.Add(widget.NewSeparator())
|
|
offspringRiskScoreColumn.Add(widget.NewSeparator())
|
|
userNumberOfLociTestedColumn.Add(widget.NewSeparator())
|
|
offspringNumberOfLociTestedColumn.Add(widget.NewSeparator())
|
|
viewSampleOffspringsChartButtonsColumn.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)
|
|
})
|
|
userNumberOfLociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage)
|
|
})
|
|
|
|
offspringNumberOfLociTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setOffspringPolygenicDiseaseNumberOfLociTestedExplainerPage(window, currentPage)
|
|
})
|
|
|
|
userRiskScoreColumn.Add(userRiskScoreHelpButton)
|
|
offspringRiskScoreColumn.Add(offspringRiskScoreHelpButton)
|
|
userNumberOfLociTestedColumn.Add(userNumberOfLociTestedHelpButton)
|
|
offspringNumberOfLociTestedColumn.Add(offspringNumberOfLociTestedHelpButton)
|
|
|
|
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, viewSampleOffspringsChartButtonsColumn, viewDiseaseInfoButtonsColumn, layout.NewSpacer())
|
|
|
|
return diseaseInfoGrid, nil
|
|
}
|
|
|
|
diseaseInfoGrid, err := getDiseaseInfoGrid()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), userOrOffspringSelectorCentered, widget.NewSeparator(), diseaseInfoGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_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)
|
|
|
|
// Outputs:
|
|
// -map[int64]locusValue.LocusValue: Map Structure: Locus rsID -> Locus Value
|
|
// -error
|
|
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
|
|
|
|
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
|
|
if (err != nil) { return nil, err }
|
|
|
|
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
|
|
|
|
emptyMap := make(map[int64]locusValue.LocusValue)
|
|
return emptyMap, nil
|
|
}
|
|
|
|
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
|
|
if (err != nil) { return nil, err }
|
|
|
|
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
|
|
if (exists == false){
|
|
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
|
|
}
|
|
|
|
return myGenomeLocusValuesMap, nil
|
|
}
|
|
|
|
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
getUserLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
|
|
|
|
// Map Structure: Locus rsID -> Locus Value
|
|
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
|
|
|
|
for _, locusObject := range diseaseLocusObjectsList{
|
|
|
|
locusRSID := locusObject.LocusRSID
|
|
|
|
locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
|
|
|
|
locusValueAttributeName := "LocusValue_rs" + locusRSIDString
|
|
|
|
userLocusValueExists, _, userLocusValue, err := getAnyUserProfileAttributeFunction(locusValueAttributeName)
|
|
if (err != nil) { return nil, err }
|
|
if (userLocusValueExists == false){
|
|
continue
|
|
}
|
|
|
|
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusValue, ";")
|
|
if (semicolonFound == false){
|
|
return nil, errors.New("Database corrupt: Contains profile with invalid " + locusValueAttributeName + " value: " + userLocusValue)
|
|
}
|
|
|
|
userLocusIsPhasedAttributeName := "LocusIsPhased_rs" + locusRSIDString
|
|
|
|
userLocusIsPhasedExists, _, userLocusIsPhasedString, err := getAnyUserProfileAttributeFunction(userLocusIsPhasedAttributeName)
|
|
if (err != nil) { return nil, err }
|
|
if (userLocusIsPhasedExists == false){
|
|
return nil, errors.New("Database corrupt: Contains profile with locusValue but not locusIsPhased status for locus: " + locusRSIDString)
|
|
}
|
|
|
|
userLocusIsPhased, err := helpers.ConvertYesOrNoStringToBool(userLocusIsPhasedString)
|
|
if (err != nil) { return nil, err }
|
|
|
|
userLocusValueObject := locusValue.LocusValue{
|
|
Base1Value: userLocusBase1,
|
|
Base2Value: userLocusBase2,
|
|
LocusIsPhased: userLocusIsPhased,
|
|
}
|
|
|
|
userDiseaseLocusValuesMap[locusRSID] = userLocusValueObject
|
|
}
|
|
|
|
return userDiseaseLocusValuesMap, nil
|
|
}
|
|
|
|
userDiseaseLocusValuesMap, err := getUserLocusValuesMap()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
anyUserLociTested, _, userNumberOfLociTested, userDiseaseLocusInfoMap, err := createPersonGeneticAnalysis.GetPersonGenomePolygenicDiseaseInfo(diseaseLocusObjectsList, userDiseaseLocusValuesMap, true)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
anyOffspringLociTested, _, offspringNumberOfLociTested, offspringLociInfoMap, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLocusObjectsList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
getNumberOfLociTested := func()int{
|
|
|
|
if (userOrOffspring == "Offspring"){
|
|
|
|
if (anyOffspringLociTested == false){
|
|
return 0
|
|
}
|
|
|
|
return offspringNumberOfLociTested
|
|
}
|
|
|
|
return userNumberOfLociTested
|
|
}
|
|
|
|
numberOfLociTested := getNumberOfLociTested()
|
|
|
|
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{
|
|
|
|
locusIdentifierHex := locusObject.LocusIdentifier
|
|
|
|
locusIdentifier, err := encoding.DecodeHexStringTo3ByteArray(locusIdentifierHex)
|
|
if (err != nil) { return nil, err }
|
|
|
|
locusRSID := locusObject.LocusRSID
|
|
locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
|
|
locusName := "rs" + locusRSIDString
|
|
|
|
//Outputs:
|
|
// -bool: User locus Info is known
|
|
// -int: User locus Risk weight
|
|
// -bool: User locus odds ratio known
|
|
// -string: User locus odds ratio formatted
|
|
// -error
|
|
getUserLocusInfo := func()(bool, int, bool, string, error){
|
|
|
|
if (anyUserLociTested == false){
|
|
return false, 0, false, "", nil
|
|
}
|
|
|
|
locusInfoObject, exists := userDiseaseLocusInfoMap[locusIdentifier]
|
|
if (exists == false){
|
|
return false, 0, false, "", nil
|
|
}
|
|
|
|
userLocusRiskWeight := locusInfoObject.RiskWeight
|
|
|
|
oddsRatioIsKnown := locusInfoObject.OddsRatioIsKnown
|
|
|
|
if (oddsRatioIsKnown == false){
|
|
return true, userLocusRiskWeight, false, "", nil
|
|
}
|
|
|
|
userLocusOddsRatio := locusInfoObject.OddsRatio
|
|
|
|
locusOddsRatioString := helpers.ConvertFloat64ToStringRounded(userLocusOddsRatio, 2)
|
|
|
|
locusOddsRatioFormatted := locusOddsRatioString + "x"
|
|
|
|
return true, userLocusRiskWeight, true, locusOddsRatioFormatted, nil
|
|
}
|
|
|
|
userLocusInfoIsKnown, userLocusRiskWeight, userLocusOddsRatioIsKnown, userLocusOddsRatioFormatted, err := getUserLocusInfo()
|
|
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 (userLocusOddsRatioIsKnown == 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 (anyOffspringLociTested == false){
|
|
return false, 0, false, "", nil
|
|
}
|
|
|
|
offspringLocusInfoObject, exists := offspringLociInfoMap[locusIdentifier]
|
|
if (exists == false){
|
|
return false, 0, false, "", nil
|
|
}
|
|
|
|
offspringLocusRiskWeight := offspringLocusInfoObject.OffspringAverageRiskWeight
|
|
offspringOddsRatioIsKnown := offspringLocusInfoObject.OffspringOddsRatioIsKnown
|
|
|
|
if (offspringOddsRatioIsKnown == false){
|
|
return true, offspringLocusRiskWeight, false, "", nil
|
|
}
|
|
|
|
offspringOddsRatio := offspringLocusInfoObject.OffspringAverageOddsRatio
|
|
unknownOddsRatiosWeightSum := offspringLocusInfoObject.OffspringAverageUnknownOddsRatiosWeightSum
|
|
|
|
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, locusIdentifierHex, 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, widget.NewSeparator(), lociTestedRow, widget.NewSeparator(), diseaseLociGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_GeneticTraits(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
currentPage := func(){setViewMateProfilePage_GeneticTraits(window, getAnyUserProfileAttributeFunction, previousPage)}
|
|
|
|
title := getPageTitleCentered(translate("View Profile - Physical"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Genetic Traits"))
|
|
|
|
description1 := getLabelCentered("Choose if you want to view Discrete traits or Numeric traits.")
|
|
description2 := getLabelCentered("Discrete traits are traits which have discrete outcomes, such as Eye Color")
|
|
description3 := getLabelCentered("Numeric traits are traits with numeric outcomes, such as Height.")
|
|
|
|
discreteTraitsButton := widget.NewButton("Discrete Traits", func(){
|
|
setViewMateProfilePage_DiscreteGeneticTraits(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
numericTraitsButton := widget.NewButton("Numeric Traits", func(){
|
|
setViewMateProfilePage_NumericGeneticTraits(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
|
|
})
|
|
|
|
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, discreteTraitsButton, numericTraitsButton))
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), buttonsGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
if (userOrOffspring != "User" && userOrOffspring != "Offspring"){
|
|
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiscreteGeneticTraits called with invalid userOrOffspring: " + userOrOffspring), previousPage)
|
|
return
|
|
}
|
|
|
|
if (userOrOffspring == "Offspring"){
|
|
setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...")
|
|
}
|
|
|
|
currentPage := func(){setViewMateProfilePage_DiscreteGeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
|
|
|
|
title := getPageTitleCentered(translate("View Profile - Physical"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Discrete Genetic Traits"))
|
|
|
|
description1 := getLabelCentered("Below is the discrete genetic trait analysis for this user.")
|
|
description2 := getLabelCentered("You can choose to view the analysis of the user or an offspring between you and the user.")
|
|
description3 := getLabelCentered("You must link your genome person in the Build Profile menu to see offspring information.")
|
|
|
|
handleSelectButton := func(newUserOrOffspring string){
|
|
if (userOrOffspring == newUserOrOffspring){
|
|
return
|
|
}
|
|
setViewMateProfilePage_DiscreteGeneticTraits(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
|
|
}
|
|
|
|
userOrOffspringSelector := widget.NewSelect([]string{"User", "Offspring"}, handleSelectButton)
|
|
userOrOffspringSelector.Selected = userOrOffspring
|
|
|
|
userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector)
|
|
|
|
getTraitsInfoGrid := func()(*fyne.Container, error){
|
|
|
|
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
|
|
if (err != nil) { return nil, err }
|
|
|
|
//Outputs:
|
|
// -map[int64]locusValue.LocusValue
|
|
// -error
|
|
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
|
|
|
|
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
|
|
emptyMap := make(map[int64]locusValue.LocusValue)
|
|
return emptyMap, nil
|
|
}
|
|
|
|
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
|
|
if (err != nil) { return nil, err }
|
|
|
|
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
|
|
if (exists == false){
|
|
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
|
|
}
|
|
|
|
return myGenomeLocusValuesMap, nil
|
|
}
|
|
|
|
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
emptyLabel1 := widget.NewLabel("")
|
|
traitNameLabel := getItalicLabelCentered("Trait Name")
|
|
|
|
emptyLabel2 := widget.NewLabel("")
|
|
userPredictedOutcomeTitle := getItalicLabelCentered("User Predicted Outcome")
|
|
|
|
emptyLabel3 := widget.NewLabel("")
|
|
offspringOutcomeProbabilitiesLabel := getItalicLabelCentered("Offspring Outcome Probabilities")
|
|
|
|
quantityOfLabel1 := getItalicLabelCentered("Quantity Of")
|
|
lociKnownLabel := getItalicLabelCentered("Loci Known")
|
|
|
|
emptyLabel4 := widget.NewLabel("")
|
|
emptyLabel5 := widget.NewLabel("")
|
|
|
|
traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator())
|
|
userPredictedOutcomeColumn := container.NewVBox(emptyLabel2, userPredictedOutcomeTitle, widget.NewSeparator())
|
|
|
|
offspringOutcomeProbabilitiesColumn := container.NewVBox(emptyLabel3, offspringOutcomeProbabilitiesLabel, widget.NewSeparator())
|
|
|
|
quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel1, lociKnownLabel, widget.NewSeparator())
|
|
viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
|
|
|
|
traitObjectsList, err := traits.GetTraitObjectsList()
|
|
if (err != nil) { return nil, err }
|
|
|
|
for _, traitObject := range traitObjectsList{
|
|
|
|
traitName := traitObject.TraitName
|
|
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
|
|
if (traitIsDiscreteOrNumeric != "Discrete"){
|
|
continue
|
|
}
|
|
|
|
traitRulesList := traitObject.RulesList
|
|
totalNumberOfTraitRules := len(traitRulesList)
|
|
|
|
traitLociList := traitObject.LociList
|
|
|
|
numberOfTraitLoci := len(traitLociList)
|
|
|
|
if (totalNumberOfTraitRules == 0 && numberOfTraitLoci == 0){
|
|
// We are not able to analyze these traits yet
|
|
continue
|
|
}
|
|
|
|
traitNeuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
|
|
if (traitNeuralNetworkExists == false && totalNumberOfTraitRules == 0){
|
|
// We are not able to analyze these traits yet
|
|
continue
|
|
}
|
|
|
|
traitNameText := getBoldLabelCentered(translate(traitName))
|
|
traitNameColumn.Add(traitNameText)
|
|
|
|
viewTraitDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
|
if (traitNeuralNetworkExists == true){
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
} else {
|
|
setViewMateProfilePage_DiscreteTraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage)
|
|
}
|
|
})
|
|
|
|
viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton)
|
|
|
|
// We construct the user's trait locus values map
|
|
// Map Structure: Locus rsID -> locusValue.LocusValue
|
|
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
|
|
|
|
for _, rsID := range traitLociList{
|
|
|
|
rsIDString := helpers.ConvertInt64ToString(rsID)
|
|
|
|
userLocusValueAttributeName := "LocusValue_rs" + rsIDString
|
|
|
|
userLocusValueIsKnown, _, userLocusValue, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
|
|
if (err != nil) { return nil, err }
|
|
if (userLocusValueIsKnown == false){
|
|
continue
|
|
}
|
|
|
|
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusValue, ";")
|
|
if (semicolonFound == false){
|
|
return nil, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + " value: " + userLocusValue)
|
|
}
|
|
|
|
userLocusIsPhasedAttributeName := "LocusIsPhased_rs" + rsIDString
|
|
|
|
userLocusIsPhasedExists, _, userLocusIsPhasedString, err := getAnyUserProfileAttributeFunction(userLocusIsPhasedAttributeName)
|
|
if (err != nil) { return nil, err }
|
|
if (userLocusIsPhasedExists == false){
|
|
return nil, errors.New("Database corrupt: Contains profile with locusValue but not locusIsPhased status for locus: " + rsIDString)
|
|
}
|
|
|
|
userLocusIsPhased, err := helpers.ConvertYesOrNoStringToBool(userLocusIsPhasedString)
|
|
if (err != nil) { return nil, err }
|
|
|
|
userLocusValueObject := locusValue.LocusValue{
|
|
Base1Value: userLocusBase1,
|
|
Base2Value: userLocusBase2,
|
|
LocusIsPhased: userLocusIsPhased,
|
|
}
|
|
|
|
userTraitLocusValuesMap[rsID] = userLocusValueObject
|
|
}
|
|
|
|
if (userOrOffspring == "User"){
|
|
|
|
if (traitNeuralNetworkExists == true){
|
|
|
|
traitNeuralNetworkExists, anyLocusValuesAreKnown, predictedOutcome, _, quantityOfLociKnown, _, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject, userTraitLocusValuesMap, true)
|
|
if (err != nil) { return nil, err }
|
|
if (traitNeuralNetworkExists == false){
|
|
return nil, errors.New("GetGenomeTraitAnalysis_NeuralNetwork claims neural network doesn't exist for trait, but we already checked.")
|
|
}
|
|
if (anyLocusValuesAreKnown == false){
|
|
unknownLabel := getItalicLabelCentered(translate("Unknown"))
|
|
userPredictedOutcomeColumn.Add(unknownLabel)
|
|
} else {
|
|
predictedOutcomeLabel := getBoldLabelCentered(predictedOutcome)
|
|
userPredictedOutcomeColumn.Add(predictedOutcomeLabel)
|
|
}
|
|
|
|
totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci)
|
|
|
|
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
|
|
|
|
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString
|
|
|
|
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
|
|
|
|
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
|
|
|
|
} else {
|
|
|
|
// We use the rules-based analysis
|
|
|
|
anyRulesExist, _, quantityOfLociKnown_Rules, _, predictedOutcomeIsKnown, predictedOutcome, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_Rules(traitObject, userTraitLocusValuesMap, true)
|
|
if (err != nil) { return nil, err }
|
|
if (anyRulesExist == false){
|
|
return nil, errors.New("GetGenomeTraitAnalysis_Rules claims no rules exist when we already checked.")
|
|
}
|
|
|
|
if (predictedOutcomeIsKnown == false){
|
|
unknownLabel := getItalicLabelCentered("Unknown")
|
|
userPredictedOutcomeColumn.Add(unknownLabel)
|
|
} else {
|
|
predictedOutcomeLabel := getBoldLabelCentered(predictedOutcome)
|
|
userPredictedOutcomeColumn.Add(predictedOutcomeLabel)
|
|
}
|
|
|
|
traitLociList_Rules := traitObject.LociList_Rules
|
|
|
|
totalQuantityOfLoci := len(traitLociList_Rules)
|
|
|
|
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown_Rules)
|
|
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
|
|
|
|
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
|
|
|
|
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
|
|
|
|
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
|
|
}
|
|
} else {
|
|
|
|
// userOrOffspring == "Offspring"
|
|
|
|
if (traitNeuralNetworkExists == true){
|
|
|
|
neuralNetworkExists, anyLociKnown, outcomeProbabilitiesMap, _, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap)
|
|
if (err != nil) { return nil, err }
|
|
if (neuralNetworkExists == false){
|
|
return nil, errors.New("GetOffspringTraitInfo_NeuralNetwork claiming that neural network doesn't exist when we already checked.")
|
|
}
|
|
|
|
totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci)
|
|
|
|
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
|
|
|
|
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString
|
|
|
|
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
|
|
|
|
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
|
|
|
|
if (anyLociKnown == false){
|
|
unknownLabel := getItalicLabelCentered("Unknown")
|
|
|
|
offspringOutcomeProbabilitiesColumn.Add(unknownLabel)
|
|
|
|
} else {
|
|
|
|
outcomesList := helpers.GetListOfMapKeys(outcomeProbabilitiesMap)
|
|
|
|
// We sort the outcomes in alphabetical order so they show up the same way each time
|
|
slices.Sort(outcomesList)
|
|
|
|
quantityOfAddedItems := 0
|
|
|
|
for _, outcomeName := range outcomesList{
|
|
|
|
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
|
|
if (exists == false){
|
|
return nil, errors.New("GetListOfMapKeys returning element which doesn't exist in map.")
|
|
}
|
|
if (outcomeProbability == 0){
|
|
continue
|
|
}
|
|
|
|
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
|
|
outcomeProbabilityLabelText := outcomeName + ": " + outcomeProbabilityString + "%"
|
|
|
|
outcomeProbabilityLabel := getBoldLabelCentered(outcomeProbabilityLabelText)
|
|
|
|
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilityLabel)
|
|
|
|
quantityOfAddedItems += 1
|
|
|
|
if (quantityOfAddedItems > 1){
|
|
// We add whitespace for the other columns
|
|
traitNameColumn.Add(widget.NewLabel(""))
|
|
quantityOfLociKnownColumn.Add(widget.NewLabel(""))
|
|
viewTraitDetailsButtonsColumn.Add(widget.NewLabel(""))
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// We use the rules-based analysis
|
|
|
|
anyRulesExist, rulesAnalysisExists, _, offspringQuantityOfLociKnown, _, outcomeProbabilitiesMap, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_Rules(traitObject, myGenomeLocusValuesMap, userTraitLocusValuesMap)
|
|
if (err != nil) { return nil, err }
|
|
if (anyRulesExist == false){
|
|
return nil, errors.New("GetOffspringDiscreteTraitInfo_Rules claiming that no rules exist when we already checked.")
|
|
}
|
|
|
|
lociList_Rules := traitObject.LociList_Rules
|
|
|
|
totalQuantityOfLoci := len(lociList_Rules)
|
|
|
|
quantityOfLociKnownString := helpers.ConvertIntToString(offspringQuantityOfLociKnown)
|
|
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
|
|
|
|
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
|
|
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
|
|
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
|
|
|
|
if (rulesAnalysisExists == false){
|
|
unknownLabel := getItalicLabelCentered(translate("Unknown"))
|
|
|
|
offspringOutcomeProbabilitiesColumn.Add(unknownLabel)
|
|
} else {
|
|
|
|
outcomesList := helpers.GetListOfMapKeys(outcomeProbabilitiesMap)
|
|
|
|
// We sort the outcomes in alphabetical order so they show up the same way each time
|
|
slices.Sort(outcomesList)
|
|
|
|
quantityOfAddedItems := 0
|
|
|
|
for _, outcomeName := range outcomesList{
|
|
|
|
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
|
|
if (exists == false){
|
|
return nil, errors.New("GetListOfMapKeys returning element which doesn't exist in map.")
|
|
}
|
|
|
|
if (outcomeProbability == 0){
|
|
continue
|
|
}
|
|
|
|
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
|
|
outcomeProbabilityLabelText := outcomeName + ": " + outcomeProbabilityString + "%"
|
|
|
|
outcomeProbabilityLabel := getBoldLabelCentered(outcomeProbabilityLabelText)
|
|
|
|
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilityLabel)
|
|
|
|
quantityOfAddedItems += 1
|
|
|
|
if (quantityOfAddedItems > 1){
|
|
// We add whitespace for the other columns
|
|
traitNameColumn.Add(widget.NewLabel(""))
|
|
quantityOfLociKnownColumn.Add(widget.NewLabel(""))
|
|
viewTraitDetailsButtonsColumn.Add(widget.NewLabel(""))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
traitNameColumn.Add(widget.NewSeparator())
|
|
userPredictedOutcomeColumn.Add(widget.NewSeparator())
|
|
offspringOutcomeProbabilitiesColumn.Add(widget.NewSeparator())
|
|
quantityOfLociKnownColumn.Add(widget.NewSeparator())
|
|
viewTraitDetailsButtonsColumn.Add(widget.NewSeparator())
|
|
}
|
|
|
|
if (userOrOffspring == "User"){
|
|
|
|
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
userPredictedOutcomeColumn.Add(predictedOutcomeHelpButton)
|
|
|
|
} else {
|
|
// userOrOffspring == "Offspring"
|
|
|
|
outcomeProbabilitiesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilitiesHelpButton)
|
|
}
|
|
|
|
quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton)
|
|
|
|
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn)
|
|
|
|
if (userOrOffspring == "User"){
|
|
|
|
traitsInfoGrid.Add(userPredictedOutcomeColumn)
|
|
|
|
} else {
|
|
|
|
// userOrOffspring == "Offspring"
|
|
|
|
traitsInfoGrid.Add(offspringOutcomeProbabilitiesColumn)
|
|
}
|
|
|
|
traitsInfoGrid.Add(quantityOfLociKnownColumn)
|
|
traitsInfoGrid.Add(viewTraitDetailsButtonsColumn)
|
|
traitsInfoGrid.Add(layout.NewSpacer())
|
|
|
|
return traitsInfoGrid, nil
|
|
}
|
|
|
|
traitsInfoGrid, err := getTraitsInfoGrid()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), userOrOffspringSelectorCentered, widget.NewSeparator(), traitsInfoGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName string, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
currentPage := func(){setViewMateProfilePage_DiscreteTraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
|
|
|
|
title := getPageTitleCentered("View Profile - Physical")
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(userOrOffspring + " Trait Rules")
|
|
|
|
traitNameLabel := widget.NewLabel("Trait Name:")
|
|
|
|
traitNameText := getBoldLabel(traitName)
|
|
|
|
viewTraitInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
|
|
setViewTraitDetailsPage(window, traitName, currentPage)
|
|
})
|
|
|
|
traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, viewTraitInfoButton, layout.NewSpacer())
|
|
|
|
//Outputs:
|
|
// -bool: Any trait locus value exists for this myself
|
|
// -map[int64]locusValue.LocusValue: My locus values map
|
|
// -error
|
|
getMyGenomeLocusValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
|
|
|
|
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
|
|
if (err != nil) { return false, nil, err }
|
|
|
|
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
|
|
// Without my genome person chosen, all offspring rule probabilities are unknown
|
|
return false, nil, nil
|
|
}
|
|
|
|
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
|
|
if (err != nil){ return false, nil, err }
|
|
|
|
myGenomeMap, exists := myGenomesMap[myGenomeIdentifier]
|
|
if (exists == false){
|
|
return false, nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which does not contain genome matching myGenomeIdentifier")
|
|
}
|
|
|
|
return true, myGenomeMap, nil
|
|
}
|
|
|
|
anyMyLocusValuesExist, myLocusValuesMap, err := getMyGenomeLocusValuesMap()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
traitObject, err := traits.GetTraitObject(traitName)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
traitLociList_Rules := traitObject.LociList_Rules
|
|
traitRulesList := traitObject.RulesList
|
|
|
|
if (len(traitRulesList) == 0){
|
|
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiscreteTraitRules called with trait which has no rules."), previousPage)
|
|
return
|
|
}
|
|
|
|
//Outputs:
|
|
// -bool: Any trait locus value exists for this user
|
|
// -map[int64]locusValue.LocusValue: User locus values map
|
|
// -error
|
|
getUserTraitLocusValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
|
|
|
|
// We construct the user's trait locus values map
|
|
// Map Structure: Locus rsID -> locusValue.LocusValue
|
|
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
|
|
|
|
for _, rsID := range traitLociList_Rules{
|
|
|
|
rsIDString := helpers.ConvertInt64ToString(rsID)
|
|
|
|
userLocusValueAttributeName := "LocusValue_rs" + rsIDString
|
|
|
|
userLocusValueIsKnown, _, userLocusValue, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
|
|
if (err != nil) { return false, nil, err }
|
|
if (userLocusValueIsKnown == false){
|
|
continue
|
|
}
|
|
|
|
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusValue, ";")
|
|
if (semicolonFound == false){
|
|
return false, nil, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + " value: " + userLocusValue)
|
|
}
|
|
|
|
userLocusIsPhasedAttributeName := "LocusIsPhased_rs" + rsIDString
|
|
|
|
userLocusIsPhasedExists, _, userLocusIsPhasedString, err := getAnyUserProfileAttributeFunction(userLocusIsPhasedAttributeName)
|
|
if (err != nil) { return false, nil, err }
|
|
if (userLocusIsPhasedExists == false){
|
|
return false, nil, errors.New("Database corrupt: Contains profile with locusValue but not locusIsPhased status for locus: " + rsIDString)
|
|
}
|
|
|
|
userLocusIsPhased, err := helpers.ConvertYesOrNoStringToBool(userLocusIsPhasedString)
|
|
if (err != nil) { return false, nil, err }
|
|
|
|
userLocusValueObject := locusValue.LocusValue{
|
|
Base1Value: userLocusBase1,
|
|
Base2Value: userLocusBase2,
|
|
LocusIsPhased: userLocusIsPhased,
|
|
}
|
|
|
|
userTraitLocusValuesMap[rsID] = userLocusValueObject
|
|
}
|
|
if (len(userTraitLocusValuesMap) == 0){
|
|
return false, nil, nil
|
|
}
|
|
|
|
return true, userTraitLocusValuesMap, nil
|
|
}
|
|
|
|
anyUserTraitLocusValueExists, userTraitLocusValuesMap, err := getUserTraitLocusValuesMap()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
//Outputs:
|
|
// -bool: Any offspring probability of passing rule is known
|
|
// -map[[3]byte]int: Offspring probability of passing rules map
|
|
// Map Structure: Rule Identifier -> Probability offspring will pass rule (0-100%)
|
|
// -error
|
|
getOffspringProbabilityOfPassingRulesMap := func()(bool, map[[3]byte]int, error){
|
|
|
|
if (anyMyLocusValuesExist == false || anyUserTraitLocusValueExists == false){
|
|
return false, nil, nil
|
|
}
|
|
|
|
anyRulesExist, anyOffspringRulesTested, _, _, offspringProbabilityOfPassingRulesMap, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_Rules(traitObject, myLocusValuesMap, userTraitLocusValuesMap)
|
|
if (err != nil) { return false, nil, err }
|
|
if (anyRulesExist == false){
|
|
return false, nil, errors.New("GetOffspringDiscreteTraitInfo claiming no trait rules exist when we already checked.")
|
|
}
|
|
if (anyOffspringRulesTested == false){
|
|
return false, nil, nil
|
|
}
|
|
|
|
return true, offspringProbabilityOfPassingRulesMap, nil
|
|
}
|
|
|
|
anyOffspringProbabilityOfPassingRuleIsKnown, offspringProbabilityOfPassingRulesMap, err := getOffspringProbabilityOfPassingRulesMap()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
getNumberOfRulesTested := func()(int, error){
|
|
|
|
if (userOrOffspring == "Offspring"){
|
|
if (anyOffspringProbabilityOfPassingRuleIsKnown == false){
|
|
return 0, nil
|
|
}
|
|
|
|
numberOfRulesTested := len(offspringProbabilityOfPassingRulesMap)
|
|
|
|
return numberOfRulesTested, nil
|
|
}
|
|
|
|
if (anyUserTraitLocusValueExists == false){
|
|
return 0, nil
|
|
}
|
|
|
|
numberOfRulesTested := 0
|
|
|
|
for _, ruleObject := range traitRulesList{
|
|
|
|
ruleLociList := ruleObject.LociList
|
|
|
|
ruleStatusIsKnown, _, err := createPersonGeneticAnalysis.GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
|
|
if (err != nil) { return 0, err }
|
|
if (ruleStatusIsKnown == true){
|
|
numberOfRulesTested += 1
|
|
}
|
|
}
|
|
|
|
return numberOfRulesTested, nil
|
|
}
|
|
|
|
numberOfRulesTested, err := getNumberOfRulesTested()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
rulesTestedLabel := widget.NewLabel("Rules Tested:")
|
|
|
|
totalNumberOfTraitRules := len(traitRulesList)
|
|
numberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested)
|
|
totalNumberOfTraitRulesString := helpers.ConvertIntToString(totalNumberOfTraitRules)
|
|
|
|
numberOfRulesTestedFormatted := numberOfRulesTestedString + "/" + totalNumberOfTraitRulesString
|
|
numberOfRulesTestedLabel := getBoldLabel(numberOfRulesTestedFormatted)
|
|
|
|
numberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
if (userOrOffspring == "User"){
|
|
setDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
|
|
} else {
|
|
setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
|
|
}
|
|
})
|
|
|
|
numberOfRulesTestedRow := container.NewHBox(layout.NewSpacer(), rulesTestedLabel, numberOfRulesTestedLabel, numberOfRulesTestedHelpButton, layout.NewSpacer())
|
|
|
|
getRulesGrid := func()(*fyne.Container, error){
|
|
|
|
//TODO: Sort results
|
|
|
|
viewRuleInfoButtonsColumn := container.NewVBox()
|
|
ruleIdentifierColumn := container.NewVBox()
|
|
ruleEffectsColumn := container.NewVBox()
|
|
userPassesRuleColumn := container.NewVBox()
|
|
offspringProbabilityOfPassingRuleColumn := container.NewVBox()
|
|
|
|
if (userOrOffspring == "Offspring"){
|
|
|
|
// We need this because the header row is 2 rows tall when userOrOffspring == "Offspring"
|
|
// Otherwise, it is 1 row tall
|
|
|
|
emptyLabelA := widget.NewLabel("")
|
|
emptyLabelB := widget.NewLabel("")
|
|
emptyLabelC := widget.NewLabel("")
|
|
emptyLabelD := widget.NewLabel("")
|
|
|
|
viewRuleInfoButtonsColumn.Add(emptyLabelA)
|
|
ruleIdentifierColumn.Add(emptyLabelB)
|
|
ruleEffectsColumn.Add(emptyLabelC)
|
|
userPassesRuleColumn.Add(emptyLabelD)
|
|
}
|
|
|
|
offspringProbabilityOfTitle := getItalicLabelCentered("Offspring Probability Of")
|
|
offspringProbabilityOfPassingRuleColumn.Add(offspringProbabilityOfTitle)
|
|
|
|
emptyLabelE := widget.NewLabel("")
|
|
ruleIdentifierTitle := getItalicLabelCentered("Rule Identifier")
|
|
ruleEffectsTitle := getItalicLabelCentered("Rule Effects")
|
|
userPassesRuleTitle := getItalicLabelCentered("User Passes Rule")
|
|
passingRuleTitle := getItalicLabelCentered("Passing Rule")
|
|
|
|
viewRuleInfoButtonsColumn.Add(emptyLabelE)
|
|
ruleIdentifierColumn.Add(ruleIdentifierTitle)
|
|
ruleEffectsColumn.Add(ruleEffectsTitle)
|
|
userPassesRuleColumn.Add(userPassesRuleTitle)
|
|
offspringProbabilityOfPassingRuleColumn.Add(passingRuleTitle)
|
|
|
|
viewRuleInfoButtonsColumn.Add(widget.NewSeparator())
|
|
ruleIdentifierColumn.Add(widget.NewSeparator())
|
|
ruleEffectsColumn.Add(widget.NewSeparator())
|
|
userPassesRuleColumn.Add(widget.NewSeparator())
|
|
offspringProbabilityOfPassingRuleColumn.Add(widget.NewSeparator())
|
|
|
|
for _, ruleObject := range traitRulesList{
|
|
|
|
ruleIdentifierHex := ruleObject.RuleIdentifier
|
|
|
|
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
|
|
if (err != nil) { return nil, err }
|
|
|
|
ruleLociList := ruleObject.LociList
|
|
|
|
getUserPassesRuleString := func()(string, error){
|
|
|
|
userRuleStatusIsKnown, userPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
|
|
if (err != nil) { return "", err }
|
|
|
|
if (userRuleStatusIsKnown == false){
|
|
result := translate("Unknown")
|
|
return result, nil
|
|
}
|
|
userPassesRuleString := helpers.ConvertBoolToYesOrNoString(userPassesRule)
|
|
userPassesRuleTranslated := translate(userPassesRuleString)
|
|
return userPassesRuleTranslated, nil
|
|
}
|
|
|
|
userPassesRuleString, err := getUserPassesRuleString()
|
|
if (err != nil) { return nil, err }
|
|
|
|
getOffspringProbabilityOfPassingRuleString := func()(string, error){
|
|
|
|
if (anyOffspringProbabilityOfPassingRuleIsKnown == false){
|
|
result := translate("Unknown")
|
|
return result, nil
|
|
}
|
|
|
|
probabilityOfPassingRule, probabilityIsKnown := offspringProbabilityOfPassingRulesMap[ruleIdentifier]
|
|
if (probabilityIsKnown == false){
|
|
result := translate("Unknown")
|
|
return result, nil
|
|
}
|
|
|
|
ruleProbabilityString := helpers.ConvertIntToString(probabilityOfPassingRule)
|
|
|
|
ruleProbabilityFormatted := ruleProbabilityString + "%"
|
|
return ruleProbabilityFormatted, nil
|
|
}
|
|
|
|
offspringProbabilityOfPassingRuleString, err := getOffspringProbabilityOfPassingRuleString()
|
|
if (err != nil) { return nil, err }
|
|
|
|
// We add all of the columns except for the rule effects column
|
|
// We do this because the rule effects column may be multiple rows tall
|
|
|
|
viewRuleInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
|
|
setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
|
|
})
|
|
ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifierHex)
|
|
userPassesRuleLabel := getBoldLabelCentered(userPassesRuleString)
|
|
offspringProbabilityOfPassingRuleLabel := getBoldLabelCentered(offspringProbabilityOfPassingRuleString)
|
|
|
|
viewRuleInfoButtonsColumn.Add(viewRuleInfoButton)
|
|
ruleIdentifierColumn.Add(ruleIdentifierLabel)
|
|
userPassesRuleColumn.Add(userPassesRuleLabel)
|
|
offspringProbabilityOfPassingRuleColumn.Add(offspringProbabilityOfPassingRuleLabel)
|
|
|
|
traitOutcomesList := traitObject.OutcomesList
|
|
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
|
|
|
|
// We have to sort the outcome names so they always show up in the same order
|
|
|
|
outcomeNamesListSorted := helpers.CopyAndSortStringListToUnicodeOrder(traitOutcomesList)
|
|
|
|
addedOutcomes := 0
|
|
for _, outcomeName := range outcomeNamesListSorted{
|
|
|
|
outcomeChange, exists := ruleOutcomePointsMap[outcomeName]
|
|
if (exists == false){
|
|
// This rule does not effect this outcome. Skip
|
|
continue
|
|
}
|
|
|
|
getOutcomeEffectString := func()string{
|
|
|
|
outcomeChangeString := helpers.ConvertIntToString(outcomeChange)
|
|
|
|
if (outcomeChange < 0){
|
|
return outcomeChangeString
|
|
}
|
|
outcomeEffect := "+" + outcomeChangeString
|
|
return outcomeEffect
|
|
}
|
|
|
|
outcomeEffect := getOutcomeEffectString()
|
|
|
|
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeEffect)
|
|
ruleEffectsColumn.Add(outcomeRow)
|
|
|
|
if (addedOutcomes > 0){
|
|
|
|
emptyLabelA := widget.NewLabel("")
|
|
emptyLabelB := widget.NewLabel("")
|
|
emptyLabelC := widget.NewLabel("")
|
|
emptyLabelD := widget.NewLabel("")
|
|
|
|
viewRuleInfoButtonsColumn.Add(emptyLabelA)
|
|
ruleIdentifierColumn.Add(emptyLabelB)
|
|
userPassesRuleColumn.Add(emptyLabelC)
|
|
offspringProbabilityOfPassingRuleColumn.Add(emptyLabelD)
|
|
}
|
|
addedOutcomes += 1
|
|
}
|
|
|
|
viewRuleInfoButtonsColumn.Add(widget.NewSeparator())
|
|
ruleIdentifierColumn.Add(widget.NewSeparator())
|
|
ruleEffectsColumn.Add(widget.NewSeparator())
|
|
userPassesRuleColumn.Add(widget.NewSeparator())
|
|
offspringProbabilityOfPassingRuleColumn.Add(widget.NewSeparator())
|
|
}
|
|
|
|
ruleEffectsHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
|
|
})
|
|
|
|
userPassesRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setPersonPassesDiscreteTraitRuleExplainerPage(window, currentPage)
|
|
})
|
|
|
|
offspringProbabilityOfPassingRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setOffspringProbabilityOfPassingTraitRuleExplainerPage(window, currentPage)
|
|
})
|
|
|
|
ruleEffectsColumn.Add(ruleEffectsHelpButton)
|
|
userPassesRuleColumn.Add(userPassesRuleHelpButton)
|
|
offspringProbabilityOfPassingRuleColumn.Add(offspringProbabilityOfPassingRuleHelpButton)
|
|
|
|
if (userOrOffspring == "User"){
|
|
rulesGrid := container.NewHBox(layout.NewSpacer(), viewRuleInfoButtonsColumn, ruleIdentifierColumn, ruleEffectsColumn, userPassesRuleColumn, layout.NewSpacer())
|
|
return rulesGrid, nil
|
|
}
|
|
rulesGrid := container.NewHBox(layout.NewSpacer(), viewRuleInfoButtonsColumn, ruleIdentifierColumn, ruleEffectsColumn, offspringProbabilityOfPassingRuleColumn, layout.NewSpacer())
|
|
return rulesGrid, nil
|
|
}
|
|
|
|
rulesGrid, err := getRulesGrid()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), traitNameRow, numberOfRulesTestedRow, widget.NewSeparator(), rulesGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
|
|
func setViewMateProfilePage_NumericGeneticTraits(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
if (userOrOffspring != "User" && userOrOffspring != "Offspring"){
|
|
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_NumericGeneticTraits called with invalid userOrOffspring: " + userOrOffspring), previousPage)
|
|
return
|
|
}
|
|
|
|
if (userOrOffspring == "Offspring"){
|
|
setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...")
|
|
}
|
|
|
|
//currentPage := func(){setViewMateProfilePage_NumericGeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
|
|
|
|
title := getPageTitleCentered(translate("View Profile - Physical"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Numeric Genetic Traits"))
|
|
|
|
description1 := getLabelCentered("Below is the numeric genetic trait analysis for this user.")
|
|
description2 := getLabelCentered("You can choose to view the analysis of the user or an offspring between you and the user.")
|
|
description3 := getLabelCentered("You must link your genome person in the Build Profile menu to see offspring information.")
|
|
|
|
handleSelectButton := func(newUserOrOffspring string){
|
|
if (userOrOffspring == newUserOrOffspring){
|
|
return
|
|
}
|
|
setViewMateProfilePage_NumericGeneticTraits(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
|
|
}
|
|
|
|
userOrOffspringSelector := widget.NewSelect([]string{"User", "Offspring"}, handleSelectButton)
|
|
userOrOffspringSelector.Selected = userOrOffspring
|
|
|
|
userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector)
|
|
|
|
getTraitsInfoGrid := func()(*fyne.Container, error){
|
|
|
|
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
|
|
if (err != nil) { return nil, err }
|
|
|
|
//Outputs:
|
|
// -map[int64]locusValue.LocusValue
|
|
// -error
|
|
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
|
|
|
|
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
|
|
emptyMap := make(map[int64]locusValue.LocusValue)
|
|
return emptyMap, nil
|
|
}
|
|
|
|
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
|
|
if (err != nil) { return nil, err }
|
|
|
|
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
|
|
if (exists == false){
|
|
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
|
|
}
|
|
|
|
return myGenomeLocusValuesMap, nil
|
|
}
|
|
|
|
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
emptyLabel1 := widget.NewLabel("")
|
|
traitNameLabel := getItalicLabelCentered("Trait Name")
|
|
|
|
emptyLabel2 := widget.NewLabel("")
|
|
userPredictedOutcomeTitle := getItalicLabelCentered("User Predicted Outcome")
|
|
|
|
emptyLabel3 := widget.NewLabel("")
|
|
offspringPredictedOutcomeTitle := getItalicLabelCentered("Offspring Predicted Outcome")
|
|
|
|
predictionTitle := getItalicLabelCentered("Prediction")
|
|
confidenceRangeTitle := getItalicLabelCentered("Confidence Range")
|
|
|
|
quantityOfLabel1 := getItalicLabelCentered("Quantity Of")
|
|
lociKnownLabel := getItalicLabelCentered("Loci Known")
|
|
|
|
emptyLabel4 := widget.NewLabel("")
|
|
emptyLabel5 := widget.NewLabel("")
|
|
|
|
traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator())
|
|
userPredictedOutcomeColumn := container.NewVBox(emptyLabel2, userPredictedOutcomeTitle, widget.NewSeparator())
|
|
|
|
offspringPredictedOutcomeColumn := container.NewVBox(emptyLabel3, offspringPredictedOutcomeTitle, widget.NewSeparator())
|
|
|
|
predictionConfidenceRangeColumn := container.NewVBox(predictionTitle, confidenceRangeTitle, widget.NewSeparator())
|
|
quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel1, lociKnownLabel, widget.NewSeparator())
|
|
viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
|
|
|
|
traitObjectsList, err := traits.GetTraitObjectsList()
|
|
if (err != nil) { return nil, err }
|
|
|
|
for _, traitObject := range traitObjectsList{
|
|
|
|
traitName := traitObject.TraitName
|
|
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
|
|
if (traitIsDiscreteOrNumeric != "Numeric"){
|
|
continue
|
|
}
|
|
|
|
traitLociList := traitObject.LociList
|
|
|
|
numberOfTraitLoci := len(traitLociList)
|
|
|
|
if (numberOfTraitLoci == 0){
|
|
// We are not able to analyze these traits
|
|
continue
|
|
}
|
|
|
|
traitNeuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
|
|
if (traitNeuralNetworkExists == false){
|
|
// We are not able to analyze these traits yet
|
|
continue
|
|
}
|
|
|
|
traitNameText := getBoldLabelCentered(translate(traitName))
|
|
|
|
// We construct the user's trait locus values map
|
|
// Map Structure: Locus rsID -> locusValue.LocusValue
|
|
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
|
|
|
|
for _, rsID := range traitLociList{
|
|
|
|
rsIDString := helpers.ConvertInt64ToString(rsID)
|
|
|
|
userLocusValueAttributeName := "LocusValue_rs" + rsIDString
|
|
|
|
userLocusValueIsKnown, _, userLocusValue, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
|
|
if (err != nil) { return nil, err }
|
|
if (userLocusValueIsKnown == false){
|
|
continue
|
|
}
|
|
|
|
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusValue, ";")
|
|
if (semicolonFound == false){
|
|
return nil, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + " value: " + userLocusValue)
|
|
}
|
|
|
|
userLocusIsPhasedAttributeName := "LocusIsPhased_rs" + rsIDString
|
|
|
|
userLocusIsPhasedExists, _, userLocusIsPhasedString, err := getAnyUserProfileAttributeFunction(userLocusIsPhasedAttributeName)
|
|
if (err != nil) { return nil, err }
|
|
if (userLocusIsPhasedExists == false){
|
|
return nil, errors.New("Database corrupt: Contains profile with locusValue but not locusIsPhased status for locus: " + rsIDString)
|
|
}
|
|
|
|
userLocusIsPhased, err := helpers.ConvertYesOrNoStringToBool(userLocusIsPhasedString)
|
|
if (err != nil) { return nil, err }
|
|
|
|
userLocusValueObject := locusValue.LocusValue{
|
|
Base1Value: userLocusBase1,
|
|
Base2Value: userLocusBase2,
|
|
LocusIsPhased: userLocusIsPhased,
|
|
}
|
|
|
|
userTraitLocusValuesMap[rsID] = userLocusValueObject
|
|
}
|
|
|
|
//Outputs:
|
|
// -bool: Analysis results exist
|
|
// -float64: Predicted outcome
|
|
// -map[int]float64: Prediction confidence ranges map
|
|
// -int: Quantity of known loci
|
|
// -error
|
|
getAnalysisResults := func()(bool, float64, map[int]float64, int, error){
|
|
|
|
if (userOrOffspring == "User"){
|
|
|
|
traitNeuralNetworkExists, anyLocusValuesAreKnown, predictedOutcome, predictionAccuracyRangesMap, quantityOfLociKnown, _, err := createPersonGeneticAnalysis.GetGenomeNumericTraitAnalysis(traitObject, userTraitLocusValuesMap, true)
|
|
if (err != nil) { return false, 0, nil, 0, err }
|
|
if (traitNeuralNetworkExists == false){
|
|
return false, 0, nil, 0, errors.New("GetGenomeNumericTraitAnalysis claims neural network doesn't exist for trait, but we already checked.")
|
|
}
|
|
|
|
return anyLocusValuesAreKnown, predictedOutcome, predictionAccuracyRangesMap, quantityOfLociKnown, nil
|
|
} else {
|
|
|
|
// userOrOffspring == "Offspring"
|
|
|
|
neuralNetworkExists, anyLociKnown, predictedOutcome, predictionAccuracyRangesMap, _, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringNumericTraitInfo(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap)
|
|
if (err != nil) { return false, 0, nil, 0, err }
|
|
if (neuralNetworkExists == false){
|
|
return false, 0, nil, 0, errors.New("GetOffspringTraitInfo_NeuralNetwork claiming that neural network doesn't exist when we already checked.")
|
|
}
|
|
|
|
return anyLociKnown, predictedOutcome, predictionAccuracyRangesMap, quantityOfLociKnown, nil
|
|
}
|
|
}
|
|
|
|
analysisExists, predictedOutcome, predictionConfidenceRangesMap, quantityOfLociKnown, err := getAnalysisResults()
|
|
if (err != nil) { return nil, err }
|
|
|
|
getPredictedOutcomeLabel := func()fyne.Widget{
|
|
|
|
if (analysisExists == false){
|
|
unknownLabel := widget.NewLabel("Unknown")
|
|
return unknownLabel
|
|
}
|
|
|
|
predictedOutcomeString := helpers.ConvertFloat64ToStringRounded(predictedOutcome, 2)
|
|
|
|
//TODO: Retrieve units from traits package
|
|
predictedOutcomeFormatted := predictedOutcomeString + " centimeters"
|
|
|
|
predictedOutcomeLabel := getBoldLabel(predictedOutcomeFormatted)
|
|
|
|
return predictedOutcomeLabel
|
|
}
|
|
|
|
predictedOutcomeLabel := getPredictedOutcomeLabel()
|
|
|
|
predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel)
|
|
|
|
getConfidenceRangeLabel := func()(fyne.Widget, error){
|
|
if (analysisExists == false){
|
|
|
|
result := widget.NewLabel("Unknown")
|
|
return result, nil
|
|
}
|
|
|
|
// This is a list of the percentage accuracies in the map
|
|
// For example: 80% == The distance from the prediction you must travel for 80% of the predictions to be
|
|
// accurate within that range
|
|
confidenceRangePercentagesList := helpers.GetListOfMapKeys(predictionConfidenceRangesMap)
|
|
|
|
// We sort the list so the percentage is always the same upon refreshing the page
|
|
slices.Sort(confidenceRangePercentagesList)
|
|
|
|
closestToEightyPercentage, err := helpers.GetClosestIntInList(confidenceRangePercentagesList, 80)
|
|
if (err != nil) { return nil, err }
|
|
|
|
closestToEightyPercentageConfidenceDistance, exists := predictionConfidenceRangesMap[closestToEightyPercentage]
|
|
if (exists == false){
|
|
return nil, errors.New("GetListOfMapKeys returning list of elements which contains element which is not in the map.")
|
|
}
|
|
|
|
closestConfidenceDistanceString := helpers.ConvertFloat64ToStringRounded(closestToEightyPercentageConfidenceDistance, 2)
|
|
|
|
closestToEightyPercentageString := helpers.ConvertIntToString(closestToEightyPercentage)
|
|
|
|
confidenceRangeLabelValueFormatted := "+/- " + closestConfidenceDistanceString + " (" + closestToEightyPercentageString + "% Confidence)"
|
|
|
|
confidenceRangeLabel := getBoldLabel(confidenceRangeLabelValueFormatted)
|
|
|
|
return confidenceRangeLabel, nil
|
|
}
|
|
|
|
confidenceRangeLabel, err := getConfidenceRangeLabel()
|
|
if (err != nil) { return nil, err }
|
|
|
|
confidenceRangeLabelCentered := getWidgetCentered(confidenceRangeLabel)
|
|
|
|
totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci)
|
|
|
|
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
|
|
|
|
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString
|
|
|
|
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
|
|
|
|
viewTraitDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
traitNameColumn.Add(traitNameText)
|
|
|
|
if (userOrOffspring == "User"){
|
|
userPredictedOutcomeColumn.Add(predictedOutcomeLabelCentered)
|
|
} else {
|
|
offspringPredictedOutcomeColumn.Add(predictedOutcomeLabelCentered)
|
|
}
|
|
|
|
predictionConfidenceRangeColumn.Add(confidenceRangeLabelCentered)
|
|
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
|
|
viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton)
|
|
|
|
traitNameColumn.Add(widget.NewSeparator())
|
|
userPredictedOutcomeColumn.Add(widget.NewSeparator())
|
|
offspringPredictedOutcomeColumn.Add(widget.NewSeparator())
|
|
predictionConfidenceRangeColumn.Add(widget.NewSeparator())
|
|
quantityOfLociKnownColumn.Add(widget.NewSeparator())
|
|
viewTraitDetailsButtonsColumn.Add(widget.NewSeparator())
|
|
}
|
|
|
|
if (userOrOffspring == "User"){
|
|
|
|
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
userPredictedOutcomeColumn.Add(predictedOutcomeHelpButton)
|
|
|
|
} else {
|
|
// userOrOffspring == "Offspring"
|
|
|
|
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
offspringPredictedOutcomeColumn.Add(predictedOutcomeHelpButton)
|
|
}
|
|
|
|
predictionConfidenceRangeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
predictionConfidenceRangeColumn.Add(predictionConfidenceRangeHelpButton)
|
|
|
|
quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
//TODO
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton)
|
|
|
|
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn)
|
|
|
|
if (userOrOffspring == "User"){
|
|
|
|
traitsInfoGrid.Add(userPredictedOutcomeColumn)
|
|
|
|
} else {
|
|
|
|
// userOrOffspring == "Offspring"
|
|
|
|
traitsInfoGrid.Add(offspringPredictedOutcomeColumn)
|
|
}
|
|
|
|
traitsInfoGrid.Add(predictionConfidenceRangeColumn)
|
|
traitsInfoGrid.Add(quantityOfLociKnownColumn)
|
|
traitsInfoGrid.Add(viewTraitDetailsButtonsColumn)
|
|
traitsInfoGrid.Add(layout.NewSpacer())
|
|
|
|
return traitsInfoGrid, nil
|
|
}
|
|
|
|
traitsInfoGrid, err := getTraitsInfoGrid()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), userOrOffspringSelectorCentered, widget.NewSeparator(), traitsInfoGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_Diet(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
title := getPageTitleCentered(translate("View Profile - Lifestyle"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered("Diet")
|
|
|
|
description := getLabelCentered("Below are the user's food ratings.")
|
|
|
|
foodNameTitle := getItalicLabelCentered("Food Name")
|
|
|
|
ratingTitle := getItalicLabelCentered("Rating")
|
|
|
|
foodNameColumn := container.NewVBox(foodNameTitle, widget.NewSeparator())
|
|
|
|
foodRatingColumn := container.NewVBox(ratingTitle, widget.NewSeparator())
|
|
|
|
foodsList := []string{"Fruit", "Vegetables", "Nuts", "Grains", "Dairy", "Seafood", "Beef", "Pork", "Poultry", "Eggs", "Beans"}
|
|
|
|
for _, foodName := range foodsList{
|
|
|
|
foodAttributeName := foodName + "Rating"
|
|
|
|
ratingExists, _, foodRating, err := getAnyUserProfileAttributeFunction(foodAttributeName)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
foodNameLabel := getBoldLabelCentered(foodName)
|
|
|
|
getRatingLabel := func()*fyne.Container{
|
|
if (ratingExists == false){
|
|
result := getBoldItalicLabelCentered(translate("No Response"))
|
|
return result
|
|
}
|
|
|
|
result := getBoldLabelCentered(foodRating + "/10")
|
|
return result
|
|
}
|
|
|
|
ratingLabel := getRatingLabel()
|
|
|
|
foodNameColumn.Add(foodNameLabel)
|
|
foodRatingColumn.Add(ratingLabel)
|
|
|
|
foodNameColumn.Add(widget.NewSeparator())
|
|
foodRatingColumn.Add(widget.NewSeparator())
|
|
}
|
|
|
|
foodsGrid := container.NewHBox(layout.NewSpacer(), foodNameColumn, foodRatingColumn, layout.NewSpacer())
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), foodsGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setViewMateProfilePage_Language(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
|
|
|
|
title := getPageTitleCentered(translate("View Profile - Mental"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
subtitle := getPageSubtitleCentered(translate("Language"))
|
|
|
|
description := getLabelCentered("Below describes the language(s) the user can speak.")
|
|
|
|
userLanguagesExist, _, userLanguageAttribute, err := getAnyUserProfileAttributeFunction("Language")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (userLanguagesExist == false){
|
|
|
|
noLanguagesExistLabel := getBoldLabelCentered("This user's profile has no languages listed.")
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), noLanguagesExistLabel)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
getLanguagesGrid := func()(*fyne.Container, error){
|
|
|
|
//TODO: Sort by fluency
|
|
|
|
languageTitle := getItalicLabelCentered("Language")
|
|
fluencyTitle := getItalicLabelCentered("Fluency")
|
|
|
|
languageNameColumn := container.NewVBox(languageTitle, widget.NewSeparator())
|
|
languageFluencyColumn := container.NewVBox(fluencyTitle, widget.NewSeparator())
|
|
|
|
languageItemsList := strings.Split(userLanguageAttribute, "+&")
|
|
|
|
worldLanguageObjectsMap, err := worldLanguages.GetWorldLanguageObjectsMap()
|
|
if (err != nil) { return nil, err }
|
|
|
|
for _, languageItem := range languageItemsList{
|
|
|
|
languageName, languageRating, delimiterFound := strings.Cut(languageItem, "$")
|
|
if (delimiterFound == false){
|
|
return nil, errors.New("setViewMateProfilePage_Language called with profile containing invalid language attribute item: " + languageItem)
|
|
}
|
|
|
|
getLanguageNameFormatted := func()string{
|
|
|
|
// We only translate if language name is canonical
|
|
|
|
_, languageIsCanonical := worldLanguageObjectsMap[languageName]
|
|
if (languageIsCanonical == false){
|
|
return languageName
|
|
}
|
|
languageNameTranslated := translate(languageName)
|
|
return languageNameTranslated
|
|
}
|
|
|
|
languageNameFormatted := getLanguageNameFormatted()
|
|
|
|
languageFluencyFormatted := languageRating + "/5"
|
|
|
|
languageNameLabel := getBoldLabelCentered(languageNameFormatted)
|
|
languageFluencyLabel := getBoldLabelCentered(languageFluencyFormatted)
|
|
|
|
languageNameColumn.Add(languageNameLabel)
|
|
languageFluencyColumn.Add(languageFluencyLabel)
|
|
|
|
languageNameColumn.Add(widget.NewSeparator())
|
|
languageFluencyColumn.Add(widget.NewSeparator())
|
|
}
|
|
|
|
languagesGrid := container.NewHBox(layout.NewSpacer(), languageNameColumn, languageFluencyColumn, layout.NewSpacer())
|
|
|
|
return languagesGrid, nil
|
|
}
|
|
|
|
languagesGrid, err := getLanguagesGrid()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), languagesGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
|
|
// We use this to view an ancestry composition for moderation/comparing profile changes
|
|
func setViewUser23andMeAncestryCompositionPage(window fyne.Window, inputAncestryCompositionAttribute string, previousPage func()){
|
|
|
|
title := getPageTitleCentered("Viewing 23andMe Ancestry Composition")
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
attributeIsValid, continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap, err := companyAnalysis.ReadAncestryCompositionAttribute_23andMe(true, inputAncestryCompositionAttribute)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (attributeIsValid == false){
|
|
setErrorEncounteredPage(window, errors.New("setViewUser23andMeAncestryCompositionPage called with invalid inputAncestryCompositionAttribute"), previousPage)
|
|
return
|
|
}
|
|
|
|
userCompositionDisplay, err := get23andMeAncestryCompositionDisplay(continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
header := container.NewVBox(title, backButton, widget.NewSeparator())
|
|
|
|
page := container.NewBorder(header, nil, nil, nil, userCompositionDisplay)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func get23andMeAncestryCompositionDisplay(inputContinentPercentagesMap map[string]float64, inputRegionPercentagesMap map[string]float64, inputSubregionPercentagesMap map[string]float64)(fyne.Widget, error){
|
|
|
|
mapsAreValid, continentPercentagesMap, regionPercentagesMap, subregionPercentagesMap, err := companyAnalysis.AddMissingParentsToAncestryCompositionMaps_23andMe(inputContinentPercentagesMap, inputRegionPercentagesMap, inputSubregionPercentagesMap)
|
|
if (err != nil){ return nil, err }
|
|
if (mapsAreValid == false){
|
|
return nil, errors.New("get23andMeAncestryCompositionDisplay called with invalid ancestry location maps.")
|
|
}
|
|
|
|
// This map is used to create a fyne tree widget
|
|
// Each item maps to a list of child items
|
|
treeMap := make(map[string][]string)
|
|
|
|
continentsList := make([]string, 0, len(continentPercentagesMap))
|
|
|
|
// Map Structure: Continent Description -> Continent percentage
|
|
continentDescriptionPercentagesMap := make(map[string]float64)
|
|
|
|
for continentName, continentPercentage := range continentPercentagesMap{
|
|
|
|
continentPercentageString := helpers.ConvertFloat64ToStringRounded(continentPercentage, 1)
|
|
|
|
continentDescription := continentName + " - " + continentPercentageString + "%"
|
|
|
|
continentDescriptionPercentagesMap[continentDescription] = continentPercentage
|
|
|
|
continentsList = append(continentsList, continentDescription)
|
|
|
|
currentContinentRegionsList := make([]string, 0)
|
|
|
|
allContinentRegionsList, err := companyAnalysis.GetAncestryContinentRegionsList_23andMe(continentName)
|
|
if (err != nil) {
|
|
return nil, errors.New("continentPercentagesMap contains invalid continent.")
|
|
}
|
|
|
|
// Map Structure: Region Description -> Region percentage
|
|
regionDescriptionPercentagesMap := make(map[string]float64)
|
|
|
|
for regionName, regionPercentage := range regionPercentagesMap{
|
|
|
|
regionIsRelevant := slices.Contains(allContinentRegionsList, regionName)
|
|
if (regionIsRelevant == false){
|
|
continue
|
|
}
|
|
|
|
regionPercentageString := helpers.ConvertFloat64ToStringRounded(regionPercentage, 1)
|
|
|
|
regionDescription := regionName + " - " + regionPercentageString + "%"
|
|
|
|
regionDescriptionPercentagesMap[regionDescription] = regionPercentage
|
|
|
|
currentContinentRegionsList = append(currentContinentRegionsList, regionDescription)
|
|
|
|
allRegionSubregionsList, err := companyAnalysis.GetAncestryRegionSubregionsList_23andMe(continentName, regionName)
|
|
if (err != nil){ return nil, err }
|
|
|
|
currentRegionSubregionsList := make([]string, 0, len(subregionPercentagesMap))
|
|
|
|
// Map Structure: Subregion description -> Subregion percentage
|
|
subregionDescriptionPercentagesMap := make(map[string]float64)
|
|
|
|
for subregionName, subregionPercentage := range subregionPercentagesMap{
|
|
|
|
subregionIsRelevant := slices.Contains(allRegionSubregionsList, subregionName)
|
|
if (subregionIsRelevant == false){
|
|
continue
|
|
}
|
|
|
|
subregionPercentageString := helpers.ConvertFloat64ToStringRounded(subregionPercentage, 1)
|
|
|
|
subregionDescription := subregionName + " - " + subregionPercentageString + "%"
|
|
|
|
subregionDescriptionPercentagesMap[subregionDescription] = subregionPercentage
|
|
|
|
currentRegionSubregionsList = append(currentRegionSubregionsList, subregionDescription)
|
|
}
|
|
|
|
// We sort region subregions list
|
|
// We sort them from highest to lowest in percentage.
|
|
|
|
compareSubregionsFunction := func(subregion1Description string, subregion2Description string)int{
|
|
|
|
if (subregion1Description == subregion2Description){
|
|
panic("compareSubregionsFunction called with identical subregion descriptions.")
|
|
}
|
|
|
|
subregion1Percentage, exists := subregionDescriptionPercentagesMap[subregion1Description]
|
|
if (exists == false){
|
|
panic("subregionPercentagesMap missing subregion during sort.")
|
|
}
|
|
|
|
subregion2Percentage, exists := subregionDescriptionPercentagesMap[subregion2Description]
|
|
if (exists == false){
|
|
panic("subregionPercentagesMap missing subregion during sort.")
|
|
}
|
|
if (subregion1Percentage == subregion2Percentage){
|
|
// We sort subregions in unicode order
|
|
if (subregion1Description < subregion2Description){
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
if (subregion1Percentage > subregion2Percentage){
|
|
return -1
|
|
}
|
|
|
|
return 1
|
|
}
|
|
|
|
slices.SortFunc(currentRegionSubregionsList, compareSubregionsFunction)
|
|
|
|
treeMap[regionDescription] = currentRegionSubregionsList
|
|
}
|
|
|
|
// We sort continent regions list by highest to lowest percentage.
|
|
|
|
compareRegionsFunction := func(region1Description string, region2Description string)int{
|
|
|
|
if (region1Description == region2Description){
|
|
panic("compareRegionsFunction called with identical regions.")
|
|
}
|
|
|
|
region1Percentage, exists := regionDescriptionPercentagesMap[region1Description]
|
|
if (exists == false){
|
|
panic("regionPercentagesMap missing subregion during sort.")
|
|
}
|
|
|
|
region2Percentage, exists := regionDescriptionPercentagesMap[region2Description]
|
|
if (exists == false){
|
|
panic("regionPercentagesMap missing subregion during sort.")
|
|
}
|
|
if (region1Percentage == region2Percentage){
|
|
// We sort regions in unicode order
|
|
if (region1Description < region2Description){
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
|
|
if (region1Percentage > region2Percentage){
|
|
return -1
|
|
}
|
|
|
|
return 1
|
|
}
|
|
|
|
slices.SortFunc(currentContinentRegionsList, compareRegionsFunction)
|
|
|
|
treeMap[continentDescription] = currentContinentRegionsList
|
|
}
|
|
|
|
// We sort root list by highest to lowest proportions
|
|
|
|
compareContinentsFunction := func(continent1Description string, continent2Description string)int{
|
|
|
|
if (continent1Description == continent2Description){
|
|
panic("compareContinentsFunction called with identical continents.")
|
|
}
|
|
|
|
continent1Percentage, exists := continentDescriptionPercentagesMap[continent1Description]
|
|
if (exists == false){
|
|
panic("Continent percentage not found when sorting root list.")
|
|
}
|
|
|
|
continent2Percentage, exists := continentDescriptionPercentagesMap[continent2Description]
|
|
if (exists == false){
|
|
panic("Continent percentage not found when sorting root list.")
|
|
}
|
|
if (continent1Percentage == continent2Percentage){
|
|
// We sort continents in unicode order
|
|
if (continent1Description < continent2Description){
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
if (continent1Percentage > continent2Percentage){
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
|
|
slices.SortFunc(continentsList, compareContinentsFunction)
|
|
|
|
treeMap[""] = continentsList
|
|
|
|
resultTree := widget.NewTreeWithStrings(treeMap)
|
|
resultTree.OpenAllBranches()
|
|
|
|
return resultTree, nil
|
|
}
|
|
|
|
|
|
|
|
|