Ifeanyi commited on
Commit
46f39c7
1 Parent(s): a9398ae

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +82 -556
app.R CHANGED
@@ -1,579 +1,105 @@
1
  library(shiny)
 
 
 
 
 
2
  library(shinyalert)
3
  library(shinythemes)
4
- library(shinydisconnect)
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
- # load and run the CSS script
84
- includeCSS("style.css"),
85
- # load and run the javascript script
86
- includeScript("JSCode.js"),
87
- sidebarLayout(
88
- sidebarPanel(
89
- width = 2, id = "sidebar",
90
- actionButton("info", strong("Info"), icon = icon("info")), br(), br(), br(), tags$a(img(src = "spotify.png"), href = "https://open.spotify.com/",target = "_blank"), br(), hr(),
91
- textInput("id", h5(strong("Enter Artist's Spotify Id"))),
92
- actionButton("run", strong("Get Data"), icon = icon("caret-right")),br(),hr(),br(),
93
- h5(strong("Collaborators Data")),
94
- actionButton("fetch", strong("Get Data"), icon = icon("caret-right")),hr()
95
- ),
96
-
97
- mainPanel(
98
- # go to top button
99
- spsGoTop(id = "up",right = "3%",bottom = "10%",icon = icon("arrow-up",color = "green")),
100
- fluidRow(column(12, h3(strong(span(style = "color:white;text-align:center;",h4("Related Artists Network Data")))))),
101
- fluidRow(
102
- column(12, withSpinner(reactableOutput("network_data",width = 1000,height = 400), type = 1)),
103
- ),
104
- fluidRow(
105
- column(6, downloadButton("down_csv", strong("Download CSV"), icon = icon("download"))),
106
- column(6, downloadButton("down_graphml", strong("Download GraphML"), icon = icon("download")))
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
- # activate the search box
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
- # output$out <- renderUI({
239
- # search_result()
240
- # })
241
 
242
- # render spotify
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
- # create flat file event reactive object
363
- flat_react <- eventReactive(input$run,{
364
- flat_file()
365
- })
366
-
367
- # # render outputs
368
- # output$network <- renderVisNetwork({
369
- # network_react()
370
- # })
371
- #
372
- # output$plot <- renderPlotly({
373
- # artist_react()
374
- # })
375
 
376
- # function to download nodes data file
377
- output$down_csv <- downloadHandler(
378
- filename = function() {
379
- paste("Related_artists", ".csv", sep = "")
380
- },
381
- content = function(file) {
382
- write.csv(flat_react(), file)
383
- }
384
- )
385
 
386
- output$network_data <- renderReactable({
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
- V(graph)$Name <- nodes$name
427
- V(graph)$Popularity <- nodes$popularity
428
- V(graph)$Followers <- nodes$followers
429
- V(graph)$Profile <- nodes$profile
430
- V(graph)$Images <- nodes$images
431
- V(graph)$Genre <- nodes$genre
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
- content = function(file){
504
- write.csv(collabs_react(),file)
505
  }
506
  )
507
-
508
- # function to download edges data file
509
- # output$down_edges <- downloadHandler(
510
- # filename = function() {
511
- # paste("Edges", ".csv", sep = "")
512
- # },
513
- # content = function(file) {
514
- # write.csv(edges_react(), file)
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)