Documentation



This is a technical documentation of the web applications of this project. Its purpose is to elucidate the structure of the app, the principal elements and solutions to the challenges encountered during the development process. The project was undertaken as part of the research conducted by the group Governance of the Universal Church after the Council of Trent Papal Administrative Concepts and Practices as exemplified by the Congregation of the Council between the Early Modern Period and the Present at the Max Planck Institute for Legal History and Legal Theory.



Motivation


The primary goal objective of this project is to develop a web-application for interactive search in library catalogues and provide graphic visualization of the results. The application must be multi-platform, lightweight, fast, and applicable to any thematic catalogue of any subject. Therefore, the code of this app could be used for other specified libraries.

The objective of this project is to develop a set of visualization tools for the Congregation of the Council bibliographical catalogue, offering a range of search and graphic options.


Instruments


The primary language of this application is R. Its selection was based on its simplicity, flexibility in operations with data tables, and the plasticity of its graphic visualization in the ggplot2 package. We use shiny library for creating the apps. Active elements of the application use shinyjs or JavaScript extensions.

Other packages:

library(shiny)
library(shinyjs)
library(shinythemes)
library(shinydashboard)
library(shinyWidgets)
library(shinycssloaders)
library(tidyverse)
library(data.table)
library(DT)
library(shinyBS)
library(bsplus)
library(ggplot2)
library(htmltools)
library(bib2df)

The dataset comprises over 1,300 books on a range of topics, exported from EndNote as a .bib file and transformed into a data frame for further analysis and graphic representation./p>

A preliminary step in the development process involved the preparation of the bib for further transformation. To address this requirement, the CRAN provides a package bib2df that converts .bib files to data frames, enabling the manipulation of .bib data in R in a tabular format. Documentation for this package ccan be found here CRAN or GitHub.

dataset <- data.frame(bib2df('export.bib') %>% dplyr::select(AUTHOR, YEAR, TITLE, JOURNAL, BOOKTITLE, NUMBER, PAGES, SERIES, VOLUME, EDITOR, PUBLISHER, ADDRESS, TYPE, KEYWORDS, CATEGORY, BIBTEXKEY)) #dplyr::select() for selecting only columns that have sense 


Content

Search Data


This page offers users the opportunity to access the dataset through a variety of search options. The sidebar contains two distinct search types: a general search and an advanced search.

General Search

Type names, words or numbers you wish to find

The general search function allows for the retrieval of any kind of input, including dates, names, and other elements, from all titles. The input can include character or numeric symbols, and all possible results will be displayed.

This element is based on the proxy data table. Its purpose is to facilitate the updating of an existing data table, rather than the generation of a redundant rendering. In this case, in the server side we use observeEvent() and function updateSearch() from DT:

ui 
textInput("search", "Everything in the database")

server

DTproxy <- dataTableProxy("Table")
observeEvent(input$search,{
updateSearch(DTproxy, keywords = list(global = input$search, columns = NULL))
})

Advanced Search


The second element, Advanced search, has a time slider, several searching bars: AUTHOR, TITLE, YEAR, PUBLISHER, PLACE, LANGUAGE, and the Keywords.

Time Slider


The slider permits the user to select a subset of the dataset based on the year of publication. As the slider is only capable of operating with natural numbers (i.e. 2000, but not a range such as 2000-2001), all selections can be made based on the first year in the case of double-digit numbers.

In order to create this option and set the limits, it is necessary to take the minimum min() and maximum max() possible numbers. Subsequently, in the server part, this element should be included within a reactive subset, which combines keywords and a slider. The code for this part is as follows:

ui
h4("Time period"),
sliderInput("slider", label=("Choose particular years of the bibliography"),
min=min(dataset$year), max=max(dataset$year), #set up year limits
value=c(min(dataset$year),max(dataset$year)), sep = "")
server side 
...
# the dataset is dynamic, therefore we need a reactive function

dsub_dataset <- reactive({
key_search <- paste0(c(input$type_literature), collapse = "|") # we should indicate keywords inputs we use in the checkboxes 
key_search <- gsub(",","|",key_search) #here we create a condition for applying filters to KEYWORDS column. We need to replace commas by '|'. It allows to separate keywords and apply checkbox input for filtering results
dataset[dataset$year >= input$slider[1] & dataset$year <= input$slider[2] & grepl(key_search, KEYWORDS)] #set up the year limits and applying a search and match function (grepl) to KEYWORDS column, using conditions, we indicated in key_search
})

Search Bars


The search bars permit the user to specify the results by entering a concrete search term in a particular column.

This section presents a challenge requiring the application of sophisticated techniques. Firstly, we selected ccolums Author, Title, Year, Publisher, Place, Language. These are selectizeInput with multiple selection option.

Secondly, we established a connection between bars and time sliders, as well as keywords. For this reasons, we need updateSelectizeInput for applying to a reactive dataset (key_search)

Thirdly, in renderDataTable we create a dataframe subset (res) and select_inputs parameter.

Code for this part:



ui
selectizeInput('AUTHOR','AUTHOR:',unique(dataset$AUTHOR),multiple=T),
selectizeInput('TITLE','TITLE:',unique(dataset$TITLE),multiple=T),
...
server side
observe({
updateSelectizeInput(session, 'AUTHOR', choices=key_search()$AUTHOR)
    }) ##update variables in accordance with a reactive dataset, i.e. which reacts on the slider input and keywords
...
#inside a datatable 
output$Table <- DT::renderDataTable({
    res <- as.data.frame(unique(key_search()))
    select_inputs = c('AUTHOR','TITLE','YEAR', 'PUBLISHER', 'PLACE', 'LANGUAGE')
    for (inp in select_inputs){
       if(!is.null(input[[inp]])){
       res = res[res[[inp]] %in% input[[inp]],]
          }
        }
    res <- datatable(res,
                    filter = 'top',
                    style = "bootstrap",
                    extensions = c("Buttons"),
                    options = list(scrollX = TRUE,
                           autoWidth = TRUE,
                           pageLength = 10,
                           columnDefs = list(
                                list(
                                targets = 3,
                                searchable = TRUE,
                                visible = FALSE),
                                   list(width = '400px', targets = c(4, 15, 16))),
                                   initComplete = JS("function(settings, json)
                                     {","$(this.api().table().header());","}"),
                                        dom = 'i>',
                                        info = T,
                                        buttons =  list('copy', 'print',
                                            list(
                                            extend = "collection",
                                            text = 'Bibtex',
                                            action = DT::JS("function ( e, dt, node, config ) {
                                    Shiny.setInputValue('Download_DATA', true, {priority: 'event'});
                                    }"))
                )
           )
      )
      return(res)
    })

Bibtex download


Results can be downloaded as a .bib file.

We use shinymodal dialogue for creating a windows with downloading options.







server side
myModal_bib <- function() {
    div(id = "Download_DATA",
    modalDialog(h4('You can download results as Bibtex file'),
      downloadButton("download","Download everything as Bibtex"),
      downloadButton("download_part","Download selected rows as Bibtex"),
      easyClose = TRUE, title = "Download Bibtex"))
    }

    observeEvent(input$Download_DATA, {
      showModal(myModal_bib())
        })

    output$download <- downloadHandler(
        filename = function() {
            "Bibliography"
        },
        content = function(filename) {
            df2bib(key_search()%>% dplyr::select(AUTHOR, YEAR, TITLE, JOURNAL, BOOKTITLE, EDITOR, PUBLISHER, PLACE, SERIES, VOLUME, NUMBER, PAGES, LANGUAGE, TYPE, KEYWORDS, BIBTEXKEY, CATEGORY), filename, append = F)
        })
    
    output$download_part <- downloadHandler(
        filename = function() {
            "Bibliography"
        },
        content = function(filename) {
            df2bib(key_search()[input$Table_rows_selected,], filename, append = F)    
        })    
    

Graph Drawing


The graph rendering functionality is based on the ggplot2 package, which is a universal package for graphic visualization in R.

The graphic part presents a side panel with keywords and additional elements. The option for setting the visualization offers a variety of graphics, including bubble, bar, and pie. This part also provides options for X, Y axis settings.

As not all variables are suitable for graphic representation, X, Y are constrained by categories that can be depicted as graphs.

The initial step is to create X and Y axis selectors in order to define the variables that will be visualised as a graph.

The most straightforward approach is to construct a data frame comprising the requisite variables, which will align with the general dataset. It should be noted that for the year column, all years must be expressed as natural numbers, rather than a range.




Code of this element:



#data  
First_publication <- c(dataset$First_publication)
Type <-c(dataset$Type)
Place <-c(dataset$Place)
Language <-c(dataset$Language)
graph_cols <-data.frame(First_publication, Type, Place, Language)

ui
pickerInput("Y_axis_Category",label="Y axis Category",choices=as.list(colnames(graph_cols)),selected="First_publication"),
pickerInput("X_axis_Category",label="X axis Category",choices=as.list(colnames(graph_cols)),selected="Type"),
pickerInput("Type_of_Entity",label="Type of Entity",choices=as.list(colnames(graph_cols)),selected="Type")



Interactive Desk

TThe second element is a graph selector. It is a selectInput with several options. The server-side implementation is slightly more complex, as each plot must be connected to a selector input using an 'if' condition.



Code of this element:



ui
selectInput("plot_type", 
h4("Select type of the plot"), 
c("Bubble", 'Bar', "Pie"), selected = "Bubble")

#main panel
column(12, plotOutput("plotui", height=600, brush=brushOpts("plot_brush",resetOnNew=T))) 

server side 
#create a reactive plot function, which connects selectInput and render with 'if' conditions
plot <- reactive({
if(input$plot_type == 'Bubble'){
   plot=ggplot(dsub_graph(), aes_string(x=input$X_axis_Category, 
   y=input$Y_axis_Category, size=0.1,colour=input$Z_axis_Category)) 
   + theme_bw() + geom_point()}
        
if(input$plot_type == 'Bar'){
   plot= ggplot(dsub_graph(), aes_string(x = input$X_axis_Category, 
   y = input$Y_axis_Category)) +
   geom_bar(aes_string(fill= input$Y_axis_Category), 
   position = position_stack(reverse = TRUE), stat='identity') + 
   theme_bw() + 
   theme(axis.text.x=element_blank(), axis.title.x=element_blank(), 
   axis.text.y=element_blank(), axis.title.y=element_blank()) +
   coord_flip() + 
   theme(legend.position = "right")}
        
if(input$plot_type == 'Pie'){
   plot=ggplot(dsub_graph(), aes(x = factor(1), 
   fill = factor(dsub_graph()[[input$Z_axis_Category]]))) + 
   geom_bar(width = 1) + coord_polar(theta = "y") + theme_bw() +  
   theme(legend.title=element_blank(), 
   axis.title.y=element_blank(), axis.title.x=element_blank())}
        
    plot
    })
    
output$plotui <- renderPlot({plot()})

Brush

Points on the graphic desk can be selected within an area. The data pertaining to the area will be rendered in the data table below. In order to perform this operation, it is necessary to connect the graph to the renderDataTable() function.



Code of this element:



res <- reactive({
dsub_graph <- dsub_graph()
brushedPoints(dsub_graph, input$plot_brush)})
    
output$plot_brushed_points<-DT::renderDataTable({
    res<-brushedPoints(res(),input$plot_brush)
    subset_res<-subset(res,select=c(AUTHOR, YEAR, TITLE, 
    PUBLISHER, TYPE, PLACE, KEYWORDS, LANGUAGE))
    
    datatable(subset_res, filter = 'top', style = 'bootstrap', 
      options = list(scrollX = TRUE,
      autoWidth = TRUE,
      pageLength = 10,
      info = T))
})

The plot and data table can be downoladed. The data table can be exported as .csv, .xls, .bib.



Code of this element:



ui
downloadButton('downloadPlot','Download Plot'),
actionButton("Download", "Download this table") #note, actionButton is needed as it has several downloading options
 
modal <- function() {
  div(id = "Download",
    modalDialog(downloadButton("download_csv","Download as csv"),
    downloadButton("download_xls","Download as Excel table"),
    downloadButton("download_bib","Download as Bibtex"),
    easyClose = TRUE, title = "Download Table")
    )}
    
observeEvent(input$Download, {
   showModal(modal())
   })
    
output$download_csv <- downloadHandler(
    filename = function() {
    "brushed_data.csv"
    },
    content = function(filename) {
    write_csv2(res(), filename, append = F)   
    })
    
output$download_xls <- downloadHandler(
     filename = function() {
     "brushed_data.xls"
      },
      content = function(filename) {
      write_excel_csv2(res(), filename, append = F)    
      })
    
output$download_bib <- downloadHandler(
      filename = function() {
      "Bibliography"
        },
        content = function(filename) {
        df2bib(res()%>% dplyr::select(AUTHOR, YEAR, TITLE, JOURNAL, 
        BOOKTITLE, EDITOR, PUBLISHER, PLACE, SERIES, VOLUME, NUMBER, 
        PAGES, LANGUAGE, TYPE, KEYWORDS, BIBTEXKEY, CATEGORY), 
        filename, append = F)    
        })
    
 output$downloadPlot <- downloadHandler(
        filename = function(){
        paste('Graph', '.png', sep = '')
        },
        content = function(file){
        req(plot())
        ggsave(file, plot = plot(), width = 10, height = 8, dpi = 150, device = 'png')
        })