Skip to content

Commit 447901c

Browse files
committed
New version of challenge shiny app added
1 parent deb3a95 commit 447901c

File tree

2 files changed

+205
-0
lines changed
  • inst/shiny

2 files changed

+205
-0
lines changed

inst/shiny/C03Sa_challenge/app.R

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Input solution for the challenge and get ranking
2+
# ================================================
3+
# Note: the shared folder is created like this using SSH onto the server
4+
# cd /data1
5+
# sudo mkdir C03_challenge
6+
# sudo chown rstudio-connect: C03_challenge
7+
# Then, put wine2quality.rds (from sdd_preparation ) into that directory
8+
# scp wine2quality.rds econum@sdd.umons.ac.be:.
9+
# sudo mv /home/econum/wine2quality.rds /data1/C03_challenge
10+
# sudo chown rstudio-connect:/data1/C03_challenge/wine2quality.rds
11+
12+
# We also need flipdown from:
13+
#remotes::install_github("feddelegrand7/flipdownr")
14+
15+
library(data.io)
16+
library(mlearning)
17+
library(RSQLite)
18+
library(flipdownr)
19+
20+
# Indicate title and deadline here
21+
title <- "Challenge vins"
22+
deadline <- "2020-11-16 20:00:00"
23+
24+
# Read data from the SQLite database
25+
dir <- "/data1/C03_challenge"
26+
if (!file.exists(dir))
27+
dir <- "~/C03_challenge" # Alternate dir for local tests
28+
database <- file.path(dir, "wine.sqlite")
29+
table <- "wines"
30+
wine2quality <- read$rds(file.path(dir, "wine2quality.rds"))$value
31+
32+
# Is the countdown over?
33+
is_done <- function()
34+
as.POSIXct(deadline) < Sys.time()
35+
36+
# The function that calculates score and returns also a message
37+
score_model <- function(x, reference = wine2quality) {
38+
if (!is.factor(x))
39+
return(structure(NA,
40+
message = "Le fichier doit contenir un objet de classe 'factor'. Corrigez et resoumettez !"))
41+
if (length(x) != length(reference))
42+
return(structure(NA,
43+
message = paste("Le r\u00e9sultat doit contenir", length(reference),
44+
"items, or vous en fournissez", length(x), ". Corrigez et resoumettez !")))
45+
if (!"excellent" %in% levels(x))
46+
return(structure(NA,
47+
message = "Il faut un niveau de la variable 'factor' qui soit nomm\u00e9 'excellent'. Corrigez et resoumettez !"))
48+
# In case of a recoding , we contrast "excellent" with the rest
49+
wine_pred2 <-
50+
c("autre", "excellent")[(as.character(x) == "excellent") + 1]
51+
wine_true2 <-
52+
c("autre", "excellent")[(as.character(reference) == "excellent") + 1]
53+
res <- summary(confusion(as.factor(wine_pred2), as.factor(wine_true2)))
54+
#res
55+
# Precision for 'excellent' must by higher than 25%
56+
prec <- res["excellent", "Precision"]
57+
if (prec < 0.25)
58+
return(structure(NA,
59+
message = paste0("La pr\u00e9cision pour la classe 'excellent' ne peut pas \u00eatre en dessous de 25% et vous avez ",
60+
round(prec * 100, 1), "%. Votre proposition n'est pas retenue !")))
61+
# Le classement du modèle se fait sur base du rappel pour la classe "excellent"
62+
recall <- res["excellent", "Recall"]
63+
score <- recall * 100 # In percents
64+
structure(score,
65+
message = paste0("Votre proposition est accept\u00e9e. Son score est de ",
66+
round(score, 3), "."))
67+
}
68+
69+
# Manage results into the SQLite database
70+
empty_data <- function()
71+
data.frame(project = character(0), model = character(0),
72+
date = as.POSIXct(character(0)), score = numeric(0))
73+
74+
save_data <- function(data) {
75+
# Connect to the database
76+
db <- dbConnect(SQLite(), database)
77+
# Make sure table exists in the database
78+
try(dbWriteTable(db, table, empty_data()), silent = TRUE)
79+
# Construct the update query by looping over the data fields
80+
query <- sprintf(
81+
"INSERT INTO %s (%s) VALUES ('%s')",
82+
table,
83+
paste(names(data), collapse = ", "),
84+
paste(data, collapse = "', '")
85+
)
86+
# Submit the update query and disconnect
87+
dbGetQuery(db, query)
88+
dbDisconnect(db)
89+
}
90+
91+
load_data <- function() {
92+
# Connect to the database
93+
db <- dbConnect(SQLite(), database)
94+
# Construct the fetching query
95+
query <- sprintf("SELECT * FROM %s", table)
96+
# Submit the fetch query and disconnect
97+
data <- try(dbGetQuery(db, query), silent = TRUE)
98+
dbDisconnect(db)
99+
if (inherits(data, "try-error")) {
100+
empty_data()
101+
} else {
102+
data
103+
}
104+
}
105+
106+
ui <- fluidPage(
107+
titlePanel(title),
108+
109+
sidebarLayout(
110+
sidebarPanel(
111+
fileInput("file", "Votre proposition (fichier RDS)", accept = ".rds"),
112+
textOutput("message")
113+
),
114+
mainPanel(
115+
h3("Temps restant pour le challenge :"),
116+
flipdown(downto = deadline, id = "csfrench", theme = "dark",
117+
headings = c("jours", "heures", "minutes", "secondes")),
118+
hr(),
119+
h3("Classement :"),
120+
tableOutput("ranking")
121+
)
122+
)
123+
)
124+
125+
server <- function(input, output) {
126+
output$message <- renderText({
127+
file <- input$file
128+
ext <- tools::file_ext(file$datapath)
129+
req(file)
130+
validate(need(ext == "rds", "Vous devez indiquer un fichier RDS"))
131+
# Check that there is still time remaining
132+
if (is_done()) {
133+
"Ce challenge est fini, vous ne pouvez plus soumettre de proposition !"
134+
} else {
135+
# Check that filename is correct (repos__model.rds)
136+
if (!grepl("^.+__.+\\.rds", file$name)) {
137+
"Le nom de votre fichier est incorrect : il faut <repos>__<model>.rds. Corrigez et resoumettez."
138+
} else {
139+
solution <- data.io::read$rds(file$datapath)$value
140+
# Check if a model of the same name already exists
141+
name <- file$name
142+
project <- sub("(^.+)__.+$", "\\1", name)
143+
model <- sub(("^.+__(.+)\\.rds$"), "\\1", name)
144+
ranking <- load_data()
145+
if (NROW(ranking[ranking$project == project & ranking$model == model, ])) {
146+
"Un mod\u00e8le de m\u00eame nom existe dans le classement, changez le nom avant de soumettre une nouvelle variante."
147+
} else {
148+
attr(score_model(solution), "message")
149+
}
150+
}
151+
}
152+
})
153+
154+
output$ranking <- renderTable({
155+
file <- input$file
156+
if (!is.null(file$datapath) && grepl("^.+__.+\\.rds", file$name) &&
157+
!is_done()) {
158+
solution <- data.io::read$rds(file$datapath)$value
159+
score <- score_model(solution)
160+
name <- file$name
161+
project <- sub("(^.+)__.+$", "\\1", name)
162+
model <- sub(("^.+__(.+)\\.rds$"), "\\1", name)
163+
} else {
164+
score <- NA
165+
}
166+
ranking <- load_data()
167+
# Record an entry in the mongoDB database
168+
# But we need the login of *all* members of the team, and we don't have them
169+
# right now => leave this to a post-process task instead!
170+
if (!is.na(score)) {
171+
# Check if it is not submitted yet
172+
if (!NROW(ranking[ranking$project == project & ranking$model == model, ])) {
173+
save_data(list(
174+
project = project, model = model, date = Sys.time(),
175+
score = as.numeric(score)
176+
))
177+
# Reload the full dataset
178+
ranking <- load_data()
179+
}
180+
}
181+
# Rework the ranking table
182+
if (NROW(ranking)) {
183+
ranking <- ranking[order(-ranking$score, as.numeric(ranking$date)), ]
184+
ranking$date <- as.POSIXct(ranking$date, origin = "1960-01-01")
185+
ranking$date <- format(ranking$date, "%Y-%m-%d %H:%M:%S")
186+
}
187+
# Add a column with medals for the three first results
188+
n <- NROW(ranking)
189+
if (n == 0) {
190+
medals <- character(0)
191+
} else {
192+
medals <- c("\U1F947", "\U1F948", "\U1F949")
193+
if (n < 4) {
194+
medals <- medals[1:n]
195+
} else {
196+
medals <- c(medals, rep("", 1:(n - 3)))
197+
}
198+
}
199+
ranking <- data.frame(rank = medals, ranking)
200+
names(ranking) <- c("", "Projet", "Mod\u00e8le", "Date", "Score")
201+
ranking
202+
})
203+
}
204+
205+
shinyApp(ui, server)

0 commit comments

Comments
 (0)