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 } 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) }