File size: 5,114 Bytes
5e8ceb0
2029588
 
232e378
5e8ceb0
 
 
749e7d3
5e8ceb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232e378
5e8ceb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232e378
 
5e8ceb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232e378
5e8ceb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232e378
 
5e8ceb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1ffd2e5
5e8ceb0
 
 
 
 
 
 
 
232e378
 
5e8ceb0
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# Load required libraries
library(caret)
library(dplyr)
library(shiny)
library(jsonlite)  # For JSON conversion

# Use the system's Python executable or configure Python path
python_path <- "python3"
model= readRDS("model.rds")

# Sample data table to display
difficulty_table <- data.frame(
  Grade = c("Grade 3", "Grade 4", "Grade 5", "Grade 6", "Grade 7", "Grade 8"),
  `Mean Grade-Level Difficulty` = c(0.3, 0.431, 0.533, 0.611, 0.656, 0.7)
)

# Define UI for the Shiny application
ui <- fluidPage(
  titlePanel("Reading comprehension difficulty prediction using BERT embeddings"),
  fluidRow(
    column(
      width = 12,
      h4("How does this work?"),
      p("This app predicts average difficulty for an item. Difficulty can be interpreted using the table below. As the table shows, difficulty increases with grade level.
        For example, an item of difficulty 0.3 is of average difficulty for Grade 3, an item of difficulty 0.4 is of average difficulty for Grade 4, and so on. Note that as difficulty increases, probability of correct answer reduces."),

      # Display the table as a rendered output
      h5(""),
      tableOutput("difficultyTable"),

      # Add external link for NWEA norms
      p("Difficulty outputs are on a linear scale. The scale is defined to have mean difficulty of 0.3 at Grade 3 and mean difficulty 0.7 at Grade 8. This scale is based on grade level growth norms reported by ",
        a("NWEA MAP Spring 2020 Reading Student Achievement Norms",
          href = "https://www.nwea.org/uploads/MAP-Growth-Normative-Data-Overview.pdf", target = "_blank"))
    )
  ),

  # App UI elements
  sidebarLayout(
    sidebarPanel(
      width = 12,
      textInput("passage", "Passage", placeholder = "Enter passage text here"),
      textInput("question", "Question Text", placeholder = "Enter question text here"),
      textInput("correctAnswer", "Correct Answer", placeholder = "Enter the correct answer here"),

      # Numeric input to ask how many incorrect options (distractors)
      numericInput("numDistractors", "Number of Incorrect Options:", value = 1, min = 1, max = 10),

      # Dynamic UI for the distractors
      uiOutput("distractorsInputs"),

      actionButton("printBtn", "Estimate difficulty")
    ),

    mainPanel(
      h3("Estimated difficulty"),
      verbatimTextOutput("inputsOutput")
    )
  )
)

# Function to call Python script and get combined input
call_python_script <- function(passage, question, distractors) {
  # Prepare input data as JSON
  input_data <- toJSON(list(
    Passage = passage,
    QuestionText = question,
    Distractors = distractors
  ), auto_unbox = TRUE)


  # Call the Python script and capture the output
  result <- system2(
    command = python_path,         # Python executable
    args = c("bertembedtoy.py"),    # Python script name
    input = input_data,          # Pass input data as JSON
    stdout = TRUE                # Capture script output
  )

  # Return the result from Python
  return(result)
}


# Read the CSV data into a dataframe
  #embedding_df <- read.csv(text =result)
# Define server logic
server <- function(input, output, session) {
  # Reactive value to store predictions
  predictions <- reactiveVal(NULL)

  #df <- reactiveVal(data.frame())

  # Render the difficulty table in the UI
  output$difficultyTable <- renderTable({
    difficulty_table
  }, rownames = FALSE)  # Avoid showing rownames in the table

  # Dynamically generate text inputs for distractors based on user input
  output$distractorsInputs <- renderUI({
    n <- input$numDistractors  # Get the number of distractors
    if (is.null(n) || n <= 0) return(NULL)  # No inputs if value is invalid

    # Generate textInput fields dynamically
    lapply(1:n, function(i) {
      textInput(inputId = paste0("distractor", i),
                label = paste("Wrong answer", i),
                placeholder = paste("Enter wrong answer", i))
    })
  })


  # Event triggered when the button is clicked
  observeEvent(input$printBtn, {
    distractors <- c(sapply(1:input$numDistractors, function(i) {input[[paste0("distractor", i)]]}))
    # Combine the distractors into a single string
    for (i in 1:length(distractors)) {
      distractors[i]= paste0("wrong answer ",i,":", distractors[i])
    }
    distractors <- c(paste0("Correct answer:",input$correctAnswer), distractors)
    distractors=paste(distractors, collapse = " \n")
    print(distractors)

    # Call the Python script and capture the dataframe
    python_output <- call_python_script(input$passage,
                                        input$question,
                                        distractors)
    result_vector <- as.numeric(unlist(strsplit(python_output, ",")))
    result_vector= as.data.frame(t(result_vector) )
    names(result_vector) = paste0("embed.bert", 1:768)
    model_predictions= predict(model, result_vector)
    predictions(model_predictions)

  })

  # Output predictions to the UI (or use it elsewhere)
  output$inputsOutput <- renderPrint({
    predictions()
  })
}

# Run the Shiny App
shinyApp(ui = ui, server = server)