Added an Estimated Time Remaining label to 2 processes within the Create Genetic Models utility.

This commit is contained in:
Simon Sarasova 2024-06-15 11:02:31 +00:00
parent 72d9c0a04e
commit 019b1a577e
No known key found for this signature in database
GPG key ID: EEDA4103C9C36944
4 changed files with 253 additions and 57 deletions

View file

@ -6,6 +6,7 @@ Small and insignificant changes may not be included in this log.
## Unversioned Changes
* Added an Estimated Time Remaining label to 2 processes within the Create Genetic Models utility. - *Simon Sarasova*
* Split the createGeneticAnalysis package into 2 packages: createPersonGeneticAnalysis and createCoupleGeneticAnalysis. - *Simon Sarasova*
* Upgraded Circl to version 1.3.9. - *Simon Sarasova*
* Renamed BroadcastTime/SentTime to CreationTime for every instance in which it is a better term to use. - *Simon Sarasova*

View file

@ -9,4 +9,4 @@ Many other people have written code for modules which are imported by Seekia. Th
Name | Date Of First Commit | Number Of Commits
--- | --- | ---
Simon Sarasova | June 13, 2023 | 256
Simon Sarasova | June 13, 2023 | 257

View file

@ -446,49 +446,52 @@ func GetContentTypeFromContentHash(contentHash []byte)(string, error){
hashLength := len(contentHash)
if (hashLength == 26){
switch hashLength{
return "Message", nil
case 26:{
} else if (hashLength == 27){
metadataByte := contentHash[26]
if (metadataByte >= 1 && metadataByte <= 6){
return "Attribute", nil
return "Message", nil
}
case 27:{
} else if (hashLength == 28){
metadataByte := contentHash[26]
metadataByte := contentHash[27]
if (metadataByte >= 1 && metadataByte <= 6){
if (metadataByte >= 1 && metadataByte <= 6){
return "Profile", nil
return "Attribute", nil
}
}
case 28:{
} else if (hashLength == 29){
metadataByte := contentHash[27]
metadataByte := contentHash[28]
if (metadataByte >= 1 && metadataByte <= 6){
if (metadataByte >= 1 && metadataByte <= 4){
return "Review", nil
return "Profile", nil
}
}
case 29:{
} else if (hashLength == 30){
metadataByte := contentHash[28]
metadataByte := contentHash[29]
if (metadataByte >= 1 && metadataByte <= 4){
if (metadataByte >= 1 && metadataByte <= 4){
return "Report", nil
return "Review", nil
}
}
case 30:{
} else if (hashLength == 31){
metadataByte := contentHash[29]
return "Parameters", nil
if (metadataByte >= 1 && metadataByte <= 4){
return "Report", nil
}
}
case 31:{
return "Parameters", nil
}
}
contentHashHex := encoding.EncodeBytesToHexString(contentHash)

View file

@ -39,7 +39,7 @@ import "sync"
import "slices"
import mathRand "math/rand/v2"
import goFilepath "path/filepath"
import "time"
func main(){
@ -489,6 +489,7 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
dataArchiveFolderpath := string(fileContents)
progressDetailsBinding := binding.NewString()
estimatedTimeRemainingBinding := binding.NewString()
progressPercentageBinding := binding.NewFloat()
loadingBar := getWidgetCentered(widget.NewProgressBarWithData(progressPercentageBinding))
@ -504,6 +505,15 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
progressDetailsLabelCentered := getWidgetCentered(progressDetailsLabel)
estimatedTimeRemainingLabel := widget.NewLabelWithData(estimatedTimeRemainingBinding)
estimatedTimeRemainingLabel.TextStyle = fyne.TextStyle{
Bold: false,
Italic: true,
Monospace: false,
}
estimatedTimeRemainingLabelCentered := getWidgetCentered(estimatedTimeRemainingLabel)
// We set this bool to true to stop the createData process
var createDataIsStoppedBoolMutex sync.RWMutex
createDataIsStoppedBool := false
@ -517,12 +527,88 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
previousPage()
}))
page := container.NewVBox(title, widget.NewSeparator(), loadingBar, progressDetailsTitle, progressDetailsLabelCentered, widget.NewSeparator(), cancelButton)
page := container.NewVBox(title, widget.NewSeparator(), loadingBar, progressDetailsTitle, progressDetailsLabelCentered, estimatedTimeRemainingLabelCentered, widget.NewSeparator(), cancelButton)
window.SetContent(page)
createTrainingDataFunction := func(){
var processProgressMutex sync.RWMutex
// This stores the progress of creating the training data (0-1)
processProgress := float64(0)
startUpdateTimeRemainingDisplayFunction := func(){
// This function updates the estimated time remaining label binding
updateTimeRemainingDisplayFunction := func()error{
startTime := time.Now().Unix()
for{
createDataIsStoppedBoolMutex.RLock()
createDataIsStopped := createDataIsStoppedBool
createDataIsStoppedBoolMutex.RUnlock()
if (createDataIsStopped == true){
// User exited the process/Process has completed
return nil
}
processProgressMutex.RLock()
currentProcessProgress := processProgress
processProgressMutex.RUnlock()
if (currentProcessProgress == 0){
estimatedTimeRemainingBinding.Set("Calculating required time...")
time.Sleep(time.Second)
continue
}
// We calculate how long we think it will take for the process to complete
currentTime := time.Now().Unix()
secondsElapsed := currentTime - startTime
// processProgress is a float64 which stores the progress as a value between 0-1
// To get the estimated total time the process will take, we divide the seconds elapsed by the proportion of progress
// For example:
// 0.1 (10%) at 10 seconds == Total process will take 100 seconds
// 0.5 (50%) at 20 seconds == Total process will take 40 seconds
totalSeconds := float64(secondsElapsed)/currentProcessProgress
estimatedSecondsRemaining := int64(totalSeconds) - secondsElapsed
estimatedTimeRemainingTranslated, err := helpers.ConvertUnixTimeDurationToUnitsTimeTranslated(estimatedSecondsRemaining, false)
if (err != nil) { return err }
estimatedTimeRemainingBinding.Set("Estimated Time Remaining: " + estimatedTimeRemainingTranslated)
time.Sleep(time.Second)
}
// This should never be reached
return errors.New("updateTimeRemainingDisplayFunction loop has broken.")
}
err := updateTimeRemainingDisplayFunction()
if (err != nil){
createDataIsStoppedBoolMutex.Lock()
createDataIsStoppedBool = true
createDataIsStoppedBoolMutex.Unlock()
setErrorEncounteredPage(window, err, previousPage)
return
}
}
go startUpdateTimeRemainingDisplayFunction()
//Outputs:
// -bool: Process completed (true == was not stopped mid-way)
// -bool: Data archive is well formed
@ -624,13 +710,23 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
}
}
// We create folder to store training data
// We create folder to store the training data
_, err = localFilesystem.CreateFolder("./TrainingData")
if (err != nil) { return false, false, err }
_, err = localFilesystem.CreateFolder("./TrainingData/EyeColor")
if (err != nil) { return false, false, err }
//TODO: Add more traits
traitNamesList := []string{"Eye Color"}
// We create the folders for each trait's training data
for _, traitName := range traitNamesList{
folderpath := goFilepath.Join("./TrainingData/", traitName)
_, err = localFilesystem.CreateFolder(folderpath)
if (err != nil) { return false, false, err }
}
numberOfUserPhenotypeDataObjects := len(userPhenotypesList_OpenSNP)
maximumIndex := numberOfUserPhenotypeDataObjects-1
@ -639,15 +735,6 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
for index, userPhenotypeDataObject := range userPhenotypesList_OpenSNP{
trainingProgressPercentage, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 0, 100)
if (err != nil) { return false, false, err }
trainingProgressFloat64 := float64(trainingProgressPercentage)/100
err = progressPercentageBinding.Set(trainingProgressFloat64)
if (err != nil) { return false, false, err }
createDataIsStoppedBoolMutex.RLock()
createDataIsStopped := createDataIsStoppedBool
createDataIsStoppedBoolMutex.RUnlock()
@ -664,6 +751,18 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
err = progressDetailsBinding.Set(progressDetailsStatus)
if (err != nil) { return false, false, err }
trainingProgressPercentage, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 0, 100)
if (err != nil) { return false, false, err }
trainingProgressFloat64 := float64(trainingProgressPercentage)/100
err = progressPercentageBinding.Set(trainingProgressFloat64)
if (err != nil) { return false, false, err }
processProgressMutex.Lock()
processProgress = trainingProgressFloat64
processProgressMutex.Unlock()
userID := userPhenotypeDataObject.UserID
userRawGenomeFilepathsList, exists := userRawGenomeFilepathsMap[userID]
@ -727,7 +826,6 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
getUserLociValuesMap := func()(map[int64]locusValue.LocusValue, error){
updatePercentageCompleteFunction := func(_ int)error{
return nil
}
@ -766,9 +864,6 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
userLociValuesMap, err := getUserLociValuesMap()
if (err != nil) { return false, false, err }
//TODO: Add more traits
traitNamesList := []string{"Eye Color"}
for _, traitName := range traitNamesList{
traitNameWithoutWhitespace := strings.ReplaceAll(traitName, " ", "")
@ -799,11 +894,19 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
}
}
return true, false, nil
createDataIsStoppedBoolMutex.Lock()
createDataIsStoppedBool = true
createDataIsStoppedBoolMutex.Unlock()
return true, true, nil
}
processIsComplete, archiveIsCorrupt, err := createTrainingData()
processIsComplete, archiveIsWellFormed, err := createTrainingData()
if (err != nil){
createDataIsStoppedBoolMutex.Lock()
createDataIsStoppedBool = true
createDataIsStoppedBoolMutex.Unlock()
setErrorEncounteredPage(window, err, previousPage)
return
}
@ -811,7 +914,7 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
// User exited the page
return
}
if (archiveIsCorrupt == true){
if (archiveIsWellFormed == false){
title := getBoldLabelCentered("OpenSNP Archive Is Corrupt")
@ -881,6 +984,7 @@ func setStartAndMonitorTrainModelsPage(window fyne.Window, previousPage func()){
//TODO: Verify TrainingData folder integrity
progressDetailsBinding := binding.NewString()
estimatedTimeRemainingBinding := binding.NewString()
progressPercentageBinding := binding.NewFloat()
loadingBar := getWidgetCentered(widget.NewProgressBarWithData(progressPercentageBinding))
@ -896,6 +1000,15 @@ func setStartAndMonitorTrainModelsPage(window fyne.Window, previousPage func()){
progressDetailsLabelCentered := getWidgetCentered(progressDetailsLabel)
estimatedTimeRemainingLabel := widget.NewLabelWithData(estimatedTimeRemainingBinding)
estimatedTimeRemainingLabel.TextStyle = fyne.TextStyle{
Bold: false,
Italic: true,
Monospace: false,
}
estimatedTimeRemainingLabelCentered := getWidgetCentered(estimatedTimeRemainingLabel)
// We set this bool to true to stop the trainModels process
var trainModelsIsStoppedBoolMutex sync.RWMutex
trainModelsIsStoppedBool := false
@ -909,12 +1022,88 @@ func setStartAndMonitorTrainModelsPage(window fyne.Window, previousPage func()){
previousPage()
}))
page := container.NewVBox(title, widget.NewSeparator(), loadingBar, progressDetailsTitle, progressDetailsLabelCentered, widget.NewSeparator(), cancelButton)
page := container.NewVBox(title, widget.NewSeparator(), loadingBar, progressDetailsTitle, progressDetailsLabelCentered, estimatedTimeRemainingLabelCentered, widget.NewSeparator(), cancelButton)
window.SetContent(page)
trainModelsFunction := func(){
var processProgressMutex sync.RWMutex
// This stores the amount of progress which has been completed (0-1)
processProgress := float64(0)
startUpdateTimeRemainingDisplayFunction := func(){
// This function updates the estimated time remaining label binding
updateTimeRemainingDisplayFunction := func()error{
startTime := time.Now().Unix()
for{
trainModelsIsStoppedBoolMutex.RLock()
trainModelsIsStopped := trainModelsIsStoppedBool
trainModelsIsStoppedBoolMutex.RUnlock()
if (trainModelsIsStopped == true){
// User exited the process/Process has completed
return nil
}
processProgressMutex.RLock()
currentProcessProgress := processProgress
processProgressMutex.RUnlock()
if (currentProcessProgress == 0){
estimatedTimeRemainingBinding.Set("Calculating required time...")
time.Sleep(time.Second)
continue
}
// We calculate how long we think it will take for the process to complete
currentTime := time.Now().Unix()
secondsElapsed := currentTime - startTime
// processProgress is a float64 which stores the progress as a value between 0-1
// To get the estimated total time the process will take, we divide the seconds elapsed by the proportion of progress
// For example:
// 0.1 (10%) at 10 seconds == Total process will take 100 seconds
// 0.5 (50%) at 20 seconds == Total process will take 40 seconds
totalSeconds := float64(secondsElapsed)/currentProcessProgress
estimatedSecondsRemaining := int64(totalSeconds) - secondsElapsed
estimatedTimeRemainingTranslated, err := helpers.ConvertUnixTimeDurationToUnitsTimeTranslated(estimatedSecondsRemaining, false)
if (err != nil) { return err }
estimatedTimeRemainingBinding.Set("Estimated Time Remaining: " + estimatedTimeRemainingTranslated)
time.Sleep(time.Second)
}
// This should never be reached
return errors.New("updateTimeRemainingDisplayFunction loop has broken.")
}
err := updateTimeRemainingDisplayFunction()
if (err != nil){
trainModelsIsStoppedBoolMutex.Lock()
trainModelsIsStoppedBool = true
trainModelsIsStoppedBoolMutex.Unlock()
setErrorEncounteredPage(window, err, previousPage)
return
}
}
go startUpdateTimeRemainingDisplayFunction()
//Outputs:
// -bool: Process completed (true == was not stopped mid-way)
// -error
@ -967,12 +1156,13 @@ func setStartAndMonitorTrainModelsPage(window fyne.Window, previousPage func()){
progressDetailsBinding.Set(numberOfExamplesProgress)
percentageProgressInt, err := helpers.ScaleNumberProportionally(true, index, 0, finalIndex, 0, 100)
if (err != nil) { return false, err }
newProgressFloat64 := float64(percentageProgressInt)/100
newProgressFloat64 := float64(index)/float64(finalIndex)
progressPercentageBinding.Set(newProgressFloat64)
processProgressMutex.Lock()
processProgress = newProgressFloat64
processProgressMutex.Unlock()
}
// Network training is complete.
@ -996,6 +1186,11 @@ func setStartAndMonitorTrainModelsPage(window fyne.Window, previousPage func()){
processIsComplete, err := trainModels()
if (err != nil){
trainModelsIsStoppedBoolMutex.Lock()
trainModelsIsStoppedBool = true
trainModelsIsStoppedBoolMutex.Unlock()
setErrorEncounteredPage(window, err, previousPage)
return
}
@ -1198,10 +1393,7 @@ func setStartAndMonitorTestModelsPage(window fyne.Window, previousPage func()){
progressDetailsBinding.Set(numberOfExamplesProgress)
percentageProgressInt, err := helpers.ScaleNumberProportionally(true, index, 0, finalIndex, 0, 100)
if (err != nil) { return false, err }
newProgressFloat64 := float64(percentageProgressInt)/100
newProgressFloat64 := float64(index)/float64(finalIndex)
progressPercentageBinding.Set(newProgressFloat64)
}