diff --git a/README.md b/README.md index 5b10887..5b8a827 100755 --- a/README.md +++ b/README.md @@ -47,20 +47,7 @@ The package can also be installed after cloning the git repository: git clone git@github.com:timflutre/PlantBreedGame.git ``` -2. Then, enter into the `PlantBreedGame` directory; inside, run the script `plantbreedgame_setup.Rmd` using [Rmarkdown](http://rmarkdown.rstudio.com/) to simulate the initial data set, this can be done with the command: - -```sh -make data -``` - -> Or with R: -> ```sh -> R -e "rmarkdown::render('plantbreedgame_setup.Rmd')" -> ``` - - It also creates all the necessary files and database for the game to function, and initiate the game with two players, "test" (no password) and "admin" (password `1234`). - -3. Finally, open a R session, and execute the following commands: +2. Finally, open a R session, and execute the following commands: ``` library(shiny) @@ -95,18 +82,12 @@ Then, create a new directory for the application (let's call it `breeding-game` mkdir /srv/shiny-server/breeding-game ``` -and copy inside the content of our Shiny application you just downloaded: +and copy inside the content of the Shiny application you just downloaded: ``` cp -r ~/PlantBreedGame-master/* /srv/shiny-server/breeding-game ``` -Generate the game data with: - -```sh -R -e "rmarkdown::render('/srv/shiny-server/breeding-game/plantbreedgame_setup.Rmd')" -``` - By default, the Shiny server runs as a unix user named `shiny`. You hence need to create a unix group, named for instance `breeding`, to which the `shiny` user can be added (the Shiny server may need to be restarted for this to be taken into account). @@ -214,8 +195,7 @@ or git clone --depth=1 https://github.com/timflutre/PlantBreedGame.git ``` -2. modify the file `PlantBreedGame/plantbreedgame_setup.Rmd` -3. move in the app code folder and build a new image: +2. move in the app code folder and build a new image: ```sh cd PlantBreedGame @@ -224,8 +204,23 @@ docker build -t customplantbreedgame ./ You can then run this image by using the same commands as above replacing `juliendiot/plantbreedgame` by `customplantbreedgame` + # Usage +## Game Initialisation + +To start playing, the game need some specific data (eg. the genotypes and haplotypes of the initial population, a data-base...). This initialisation can be done through the game. + +The first time you run the application, most of the game menus will show a message asking you to initialise the game. To do so you need to go to the `Admin` menue, and in `Game Initialisation` tab. There you will find a button that will start the game initialisation. Once the initialisation is completed (which takes about 2 minutes), the page will automatically reload and you will be able to connect and play the game. + +The game initialisation will automatically create an `admin` breeder with the default password `1234`. + +If the game have already been initialise, it is also possible to re-initialise it to start a new "fresh game". However in such case **all the data of the game will be lost**. + +> NOTE: Currently the game do not let you choose the game initialisation parameters, in order to change them, you need to manually modify the file `plantbreedgame_setup.Rmd` befor proceding to the initialisation. In a near future, you will be able to set the intialisation parameters from the game. + +## How to play + Once the application is installed and working, _please_ read the game rules (tab `How to play?`) and start by downloading the initial data set as well as example files showing how requests should be formatted (all files listed at the bottom of the tab `How to play?`). Before making any request, such as phenotyping, you need to log in (tab `Identification`). diff --git a/src/fun/func_dbRequests.R b/src/fun/func_dbRequests.R index a0e292f..ef55b21 100644 --- a/src/fun/func_dbRequests.R +++ b/src/fun/func_dbRequests.R @@ -119,3 +119,148 @@ getBreedersIndividuals <- function(breeder) { query <- paste0("SELECT * FROM ", tbl) return(db_get_request(query)) } + +clean_data_root <- function(data_root = DATA_ROOT) { + # WARN / TODO --- IMPORTANT ! --- + # the initialisation script do not allow its execution if "the data" folder + # already exists. + # Therefore here we will delete this folder, however, in general, + # IT IS QUITE RISKY to delete folder with code. For example: + # - if `DATA_ROOT` have been wrongly defined + # - if a malicious agent placed files/folder inside DATA_ROOT + # - if files are currently beeing created + # + # A better approach could be instead to only create a new `DATA_ROOT` folder + # and save in the data-base (that should only be erase, not deleted) the current + # `DATA_ROOT` to use. + # The server administrator would then responsible to safely remove the unecessary data. + # + # Here, to mitigate the risks the application will remove the files it + # has created (based on their names) and the folders if they are empty. + # + # WARN / TODO --- IMPORTANT ! --- + + data_truth <- file.path(data_root, "truth") + data_shared <- file.path(data_root, "shared") + data_initial_data <- file.path(data_shared, "initial_data") + data_db <- file.path(data_root, "breeding-game.sqlite") + + + # initial files. + initial_haplo_files <- sprintf( # `Coll0001_haplos.RData` to `Coll1000_haplos.RData` + paste0( + "Coll", + "%0", floor(log10(1000)) + 1, "i", + "_haplos.RData" + ), + seq(1000) + ) + + initial_files_truth <- file.path(data_truth, c( + "afs0.RData", + "allBV.RData", + "coll.RData", + "g0.RData", + "p0.RData", + initial_haplo_files + )) + initial_files_shared <- file.path(data_shared, c( + file.path("initial_data", c( + "controls.txt", + "example_request_plant_material.txt", + "Result_phenos_controls.txt.gz", + "snp_coords_hd.txt.gz", + "example_request_data.txt", + "Result_genos_subset-initialColl-hd.txt.gz", + "Result_phenos_initialColl.txt.gz", + "snp_coords_ld.txt.gz" + )), + "Evaluation.txt" + )) + + # breeder related files + breeders_files <- unlist(lapply(getBreederList(data_db), function(breeder) { + # truth + all_breeder_inds <- getBreedersIndividuals(breeder)$child + haplo_files <- c( + initial_haplo_files, + paste0(all_breeder_inds, "_haplos.RData") + ) + truth_files <- file.path(data_truth, breeder, haplo_files) + + # shared + shared_files <- file.path(data_shared, breeder, c( + list.files( + file.path(data_shared, breeder), + pattern = "^IndList_\\d{4}-\\d{2}-\\d{2}(_\\d+)*\\.txt$" + ), + list.files( + file.path(data_shared, breeder), + pattern = "^Request-geno_(.*)\\.txt$" + ), + list.files( + file.path(data_shared, breeder), + pattern = "^Request-pheno_(.*)\\.txt$" + ), + list.files( + file.path(data_shared, breeder), + pattern = "^Request-pltMat_(.*)\\.txt$" + ), + list.files( + file.path(data_shared, breeder), + pattern = "^Result_genos-((hd)|(ld)|(single-snps))_(.*)_\\d{4}-\\d{2}-\\d{2}(_\\d)*\\.txt\\.gz$" + ), + list.files( + file.path(data_shared, breeder), + pattern = "^Result_pheno-((field)|(patho))_(.*)_\\d{4}-\\d{2}-\\d{2}(_\\d)*\\.txt\\.gz$" + ) + )) + return(c(truth_files, shared_files)) + })) + + # delete files + all_files <- c( + initial_files_truth, + initial_files_shared, + breeders_files + ) + browser() + file.remove(all_files[file.exists(all_files)]) + + lapply(getBreederList(data_db), function(breeder) { + if (length(list.files(file.path(data_shared, breeder))) == 0) { + file.remove(file.path(data_shared, breeder)) + } else { + stop(paste("can't remove", file.path(data_shared, breeder), "folder not empty.")) + } + if (length(list.files(file.path(data_truth, breeder))) == 0) { + file.remove(file.path(data_truth, breeder)) + } else { + stop(paste("can't remove", file.path(data_truth, breeder), "folder not empty.")) + } + }) + + if (length(list.files(data_truth)) == 0) { + file.remove(data_truth) + } else { + stop(paste("can't remove", data_truth, "folder not empty.")) + } + if (length(list.files(file.path(data_shared, "initial_data"))) == 0) { + file.remove(file.path(data_shared, "initial_data")) + } else { + stop(paste("can't remove", file.path(data_shared, "initial_data"), "folder not empty.")) + } + if (length(list.files(data_shared)) == 0) { + file.remove(data_shared) + } else { + stop(paste("can't remove", data_shared, "folder not empty.")) + } + + file.remove(data_db) + + if (length(list.files(data_root)) == 0) { + file.remove(data_root) + } else { + stop(paste("can't remove", data_root, "folder not empty.")) + } +} diff --git a/src/server/server_admin.R b/src/server/server_admin.R index 7a83b25..bd107d0 100644 --- a/src/server/server_admin.R +++ b/src/server/server_admin.R @@ -365,8 +365,8 @@ output$InfoCurrentMaxDiskUsage <- renderText({ ## Game progress ---- admin_gameProgressDta <- eventReactive(input$admin_progressButton, { - progressPheno <- shiny::Progress$new(session, min = 0, max = 4) - calcGameProgress(progressPheno) + progress_bar <- shiny::Progress$new(session, min = 0, max = 4) + calcGameProgress(progress_bar) }) @@ -546,3 +546,101 @@ output$admin_T1T2GameProgress <- renderPlotly({ ) ) }) + + + +output$initialisation_button <- renderUI({ + if (!gameInitialised()) { + return( + actionButton("initialiseGame", "Initialise Game") + ) + } + + return( + div( + div( + h3("Important!"), + p( + "The game is already initialised. Reinitialising the game", + strong("will erase all the current game data"), + ". (All the breeders will be deleted along wiht their data.)" + ), + p("To reinitialise the game, write", code("plantbreedgame"), "in the", code("Confirmation"), "field below", "and click on the", code("Re-Initialise Game"), "button below.") + ), + div( + style = "display: table-row", + div( + style = "display: table-cell; padding-right: 5px;", + textInput("initialisation_security_text", label = "Confirmation:", value = "This action will erase all the data.") + ), + div( + style = "display: table-cell; padding-left: 5px; vertical-align: bottom", + actionButton("initialiseGame", "Re-Initialise Game") + ) + ) + ) + ) +}) + +observe({ + if (identical(input$initialisation_security_text, "plantbreedgame")) { + shinyjs::enable("initialiseGame") + return(TRUE) + } + shinyjs::disable("initialiseGame") +}) + + +observeEvent(input$initialiseGame, { + progress_bar <- shiny::Progress$new(session, min = 0, max = 1) + + progress_bar$set( + value = 1 / 4, + message = "Game Initialisation:", + detail = "Initialisation..." + ) + if (dir.exists(DATA_ROOT)) { + # WARN / TODO --- IMPORTANT ! --- + # the initialisation script do not allow its execution if "the data" folder + # already exists. + # Therefore here we will delete this folder, however, in general, + # IT IS QUITE RISKY to delete folder with code. For example: + # - if `DATA_ROOT` have been wrongly defined + # - if a malicious agent placed files/folder inside DATA_ROOT + # - if files are currently beeing created + # + # A better approach could be instead to only create a new `DATA_ROOT` folder + # and save in the data-base (that should only be erase, not deleted) the current + # `DATA_ROOT` to use. + # The server administrator would then responsible to safely remove the unecessary data. + # + # Here, to mitigate the risks the application will remove the files it + # has created (based on their names). This is not perfect as if one of this + # file have been is symlinked to another, the unintended file could be deleted. + # + # WARN / TODO --- IMPORTANT ! --- + progress_bar$set( + value = 1 / 4, + message = "Game Initialisation:", + detail = "Delete existing data..." + ) + clean_data_root() + } + progress_bar$set( + value = 2 / 4, + message = "Game Initialisation:", + detail = "game setup..." + ) + rmarkdown::render("./plantbreedgame_setup.Rmd", + output_file = "./plantbreedgame_setup.html", + encoding = "UTF-8" + ) + progress_bar$set( + value = 1, + message = "Game Initialisation:", + detail = "Done" + ) + alert("Game initialisation finished. This page will automatically refresh.") + gameInitialised() + shinyjs::refresh() +}) diff --git a/src/ui/ui_admin_loggedIn.R b/src/ui/ui_admin_loggedIn.R index 852fa31..39ce06e 100644 --- a/src/ui/ui_admin_loggedIn.R +++ b/src/ui/ui_admin_loggedIn.R @@ -23,316 +23,352 @@ ## this file is sourced in "server_admin.R" in a renderUI() function ############################ -list( - shinydashboard::tabBox( - width = 12, title = "Admin", id = "admin_tabset", side = "left", - tabPanel( - "Manage sessions", + + +if (gameInitialised()) { + manage_sessions_tab_content <- div( + div( + style = "margin-bottom:50px;", + h3("Current sessions:"), + tableOutput("sessionsTable") + ), + div( # add New session div( - style = "margin-bottom:50px;", - h3("Current sessions:"), - tableOutput("sessionsTable") - ), - div( # add New session + style = "margin-bottom: 20px;", # inputs + h3("Add a new session:"), div( - style = "margin-bottom: 20px;", # inputs - h3("Add a new session:"), - div( - style = "display: inline-block; vertical-align:top; width: 33%; min-width:300px;", # start - h4("Start"), - tags$table( - style = "width: 300px; border-collapse: collapse;", # start table 1 - tags$td( - style = "width: 34%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - dateInput("startDate", "date", - width = "100px" - ) - ), - tags$td( - style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - numericInput("startHour", "hour", - value = 9, min = 0, max = 23, step = 1, - width = "75px" - ) - ), - tags$td( - style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - numericInput("startMin", "minute", - value = 0, min = 0, max = 59, step = 1, - width = "75px" - ) + style = "display: inline-block; vertical-align:top; width: 33%; min-width:300px;", # start + h4("Start"), + tags$table( + style = "width: 300px; border-collapse: collapse;", # start table 1 + tags$td( + style = "width: 34%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + dateInput("startDate", "date", + width = "100px" ) - ) # end table 1 - ), # end div "start" - - div( - style = "display: inline-block; vertical-align:top; width: 33%; min-width:300px;", # end - h4("End"), - tags$table( - style = "width: 300px; border-collapse: collapse;", # start table 2 - tags$td( - style = "width: 34%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - dateInput("endDate", "date", - width = "100px" - ) - ), - tags$td( - style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - numericInput("endHour", "hour", - value = 9, min = 0, max = 23, step = 1, - width = "75px" - ) - ), - tags$td( - style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - numericInput("endMin", "minute", - value = 0, min = 0, max = 59, step = 1, - width = "75px" - ) + ), + tags$td( + style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + numericInput("startHour", "hour", + value = 9, min = 0, max = 23, step = 1, + width = "75px" ) - ) # end table 2 - ), # end div "end" - - div( - style = "display: inline-block; vertical-align:top; width:33%; min-width:300px;", # year time - h4("Year time"), - tags$table( - style = "border-collapse: collapse;", # start table 3 - tags$td( - style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - numericInput("yearTime", "Duration of one year (in minutes)", value = 60, min = 0, max = Inf, step = 1) + ), + tags$td( + style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + numericInput("startMin", "minute", + value = 0, min = 0, max = 59, step = 1, + width = "75px" ) - ) # end table 3 - ) # end div "year time" - ), # end div inputs - - div( - style = "display: inline-block; vertical-align:top; width:25%; margin-bottom: 50px; padding-left: 10px;", # button - actionButton("addSession", "Add this new session") - ) # end div "button" - ), # end div "add New session" - - - - div( - style = "margin-bottom:100px;", # delete session - h3("Delete sessions:"), - tags$table( - style = "width: 100%; border-collapse: collapse;", - tags$td( - style = "width: 50%; vertical-align: bottom; padding: 10px;", - selectInput("delSession", "Session's number", - choices = c("", sessionsList()$num), - selected = "", width = "100%" - ) - ), - tags$td( - style = "width: 50%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - actionButton("deleteSession", "DO NOT click! (unless you are sure to delete this session)", - width = "100%", style = "margin-bottom: 0px;", - style = "background-color:#ff3333; color:white;" ) - ) - ) - ) # end div "delete session" - ), # end tabPanel sessions managment - - - - - + ) # end table 1 + ), # end div "start" - tabPanel( - "Manage breeders", - div( # add New breeders - h3("Add a new breeder:"), - tags$head( - tags$style(HTML(".shiny-input-container{margin-bottom: 0px;} - .selectize-control{margin-bottom: 0px;}")) - ), - tags$table( - style = "width: 100%; border-collapse: collapse;", - tags$tr( - tags$td( - style = "width: 25%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - textInput("newBreederName", "Breeder's name", - placeholder = "Only a-z, A-Z, 0-9 and '_' are allowed", - width = "100%" - ) - ), + div( + style = "display: inline-block; vertical-align:top; width: 33%; min-width:300px;", # end + h4("End"), + tags$table( + style = "width: 300px; border-collapse: collapse;", # start table 2 tags$td( - style = "width: 25%; vertical-align: bottom; padding: 10px;", - selectInput("newBreederStatus", "Status", - choices = c("player", "tester", "game master"), - width = "100%" + style = "width: 34%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + dateInput("endDate", "date", + width = "100px" ) ), tags$td( - style = "width: 25%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - passwordInput("newBreederPsw", "Password", - width = "100%" + style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + numericInput("endHour", "hour", + value = 9, min = 0, max = 23, step = 1, + width = "75px" ) ), tags$td( - style = "width: 25%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - actionButton("addNewBreeder", "Add this new breeder", - width = "100%", style = "margin-bottom: 0px;" + style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + numericInput("endMin", "minute", + value = 0, min = 0, max = 59, step = 1, + width = "75px" ) ) - ) - ) # end tags$table - ), # end div "add new breeder" + ) # end table 2 + ), # end div "end" + div( + style = "display: inline-block; vertical-align:top; width:33%; min-width:300px;", # year time + h4("Year time"), + tags$table( + style = "border-collapse: collapse;", # start table 3 + tags$td( + style = "width: 33%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + numericInput("yearTime", "Duration of one year (in minutes)", value = 60, min = 0, max = Inf, step = 1) + ) + ) # end table 3 + ) # end div "year time" + ), # end div inputs - div( # delete breeders - h3("Delete a breeder:"), - tags$table( - style = "width: 100%; border-collapse: collapse;", + div( + style = "display: inline-block; vertical-align:top; width:25%; margin-bottom: 50px; padding-left: 10px;", # button + actionButton("addSession", "Add this new session") + ) # end div "button" + ), # end div "add New session" + + + + div( + style = "margin-bottom:100px;", # delete session + h3("Delete sessions:"), + tags$table( + style = "width: 100%; border-collapse: collapse;", + tags$td( + style = "width: 50%; vertical-align: bottom; padding: 10px;", + selectInput("delSession", "Session's number", + choices = c("", sessionsList()$num), + selected = "", width = "100%" + ) + ), + tags$td( + style = "width: 50%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + actionButton("deleteSession", "DO NOT click! (unless you are sure to delete this session)", + width = "100%", style = "margin-bottom: 0px;", + style = "background-color:#ff3333; color:white;" + ) + ) + ) + ) # end div "delete session" + ) + + manage_breeders_tab_content <- div( + div( # add New breeders + h3("Add a new breeder:"), + tags$head( + tags$style(HTML(".shiny-input-container{margin-bottom: 0px;} + .selectize-control{margin-bottom: 0px;}")) + ), + tags$table( + style = "width: 100%; border-collapse: collapse;", + tags$tr( + tags$td( + style = "width: 25%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + textInput("newBreederName", "Breeder's name", + placeholder = "Only a-z, A-Z, 0-9 and '_' are allowed", + width = "100%" + ) + ), tags$td( - style = "width: 50%; vertical-align: bottom; padding: 10px;", - breeder_list_ui("admin_breeder_list_for_deletion"), + style = "width: 25%; vertical-align: bottom; padding: 10px;", + selectInput("newBreederStatus", "Status", + choices = c("player", "tester", "game master"), + width = "100%" + ) + ), + tags$td( + style = "width: 25%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + passwordInput("newBreederPsw", "Password", + width = "100%" + ) ), tags$td( - style = "width: 50%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", - actionButton("deleteBreeder", "DO NOT click! (unless you are sure to delete this breeder)", - width = "100%", style = "margin-bottom: 0px;", - style = "background-color:#ff3333; color:white;" + style = "width: 25%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + actionButton("addNewBreeder", "Add this new breeder", + width = "100%", style = "margin-bottom: 0px;" ) ) ) - ) # end div "delete breeders" - ), # end tabPanel "Add/Delete Breeders" + ) # end tags$table + ), # end div "add new breeder" - tabPanel( - "Manage constants", - # tabPanel to manage some game constants - - # see.year.effct - div( - id = "admin_seedYearEffect", - style = "margin: 0px 0px 40px 0px;", - - # input: - div( - id = "admin_div_numInput_seedYearEfect", - style = "display: inline-block; - vertical-align: top;", - numericInput("admin_seedYearEfect", "seed.year.effect", - value = 4321, - min = 0, - max = NA, - step = 1 - ) - ), - - # button to request update: - div( - id = "admin_div_button_seedYearEfect", - style = "display: inline-block; - vertical-align: top; - padding-top: 25px", # button align with numInput - actionButton("admin_button_seedYearEfect", "update seed.year.effect") + div( # delete breeders + h3("Delete a breeder:"), + tags$table( + style = "width: 100%; border-collapse: collapse;", + tags$td( + style = "width: 50%; vertical-align: bottom; padding: 10px;", + breeder_list_ui("admin_breeder_list_for_deletion"), ), - - # current value: - div( - id = "admin_currentSYE", - "Current", code("seed.year.effect"), ":", textOutput("admin_currentSYE", container = span) + tags$td( + style = "width: 50%; vertical-align: bottom; padding: 10px; padding-bottom: 13.8px;", + actionButton("deleteBreeder", "DO NOT click! (unless you are sure to delete this breeder)", + width = "100%", style = "margin-bottom: 0px;", + style = "background-color:#ff3333; color:white;" + ) ) - ) # end div "admin_seedYearEffect" - ), # end tabPanel "Manage constants" - - + ) + ) # end div "delete breeders" + ) + manage_constants_tab_content <- div( + div( + id = "admin_seedYearEffect", + style = "margin: 0px 0px 40px 0px;", - - - tabPanel( - "Disk usage", + # input: div( - id = "admin_diskU_data", + id = "admin_div_numInput_seedYearEfect", style = "display: inline-block; - vertical-align:top; - width: 33%; - min-width:300px;", - h3("Disk usage:"), - tableOutput("sizeDataFolder") + vertical-align: top;", + numericInput("admin_seedYearEfect", "seed.year.effect", + value = 4321, + min = 0, + max = NA, + step = 1 + ) ), + + # button to request update: div( - id = "admin_diskU_input", + id = "admin_div_button_seedYearEfect", style = "display: inline-block; - vertical-align:top; - width: 66%;", - p("To prevent over disk usage on your server, you can specifiy here the maximum size for all game data.", - style = "margin-top:20px;" - ), - p("In case the size of all data exceeds this threshold, players will not be allowed to connect any more, and you will have to delete haplotypes of some breeders."), - p(textOutput("InfoCurrentMaxDiskUsage")), - div( - style = "width: 50%; - display: inline-block; - vertical-align: top;", - numericInput("admin_maxDiskUsage", - label = "Maximum disk usage (in Gb)", - value = 10, - min = 2 - ) + vertical-align: top; + padding-top: 25px", # button align with numInput + actionButton("admin_button_seedYearEfect", "update seed.year.effect") + ), + + # current value: + div( + id = "admin_currentSYE", + "Current", code("seed.year.effect"), ":", textOutput("admin_currentSYE", container = span) + ) + ) # end div "admin_seedYearEffect" + ) + + disk_usage_tab_content <- div( + div( + id = "admin_diskU_data", + style = "display: inline-block; + vertical-align:top; + width: 33%; + min-width:300px;", + h3("Disk usage:"), + tableOutput("sizeDataFolder") + ), + div( + id = "admin_diskU_input", + style = "display: inline-block; + vertical-align:top; + width: 66%;", + p("To prevent over disk usage on your server, you can specifiy here the maximum size for all game data.", + style = "margin-top:20px;" + ), + p("In case the size of all data exceeds this threshold, players will not be allowed to connect any more, and you will have to delete haplotypes of some breeders."), + p(textOutput("InfoCurrentMaxDiskUsage")), + div( + style = "width: 50%; + display: inline-block; + vertical-align: top;", + numericInput("admin_maxDiskUsage", + label = "Maximum disk usage (in Gb)", + value = 10, + min = 2 + ) + ), + div( + style = "width: 30%; + padding-top: 26px; + display: inline-block; + vertical-align: top;", + actionButton("updateMaxDiskUsage", + label = "Update" + ) + ) + ) # end div "admin_diskU_input" + ) + + + game_progress_tab_content <- div( + fluidRow( + div( + class = "col-sm-12 col-md-12 col-lg-12", + selectInput( + inputId = "admin_progressTrait", + label = "Trait", + choices = c("Trait 1", "Trait 2"), + selected = "Trait 1" ), - div( - style = "width: 30%; - padding-top: 26px; - display: inline-block; - vertical-align: top;", - actionButton("updateMaxDiskUsage", - label = "Update" - ) + actionButton( + inputId = "admin_progressButton", + label = "Refresh !", + icon = icon("refresh"), + style = "background-color: #00a65a; + color: #ffffff;" ) - ) # end div "admin_diskU_input" - ), # end tabPanel "Disk usage" + ), + div( + class = "col-sm-12 col-md-12 col-lg-6", + plotlyOutput("admin_plotAllIndGameProgress") %>% withSpinner() + ), + div( + class = "col-sm-12 col-md-12 col-lg-6", + plotlyOutput("admin_plotMaxIndGameProgress") %>% withSpinner() + ), + div( + class = "col-sm-12 col-md-12 col-lg-6", + plotlyOutput("admin_boxPlotGameProgress") %>% withSpinner() + ), + div( + class = "col-sm-12 col-md-12 col-lg-6", + breeder_list_ui("admin_breeder_list_gameProgress"), + plotlyOutput("admin_T1T2GameProgress") %>% withSpinner() + ) + ) # end fluidRow + ) + + +} else { + game_not_initialised_msg <- div( + h3("Game not initialised"), + p("The game have not been initialised. It is therefore currently impossible to play.") + ) + manage_sessions_tab_content <- game_not_initialised_msg + manage_breeders_tab_content <- game_not_initialised_msg + manage_constants_tab_content <- game_not_initialised_msg + disk_usage_tab_content <- game_not_initialised_msg + game_progress_tab_content <- game_not_initialised_msg +} + + +game_initialisation_tab_content <- div( + p("By pressing the button below, you can initialise the game."), + p("Once the initialisation is completed (which takes about 2 minutes), the page will automatically reload and you will be able to connect and play the game."), + div( + h2("Information:"), + p("Some breeders accounts will be automatically created:"), + tags$ul( + tags$li(code("Admin"), "with the default password", code("1234")), + tags$li(code("Tester"), "(this breeder do not have a password, you can leave the password field empty to connect)") + ) + ), + uiOutput("initialisation_button") +) - #----- Game progress ----- - # This tab displays the progression of the players. +list( + shinydashboard::tabBox( + width = 12, title = "Admin", id = "admin_tabset", side = "left", + tabPanel( + "Manage sessions", + manage_sessions_tab_content + ), + tabPanel( + "Manage breeders", + manage_breeders_tab_content + ), + tabPanel( + "Manage constants", + manage_constants_tab_content + ), + tabPanel( + "Disk usage", + disk_usage_tab_content + ), tabPanel( "Game progress", - fluidRow( - div( - class = "col-sm-12 col-md-12 col-lg-12", - selectInput( - inputId = "admin_progressTrait", - label = "Trait", - choices = c("Trait 1", "Trait 2"), - selected = "Trait 1" - ), - actionButton( - inputId = "admin_progressButton", - label = "Refresh !", - icon = icon("refresh"), - style = "background-color: #00a65a; - color: #ffffff;" - ) - ), - div( - class = "col-sm-12 col-md-12 col-lg-6", - plotlyOutput("admin_plotAllIndGameProgress") %>% withSpinner() - ), - div( - class = "col-sm-12 col-md-12 col-lg-6", - plotlyOutput("admin_plotMaxIndGameProgress") %>% withSpinner() - ), - div( - class = "col-sm-12 col-md-12 col-lg-6", - plotlyOutput("admin_boxPlotGameProgress") %>% withSpinner() - ), - div( - class = "col-sm-12 col-md-12 col-lg-6", - breeder_list_ui("admin_breeder_list_gameProgress"), - plotlyOutput("admin_T1T2GameProgress") %>% withSpinner() - ) - ) # end fluidRow - ) # end tabPanel "Game progress" - ) # close tabBox -) # close list + game_progress_tab_content + ), + tabPanel( + "Game Initialisation", + game_initialisation_tab_content + ) + ) +) diff --git a/tests_UI/test-1.spec.ts b/tests_UI/test-1.spec.ts index be0df3e..e46655a 100644 --- a/tests_UI/test-1.spec.ts +++ b/tests_UI/test-1.spec.ts @@ -130,6 +130,11 @@ test.describe("PlantBreedGame_UI", () => { await runEvaluation(page, registered_inds); }); + + // TODO add tests related to game initialisation + + // TODO add tests where several requests are made with the same file + // this is a edge case that could happend }); async function login(page: Page, username: string, password: string) {