seekia/gui/manageGeneticsGui.go

1828 lines
66 KiB
Go
Raw Normal View History

package gui
// manageGeneticsGui.go implements the pages to manage genome people/couples, and to create genetic analyses
// viewAnalysisGui_Person.go and viewAnalysisGui_Couple.go implement the code to view genetic analyses
import "fyne.io/fyne/v2"
import "fyne.io/fyne/v2/widget"
import "fyne.io/fyne/v2/theme"
import "fyne.io/fyne/v2/container"
import "fyne.io/fyne/v2/layout"
import "fyne.io/fyne/v2/dialog"
import "fyne.io/fyne/v2/data/binding"
import "seekia/internal/appMemory"
import "seekia/internal/encoding"
import "seekia/internal/genetics/myGenomes"
import "seekia/internal/genetics/myPeople"
import "seekia/internal/genetics/myCouples"
import "seekia/internal/genetics/myAnalyses"
import "seekia/internal/genetics/readRawGenomes"
import "seekia/internal/genetics/sampleAnalyses"
import "seekia/internal/helpers"
import "seekia/internal/localFilesystem"
import "time"
import "strings"
import "errors"
func setGeneticsPage(window fyne.Window){
currentPage := func(){setGeneticsPage(window)}
title := getPageTitleCentered("Genetics")
description1 := getLabelCentered("Seekia provides tools to analyze genomes.")
description2 := getLabelCentered("You can import raw genome files from multiple sequencing companies.")
description3 := getLabelCentered("You can share your genome information on your profile.")
description4 := getLabelCentered("When an analysis is created, the genomes are never sent or shared anywhere.")
managePeopleIcon, err := getFyneImageIcon("Person")
if (err != nil){
setErrorEncounteredPage(window, err, func(){setHomePage(window)})
return
}
managePeopleButton := widget.NewButton("Manage People", func(){
setManageGenomePeoplePage(window, currentPage)
})
managePeopleButtonWithIcon := container.NewGridWithColumns(1, managePeopleIcon, managePeopleButton)
analyzeCoupleIcon, err := getFyneImageIcon("Couple")
if (err != nil){
setErrorEncounteredPage(window, err, func(){setHomePage(window)})
return
}
analyzeCoupleButton := widget.NewButton("Analyze Couple", func(){
setManageCouplesPage(window, currentPage)
})
analyzeCoupleButtonWithIcon := container.NewGridWithColumns(1, analyzeCoupleIcon, analyzeCoupleButton)
buttonsRow := container.NewHBox(layout.NewSpacer(), managePeopleButtonWithIcon, analyzeCoupleButtonWithIcon, layout.NewSpacer())
viewSampleAnalysesIcon, err := getFyneImageIcon("Questionnaire")
if (err != nil){
setErrorEncounteredPage(window, err, func(){setHomePage(window)})
return
}
viewSampleAnalysesButton := widget.NewButton("View Sample Analyses", func(){setViewSampleGeneticAnalysesPage(window, currentPage)})
viewSampleAnalysesButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, viewSampleAnalysesIcon, viewSampleAnalysesButton))
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), buttonsRow, widget.NewSeparator(), viewSampleAnalysesButtonWithIcon)
setPageContent(page, window)
}
func setViewSampleGeneticAnalysesPage(window fyne.Window, previousPage func()){
currentPage := func(){setViewSampleGeneticAnalysesPage(window, previousPage)}
title := getPageTitleCentered("Genetics - View Sample Analyses")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Seekia can create genetic analyses from imported genome files.")
description2 := getLabelCentered("You can view example analyses on the pages below.")
personIcon, err := getFyneImageIcon("Person")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
personAnalysisButton := widget.NewButton("Person Analysis", func(){
personIdentifier := "111111111111111111111111111111"
analysisObject, err := sampleAnalyses.GetSamplePerson1Analysis()
if (err != nil) {
setErrorEncounteredPage(window, err, currentPage)
return
}
setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisObject, 1, currentPage)
})
personAnalysisButtonWithIcon := container.NewGridWithColumns(1, personIcon, personAnalysisButton)
coupleIcon, err := getFyneImageIcon("Couple")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
coupleAnalysisButton := widget.NewButton("Couple Analysis", func(){
person1Identifier := "111111111111111111111111111111"
person2Identifier := "222222222222222222222222222222"
person1AnalysisObject, err := sampleAnalyses.GetSamplePerson1Analysis()
if (err != nil) {
setErrorEncounteredPage(window, err, currentPage)
return
}
person2AnalysisObject, err := sampleAnalyses.GetSamplePerson2Analysis()
if (err != nil) {
setErrorEncounteredPage(window, err, currentPage)
return
}
coupleAnalysisObject, err := sampleAnalyses.GetSampleCoupleAnalysis()
if (err != nil) {
setErrorEncounteredPage(window, err, currentPage)
return
}
setViewCoupleGeneticAnalysisPage(window, person1Identifier, person2Identifier, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, 1, 1, currentPage)
})
coupleAnalysisButtonWithIcon := container.NewGridWithColumns(1, coupleIcon, coupleAnalysisButton)
buttonsRow := container.NewHBox(layout.NewSpacer(), personAnalysisButtonWithIcon, coupleAnalysisButtonWithIcon, layout.NewSpacer())
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), buttonsRow)
setPageContent(page, window)
}
func setManageGenomePeoplePage(window fyne.Window, previousPage func()){
currentPage := func(){setManageGenomePeoplePage(window, previousPage)}
title := getPageTitleCentered("Genetics - Manage People")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("A Person is used to organize genome files.")
description2 := getLabelCentered("You can import multiple genome files for the same person.")
createPersonButton := getWidgetCentered(widget.NewButtonWithIcon("Create Person", theme.ContentAddIcon(), func(){
setCreateGenomePersonPage(window, currentPage, currentPage)
}))
getPeopleContainer := func()(*fyne.Container, error){
peopleMapList, err := myPeople.GetMyGenomePeopleMapList()
if (err != nil) { return nil, err }
if (len(peopleMapList) == 0){
noPeopleExistDescription := getBoldLabelCentered("No people found.")
return noPeopleExistDescription, nil
}
myPeopleLabel := getItalicLabelCentered("My People:")
manageButtonsGrid := container.NewGridWithColumns(1)
for _, personMap := range peopleMapList{
personNameString, exists := personMap["PersonName"]
if (exists == false) {
return nil, errors.New("Malformed GenomePeople map list: Item missing PersonName.")
}
personIdentifier, exists := personMap["PersonIdentifier"]
if (exists == false) {
return nil, errors.New("Malformed GenomePeople map list: Item missing PersonIdentifier.")
}
personNameTrimmed, _, err := helpers.TrimAndFlattenString(personNameString, 15)
if (err != nil) { return nil, err }
managePersonButton := widget.NewButton(personNameTrimmed, func(){
setManageGenomePersonPage(window, personIdentifier, currentPage)
})
manageButtonsGrid.Add(managePersonButton)
}
buttonsGridCentered := getContainerCentered(manageButtonsGrid)
peopleContainer := container.NewVBox(myPeopleLabel, buttonsGridCentered)
return peopleContainer, nil
}
peopleContainer, err := getPeopleContainer()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), createPersonButton, widget.NewSeparator(), peopleContainer)
setPageContent(page, window)
}
func setCreateGenomePersonPage(window fyne.Window, previousPage func(), nextPage func()){
currentPage := func(){setCreateGenomePersonPage(window, previousPage, nextPage)}
title := getPageTitleCentered(translate("Genetics - Create Person"))
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Create a Person to manage genome files.")
enterNameDescription := getBoldLabelCentered("Enter Person Name:")
enterNameEntry := widget.NewEntry()
enterNameEntry.SetPlaceHolder(translate("Enter Name..."))
enterNameEntryBoxed := getWidgetBoxed(enterNameEntry)
enterNameDescriptionWithEntry := getContainerCentered(container.NewGridWithColumns(1, enterNameDescription, enterNameEntryBoxed))
option1Translated := translate("Male")
option2Translated := translate("Female")
option3Translated := translate("Intersex")
untranslatedOptionsMap := map[string]string{
option1Translated: "Male",
option2Translated: "Female",
option3Translated: "Intersex",
}
sexSelectorOptions := []string{option1Translated, option2Translated, option3Translated}
selectSexLabel := getBoldLabelCentered(translate("Select Sex:"))
sexSelector := widget.NewSelect(sexSelectorOptions, nil)
sexSelector.SetSelectedIndex(0)
sexSelectorCentered := getWidgetCentered(sexSelector)
createPersonButton := getWidgetCentered(widget.NewButtonWithIcon("Create Person", theme.ConfirmIcon(), func(){
newPersonName := enterNameEntry.Text
if (newPersonName == ""){
dialogTitle := translate("Missing Person Name.")
dialogMessageA := getLabelCentered("You must enter a name for this person.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
if (len(newPersonName) > 40){
dialogTitle := translate("Name Is Too Long.")
dialogMessageA := getLabelCentered("You must enter a name that is less than 40 characters.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
personSexTranslated := sexSelector.Selected
personSex, exists := untranslatedOptionsMap[personSexTranslated]
if (exists == false) {
setErrorEncounteredPage(window, errors.New("untranslatedOptionsMap missing personSexTranslated: " + personSexTranslated), currentPage)
return
}
duplicateNameExists, err := myPeople.AddPerson(newPersonName, personSex)
if (err != nil) {
setErrorEncounteredPage(window, err, currentPage)
return
}
if (duplicateNameExists == true){
dialogTitle := translate("Invalid Person Name.")
dialogMessageA := getLabelCentered("A Person with this name already exists.")
dialogMessageB := getLabelCentered("Enter a different name for the Person.")
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
nextPage()
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, widget.NewSeparator(), enterNameDescriptionWithEntry, selectSexLabel, sexSelectorCentered, createPersonButton)
setPageContent(page, window)
}
// This is a page to manage a genome Person
func setManageGenomePersonPage(window fyne.Window, personIdentifier string, previousPage func()){
currentPage := func(){setManageGenomePersonPage(window, personIdentifier, previousPage)}
title := getPageTitleCentered("Genetics - Manage Person")
backButton := getBackButtonCentered(previousPage)
personFound, personName, _, personSex, err := myPeople.GetPersonInfo(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (personFound == false){
setErrorEncounteredPage(window, errors.New("setManageGenomePersonPage called with missing personIdentifier"), previousPage)
return
}
personNameLabel := getLabelCentered("Person Name:")
personNameText := getBoldLabelCentered(personName)
personSexLabel := getLabelCentered("Person Sex:")
personSexText := getBoldLabelCentered(personSex)
analyzeIcon, err := getFyneImageIcon("Stats")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
analyzeGeneticsButton := widget.NewButton("Analyze Genetics", func(){
setAnalyzePersonGeneticsPage(window, personIdentifier, currentPage)
})
analyzeGeneticsButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, analyzeIcon, analyzeGeneticsButton))
genomesIcon, err := getFyneImageIcon("Genome")
manageGenomesButton := widget.NewButton("Manage Genomes", func(){
setManagePersonGenomesPage(window, personIdentifier, currentPage)
})
manageGenomesButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, genomesIcon, manageGenomesButton))
renamePersonButton := widget.NewButtonWithIcon("Rename", theme.DocumentCreateIcon(), func(){
setRenameGenomePersonPage(window, personIdentifier, currentPage, currentPage)
})
changeSexButton := widget.NewButtonWithIcon("Change Sex", theme.DocumentCreateIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
deletePersonButton := widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
setDeleteGenomePersonPage(window, personIdentifier, currentPage, previousPage)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, renamePersonButton, changeSexButton, deletePersonButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), personNameLabel, personNameText, widget.NewSeparator(), personSexLabel, personSexText, widget.NewSeparator(), analyzeGeneticsButtonWithIcon, manageGenomesButtonWithIcon, widget.NewSeparator(), buttonsGrid)
setPageContent(page, window)
}
func setRenameGenomePersonPage(window fyne.Window, personIdentifier string, previousPage func(), nextPage func()){
currentPage := func(){setRenameGenomePersonPage(window, personIdentifier, previousPage, nextPage)}
title := getPageTitleCentered("Genetics - Rename Person")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Rename Person")
personFound, personName, _, personSex, err := myPeople.GetPersonInfo(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (personFound == false){
setErrorEncounteredPage(window, errors.New("setRenameGenomePersonPage called with person who does not exist."), previousPage)
return
}
currentNameLabel := widget.NewLabel("Current Name:")
currentNameText := getBoldLabel(personName)
currentNameRow := container.NewHBox(layout.NewSpacer(), currentNameLabel, currentNameText, layout.NewSpacer())
enterNameDescription := widget.NewLabel("Enter new name:")
newNameEntry := widget.NewEntry()
newNameEntry.SetPlaceHolder("Enter name...")
enterNameEntryWithDescription := getContainerCentered(container.NewGridWithColumns(1, enterNameDescription, newNameEntry))
renameButton := getWidgetCentered(widget.NewButtonWithIcon("Rename", theme.ConfirmIcon(), func(){
newName := newNameEntry.Text
if (newName == ""){
dialogTitle := translate("No Name Provided.")
dialogMessageA := getLabelCentered("You must enter a new name.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
if (len(newName) > 40){
dialogTitle := translate("Name Is Too Long.")
dialogMessageA := getLabelCentered("You must enter a name that is less than 40 characters.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
nameIsDuplicate, err := myPeople.EditPerson(personIdentifier, newName, personSex)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (nameIsDuplicate == true){
dialogTitle := translate("Name Already Exists.")
dialogMessageA := getLabelCentered("Another person already has this name.")
dialogMessageB := getLabelCentered("You must enter a unique name.")
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
nextPage()
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), currentNameRow, widget.NewSeparator(), enterNameEntryWithDescription, renameButton)
setPageContent(page, window)
}
func setDeleteGenomePersonPage(window fyne.Window, personIdentifier string, previousPage func(), nextPage func()){
currentPage := func(){setDeleteGenomePersonPage(window, personIdentifier, previousPage, nextPage)}
title := getPageTitleCentered("Genetics - Delete Person")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Delete Person")
personFound, personName, personCreatedTime, _, err := myPeople.GetPersonInfo(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (personFound == false){
setErrorEncounteredPage(window, errors.New("setDeleteGenomePersonPage called with unknown person."), previousPage)
return
}
description1 := getLabelCentered("Confirm to delete " + personName + "?")
createdTimeAgoText, err := helpers.ConvertUnixTimeToTimeAgoTranslated(personCreatedTime, false)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
description2 := getItalicLabelCentered("Person created " + createdTimeAgoText + ".")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2)
//Outputs:
// -bool: Any couples/genomes exist for this person
// -*fyne.Container: label describing the couples and genomes that will be deleted
// -error
getPersonGenomesAndCouplesLabel := func()(bool, *fyne.Container, error){
allPersonGenomesList, err := myGenomes.GetAllPersonGenomesMapList(personIdentifier)
if (err != nil) { return false, nil, err }
numberOfPersonGenomes := len(allPersonGenomesList)
numberOfPersonCouples, err := myCouples.GetNumberOfCouplesForPerson(personIdentifier)
if (err != nil){ return false, nil, err }
if (numberOfPersonGenomes == 0 && numberOfPersonCouples == 0){
return false, nil, nil
}
numberOfPersonGenomesString := helpers.ConvertIntToString(numberOfPersonGenomes)
numberOfPersonCouplesString := helpers.ConvertIntToString(numberOfPersonCouples)
getGenomeOrGenomesText := func()string{
if (numberOfPersonGenomes == 1){
result := translate("genome")
return result
}
result := translate("genomes")
return result
}
genomeOrGenomesText := getGenomeOrGenomesText()
getCoupleOrCouplesText := func()string{
if (numberOfPersonCouples == 1){
result := translate("couple")
return result
}
result := translate("couples")
return result
}
coupleOrCouplesText := getCoupleOrCouplesText()
if (numberOfPersonGenomes != 0 && numberOfPersonCouples != 0){
genomesAndCouplesText := numberOfPersonGenomesString + " " + genomeOrGenomesText + " & " + numberOfPersonCouplesString + " " + coupleOrCouplesText + "."
genomesAndCouplesLabel := getBoldLabelCentered(genomesAndCouplesText)
return true, genomesAndCouplesLabel, nil
}
if (numberOfPersonGenomes != 0){
genomesText := numberOfPersonGenomesString + " " + genomeOrGenomesText + "."
genomesLabel := getBoldLabelCentered(genomesText)
return true, genomesLabel, nil
}
couplesText := numberOfPersonCouplesString + " " + coupleOrCouplesText + "."
couplesLabel := getBoldLabelCentered(couplesText)
return true, couplesLabel, nil
}
anyGenomesOrCouplesExist, personGenomesAndCouplesLabel, err := getPersonGenomesAndCouplesLabel()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (anyGenomesOrCouplesExist == true){
deletionDescription := getLabelCentered("Deleting this person will delete:")
page.Add(widget.NewSeparator())
page.Add(deletionDescription)
page.Add(personGenomesAndCouplesLabel)
page.Add(widget.NewSeparator())
}
deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
err := myPeople.DeletePerson(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
nextPage()
}))
page.Add(deleteButton)
setPageContent(page, window)
}
// This function provides a page to manage a Person's genomes
func setManagePersonGenomesPage(window fyne.Window, personIdentifier string, previousPage func()){
currentPage := func(){setManagePersonGenomesPage(window, personIdentifier, previousPage)}
title := getPageTitleCentered("Genetics - Manage Person Genomes")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Manage the person's genome files below.")
importGenomeButton := getWidgetCentered(widget.NewButtonWithIcon("Import Genome", theme.ContentAddIcon(), func(){
setImportRawGenomePage(window, personIdentifier, currentPage, currentPage)
}))
getPersonGenomesContainer := func()(*fyne.Container, error){
allGenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(personIdentifier)
if (err != nil){ return nil, err }
if (len(allGenomesMapList) == 0){
noGenomesExistLabel := getBoldLabelCentered("No genomes exist.")
return noGenomesExistLabel, nil
}
indexColumn := container.NewVBox()
genomeNameColumn := container.NewVBox()
manageButtonColumn := container.NewVBox()
for index, genomeMap := range allGenomesMapList{
indexString := helpers.ConvertIntToString(index+1)
indexLabel := getBoldLabel(indexString + ".")
companyName, exists := genomeMap["CompanyName"]
if (exists == false){
return nil, errors.New("Malformed myGenomesMapList: Item missing CompanyName")
}
companyNameLabel := widget.NewLabel(companyName)
genomeIdentifierHex, exists := genomeMap["GenomeIdentifier"]
if (exists == false){
return nil, errors.New("Malformed myGenomesMapList: Item missing GenomeIdentifier")
}
genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex)
if (err != nil){
return nil, errors.New("Malformed myGenomesMapList: Item contains invalid GenomeIdentifier: " + genomeIdentifierHex)
}
manageButton := widget.NewButtonWithIcon("Manage", theme.VisibilityIcon(), func(){
setManageGenomePage(window, genomeIdentifier, currentPage)
})
indexColumn.Add(indexLabel)
genomeNameColumn.Add(companyNameLabel)
manageButtonColumn.Add(manageButton)
if (index != len(allGenomesMapList)-1){
indexColumn.Add(widget.NewSeparator())
genomeNameColumn.Add(widget.NewSeparator())
manageButtonColumn.Add(widget.NewSeparator())
}
}
genomesContainer := container.NewHBox(layout.NewSpacer(), indexColumn, genomeNameColumn, manageButtonColumn, layout.NewSpacer())
return genomesContainer, nil
}
personGenomesContainer, err := getPersonGenomesContainer()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, importGenomeButton, widget.NewSeparator(), personGenomesContainer)
setPageContent(page, window)
}
func setImportRawGenomePage(window fyne.Window, personIdentifier string, previousPage func(), nextPage func()){
currentPage := func(){setImportRawGenomePage(window, personIdentifier, previousPage, nextPage)}
title := getPageTitleCentered("Import Raw Genome")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Import a raw genome file below.")
description2 := getLabelCentered("You must export your raw data from a sequencing company.")
description3 := getLabelCentered("Supported Companies: 23andMe, AncestryDNA")
selectFileButton := getWidgetCentered(widget.NewButtonWithIcon("Select File", theme.FileIcon(), func(){
openFileCallbackFunction := func(file fyne.URIReadCloser, err error){
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (file == nil){
return
}
uriFilePath := file.URI().String()
filePath := strings.TrimPrefix(uriFilePath, "file://")
setLoadingScreen(window, "Importing Genome", "Importing genome file...")
fileExists, fileBytes, err := localFilesystem.GetFileContents(filePath)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (fileExists == false){
setErrorEncounteredPage(window, errors.New("Unable to read chosen file: File not found."), currentPage)
return
}
fileString := string(fileBytes)
fileIsValid, fileAlreadyExists, err := myGenomes.AddRawGenome(personIdentifier, fileString)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (fileIsValid == false){
currentPage()
dialogTitle := translate("Unable To Read File.")
dialogMessageA := getLabelCentered("Seekia was unable to read the selected genome file.")
dialogMessageB := getLabelCentered("Only 23andMe and AncestryDNA files are supported.")
dialogMessageC := getLabelCentered("More companies will be supported in the future.")
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
if (fileAlreadyExists == true){
currentPage()
dialogTitle := translate("Genome Already Exists.")
dialogMessageA := getLabelCentered("The genome file you selected is already imported.")
dialogMessageB := getLabelCentered("You can only import the same file once for each Person.")
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
nextPage()
}
dialog.ShowFileOpen(openFileCallbackFunction, window)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, selectFileButton)
setPageContent(page, window)
}
// This provides a page to manage a person's Genome
func setManageGenomePage(window fyne.Window, genomeIdentifier [16]byte, previousPage func()){
currentPage := func(){setManageGenomePage(window, genomeIdentifier, previousPage)}
setLoadingScreen(window, "Loading Genome", "Loading genome...")
title := getPageTitleCentered("Genetics - Manage Genome")
backButton := getBackButtonCentered(previousPage)
genomeFound, personIdentifier, exportTime, importTime, isPhased, snpCount, companyName, importVersion, fileHash, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (genomeFound == false){
setErrorEncounteredPage(window, errors.New("setManageGenomePage called with missing genomeIdentifier"), previousPage)
return
}
currentImportVersion, err := readRawGenomes.GetCurrentCompanyImportVersion(companyName)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (importVersion != currentImportVersion){
// We will refresh the metadata for the genome
err := myGenomes.RefreshRawGenomeMetadata(genomeIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
currentPage()
return
}
isPhasedString := helpers.ConvertBoolToYesOrNoString(isPhased)
snpCountString := helpers.ConvertInt64ToString(snpCount)
companyLabel := widget.NewLabel("Company:")
companyNameLabel := getBoldLabel(companyName)
companyNameRow := container.NewHBox(layout.NewSpacer(), companyLabel, companyNameLabel, layout.NewSpacer())
snpCountLabel := widget.NewLabel("SNP Count:")
snpCountText := getBoldLabel(snpCountString)
snpCountHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setGenomeSNPCountExplainerPage(window, currentPage)
})
snpCountRow := container.NewHBox(layout.NewSpacer(), snpCountLabel, snpCountText, snpCountHelpButton, layout.NewSpacer())
isPhasedLabel := widget.NewLabel("Is Phased:")
isPhasedText := getBoldLabel(isPhasedString)
isPhasedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setGenomePhasingExplainerPage(window, currentPage)
})
isPhasedRow := container.NewHBox(layout.NewSpacer(), isPhasedLabel, isPhasedText, isPhasedHelpButton, layout.NewSpacer())
fileHashTrimmed, _, err := helpers.TrimAndFlattenString(fileHash, 6)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
fileHashLabel := widget.NewLabel("File Hash: ")
fileHashText := getBoldLabel(fileHashTrimmed)
fileHashRow := container.NewHBox(layout.NewSpacer(), fileHashLabel, fileHashText, layout.NewSpacer())
exportTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(exportTime, false)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
exportedTimeLabel := getItalicLabelCentered("Exported from " + companyName + " " + exportTimeAgo + ".")
importedTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(importTime, false)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
importedTimeLabel := getItalicLabelCentered("Imported " + importedTimeAgo + ".")
deleteGenomeButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Genome", theme.DeleteIcon(), func(){
setConfirmDeletePersonGenomePage(window, personIdentifier, genomeIdentifier, currentPage, previousPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), companyNameRow, snpCountRow, isPhasedRow, fileHashRow, widget.NewSeparator(), exportedTimeLabel, importedTimeLabel, widget.NewSeparator(), deleteGenomeButton)
setPageContent(page, window)
}
func setConfirmDeletePersonGenomePage(window fyne.Window, personIdentifier string, genomeIdentifier [16]byte, previousPage func(), nextPage func()){
currentPage := func(){setConfirmDeletePersonGenomePage(window, personIdentifier, genomeIdentifier, previousPage, nextPage)}
title := getPageTitleCentered("Confirm Delete Genome")
backButton := getBackButtonCentered(previousPage)
description1 := getBoldLabelCentered("Delete Genome?")
description2 := getLabelCentered("This will delete this person's raw genome.")
description3 := getLabelCentered("You must run a new genetic analysis afterwards.")
genomeFound, genomePersonIdentifier, timeGenomeWasExported, timeGenomeWasImported, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (genomeFound == false){
setErrorEncounteredPage(window, errors.New("setDeletePersonGenomePage called with missing genome."), previousPage)
return
}
if (genomePersonIdentifier != personIdentifier){
setErrorEncounteredPage(window, errors.New("Cannot delete genome: Genome person identifier does not match."), previousPage)
return
}
exportTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeGenomeWasExported, false)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
exportedTimeLabel := getItalicLabelCentered("Exported from " + companyName + " " + exportTimeAgo + ".")
importedTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeGenomeWasImported, false)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
importedTimeLabel := getItalicLabelCentered("Imported " + importedTimeAgo + ".")
deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Genome", theme.DeleteIcon(), func(){
err := myGenomes.DeleteMyRawGenome(genomeIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
nextPage()
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), exportedTimeLabel, importedTimeLabel, widget.NewSeparator(), deleteButton)
setPageContent(page, window)
}
func setAnalyzePersonGeneticsPage(window fyne.Window, personIdentifier string, previousPage func()){
appMemory.SetMemoryEntry("CurrentViewedPage", "AnalyzePersonGeneticsPage")
currentPage := func(){setAnalyzePersonGeneticsPage(window, personIdentifier, previousPage)}
title := getPageTitleCentered(translate("Genetics - Analyze Person Genetics"))
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered(translate("Analyze this person's genetics."))
personFound, personName, _, _, err := myPeople.GetPersonInfo(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (personFound == false){
setErrorEncounteredPage(window, errors.New("setAnalyzePersonGeneticsPage called with missing person"), previousPage)
return
}
allPersonRawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (len(allPersonRawGenomeIdentifiersList) == 0){
noGenomesExistLabel := getBoldLabelCentered("No genomes found.")
description2 := getLabelCentered("You must import a genome to perform a genetic analysis.")
description3 := getLabelCentered("Go to the previous page and select Manage Genomes.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), noGenomesExistLabel, description2, description3)
setPageContent(page, window)
return
}
anyAnalysisFound, newestAnalysisIdentifier, timeOfNewestAnalysis, newestAnalysisListOfGenomesAnalyzed, newerAnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (anyAnalysisFound == false){
// We will skip to the ConfirmPerformAnalysis page
setConfirmPerformPersonAnalysisPage(window, personIdentifier, previousPage, currentPage)
return
}
if (newerAnalysisVersionAvailable == true){
description1 := getLabelCentered("A new analysis method is available!")
description2 := getLabelCentered("You must perform a new analysis to see your new results.")
performNewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform New Analysis", theme.NavigateNextIcon(), func(){
setConfirmPerformPersonAnalysisPage(window, personIdentifier, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), description1, description2, performNewAnalysisButton)
setPageContent(page, window)
return
}
// This will return true if a newer analysis can be performed using newly imported genomes
getAnalysisIsMissingGenomesBool := func()(bool, error){
genomesAreIdentical := helpers.CheckIfTwoListsContainIdenticalItems(allPersonRawGenomeIdentifiersList, newestAnalysisListOfGenomesAnalyzed)
if (genomesAreIdentical == false){
return true, nil
}
return false, nil
}
analysisIsMissingGenomes, err := getAnalysisIsMissingGenomesBool()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (analysisIsMissingGenomes == true){
description1 := getLabelCentered("You have imported/deleted a genome.")
description2 := getLabelCentered("You must perform a new analysis.")
performAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform Analysis", theme.NavigateNextIcon(), func(){
setConfirmPerformPersonAnalysisPage(window, personIdentifier, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), description1, description2, performAnalysisButton)
setPageContent(page, window)
return
}
analysisReadyLabel := getBoldLabelCentered("Genetic Analysis Complete!")
personAnalyzedLabel := widget.NewLabel("Person Analyzed:")
personNameLabel := getBoldLabel(personName)
personAnalyzedRow := container.NewHBox(layout.NewSpacer(), personAnalyzedLabel, personNameLabel, layout.NewSpacer())
numberOfGenomesInAnalysis := len(newestAnalysisListOfGenomesAnalyzed)
numberOfGenomesInAnalysisString := helpers.ConvertIntToString(numberOfGenomesInAnalysis)
numberOfGenomesInAnalysisLabel := widget.NewLabel("Number of genomes analyzed:")
numberOfGenomesInAnalysisText := getBoldLabel(numberOfGenomesInAnalysisString)
numberOfGenomesInAnalysisRow := container.NewHBox(layout.NewSpacer(), numberOfGenomesInAnalysisLabel, numberOfGenomesInAnalysisText, layout.NewSpacer())
timeAgoPerformed, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeOfNewestAnalysis, false)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
timeAgoPerformedLabel := getItalicLabelCentered("Analysis performed " + timeAgoPerformed + ".")
viewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("View Analysis", theme.VisibilityIcon(), func(){
analysisFound, analysisObject, err := myAnalyses.GetPersonGeneticAnalysis(newestAnalysisIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (analysisFound == false){
setErrorEncounteredPage(window, errors.New("Person analysis not found after being found already."), currentPage)
return
}
setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisObject, numberOfGenomesInAnalysis, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), analysisReadyLabel, personAnalyzedRow, numberOfGenomesInAnalysisRow, timeAgoPerformedLabel, viewAnalysisButton)
setPageContent(page, window)
}
func setConfirmPerformPersonAnalysisPage(window fyne.Window, personIdentifier string, previousPage func(), pageToVisitAfter func()){
appMemory.SetMemoryEntry("CurrentViewedPage", "ConfirmPerformGeneticAnalysisPage")
currentPage := func(){setConfirmPerformPersonAnalysisPage(window, personIdentifier, previousPage, pageToVisitAfter)}
title := getPageTitleCentered("Genetics - Perform Analysis")
backButton := getBackButtonCentered(previousPage)
allPersonGenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfPersonGenomes := len(allPersonGenomesMapList)
if (numberOfPersonGenomes == 0){
setErrorEncounteredPage(window, errors.New("setConfirmPerformPersonAnalysisPage called with person who has no genomes."), previousPage)
return
}
// Now we check if there is an analysis running
anyProcessFound, processIdentifier, err := myAnalyses.GetPersonGeneticAnalysisProcessIdentifier(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (anyProcessFound == true){
processFound, processIsComplete, _, _, _, err := myAnalyses.GetAnalysisProcessInfo(processIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (processFound == false){
setErrorEncounteredPage(window, errors.New("Genetic analysis process not found after being found already."), previousPage)
return
}
if (processIsComplete == false){
// An analysis is already running.
description1 := getLabelCentered("A genetic analysis is already being generated.")
description2 := getLabelCentered("View the status of the analysis?")
viewStatusButton := getWidgetCentered(widget.NewButtonWithIcon("View Status", theme.VisibilityIcon(), func(){
setMonitorGeneticAnalysisGenerationPage(window, processIdentifier, currentPage, pageToVisitAfter)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, viewStatusButton)
setPageContent(page, window)
return
}
// Existing analysis probably encountered error.
// We will just show a retry option.
}
description1 := getBoldLabelCentered("Perform genetic analysis?")
description2 := getLabelCentered("This will create an analysis of all of this person's genomes.")
description3 := getLabelCentered("It is performed offline and is not shared without your approval.")
startAnalysisFunction := func(){
newProcessIdentifier, err := myAnalyses.StartCreateNewPersonGeneticAnalysis(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
setMonitorGeneticAnalysisGenerationPage(window, newProcessIdentifier, currentPage, pageToVisitAfter)
}
startAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Start Analysis", theme.ConfirmIcon(), startAnalysisFunction))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, startAnalysisButton)
setPageContent(page, window)
}
// This function provides a page to monitor an active analysis generation
func setMonitorGeneticAnalysisGenerationPage(window fyne.Window, processIdentifier string, previousPage func(), pageToVisitAfter func()){
pageIdentifier, err := helpers.GetNewRandomHexString(16)
if (err != nil) {
setErrorEncounteredPage(window, err, pageToVisitAfter)
return
}
appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier)
checkIfPageHasChangedFunction := func()bool{
exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage")
if (exists == true && currentViewedPage == pageIdentifier){
return false
}
return true
}
title := getPageTitleCentered("Monitor Genetic Analysis Generation")
backButton := getBackButtonCentered(previousPage)
processDetailsABinding := binding.NewString()
processDetailsBBinding := binding.NewString()
progressPercentageBinding := binding.NewFloat()
processDetailsABinding.Set("Genetic analysis is being generated...")
processDetailsBBinding.Set("You can leave this page.")
processDetailsALabel := widget.NewLabelWithData(processDetailsABinding)
processDetailsALabel.TextStyle = getFyneTextStyle_Bold()
processDetailsBLabel := widget.NewLabelWithData(processDetailsBBinding)
processDetailsALabelCentered := getWidgetCentered(processDetailsALabel)
processDetailsBLabelCentered := getWidgetCentered(processDetailsBLabel)
loadingBar := getWidgetCentered(widget.NewProgressBarWithData(progressPercentageBinding))
updateBindingsFunction := func(){
for{
pageHasChanged := checkIfPageHasChangedFunction()
if (pageHasChanged == true){
return
}
processFound, processIsComplete, processEncounteredError, errorEncounteredByProcess, processPercentageComplete, err := myAnalyses.GetAnalysisProcessInfo(processIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (processFound == false){
setErrorEncounteredPage(window, errors.New("setMonitorGeneticAnalysisGenerationPage called with missing process"), previousPage)
return
}
if (processIsComplete == false){
percentageCompleteFloat := float64(processPercentageComplete)/100
progressPercentageBinding.Set(percentageCompleteFloat)
time.Sleep(time.Millisecond * 50)
continue
}
// Process is complete
progressPercentageBinding.Set(1)
if (processEncounteredError == true){
processDetailsABinding.Set("Process Encountered Error!")
processDetailsBBinding.Set("ERROR: " + errorEncounteredByProcess.Error())
return
}
pageToVisitAfter()
return
}
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), processDetailsALabelCentered, processDetailsBLabelCentered, loadingBar)
setPageContent(page, window)
go updateBindingsFunction()
}
func setManageCouplesPage(window fyne.Window, previousPage func()){
currentPage := func(){setManageCouplesPage(window, previousPage)}
title := getPageTitleCentered("Genetics - Manage Couples")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Perform a genetic analysis of a couple.")
createACoupleButton := getWidgetCentered(widget.NewButtonWithIcon("Create Couple", theme.ContentAddIcon(), func(){
setCreateCouplePage(window, currentPage)
}))
getManageMyCouplesContainer := func()(*fyne.Container, error){
myCouplesMapList, err := myCouples.GetMyGenomeCouplesMapList()
if (err != nil){ return nil, err }
if (len(myCouplesMapList) == 0){
noCouplesExistLabel := getBoldLabelCentered("No couples exist.")
return noCouplesExistLabel, nil
}
myCouplesText := getItalicLabelCentered("My Couples:")
manageButtonsGrid := container.NewGridWithColumns(1)
for _, coupleMap := range myCouplesMapList{
person1Identifier, exists := coupleMap["Person1Identifier"]
if (exists == false) {
return nil, errors.New("Malformed myCoupleAnalysesMapList: Item missing Person1Identifier")
}
person2Identifier, exists := coupleMap["Person2Identifier"]
if (exists == false) {
return nil, errors.New("Malformed myCoupleAnalysesMapList: Item missing Person2Identifier")
}
personFound, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier)
if (err != nil) { return nil, err }
if (personFound == false){
return nil, errors.New("Couple person not found.")
}
personFound, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier)
if (err != nil) { return nil, err }
if (personFound == false){
return nil, errors.New("Couple person not found.")
}
person1NameTrimmed, _, err := helpers.TrimAndFlattenString(person1Name, 15)
if (err != nil) { return nil, err }
person2NameTrimmed, _, err := helpers.TrimAndFlattenString(person2Name, 15)
if (err != nil) { return nil, err }
coupleName := person1NameTrimmed + " + " + person2NameTrimmed
manageCoupleButton := widget.NewButton(coupleName, func(){
setManageCouplePage(window, person1Identifier, person2Identifier, currentPage)
})
manageButtonsGrid.Add(manageCoupleButton)
}
manageButtonsGridCentered := getContainerCentered(manageButtonsGrid)
manageMyCouplesContainer := container.NewVBox(myCouplesText, manageButtonsGridCentered)
return manageMyCouplesContainer, nil
}
manageMyCouplesContainer, err := getManageMyCouplesContainer()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, widget.NewSeparator(), createACoupleButton, widget.NewSeparator(), manageMyCouplesContainer)
setPageContent(page, window)
}
func setCreateCouplePage(window fyne.Window, previousPage func()){
currentPage := func(){setCreateCouplePage(window, previousPage)}
title := getPageTitleCentered("Genetics - Create Couple")
backButton := getBackButtonCentered(previousPage)
myGenomePeopleMapList, err := myPeople.GetMyGenomePeopleMapList()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (len(myGenomePeopleMapList) < 2){
description1 := getLabelCentered("You have created fewer than 2 people.")
description2 := getLabelCentered("You must create at least 2 people to create a couple.")
description3 := getLabelCentered("Create a new person on the Genetics - Manage People page.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3)
setPageContent(page, window)
return
}
description1 := getLabelCentered("Choose 2 people to pair below.")
description2 := getLabelCentered("You can create new people on the Manage People page.")
selectedPeopleListBinding := binding.NewStringList()
getChoosePeopleGrid := func()(*fyne.Container, error){
getNumberOfGridColumns := func()int{
if (len(myGenomePeopleMapList) == 2){
return 2
}
return 3
}
numberOfGridColumns := getNumberOfGridColumns()
choosePeopleGrid := container.NewGridWithColumns(numberOfGridColumns)
for _, personMap := range myGenomePeopleMapList{
personIdentifier, exists := personMap["PersonIdentifier"]
if (exists == false){
return nil, errors.New("myGenomePeopleMapList contains item missing PersonIdentifier")
}
personName, exists := personMap["PersonName"]
if (exists == false){
return nil, errors.New("myGenomePeopleMapList contains item missing PersonName")
}
personNameTrimmed, _, err := helpers.TrimAndFlattenString(personName, 15)
if (err != nil) { return nil, err }
personCheck := widget.NewCheck(personNameTrimmed, func(response bool){
if (response == true){
err := selectedPeopleListBinding.Append(personIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
}
return
}
existingList, err := selectedPeopleListBinding.Get()
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
2024-08-11 14:31:40 +02:00
newList, deletedAny := helpers.DeleteAllMatchingItemsFromList(existingList, personIdentifier)
if (deletedAny == false){
setErrorEncounteredPage(window, errors.New("Person not found when trying to delete person from chosen people list."), currentPage)
return
}
err = selectedPeopleListBinding.Set(newList)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
}
})
personCheckBoxed := getWidgetBoxed(personCheck)
choosePeopleGrid.Add(personCheckBoxed)
}
choosePeopleGridCentered := getContainerCentered(choosePeopleGrid)
return choosePeopleGridCentered, nil
}
choosePeopleGrid, err := getChoosePeopleGrid()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
createCoupleButton := getWidgetCentered(widget.NewButtonWithIcon("Create Couple", theme.ConfirmIcon(), func(){
selectedPeopleList, err := selectedPeopleListBinding.Get()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (len(selectedPeopleList) < 2){
dialogTitle := translate("Not Enough People Selected.")
dialogMessageA := getLabelCentered("You must select two people to create a couple.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
if (len(selectedPeopleList) > 2){
dialogTitle := translate("Too Many People Selected.")
dialogMessageA := getLabelCentered("You must select two people to create a couple.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
person1Identifier := selectedPeopleList[0]
person2Identifier := selectedPeopleList[1]
_, err = myCouples.AddCouple(person1Identifier, person2Identifier)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
setManageCouplePage(window, person1Identifier, person2Identifier, previousPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), choosePeopleGrid, createCoupleButton)
setPageContent(page, window)
}
func setManageCouplePage(window fyne.Window, person1Identifier string, person2Identifier string, previousPage func()){
currentPage := func(){setManageCouplePage(window, person1Identifier, person2Identifier, previousPage)}
title := getPageTitleCentered("Genetics - Manage Couple")
backButton := getBackButtonCentered(previousPage)
person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (person1Found == false){
setErrorEncounteredPage(window, errors.New("setManageCouplePage called with unknown person1Identifier"), previousPage)
return
}
person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (person2Found == false){
setErrorEncounteredPage(window, errors.New("setManageCouplePage called with unknown person2Identifier"), previousPage)
return
}
coupleNameLabel := getLabelCentered("Couple Name:")
coupleNameText := getBoldLabelCentered(person1Name + " + " + person2Name)
analyzeIcon, err := getFyneImageIcon("Stats")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
analyzeGeneticsButton := widget.NewButton("Analyze Genetics", func(){
setAnalyzeCoupleGeneticsPage(window, person1Identifier, person2Identifier, currentPage)
})
analyzeGeneticsButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, analyzeIcon, analyzeGeneticsButton))
deleteCoupleButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Couple", theme.DeleteIcon(), func(){
setDeleteCouplePage(window, person1Identifier, person2Identifier, currentPage, previousPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), coupleNameLabel, coupleNameText, widget.NewSeparator(), analyzeGeneticsButtonWithIcon, widget.NewSeparator(), deleteCoupleButton)
setPageContent(page, window)
}
func setDeleteCouplePage(window fyne.Window, person1Identifier string, person2Identifier string, previousPage func(), nextPage func()){
currentPage := func(){setDeleteCouplePage(window, person1Identifier, person2Identifier, previousPage, nextPage)}
title := getPageTitleCentered("Genetics - Delete Couple")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Delete Couple")
person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (person1Found == false){
setErrorEncounteredPage(window, errors.New("setDeleteGenomeCouplePage called with unknown person1Identifier"), previousPage)
return
}
person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (person2Found == false){
setErrorEncounteredPage(window, errors.New("setDeleteGenomeCouplePage called with unknown person2Identifier"), previousPage)
return
}
description := getLabelCentered("Confirm to delete this couple?")
coupleName := person1Name + " + " + person2Name
coupleNameLabel := widget.NewLabel("Couple Name:")
coupleNameText := getBoldLabel(coupleName)
coupleNameRow := container.NewHBox(layout.NewSpacer(), coupleNameLabel, coupleNameText, layout.NewSpacer())
deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
err := myCouples.DeleteCouple(person1Identifier, person2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
nextPage()
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, coupleNameRow, deleteButton)
setPageContent(page, window)
}
func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPerson1Identifier string, inputPerson2Identifier string, previousPage func()){
appMemory.SetMemoryEntry("CurrentViewedPage", "AnalyzeCoupleGeneticsPage")
currentPage := func(){setAnalyzeCoupleGeneticsPage(window, inputPerson1Identifier, inputPerson2Identifier, previousPage)}
// We have to sort the person identifiers so they are in the same order as they exist in the couple analysis
person1Identifier, person2Identifier, err := myAnalyses.GetPeopleIdentifiersSortedForCouple(inputPerson1Identifier, inputPerson2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
title := getPageTitleCentered("Genetics - Analyze Couple Genetics")
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Analyze this couple's genetics.")
person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (person1Found == false){
setErrorEncounteredPage(window, errors.New("setAnalyzeCoupleGeneticsPage called with missing person"), previousPage)
return
}
person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (person2Found == false){
setErrorEncounteredPage(window, errors.New("setAnalyzeCoupleGeneticsPage called with missing person"), previousPage)
return
}
allPerson1RawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(person1Identifier)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
allPerson2RawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(person2Identifier)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (len(allPerson1RawGenomeIdentifiersList) == 0 || len(allPerson2RawGenomeIdentifiersList) == 0){
// At least one of the people is missing genomes
getGenomesMissingText := func()string{
if (len(allPerson1RawGenomeIdentifiersList) == 0 && len(allPerson2RawGenomeIdentifiersList) == 0){
missingLabelText := person1Name + " and " + person2Name + " have no genomes."
return missingLabelText
}
if (len(allPerson1RawGenomeIdentifiersList) == 0 && len(allPerson2RawGenomeIdentifiersList) != 0){
missingLabelText := person1Name + " has no genomes."
return missingLabelText
}
// len(allPerson1RawGenomeIdentifiersList) != 0 && len(allPerson2RawGenomeIdentifiersList) == 0){
missingLabelText := person2Name + " has no genomes."
return missingLabelText
}
genomesMissingText := getGenomesMissingText()
noGenomesExistLabel := getBoldLabelCentered(genomesMissingText)
description2 := getLabelCentered("Each person must have at least 1 genome to perform a couple analysis.")
description3 := getLabelCentered("Go to the Manage Person page to import a genome for a person.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), noGenomesExistLabel, description2, description3)
setPageContent(page, window)
return
}
anyPerson1AnalysisFound, newestPerson1AnalysisIdentifier, _, newestPerson1AnalysisListOfGenomesAnalyzed, newerPerson1AnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(person1Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
anyPerson2AnalysisFound, newestPerson2AnalysisIdentifier, _, newestPerson2AnalysisListOfGenomesAnalyzed, newerPerson2AnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(person2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
anyCoupleAnalysisFound, newestCoupleAnalysisIdentifier, timeOfNewestAnalysis, newestCoupleAnalysisListOfGenomesAnalyzed_Person1, newestCoupleAnalysisListOfGenomesAnalyzed_Person2, newerCoupleAnalysisVersionAvailable, err := myAnalyses.GetCoupleNewestGeneticAnalysisInfo(person1Identifier, person2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (anyCoupleAnalysisFound == false || anyPerson1AnalysisFound == false || anyPerson2AnalysisFound == false){
// We will skip to the ConfirmPerformAnalysis page
setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, previousPage, currentPage)
return
}
if (newerPerson1AnalysisVersionAvailable == true || newerPerson2AnalysisVersionAvailable == true || newerCoupleAnalysisVersionAvailable == true){
description1 := getLabelCentered("A new analysis method is available!")
description2 := getLabelCentered("You must perform a new analysis to see the couple's new results.")
performNewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform New Analysis", theme.NavigateNextIcon(), func(){
setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), description1, description2, performNewAnalysisButton)
setPageContent(page, window)
return
}
// This will return true if a newer analysis can be performed using newly imported genomes
getAnalysisIsMissingGenomesBool := func()(bool, error){
genomesAreIdentical := helpers.CheckIfTwoListsContainIdenticalItems(allPerson1RawGenomeIdentifiersList, newestPerson1AnalysisListOfGenomesAnalyzed)
if (genomesAreIdentical == false){
return true, nil
}
genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPerson2RawGenomeIdentifiersList, newestPerson2AnalysisListOfGenomesAnalyzed)
if (genomesAreIdentical == false){
return true, nil
}
genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPerson1RawGenomeIdentifiersList, newestCoupleAnalysisListOfGenomesAnalyzed_Person1)
if (genomesAreIdentical == false){
return true, nil
}
genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPerson2RawGenomeIdentifiersList, newestCoupleAnalysisListOfGenomesAnalyzed_Person2)
if (genomesAreIdentical == false){
return true, nil
}
return false, nil
}
analysisIsMissingGenomes, err := getAnalysisIsMissingGenomesBool()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (analysisIsMissingGenomes == true){
description1 := getLabelCentered("At least 1 person in the couple has imported/deleted a genome.")
description2 := getLabelCentered("A new analysis must be performed.")
performAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform Analysis", theme.NavigateNextIcon(), func(){
setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), description1, description2, performAnalysisButton)
setPageContent(page, window)
return
}
analysisReadyLabel := getBoldLabelCentered("Genetic Analysis Complete!")
coupleAnalyzedLabel := widget.NewLabel("Couple Analyzed:")
coupleName := person1Name + " + " + person2Name
coupleNameLabel := getBoldLabel(coupleName)
personAnalyzedRow := container.NewHBox(layout.NewSpacer(), coupleAnalyzedLabel, coupleNameLabel, layout.NewSpacer())
numberOfPerson1GenomesInAnalysis := len(newestPerson1AnalysisListOfGenomesAnalyzed)
numberOfPerson2GenomesInAnalysis := len(newestPerson2AnalysisListOfGenomesAnalyzed)
timeAgoPerformed, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeOfNewestAnalysis, false)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
timeAgoPerformedLabel := getItalicLabelCentered("Analysis performed " + timeAgoPerformed + ".")
viewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("View Analysis", theme.VisibilityIcon(), func(){
person1AnalysisFound, person1AnalysisObject, err := myAnalyses.GetPersonGeneticAnalysis(newestPerson1AnalysisIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (person1AnalysisFound == false){
setErrorEncounteredPage(window, errors.New("Person analysis not found after being found already."), currentPage)
return
}
person2AnalysisFound, person2AnalysisObject, err := myAnalyses.GetPersonGeneticAnalysis(newestPerson2AnalysisIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (person2AnalysisFound == false){
setErrorEncounteredPage(window, errors.New("Person analysis not found after being found already."), currentPage)
return
}
coupleAnalysisFound, coupleAnalysisObject, err := myAnalyses.GetCoupleGeneticAnalysis(newestCoupleAnalysisIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (coupleAnalysisFound == false){
setErrorEncounteredPage(window, errors.New("Couple analysis not found after being found already."), currentPage)
return
}
setViewCoupleGeneticAnalysisPage(window, person1Identifier, person2Identifier, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, numberOfPerson1GenomesInAnalysis, numberOfPerson2GenomesInAnalysis, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), analysisReadyLabel, personAnalyzedRow, timeAgoPerformedLabel, viewAnalysisButton)
setPageContent(page, window)
}
func setConfirmPerformCoupleAnalysisPage(window fyne.Window, person1Identifier string, person2Identifier string, previousPage func(), pageToVisitAfter func()){
appMemory.SetMemoryEntry("CurrentViewedPage", "ConfirmPerformCoupleGeneticAnalysisPage")
currentPage := func(){setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, previousPage, pageToVisitAfter)}
title := getPageTitleCentered("Genetics - Perform Couple Analysis")
backButton := getBackButtonCentered(previousPage)
allPerson1GenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(person1Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfPerson1Genomes := len(allPerson1GenomesMapList)
if (numberOfPerson1Genomes == 0){
setErrorEncounteredPage(window, errors.New("setConfirmPerformCoupleAnalysisPage called with person who has no genomes."), previousPage)
return
}
allPerson2GenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(person2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfPerson2Genomes := len(allPerson2GenomesMapList)
if (numberOfPerson2Genomes == 0){
setErrorEncounteredPage(window, errors.New("setConfirmPerformCoupleAnalysisPage called with person who has no genomes."), previousPage)
return
}
// Now we check if there is an analysis running
anyProcessFound, processIdentifier, err := myAnalyses.GetCoupleGeneticAnalysisProcessIdentifier(person1Identifier, person2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (anyProcessFound == true){
processFound, processIsComplete, _, _, _, err := myAnalyses.GetAnalysisProcessInfo(processIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (processFound == false){
setErrorEncounteredPage(window, errors.New("Couple genetic analysis process not found after being found already."), previousPage)
return
}
if (processIsComplete == false){
// An analysis is runnning. We will display that on the page
description1 := getLabelCentered("A genetic analysis is already being generated.")
description2 := getLabelCentered("View the status of the analysis?")
viewStatusButton := getWidgetCentered(widget.NewButtonWithIcon("View Status", theme.VisibilityIcon(), func(){
setMonitorGeneticAnalysisGenerationPage(window, processIdentifier, currentPage, pageToVisitAfter)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, viewStatusButton)
setPageContent(page, window)
return
}
// Process probably encountered error
// We will show retry page
}
description1 := getBoldLabelCentered("Perform genetic analysis?")
description2 := getLabelCentered("This will create an analysis of the couple's genomes.")
startAnalysisFunction := func(){
newProcessIdentifier, err := myAnalyses.StartCreateNewCoupleGeneticAnalysis(person1Identifier, person2Identifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
setMonitorGeneticAnalysisGenerationPage(window, newProcessIdentifier, currentPage, pageToVisitAfter)
}
startAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Start Analysis", theme.ConfirmIcon(), startAnalysisFunction))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, startAnalysisButton)
setPageContent(page, window)
}