Update app.R
Browse files
app.R
CHANGED
@@ -1,58 +1,297 @@
|
|
1 |
library(shiny)
|
2 |
-
library(
|
3 |
-
library(
|
4 |
-
library(
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
),
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
|
|
25 |
)
|
26 |
|
27 |
-
server <- function(input, output
|
28 |
-
subsetted <- reactive({
|
29 |
-
req(input$species)
|
30 |
-
df |> filter(Species %in% input$species)
|
31 |
-
})
|
32 |
-
|
33 |
-
output$scatter <- renderPlot(
|
34 |
-
{
|
35 |
-
p <- ggplot(subsetted(), aes(!!input$xvar, !!input$yvar)) +
|
36 |
-
theme_light() +
|
37 |
-
list(
|
38 |
-
theme(legend.position = "bottom"),
|
39 |
-
if (input$by_species) aes(color = Species),
|
40 |
-
geom_point(),
|
41 |
-
if (input$smooth) geom_smooth()
|
42 |
-
)
|
43 |
-
|
44 |
-
if (input$show_margins) {
|
45 |
-
margin_type <- if (input$by_species) "density" else "histogram"
|
46 |
-
p <- p |> ggExtra::ggMarginal(
|
47 |
-
type = margin_type, margins = "both",
|
48 |
-
size = 8, groupColour = input$by_species, groupFill = input$by_species
|
49 |
-
)
|
50 |
-
}
|
51 |
|
52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
},
|
54 |
-
|
|
|
|
|
55 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
}
|
57 |
|
58 |
-
|
|
|
|
1 |
library(shiny)
|
2 |
+
library(DT)
|
3 |
+
library(shinyWidgets)
|
4 |
+
library(data.table)
|
5 |
+
library(stringr)
|
6 |
+
library(markdown)
|
7 |
+
|
8 |
+
data <- readRDS("donnees_nettes/dolq_min.RDS") |> setDT()
|
9 |
+
|
10 |
+
readme_content <- readLines("README.md", warn = FALSE)
|
11 |
+
|
12 |
+
source("Fonctions.R")
|
13 |
+
|
14 |
+
ui <- fluidPage(
|
15 |
+
titlePanel("Dictionnaire des oeuvres littéraires du Québec, t. 1-6"),
|
16 |
+
tabsetPanel(
|
17 |
+
tabPanel("Recherche plein texte",
|
18 |
+
sidebarLayout(
|
19 |
+
sidebarPanel(
|
20 |
+
noUiSliderInput(inputId = "date_range",
|
21 |
+
label = "Sélectionner une période:",
|
22 |
+
min = 1830,
|
23 |
+
max = 1980,
|
24 |
+
value = c(1830, 1980),
|
25 |
+
format = wNumbFormat(decimals = 0),
|
26 |
+
tooltips = TRUE),
|
27 |
+
br(),
|
28 |
+
|
29 |
+
pickerInput(
|
30 |
+
inputId = "column",
|
31 |
+
label = h5("Choisissez les colonnes où effectuer la recherche:"),
|
32 |
+
choices = c("Auteur_oeuvre", "Titre", "Auteur_notice", "Article",
|
33 |
+
"Depouillement", "Details_bibliographiques"),
|
34 |
+
selected = "Article",
|
35 |
+
options = list(`actions-box` = TRUE),
|
36 |
+
multiple = TRUE
|
37 |
+
),
|
38 |
+
pickerInput(
|
39 |
+
inputId = "additional_columns",
|
40 |
+
label = h5("Choisissez les colonnes à afficher:"),
|
41 |
+
choices = c("Id", "Auteur_oeuvre", "Titre", "Annee_parution", "Auteur_notice", "Article", "Volume", "Depouillement", "Details_bibliographiques"),
|
42 |
+
selected = c("Auteur_oeuvre", "Titre", "Article"),
|
43 |
+
options = list(`actions-box` = TRUE),
|
44 |
+
multiple = TRUE
|
45 |
+
),
|
46 |
+
textInput(
|
47 |
+
inputId = "motcle",
|
48 |
+
label = h5("Chaine de caractères à rechercher"),
|
49 |
+
value = "Montréal"
|
50 |
+
),
|
51 |
+
prettySwitch(
|
52 |
+
inputId = "regex",
|
53 |
+
label = "Utiliser une expression régulière?",
|
54 |
+
fill = FALSE,
|
55 |
+
status = "primary"
|
56 |
+
),
|
57 |
+
downloadButton("downloadData", "Exporter le résultat de la table (csv)"),
|
58 |
+
width = 4,
|
59 |
+
hr(),
|
60 |
+
|
61 |
+
tags$head(tags$link(rel = "stylesheet", href = "https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css")),
|
62 |
+
|
63 |
+
|
64 |
+
h5("Antisèche - expressions régulières"),
|
65 |
+
tags$a(href = "regex.pdf", target = "_blank",
|
66 |
+
tags$img(src = "regex.jpg", alt = "Thumbnail", width = "100%")),
|
67 |
+
|
68 |
+
hr(),
|
69 |
+
h5("Accéder aux volumes (pdf)"),
|
70 |
+
# Add clickable images for each volume
|
71 |
+
tags$a(href = "https://crilcq.org/dictionnaire-des-oeuvres-litteraires-du-quebec-tome-1-des-origines-a-1900/", target = "_blank",
|
72 |
+
tags$img(src = "DOLQ_01.jpg", alt = "Volume 1", width = "50%")),
|
73 |
+
tags$a(href = "https://crilcq.org/dictionnaire-des-oeuvres-litteraires-du-quebec-tome-2-1900-a-1939/", target = "_blank",
|
74 |
+
tags$img(src = "DOLQ_02.jpg", alt = "Volume 2", width = "50%")),
|
75 |
+
tags$a(href = "https://crilcq.org/dictionnaire-des-oeuvres-litteraires-du-quebec-tome-3-1940-a-1959/", target = "_blank",
|
76 |
+
tags$img(src = "DOLQ_03.jpg", alt = "Volume 3", width = "50%")),
|
77 |
+
tags$a(href = "https://crilcq.org/dictionnaire-des-oeuvres-litteraires-du-quebec-tome-4-1960-1969/", target = "_blank",
|
78 |
+
tags$img(src = "DOLQ_04.jpg", alt = "Volume 4", width = "50%")),
|
79 |
+
tags$a(href = "https://crilcq.org/dictionnaire-des-oeuvres-litteraires-du-quebec-tome-5-1970-1975/", target = "_blank",
|
80 |
+
tags$img(src = "DOLQ_05.jpg", alt = "Volume 5", width = "50%")),
|
81 |
+
tags$a(href = "https://crilcq.org/dictionnaire-des-oeuvres-litteraires-du-quebec-tome-5-1970-1975/", target = "_blank",
|
82 |
+
tags$img(src = "DOLQ_06.jpg", alt = "Volume 6", width = "50%")),
|
83 |
+
),
|
84 |
+
|
85 |
+
mainPanel(h3("Distribution chronologique des notices"),
|
86 |
+
|
87 |
+
radioButtons(inputId = "dist_type",
|
88 |
+
label = "Type de distribution:",
|
89 |
+
choices = list("Distribution brute" = "raw",
|
90 |
+
"Distribution relative" = "relative"),
|
91 |
+
selected = "raw"),
|
92 |
+
|
93 |
+
numericInput("num_breaks", label = "Nombre d'intervalles:", value = 65, min = 1),
|
94 |
+
|
95 |
+
plotOutput("histogram"),
|
96 |
+
|
97 |
+
downloadButton("download_graph", "Exporter le graphique"),
|
98 |
+
downloadButton("download_table", "Exporter les données du graphique"),
|
99 |
+
|
100 |
+
h3("Table"),
|
101 |
+
textOutput("filtered_count"),
|
102 |
+
DT::dataTableOutput('table'))
|
103 |
+
)
|
104 |
),
|
105 |
+
|
106 |
+
# Second tab: Documentation
|
107 |
+
|
108 |
+
tabPanel("Documentation",
|
109 |
+
uiOutput("readme")
|
110 |
+
)
|
111 |
+
)
|
112 |
)
|
113 |
|
114 |
+
server <- function(input, output) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
|
116 |
+
reactive_data <- reactive({
|
117 |
+
filter_data(data, input, input$additional_columns)
|
118 |
+
})
|
119 |
+
|
120 |
+
reactive_data1 <- reactive({
|
121 |
+
filter_data(data, input)
|
122 |
+
})
|
123 |
+
|
124 |
+
filtered_count <- reactive({
|
125 |
+
nrow(reactive_data())
|
126 |
+
})
|
127 |
+
|
128 |
+
hist_data <- reactive({
|
129 |
+
if ("Annee_unique" %in% names(reactive_data1()) && nrow(reactive_data1()) > 0) {
|
130 |
+
# Ensure the data is numeric
|
131 |
+
data_numeric <- as.numeric(reactive_data1()[, Annee_unique])
|
132 |
+
|
133 |
+
# Remove NA values
|
134 |
+
data_numeric <- na.omit(data_numeric)
|
135 |
+
|
136 |
+
if (length(data_numeric) > 0) {
|
137 |
+
min_year <- min(data_numeric)
|
138 |
+
max_year <- max(data_numeric)
|
139 |
+
breaks_hist <- seq(min_year, max_year, length.out = input$num_breaks + 1)
|
140 |
+
|
141 |
+
# Raw counts
|
142 |
+
raw_counts <- hist(data_numeric, plot=FALSE, breaks=breaks_hist)$counts
|
143 |
+
|
144 |
+
# Relative counts
|
145 |
+
total_counts <- hist(data[data$Annee_unique %in% reactive_data1()[, Annee_unique], Annee_unique], plot=FALSE, breaks=breaks_hist)$counts
|
146 |
+
|
147 |
+
relative_counts <- ifelse(total_counts == 0, 0, raw_counts / total_counts)
|
148 |
+
|
149 |
+
breaks <- round(seq(min_year, max_year, length.out = length(raw_counts)))
|
150 |
+
|
151 |
+
# Return data based on user's selection
|
152 |
+
if (input$dist_type == "raw") {
|
153 |
+
return(data.frame(Breaks = breaks, Counts = raw_counts))
|
154 |
+
} else {
|
155 |
+
return(data.frame(Breaks = breaks, RelativeCounts = relative_counts))
|
156 |
+
}
|
157 |
+
}
|
158 |
+
}
|
159 |
+
# Ensure a dataframe is always returned
|
160 |
+
return(data.frame(Breaks = numeric(0), Counts = numeric(0)))
|
161 |
+
})
|
162 |
+
|
163 |
+
|
164 |
+
output$filtered_count <- renderText({
|
165 |
+
paste("Nombre de notices filtrées:", filtered_count())
|
166 |
+
})
|
167 |
+
|
168 |
+
output$table <- DT::renderDataTable({
|
169 |
+
datatable(reactive_data(), options = list(searching = FALSE),
|
170 |
+
callback = JS(paste0("
|
171 |
+
table.on('draw.dt', function() {
|
172 |
+
var keyword = '", input$motcle, "';
|
173 |
+
$('td').each(function() {
|
174 |
+
var content = $(this).html();
|
175 |
+
var highlighted = content.replace(new RegExp(keyword, 'gi'), function(match) {
|
176 |
+
return '<span style=\"background-color: yellow;\">' + match + '</span>';
|
177 |
+
});
|
178 |
+
$(this).html(highlighted);
|
179 |
+
});
|
180 |
+
});
|
181 |
+
")))
|
182 |
+
})
|
183 |
+
|
184 |
+
output$downloadData <- downloadHandler(
|
185 |
+
filename = function() {
|
186 |
+
paste("filtered_data-", Sys.Date(), ".csv", sep="")
|
187 |
},
|
188 |
+
content = function(file) {
|
189 |
+
fwrite(reactive_data(), file)
|
190 |
+
}
|
191 |
)
|
192 |
+
|
193 |
+
output$histogram <- renderPlot({
|
194 |
+
# Check if 'Annee_unique' column exists and has data
|
195 |
+
if ("Annee_unique" %in% names(reactive_data1()) && nrow(reactive_data1()) > 0) {
|
196 |
+
min_year <- min(na.omit(reactive_data1()[, Annee_unique]))
|
197 |
+
max_year <- max(na.omit(reactive_data1()[, Annee_unique]))
|
198 |
+
breaks_hist <- seq(min_year, max_year, length.out = input$num_breaks + 1)
|
199 |
+
|
200 |
+
if (input$dist_type == "raw") {
|
201 |
+
hist(reactive_data1()[, Annee_unique],
|
202 |
+
main = "Distribution brute des notices",
|
203 |
+
xlab = "Année",
|
204 |
+
ylab = "Nombre",
|
205 |
+
border = "blue",
|
206 |
+
col = "lightblue",
|
207 |
+
breaks = breaks_hist)
|
208 |
+
} else {
|
209 |
+
# Compute relative distribution
|
210 |
+
filtered_counts <- hist(reactive_data1()[, Annee_unique], plot=FALSE, breaks=breaks_hist)$counts
|
211 |
+
total_counts <- hist(data[data$Annee_unique %in% reactive_data1()[, Annee_unique], Annee_unique], plot=FALSE, breaks=breaks_hist)$counts
|
212 |
+
relative_counts <- ifelse(total_counts == 0, 0, filtered_counts / total_counts)
|
213 |
+
|
214 |
+
# Ensure names.arg matches the length of relative_counts
|
215 |
+
names_for_bars <- round(seq(min_year, max_year, length.out = length(relative_counts)))
|
216 |
+
|
217 |
+
barplot(relative_counts,
|
218 |
+
main = "Distribution relative des notices",
|
219 |
+
xlab = "Année",
|
220 |
+
ylab = "Fréquence relative",
|
221 |
+
border = "blue",
|
222 |
+
col = "lightblue",
|
223 |
+
space = 0,
|
224 |
+
names.arg = names_for_bars)
|
225 |
+
}
|
226 |
+
} else {
|
227 |
+
plot.new()
|
228 |
+
title(main = "No data available for the selected criteria")
|
229 |
+
}
|
230 |
+
})
|
231 |
+
|
232 |
+
output$download_graph <- downloadHandler(
|
233 |
+
filename = function() {
|
234 |
+
paste("histogram_graph", Sys.Date(), ".png", sep = "_")
|
235 |
+
},
|
236 |
+
content = function(file) {
|
237 |
+
png(file)
|
238 |
+
# Check if 'Annee_unique' column exists and has data
|
239 |
+
if ("Annee_unique" %in% names(reactive_data1()) && nrow(reactive_data1()) > 0) {
|
240 |
+
min_year <- min(na.omit(reactive_data1()[, Annee_unique]))
|
241 |
+
max_year <- max(na.omit(reactive_data1()[, Annee_unique]))
|
242 |
+
breaks_hist <- seq(min_year, max_year, length.out = input$num_breaks + 1)
|
243 |
+
|
244 |
+
if (input$dist_type == "raw") {
|
245 |
+
hist(reactive_data1()[, Annee_unique],
|
246 |
+
main = "Distribution brute des notices",
|
247 |
+
xlab = "Année",
|
248 |
+
ylab = "Nombre",
|
249 |
+
border = "blue",
|
250 |
+
col = "lightblue",
|
251 |
+
breaks = breaks_hist)
|
252 |
+
} else {
|
253 |
+
# Compute relative distribution
|
254 |
+
filtered_counts <- hist(reactive_data1()[, Annee_unique], plot=FALSE, breaks=breaks_hist)$counts
|
255 |
+
total_counts <- hist(data[data$Annee_unique %in% reactive_data1()[, Annee_unique], Annee_unique], plot=FALSE, breaks=breaks_hist)$counts
|
256 |
+
relative_counts <- ifelse(total_counts == 0, 0, filtered_counts / total_counts)
|
257 |
+
|
258 |
+
# Ensure names.arg matches the length of relative_counts
|
259 |
+
names_for_bars <- round(seq(min_year, max_year, length.out = length(relative_counts)))
|
260 |
+
|
261 |
+
barplot(relative_counts,
|
262 |
+
main = "Distribution relative des notices",
|
263 |
+
xlab = "Année",
|
264 |
+
ylab = "Fréquence relative",
|
265 |
+
border = "blue",
|
266 |
+
col = "lightblue",
|
267 |
+
space = 0,
|
268 |
+
names.arg = names_for_bars)
|
269 |
+
}
|
270 |
+
} else {
|
271 |
+
plot.new()
|
272 |
+
title(main = "No data available for the selected criteria")
|
273 |
+
}
|
274 |
+
dev.off()
|
275 |
+
}
|
276 |
+
)
|
277 |
+
|
278 |
+
output$download_table <- downloadHandler(
|
279 |
+
filename = function() {
|
280 |
+
paste("histogram_data", Sys.Date(), ".csv", sep = "_")
|
281 |
+
},
|
282 |
+
content = function(file) {
|
283 |
+
write.csv(hist_data(), file, row.names = FALSE)
|
284 |
+
}
|
285 |
+
)
|
286 |
+
output$readme <- renderUI({
|
287 |
+
# Read the content of the README.md file
|
288 |
+
readme_content <- readLines("README.md", warn = FALSE)
|
289 |
+
|
290 |
+
# Convert the markdown content to HTML for rendering in the Shiny app
|
291 |
+
HTML(markdown::markdownToHTML(text = readme_content, fragment.only = TRUE))
|
292 |
+
})
|
293 |
+
|
294 |
}
|
295 |
|
296 |
+
|
297 |
+
shinyApp(ui = ui, server = server)
|