Documentation



This is a technical documentation to the web applications of this project. It aims to explain the structure of the app, the main elements and solutions of the difficult issues that appeared during the development. The project is done as a part of the research of 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


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

The main task of this project is creating the visualization tools for the Congregation of the Council bibliographical catalog with multiple search and graphic options. It should be fast, clear and correspond to the academic needs.


Instruments


The main language of this app is R. We chose it because of simplicity, flexibility in operations with data tables and plasticity of the graphic visualization in 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 consists of 1.300+ thematic books, exported from the EndNote as the .bib file and transformed into a data frame in order to manipulate with this dataset, using different search options.

A very basic step of the development was a preparation of the bib for further transformation. For these needs the CRAN has a package bib2df. This package converts .bib to data frame that make operations with the .bib data possible in R in a table format. Documentation for this package in 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 provides an access to the dataset with different searching options. In the sidebar there are two kinds of search: the general search and advanced search.

General Search

Type names, words or numbers you wish to find

The general bar searches any kind of input among all titles, including dates, names and any other elements. The input can include character or numeric symbols and will give all possible results.

This element is based on the proxy data table. We need it for updating the already existed data table instead of a surplus 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 allows to cut the dataset by the year of publication. Since the slider can operate only with the natural numbers (i.e. 2000, but not a range 2000-2001), all selection is made by the first year in the case of double numbers.

For creating this option and set up the limits, we should take the minimum min() and maximum max() possible numbers. Then in the server part this element should be included inside a reactive subset, which combines keywords and slider. The code for this part:

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 allow to specify the results by concrete search in particular columns.

This part needs some tricky solutions. Firstly, we selected solums Author, Title, Year, Publisher, Place, Language. These are selectizeInput with multiple selection option.

Secondly, we connected bars with time slider and 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


Graph rendering is build on the ggplot2 package. This is a universal package for graphic visualization in R.

Graphic part has the same side panel with keywords and some additional elements. An option for setting the visualization has several types of graphics: bubble, bar and pie. Timeslider is moved to the center of the main part with an interactive desk. This part has options for X, Y and Z axis setting.

Since not all variables make sense in the graphic visualization, X, Y, Z are limited by categories that can be visualized as graphs.

The basic step is to create X, Y, Z axis selectors in order to define, what kind of variables will be visualized as a graph.

The simplest solution is to create a data frame with that variables, which will match the general dataset. Note, for setting the year column, all years should be natural numbers, not a range. For that reason, ‘First publication’ is used in this graph as the year variable.




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("Z_axis_Category",label="Z axis Category",choices=as.list(colnames(graph_cols)),selected="Type")



Interactive Desk

The second element is a graph selector. It’s a selectInput with several options. The server side is a bit tricky, since each plot should be connected with a selector input, using ‘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 in an area. The data of the area will be rendered in the data table below. For this operation, the graph should be connected to 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')
        })