Spaces:
Running
Running
Update app.R
Browse files
app.R
CHANGED
@@ -1,579 +1,105 @@
|
|
1 |
library(shiny)
|
|
|
|
|
|
|
|
|
|
|
2 |
library(shinyalert)
|
3 |
library(shinythemes)
|
4 |
-
library(
|
5 |
library(shinycssloaders)
|
6 |
-
library(shinyWidgets)
|
7 |
-
library(spotifyr)
|
8 |
-
library(SpotifyNetwork)
|
9 |
-
library(spsComps)
|
10 |
-
library(searcher)
|
11 |
-
library(reactable)
|
12 |
-
library(visNetwork)
|
13 |
-
library(plotly)
|
14 |
-
library(bslib)
|
15 |
-
library(dplyr)
|
16 |
-
library(igraph)
|
17 |
-
|
18 |
-
# set global options
|
19 |
-
options(
|
20 |
-
shiny.browser = T,
|
21 |
-
spinner.color = "#16F529",
|
22 |
-
spinner.color.background = "#FFFFFF",
|
23 |
-
spinner.size = 2
|
24 |
-
)
|
25 |
-
|
26 |
-
# create theme
|
27 |
-
my_theme <- bs_theme(
|
28 |
-
bg = "#fdfefe",
|
29 |
-
fg = "black",
|
30 |
-
primary = "red",
|
31 |
-
base_font = font_google("Michroma"),
|
32 |
-
"font-size-base" = "0.75rem",
|
33 |
-
version = 5,
|
34 |
-
"navbar-bg" = "#16F529"
|
35 |
-
)
|
36 |
-
|
37 |
-
|
38 |
-
# define UI for application that gets Spotify network data
|
39 |
-
ui <- navbarPage(
|
40 |
-
title = strong("Spotify4NodeXL"), id = "navbar",
|
41 |
-
# useShinyalert(),
|
42 |
-
windowTitle = "Spotify4NodeXL",
|
43 |
-
|
44 |
-
footer = h5(strong(tagList(h5(span("Spotify Data Importer for",style="color:green"), a("NodeXL", href = "https://www.nodexl.com/"))))),
|
45 |
-
theme = my_theme, collapsible = T, setBackgroundImage(src = "musical.jpg"),
|
46 |
-
# add marquee visual element
|
47 |
-
tags$html(HTML("<marquee direction = right scrollamount = '12' style = 'color:green; font-size:17px;'><strong>Spotify Data Importer For <span style='color:red'>NodeXL</span></strong></marquee>"),
|
48 |
-
),
|
49 |
-
# Add social media icons to navbar
|
50 |
-
tags$style(".navbar-nav.socialmedia { display: flex; justify-content: right; flex-direction:row; padding: 5px; font-size: 20px; }"),
|
51 |
-
tags$div(class = "navbar-nav socialmedia",
|
52 |
-
tags$a(href = "https://github.com/Ifeanyi55/Spotify4NodeXL", target = "_blank",style = "text-decoration:none;", h5(strong("GitHub"),icon("github", lib = "font-awesome")))),
|
53 |
-
|
54 |
-
tabPanel(
|
55 |
-
id = "tabOne", value = "oneTab", title = strong("Home"), icon = icon("home"),
|
56 |
-
sidebarLayout(
|
57 |
-
sidebarPanel(
|
58 |
-
id = "side", width = 3, h4(strong("Credentials")), hr(),
|
59 |
-
textInput("client_id", strong("Enter Client ID")),
|
60 |
-
textInput("client_secret", strong("Enter Client SECRET")), br(),
|
61 |
-
actionButton("validate", strong("Authenticate"), icon = icon("caret-right")), br(),hr(),
|
62 |
-
textOutput("valout")
|
63 |
-
),
|
64 |
-
mainPanel(
|
65 |
-
id = "main", width = 8,
|
66 |
-
span(style = "color:blue;",h5(strong(p(id = "dateclock")))),br(),
|
67 |
-
h3(strong("Welcome to Spotify4NodeXL!")),
|
68 |
-
p(h5(strong("Before you begin scraping data, you will need to complete the steps below:"))), hr(),
|
69 |
-
p(h5(strong(tagList("STEP 1: Go to", a("https://developer.spotify.com/dashboard/", href = "https://developer.spotify.com/dashboard/"), "and login with your credentials")))), br(),
|
70 |
-
p(h5(strong("STEP 2: Create an app, and give it a name and a description"))), br(),
|
71 |
-
p(h5(strong("STEP 3: Get the generated client ID and client Secret, and return here"))), br(),
|
72 |
-
p(h5(strong("STEP 4: Enter the client ID and client Secret in the 'Credentials' box"))), br(),
|
73 |
-
p(h5(strong("STEP 5: Click the 'Authenticate' button"))), br(),
|
74 |
-
p(h5(strong("STEP 6: Wait for the system to authenticate your credentials and print out a reference ID"))), br(),
|
75 |
-
p(h5(strong("Great! You can now proceed to scraping network data via the Spotify API."))),
|
76 |
-
actionButton("nextTab", strong("Proceed")), hr()
|
77 |
-
)
|
78 |
-
)
|
79 |
-
),
|
80 |
-
tabPanel(
|
81 |
-
id = "tabA", value = "Atab", title = strong("Network Data"), icon = icon("table"),
|
82 |
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
),br(),br(),br(),
|
108 |
-
fluidRow(column(12, h3(strong(span(style = "color:white;text-align:center;",h4("Artists Collaboration Network Data")))))),
|
109 |
-
fluidRow(
|
110 |
-
column(12,withSpinner(reactableOutput("collabs_data", width = 1000, height = 400),type = 1))
|
111 |
-
),
|
112 |
-
downloadButton("down_flat",strong("Download CSV"),icon = icon("download")),
|
113 |
-
br(), hr(), uiOutput("out")
|
114 |
-
)
|
115 |
-
)
|
116 |
-
),
|
117 |
-
tabPanel(
|
118 |
-
id = "tabB", value = "Btab", title = strong("80s Hits"), icon = icon("music"),
|
119 |
-
sidebarLayout(sidebarPanel = "", mainPanel(tags$iframe(
|
120 |
-
style = "border-radius:12px",
|
121 |
-
src = "https://open.spotify.com/embed/playlist/37i9dQZF1DXb57FjYWz00c?utm_source=generator",
|
122 |
-
width = "1350px",
|
123 |
-
height = "550px",
|
124 |
-
frameBorder = "0",
|
125 |
-
allowfullscreen = "",
|
126 |
-
allow = "autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture",
|
127 |
-
loading = "lazy"
|
128 |
-
)))
|
129 |
-
),
|
130 |
-
tabPanel(
|
131 |
-
id = "tabD", strong("NodeXL YouTube"), icon = icon("youtube"),
|
132 |
-
sidebarLayout(sidebarPanel = "", mainPanel(
|
133 |
-
tags$div(tags$iframe(
|
134 |
-
width = "620",
|
135 |
-
height = "350",
|
136 |
-
src = "https://www.youtube.com/embed/xKhYGRpbwOc",
|
137 |
-
title = "YouTube video player",
|
138 |
-
frameborder = "0",
|
139 |
-
allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
140 |
-
allowfullscreen = T
|
141 |
-
),style = "text-align:center;margin-left:370px"), hr(),
|
142 |
-
tags$div(tags$iframe(
|
143 |
-
width = "620",
|
144 |
-
height = "350",
|
145 |
-
src = "https://www.youtube.com/embed/Gs4NPuKIXdo",
|
146 |
-
title = "YouTube video player",
|
147 |
-
frameborder = "0",
|
148 |
-
allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
149 |
-
allowfullscreen = T
|
150 |
-
),style = "text-align:center;margin-left:370px"), hr(),
|
151 |
-
tags$div(tags$iframe(
|
152 |
-
width = "620",
|
153 |
-
height = "350",
|
154 |
-
src = "https://www.youtube.com/embed/J1W5uqAyHTg",
|
155 |
-
title = "YouTube video player",
|
156 |
-
frameborder = "0",
|
157 |
-
allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
158 |
-
allowfullscreen = T
|
159 |
-
),style = "text-align:center;margin-left:370px"), hr(),
|
160 |
-
tags$div(tags$iframe(
|
161 |
-
width = "620",
|
162 |
-
height = "350",
|
163 |
-
src = "https://www.youtube.com/embed/zEgrruOITHw",
|
164 |
-
title = "YouTube video player",
|
165 |
-
frameborder = "0",
|
166 |
-
allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
167 |
-
allowfullscreen = T
|
168 |
-
),style = "text-align:center;margin-left:370px"), hr(),
|
169 |
-
tags$div(tags$iframe(
|
170 |
-
width = "620",
|
171 |
-
height = "350",
|
172 |
-
src = "https://www.youtube.com/embed/pwsImFyc0lE",
|
173 |
-
title = "YouTube video player",
|
174 |
-
frameborder = "0",
|
175 |
-
allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
176 |
-
allowfullscreen = T
|
177 |
-
),style = "text-align:center;margin-left:370px"), hr(),
|
178 |
-
tags$div(tags$iframe(
|
179 |
-
width = "620",
|
180 |
-
height = "350",
|
181 |
-
src = "https://www.youtube.com/embed/mjAq8eA7uOM",
|
182 |
-
title = "YouTube video player",
|
183 |
-
frameborder = "0",
|
184 |
-
allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
185 |
-
allowfullscreen = T
|
186 |
-
),style = "text-align:center;margin-left:370px")
|
187 |
-
))
|
188 |
)
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
# define server logic required to get Spotify's network data
|
193 |
-
server <- function(input, output, session) {
|
194 |
-
|
195 |
-
# set up Spotify API credentials environment
|
196 |
-
authentication <- function(id, secret) {
|
197 |
-
client_ID <- id
|
198 |
-
client_secret <- secret
|
199 |
-
|
200 |
-
# authenticate the spotify client side
|
201 |
-
Sys.setenv(SPOTIFY_CLIENT_ID = client_ID)
|
202 |
-
Sys.setenv(SPOTIFY_CLIENT_SECRET = client_secret)
|
203 |
-
|
204 |
-
access_token <- get_spotify_access_token()
|
205 |
-
}
|
206 |
-
|
207 |
-
client_id <- reactive(as.character(input$client_id))
|
208 |
-
client_secret <- reactive(as.character(input$client_secret))
|
209 |
-
authenticate <- reactive(authentication(
|
210 |
-
client_id(),
|
211 |
-
client_secret()
|
212 |
-
))
|
213 |
-
validation <- eventReactive(input$validate, {
|
214 |
-
authenticate()
|
215 |
-
})
|
216 |
-
|
217 |
-
output$valout <- renderText({
|
218 |
-
validation()
|
219 |
-
})
|
220 |
|
221 |
-
# update nav bar page using the value of the tabPanel
|
222 |
-
# (i.e. value = "Atab")
|
223 |
-
observeEvent(input$nextTab, {
|
224 |
-
updateNavbarPage(session, inputId = "navbar", selected = "Atab")
|
225 |
-
})
|
226 |
|
|
|
227 |
|
|
|
|
|
228 |
|
229 |
-
|
230 |
-
# search_input <- reactive(input$search)
|
231 |
-
|
232 |
-
# searchGoogle <- reactive(search_google(search_input(), rlang = F))
|
233 |
-
|
234 |
-
# search_result <- eventReactive(input$search_bttn, {
|
235 |
-
# searchGoogle()
|
236 |
-
#})
|
237 |
|
238 |
-
|
239 |
-
# search_result()
|
240 |
-
# })
|
241 |
|
242 |
-
|
243 |
-
# renderUI({
|
244 |
-
# tags$iframe(src = "https://open.spotify.com/")
|
245 |
-
# })
|
246 |
-
|
247 |
-
|
248 |
-
# turn text input into a reactive object
|
249 |
-
id_input <- reactive({
|
250 |
-
as.character(input$id)
|
251 |
-
})
|
252 |
-
|
253 |
-
# wrap SpotifyNetwork functions in reactive wrappers
|
254 |
-
# related_network <- reactive({
|
255 |
-
# related_artists_network(id_input())
|
256 |
-
# })
|
257 |
-
# artist_plot <- reactive({
|
258 |
-
# artists_popularity(id_input())
|
259 |
-
# })
|
260 |
-
nodes_table <- reactive({
|
261 |
-
related_artists_nodes(id_input())
|
262 |
-
})
|
263 |
-
edges_table <- reactive({
|
264 |
-
related_artists_edges(id_input())
|
265 |
-
})
|
266 |
-
|
267 |
-
# create event reactive element for each output
|
268 |
-
# network_react <- eventReactive(input$run, {
|
269 |
-
# related_network()
|
270 |
-
# })
|
271 |
-
# artist_react <- eventReactive(input$run, {
|
272 |
-
# artist_plot()
|
273 |
-
# })
|
274 |
-
nodes_react <- eventReactive(input$run, {
|
275 |
-
nodes_table()
|
276 |
-
})
|
277 |
-
edges_react <- eventReactive(input$run, {
|
278 |
-
edges_table()
|
279 |
-
})
|
280 |
-
|
281 |
-
# function to wrangle nodes & edges data into a NodeXL flat file
|
282 |
-
create_flatTabler <- function(dfN,dfE){
|
283 |
-
|
284 |
-
|
285 |
-
# scrape data from Vertex1
|
286 |
-
# as.vector(data,mode) converts the returned list into a vector
|
287 |
-
popularity <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex1"]])[[3]])
|
288 |
-
popularity <- as.vector(popularity,"numeric")
|
289 |
-
|
290 |
-
|
291 |
-
followers <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex1"]])[[4]])
|
292 |
-
followers <- as.vector(followers,"numeric")
|
293 |
-
|
294 |
-
|
295 |
-
profile <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex1"]])[[5]])
|
296 |
-
profile <- as.vector(profile,"character")
|
297 |
-
|
298 |
-
|
299 |
-
images <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex1"]])[[6]])
|
300 |
-
images <- as.vector(images,"character")
|
301 |
-
|
302 |
-
|
303 |
-
genre <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex1"]])[[7]])
|
304 |
-
genre <- as.vector(genre,"character")
|
305 |
-
|
306 |
-
|
307 |
-
# scrape data from Vertex2
|
308 |
-
# as.vector(data,mode) converts the returned list into a vector
|
309 |
-
popularityB <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex2"]])[[3]])
|
310 |
-
popularityB <- as.vector(popularityB,"numeric")
|
311 |
-
|
312 |
-
|
313 |
-
followersB <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex2"]])[[4]])
|
314 |
-
followersB <- as.vector(followersB,"numeric")
|
315 |
-
|
316 |
-
|
317 |
-
profileB <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex2"]])[[5]])
|
318 |
-
profileB <- as.vector(profileB,"character")
|
319 |
-
|
320 |
-
|
321 |
-
imagesB <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex2"]])[[6]])
|
322 |
-
imagesB <- as.vector(imagesB,"character")
|
323 |
-
|
324 |
-
|
325 |
-
genreB <- apply(dfE,1,function(df) subset(dfN,dfN$name == df[["Vertex2"]])[[7]])
|
326 |
-
genreB <- as.vector(genreB,"character")
|
327 |
-
|
328 |
-
|
329 |
-
# assign scraped data for Vertex1 to new columns
|
330 |
-
dfE$`Vertex1 popularity` <- popularity
|
331 |
-
dfE$`Vertex1 followers` <- followers
|
332 |
-
dfE$`Vertex1 profile` <- profile
|
333 |
-
dfE$`Vertex1 images` <- images
|
334 |
-
dfE$`Vertex1 genre` <- genre
|
335 |
-
|
336 |
-
# assign scraped data for Vertex2 to new columns
|
337 |
-
dfE$`Vertex2 popularity` <- popularityB
|
338 |
-
dfE$`Vertex2 followers` <- followersB
|
339 |
-
dfE$`Vertex2 profile` <- profileB
|
340 |
-
dfE$`Vertex2 images` <- imagesB
|
341 |
-
dfE$`Vertex2 genre` <- genreB
|
342 |
-
|
343 |
-
dfE$`Edge Weight` <- round(dfE$`Vertex1 popularity`/dfE$`Vertex2 popularity`,2)
|
344 |
-
|
345 |
-
dfE <- dfE |> relocate(`Edge Weight`,.after = Vertex2)
|
346 |
-
|
347 |
-
return(dfE)
|
348 |
-
|
349 |
-
|
350 |
-
}
|
351 |
-
|
352 |
-
|
353 |
-
# parse edges_react to function
|
354 |
-
flat_file <- reactive({create_flatTabler(nodes_react(),edges_react())})
|
355 |
-
|
356 |
-
# add edge metadata
|
357 |
-
# flat_file()["Edge Weight"] <- reactive({round(flat_file()$`Vertex1 popularity`/flat_file()$`Vertex2 popularity`,2)})
|
358 |
-
#
|
359 |
-
# flat_file() <- flat_file() |>
|
360 |
-
# reactive({relocate(`Edge Weight`,.after = Vertex2)})
|
361 |
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
# network_react()
|
370 |
-
# })
|
371 |
-
#
|
372 |
-
# output$plot <- renderPlotly({
|
373 |
-
# artist_react()
|
374 |
-
# })
|
375 |
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
paste("Related_artists", ".csv", sep = "")
|
380 |
-
},
|
381 |
-
content = function(file) {
|
382 |
-
write.csv(flat_react(), file)
|
383 |
-
}
|
384 |
-
)
|
385 |
|
386 |
-
|
387 |
-
|
388 |
-
tryCatch(
|
389 |
-
{
|
390 |
-
reactable(flat_react(),
|
391 |
-
theme = reactableTheme(
|
392 |
-
highlightColor = "#00e600",
|
393 |
-
borderColor = "#00e600",
|
394 |
-
borderWidth = 3
|
395 |
-
),
|
396 |
-
outlined = T,
|
397 |
-
bordered = T,
|
398 |
-
filterable = T,
|
399 |
-
striped = T,
|
400 |
-
compact = T,
|
401 |
-
highlight = T,
|
402 |
-
defaultColDef = colDef(
|
403 |
-
align = "center",
|
404 |
-
headerStyle = list(background = "#00e600")
|
405 |
-
),
|
406 |
-
paginationType = "simple"
|
407 |
-
)
|
408 |
-
|
409 |
-
},
|
410 |
-
error = function(e){
|
411 |
-
message("There was an error!")
|
412 |
-
print(e)
|
413 |
-
}
|
414 |
-
)
|
415 |
-
})
|
416 |
-
|
417 |
-
# function to generate GraphML file
|
418 |
-
create_graphml <- function(nodes,edges){
|
419 |
-
|
420 |
-
# create new graph object
|
421 |
-
graph <- graph_from_data_frame(edges)
|
422 |
-
|
423 |
-
|
424 |
-
# add attributes to graph nodes
|
425 |
{
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
return(graph)
|
435 |
-
|
436 |
-
}
|
437 |
-
|
438 |
-
# make function reactive
|
439 |
-
graphml_react <- reactive({
|
440 |
-
create_graphml(nodes_react(),
|
441 |
-
edges_react())})
|
442 |
-
|
443 |
-
# make function event reactive so that it is triggered
|
444 |
-
# by run action button
|
445 |
-
graphmlReact <- eventReactive(input$run,{
|
446 |
-
graphml_react()
|
447 |
-
})
|
448 |
-
|
449 |
-
|
450 |
-
# write GraphML download function
|
451 |
-
output$down_graphml <- downloadHandler(
|
452 |
-
filename = function(){
|
453 |
-
paste("Related_artists",".graphml",sep = "")
|
454 |
-
},
|
455 |
-
content = function(file){
|
456 |
-
write_graph(graphmlReact(),file,format = "graphml")
|
457 |
-
}
|
458 |
-
)
|
459 |
-
|
460 |
-
# import get_artists_collaborators() function
|
461 |
-
source("Collaborators.R")
|
462 |
-
|
463 |
-
# wrap in reactive wrappers
|
464 |
-
artists_collaborations <- reactive({get_artists_collaborators(id_input())})
|
465 |
-
|
466 |
-
# make event reactive
|
467 |
-
collabs_react <- eventReactive(input$fetch,{artists_collaborations()})
|
468 |
-
|
469 |
-
output$collabs_data <- renderReactable({
|
470 |
-
tryCatch(
|
471 |
-
{
|
472 |
-
reactable(collabs_react(),
|
473 |
-
theme = reactableTheme(
|
474 |
-
highlightColor = "#3498DA",
|
475 |
-
borderColor = "#3498DA",
|
476 |
-
borderWidth = 3
|
477 |
-
),
|
478 |
-
outlined = T,
|
479 |
-
bordered = T,
|
480 |
-
filterable = T,
|
481 |
-
striped = T,
|
482 |
-
compact = T,
|
483 |
-
highlight = T,
|
484 |
-
defaultColDef = colDef(
|
485 |
-
align = "center",
|
486 |
-
headerStyle = list(background = "#3498DA")
|
487 |
-
),
|
488 |
-
paginationType = "simple"
|
489 |
-
)
|
490 |
-
},
|
491 |
-
error = function(e){
|
492 |
-
message("There was an error!")
|
493 |
-
print(e)
|
494 |
-
}
|
495 |
-
)
|
496 |
-
})
|
497 |
-
|
498 |
-
# activate download button
|
499 |
-
output$down_flat <- downloadHandler(
|
500 |
-
filename = function(){
|
501 |
-
paste("CollabData",".csv",sep = "")
|
502 |
},
|
503 |
-
|
504 |
-
|
505 |
}
|
506 |
)
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
# output$edges <- renderReactable({
|
520 |
-
# reactable(edges_react(),
|
521 |
-
# theme = reactableTheme(
|
522 |
-
# highlightColor = "#00FFAB",
|
523 |
-
# borderColor = "#00FFAB"
|
524 |
-
# ),
|
525 |
-
# outlined = T,
|
526 |
-
# bordered = T,
|
527 |
-
# filterable = T,
|
528 |
-
# striped = T,
|
529 |
-
# compact = T,
|
530 |
-
# highlight = T,
|
531 |
-
# defaultColDef = colDef(
|
532 |
-
# align = "center",
|
533 |
-
# headerStyle = list(background = "#00FFAB")
|
534 |
-
# ),
|
535 |
-
# paginationType = "simple"
|
536 |
-
# )
|
537 |
-
# })
|
538 |
-
|
539 |
-
observeEvent(input$info, {
|
540 |
-
shinyalert(
|
541 |
-
title = "About Software", closeOnEsc = T, confirmButtonCol = "#006400",
|
542 |
-
imageUrl = "spotify.png",
|
543 |
-
closeOnClickOutside = T,
|
544 |
-
confirmButtonText = "Got It", showConfirmButton = T,
|
545 |
-
animation = "pop", timer = 20000,
|
546 |
-
text = "Spotify4NodeXL allows a user to
|
547 |
-
query the Spotify API and get network data
|
548 |
-
of artists that are related to a particular artist. You can also get data of artists that have collaborated together.
|
549 |
-
|
550 |
-
It typically takes between 16 and 20
|
551 |
-
seconds to get a response from the Spotify server for related
|
552 |
-
artists network data and more than that for artists collaboration
|
553 |
-
network data."
|
554 |
-
)
|
555 |
-
})
|
556 |
-
|
557 |
-
|
558 |
-
observeEvent(input$fetch,{
|
559 |
-
shinyalert(
|
560 |
-
closeOnEsc = T,
|
561 |
-
confirmButtonText = "Got It!",
|
562 |
-
confirmButtonCol = "#3498DA",
|
563 |
-
closeOnClickOutside = T,
|
564 |
-
showConfirmButton = T,
|
565 |
-
animation = "slide-from-top",
|
566 |
-
cancelButtonText = T,
|
567 |
-
timer = 5000,
|
568 |
-
text = "This may take a while to run, sorry!"
|
569 |
-
|
570 |
-
)
|
571 |
-
})
|
572 |
-
|
573 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
574 |
}
|
575 |
|
576 |
-
|
577 |
-
|
578 |
-
# Run the application
|
579 |
shinyApp(ui = ui, server = server)
|
|
|
1 |
library(shiny)
|
2 |
+
library(fresh)
|
3 |
+
library(bs4Dash)
|
4 |
+
library(shinyjs)
|
5 |
+
library(reactable)
|
6 |
+
library(openalexR)
|
7 |
library(shinyalert)
|
8 |
library(shinythemes)
|
9 |
+
library(reactablefmtr)
|
10 |
library(shinycssloaders)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
+
options(spinner.color.background = "#FFFFFF",
|
13 |
+
spinner.color = "#37a1e7",
|
14 |
+
spinner.size = 2)
|
15 |
+
|
16 |
+
|
17 |
+
ui <- dashboardPage(scrollToTop = T,
|
18 |
+
title = "OpenAlex4NodeXL",dark = NULL,help = NULL,
|
19 |
+
dashboardHeader(h2(strong("OpenAlex For NodeXL"),style = "margin-left:380px;color:#1b7ccc;")),
|
20 |
+
dashboardSidebar(id = "side",
|
21 |
+
actionButton("info",strong("Info"),style = "margin-left:75px;color:black;"),hr(),
|
22 |
+
h6(strong("Select Search Window"),style = "margin-left:35px;color:black;"),
|
23 |
+
dateInput("start",h6(strong("From"),style = "margin-left:80px;color:black;")),
|
24 |
+
dateInput("end",h6(strong("To"),style = "margin-left:90px;color:black;")),
|
25 |
+
textInput("text",h6(strong("Keyword Search"),style = "margin-left:40px;color:black;"),placeholder = "Enter keyword(s) here"),
|
26 |
+
actionButton("query",strong("Search"),icon = icon("search"),style = "margin-left:60px;color:black;"),hr(),
|
27 |
+
tags$a(href = "https://github.com/Ifeanyi55/OpenAlex4NodeXL", target = "_blank", h4(strong("GitHub"),icon("github", lib = "font-awesome"),style = "text-align:left;")),
|
28 |
+
minified = F),
|
29 |
+
dashboardBody(
|
30 |
+
includeCSS("styles.css"),
|
31 |
+
includeScript("code.js"),
|
32 |
+
fluidRow(
|
33 |
+
br(),style = "text-align:center;"),h4(strong("Author To Publication Network Data")),
|
34 |
+
withSpinner(reactableOutput("table",width = "100%",height = 400),type = 1),br(),
|
35 |
+
downloadButton("download",strong("Download CSV"),icon = icon("download"),style = "margin-left:1px;")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
)
|
37 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
|
|
|
|
|
|
|
|
|
|
39 |
|
40 |
+
server <- function(input,output,session){
|
41 |
|
42 |
+
# read OpenAlex4NodeXL script
|
43 |
+
source("OpenAlex4NodeXL.R")
|
44 |
|
45 |
+
start_date <- reactive({input$start})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
+
end_date <- reactive({input$end})
|
|
|
|
|
48 |
|
49 |
+
keywords <- reactive({input$text})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
|
51 |
+
# make OpenAlex4NodeXL() a reactive function
|
52 |
+
OpenAlex4NodeXL_reactive <- reactive({
|
53 |
+
OpenAlex4NodeXL(
|
54 |
+
keywords = c(unlist(strsplit(keywords(),split = ","))),
|
55 |
+
pub_start_date = start_date(),
|
56 |
+
pub_end_date = end_date())
|
57 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
+
run_OpenAlex4NodeXL <- eventReactive(input$query,{
|
60 |
+
OpenAlex4NodeXL_reactive()
|
61 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
63 |
+
output$table <- renderReactable({
|
64 |
+
tryCatch(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
{
|
66 |
+
reactable(run_OpenAlex4NodeXL(),
|
67 |
+
theme = reactableTheme(
|
68 |
+
borderColor = "#37a1e7",
|
69 |
+
borderWidth = 3
|
70 |
+
),
|
71 |
+
compact = T,
|
72 |
+
filterable = T,
|
73 |
+
bordered = T)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
},
|
75 |
+
error = function(error){
|
76 |
+
safeError(error = "Please make sure to enter dates in chronological order")
|
77 |
}
|
78 |
)
|
79 |
+
})
|
80 |
+
|
81 |
+
# activate download handler
|
82 |
+
output$download <- downloadHandler(
|
83 |
+
filename = function(){
|
84 |
+
paste("Author2Pub",".csv",sep = "")
|
85 |
+
},
|
86 |
+
content = function(file){
|
87 |
+
write.csv(run_OpenAlex4NodeXL(),file,row.names = F)
|
88 |
+
}
|
89 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
|
91 |
+
observeEvent(input$info,{
|
92 |
+
shinyalert(title = "Info",
|
93 |
+
text = "Please note that the bigger the search window, the more data is collected. The more data is collected, the longer the runtime and the longer it takes to commence file download.",
|
94 |
+
confirmButtonCol = "#37a1e7",
|
95 |
+
closeOnEsc = T,
|
96 |
+
closeOnClickOutside = T,
|
97 |
+
showConfirmButton = T,
|
98 |
+
timer = 10000,
|
99 |
+
animation = "slide-from-top"
|
100 |
+
)
|
101 |
+
})
|
102 |
}
|
103 |
|
104 |
+
# Run the application
|
|
|
|
|
105 |
shinyApp(ui = ui, server = server)
|