SDtageditoR / app.R
cella110n's picture
Upload app.R
879c339
rm(list=ls())
# 必要なライブラリを読み込む
library(shiny)
# 事前に定義された関数を含むRスクリプトを読み込む
source("./script.R")
# パネル用関数
orderInputPanel <- function() {
conditionalPanel(
condition = "output.showOrderInput",
numericInput("target_order", "Target Order:", value = 1, min = 1),
actionButton("confirm_order", "Confirm Order"),
actionButton("cancel_order", "Cancel")
)
}
# キャプションの並び替え関数
sort_captions_by_search <- function(data, search_term) {
if (is.null(search_term) || search_term == "") {
return(data)
}
# 検索語でのマッチングスコアを計算
scores <- as.integer(grepl(search_term, data$caption, ignore.case = TRUE))
# スコアの降順でデータフレームを並び替え
return(data[order(-scores), ])
}
# UIを定義
ui <- fluidPage(
# アプリケーションのタイトル
titlePanel("SDtagEditoR"),
# CSSを追加
tags$style(type = "text/css",
"#image_captions { white-space: pre-wrap; }",
".shiny-action-button { margin-bottom: 50px; }"
),
# JavaScript関数を追加
tags$head(tags$script(HTML("
function copyToClipboard(text) {
var dummy = document.createElement('textarea');
document.body.appendChild(dummy);
dummy.value = text;
dummy.select();
document.execCommand('copy');
document.body.removeChild(dummy);
}
"))),
tags$script(HTML("
function handleCaptionClick(caption, source) {
var operationModeElement = document.getElementById('operation_mode_UI');
if (source === 'remove_RDB') {
Shiny.setInputValue('remove_relation_index', caption, {priority: 'event'});
} else if (operationModeElement && operationModeElement.innerText === 'direct remove') {
if (source === 'frequency') {
Shiny.setInputValue('clicked_caption_frequency', caption, {priority: 'event'});
} else {
Shiny.setInputValue('clicked_caption', caption, {priority: 'event'});
}
} else if (operationModeElement && operationModeElement.innerText === 'Database Making') {
Shiny.setInputValue('clicked_caption_RDB', caption, {priority: 'event'});
} else {
copyToClipboard(caption);
}
}
")),
# ユーザー入力を受け付ける部分
sidebarLayout(
sidebarPanel(
width = 3,
uiOutput("operation_mode_UI"),
# ディレクトリパスを入力するテキストボックス
textInput("directory_path", "Enter the directory path:", value = ""),
# キャプションデータを読み込むボタン
actionButton("load_data", "Load Captions Data"),
tags$br(),
tags$br(),
conditionalPanel(
condition = "output.dataLoaded",
# 画像の選択
selectInput("selected_image", "Choose an image:", choices = NULL),
# 前後の画像に移動するボタン
actionButton("prev_image", "Prev"),
actionButton("next_image", "Next"),
tags$br(),
tags$br(),
# Direct Removerモード
actionButton("direct_remover", "Enter Direct Remover Mode"),
conditionalPanel(
condition = "output.operation_mode_UI === 'direct remove'",
actionButton("exit_direct_remover", "Exit Direct Remover")
),
tags$br(),
tags$br(),
# Database Makingモード
actionButton("enter_database_mode", "Enter Database Making Mode"),
conditionalPanel(
condition = "output.operation_mode_UI === 'Database Making'",
actionButton("exit_database_mode", "Exit Database Making Mode"),
tags$br(),
tags$br(),
textInput("parent_caption_input", "Parent Caption:"),
textInput("child_caption_input", "Child Caption:"),
actionButton("add_to_RDB", "Add to Database"),
actionButton("clear_inputs_RDB", "Clear"),
tags$br(),
tags$br(),
fileInput("load_relation_database", "Load Relation DB", accept = ".csv"),
downloadButton("save_relation_database", "Save Relation DB"),
tags$br(),
tags$br(),
actionButton("aggregate_single", "Aggregation Single"),
actionButton("aggregate_all", "Aggregation All")
),
tags$br(),
tags$br(),
conditionalPanel(
condition = "output.operation_mode_UI !== 'direct remove' && output.operation_mode_UI !== 'Database Making'",
actionButton("shuffle_single", "Shuffle Single"),
actionButton("shuffle_all", "Shuffle All"),
tags$br(),
tags$br(),
numericInput("frequency_threshold", "Frequency Threshold:", value = 5, min = 1),
actionButton("remove_low_freq", "Remove Low Frequency Captions"),
tags$br(),
tags$br(),
textInput("edit_caption", "Caption to Edit:", ""),
actionButton("remove_single", "Remove Single"),
actionButton("remove_from_all", "Remove All"),
# actionButton("edit_interactively", "Edit Interactively"),
tags$br(),
actionButton("add_single", "Add Single"),
actionButton("add_to_all", "Add All"),
tags$br(),
actionButton("move_caption", "Move Single"),
actionButton("move_caption_all", "Move All"),
tags$br(),
# actionButton("remove_related_single", "Remove Related Single"),
# actionButton("remove_related_all", "Remove Related All"),
orderInputPanel(),
# textInputPanel(), # ここで関数を呼び出しています
tags$br(),
),
tags$br(),
tags$br(),
actionButton("restore_captions", "Restore captions"),
tags$br(),
actionButton("output_captions", "Output Edited Captions")
)
),
mainPanel(
fluidRow(
column(6,
imageOutput("image_display", width = "100%", height = "50%"),
tags$br(),
uiOutput("image_captions")
),
column(6,
verbatimTextOutput("log_output"),
textInput("search_caption", "Search Caption:", ""),
actionButton("search_button", "Search"),
actionButton("clear_search", "Clear"),
tags$br(),
tags$br(),
fluidRow(
column(4, tableOutput("selected_image_captions")),
column(4, tableOutput("caption_frequency_table_with_links")),
column(4, tableOutput("relation_database_display"))
)
)
)
)
)
)
# サーバーロジックを定義
server <- function(input, output, session) {
# リアクティブな変数を定義
directory_path <- reactiveVal()
captions_data <- reactiveVal()
caption_frequency <- reactiveVal()
operation_mode <- reactiveVal("Normal")
log_text <- reactiveVal("Log:\n")
## ログ表示用
# ログを追加する関数
add_log <- function(message) {
current_log <- log_text()
new_log <- paste(current_log, message, "\n")
log_text(new_log)
}
# ログを更新する関数
update_log <- function(message) {
log_text(message)
}
## パネルの条件分岐用
# 読み込み確認
output$dataLoaded <- reactive({
!is.null(captions_data())
})
outputOptions(output, "dataLoaded", suspendWhenHidden=FALSE)
# Operation ModeをUI側に渡す
output$operation_mode_UI <- renderText({
return(operation_mode())
})
outputOptions(output, "operation_mode_UI", suspendWhenHidden=FALSE)
## 一時保存用
# 一時保存用のリアクティブ変数を作成
temp_captions_data <- reactiveVal()
# input$selected_imageが変化したときに、現在のcaptions_data()を一時保存
observeEvent(input$selected_image, {
temp_captions_data(captions_data())
}, ignoreNULL = TRUE)
## 検索機能
searched_caption <- reactiveVal(NULL)
observeEvent(input$search_button, {
searched_caption(input$search_caption)
})
observeEvent(input$clear_search, {
searched_caption(NULL)
updateTextInput(session, "search_caption", value = "")
})
## Load Data
observeEvent(input$load_data, {
# ボタンが押されたときに実行されるコード
temp_directory_path <- isolate(input$directory_path)
temp_captions_data <- read_captions_from_directory(temp_directory_path)
# エラーが発生した場合、Shiny UI上でエラーメッセージを表示
if (is.null(temp_captions_data)) {
showNotification("Error in read_captions_from_directory: No .txt files found in the specified directory.", type = "error")
return()
}
temp_caption_frequency <- get_caption_frequency(temp_captions_data)
# リアクティブな変数を更新
directory_path(temp_directory_path)
captions_data(temp_captions_data)
caption_frequency(temp_caption_frequency)
# 画像の選択のための選択肢を更新
unique_images <- unique(captions_data()$image_path)
updateSelectInput(session, "selected_image", choices = unique_images, selected = unique_images[1])
update_log("Captions data loaded.")
})
# 画像の移動
observeEvent(input$prev_image, {
current_index <- which(unique(captions_data()$image_path) == input$selected_image)
if (current_index > 1) {
updateSelectInput(session, "selected_image", selected = unique(captions_data()$image_path)[current_index - 1])
}
})
observeEvent(input$next_image, {
current_index <- which(unique(captions_data()$image_path) == input$selected_image)
if (current_index < length(unique(captions_data()$image_path))) {
updateSelectInput(session, "selected_image", selected = unique(captions_data()$image_path)[current_index + 1])
}
})
# Direct Removerモード
observeEvent(input$direct_remover, {
operation_mode("direct remove")
update_log("DIRECT REMOVER MODE.")
})
observeEvent(input$exit_direct_remover, {
operation_mode("Normal")
update_log("Exited Direct Remover mode.")
})
## Relation Databaseモード
relation_database <- reactiveVal(data.frame(parent_caption = character(0), child_caption = character(0), stringsAsFactors = FALSE))
observeEvent(input$enter_database_mode, {
operation_mode("Database Making")
update_log("DATABASE MAKING MODE.")
})
observeEvent(input$exit_database_mode, {
operation_mode("Normal")
update_log("Exited Database Making mode.")
})
observeEvent(input$clicked_caption_RDB, {
if (input$parent_caption_input == "") {
updateTextInput(session, "parent_caption_input", value = input$clicked_caption_RDB)
} else {
updateTextInput(session, "child_caption_input", value = input$clicked_caption_RDB)
}
})
observeEvent(input$add_to_RDB, {
# 親キャプションまたは子キャプションが空欄の場合、または親キャプションと子キャプションが同じ場合、エラーログを出力
if (input$parent_caption_input == "" || input$child_caption_input == "" || input$parent_caption_input == input$child_caption_input) {
showNotification("Error: Invalid parent or child caption.", type = "error")
return()
}
# 同じ組み合わせがrelation_databaseに存在するかチェック
existing_relation <- with(relation_database(), parent_caption == input$parent_caption_input &
child_caption == input$child_caption_input)
if (any(existing_relation)) {
showNotification("Error: This relation already exists in the database.", type = "error")
return() # このobserveEventの処理を終了
}
# relation_databaseに関係を追加するコード
new_relation <- data.frame(parent_caption = input$parent_caption_input,
child_caption = input$child_caption_input)
updated_database <- rbind(relation_database(), new_relation)
relation_database(updated_database)
update_log("Relation added to database.")
})
observeEvent(input$clear_inputs_RDB, {
updateTextInput(session, "parent_caption_input", value = "")
updateTextInput(session, "child_caption_input", value = "")
})
observeEvent(input$aggregate_single, {
updated_data <- captions_data()
# 選択されている画像のcaptionを別変数に保存
captions_for_selected_image <- updated_data$caption[updated_data$image_path == input$selected_image]
if (nrow(relation_database()) > 0) {
for (i in 1:nrow(relation_database())) {
parent <- relation_database()$parent_caption[i]
child <- relation_database()$child_caption[i]
# parent_captionの存在確認を行い、存在する場合remove_caption_and_adjust_orderを実行
if (parent %in% captions_for_selected_image) {
updated_data <- remove_caption_and_adjust_order(updated_data, input$selected_image, child)
}
}
}
captions_data(updated_data)
# キャプションの頻度を更新
updated_caption_frequency <- get_caption_frequency(captions_data())
caption_frequency(updated_caption_frequency)
update_log("Captions aggregated for this image.")
})
observeEvent(input$aggregate_all, {
updated_data <- captions_data()
if (nrow(relation_database()) > 0) {
for (image_path in unique(updated_data$image_path)) {
# 各画像のcaptionを別変数に保存
captions_for_image <- updated_data$caption[updated_data$image_path == image_path]
for (i in 1:nrow(relation_database())) {
parent <- relation_database()$parent_caption[i]
child <- relation_database()$child_caption[i]
# parent_captionの存在確認を行い、存在する場合remove_caption_and_adjust_orderを実行
if (parent %in% captions_for_image) {
updated_data <- remove_caption_and_adjust_order(updated_data, image_path, child)
}
}
}
}
captions_data(updated_data)
# キャプションの頻度を更新
updated_caption_frequency <- get_caption_frequency(captions_data())
caption_frequency(updated_caption_frequency)
update_log("Captions aggregated for all images.")
})
observeEvent(input$remove_relation_index, {
index_to_remove <- as.numeric(input$remove_relation_index)
# 削除する関係の情報を保存
parent_to_remove <- relation_database()$parent_caption[index_to_remove]
child_to_remove <- relation_database()$child_caption[index_to_remove]
# 関係を削除
relation_database(relation_database()[-index_to_remove, ])
# ログを更新
update_log(paste("Removed relation:", parent_to_remove, "->", child_to_remove))
})
# Relation Databaseをセーブ
output$save_relation_database <- downloadHandler(
filename = function() {
paste("relation_database_", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(relation_database(), file, row.names = FALSE)
},
contentType = "text/csv"
)
observeEvent(input$load_relation_database, {
# ファイルが選択された場合のみ処理を実行
if (!is.null(input$load_relation_database$datapath)) {
# CSVファイルを読み込む
new_data <- read.csv(input$load_relation_database$datapath, stringsAsFactors = FALSE)
# データの整合性チェック
if (!all(c("parent_caption", "child_caption") %in% names(new_data))) {
showNotification("Error: The file does not have the required columns (parent_caption and child_caption).", type = "error")
return()
}
if (any(is.na(new_data$parent_caption)) || any(is.na(new_data$child_caption))) {
showNotification("Error: The file contains missing values in parent_caption or child_caption.", type = "error")
return()
}
if (any(nchar(new_data$parent_caption) == 0) || any(nchar(new_data$child_caption) == 0)) {
showNotification("Error: The file contains empty strings in parent_caption or child_caption.", type = "error")
return()
}
# relation_database()に新しいデータを格納
relation_database(new_data)
update_log("Relation database loaded.")
}
})
## Shuffle Order for Current Image
observeEvent(input$shuffle_single, {
if (operation_mode() != "Normal") {
showNotification("Another operation is in progress. Please finish or cancel it first.", type = "error")
return()
}
current_image <- input$selected_image
updated_data <- captions_data()
captions_for_current_image <- filter(updated_data, image_path == current_image)
# Orderをランダムにする
new_order <- sample(1:nrow(captions_for_current_image), nrow(captions_for_current_image))
captions_for_current_image$caption_order <- new_order
# データを更新
updated_data[updated_data$image_path == current_image, ] <- captions_for_current_image
captions_data(updated_data)
update_log("captions of this image was shauffled.")
})
# Shuffle Order for All Images
observeEvent(input$shuffle_all, {
if (operation_mode() != "Normal") {
showNotification("Another operation is in progress. Please finish or cancel it first.", type = "error")
return()
}
updated_data <- captions_data()
unique_images <- unique(updated_data$image_path)
for (image in unique_images) {
captions_for_image <- filter(updated_data, image_path == image)
new_order <- sample(1:nrow(captions_for_image), nrow(captions_for_image))
captions_for_image$caption_order <- new_order
updated_data[updated_data$image_path == image, ] <- captions_for_image
}
captions_data(updated_data)
update_log("captions of all images was shauffled.")
})
# remove_low_freqボタンが押されたときの処理
observeEvent(input$remove_low_freq, {
if (operation_mode() != "Normal") {
showNotification("Another operation is in progress. Please finish or cancel it first.", type = "error")
return()
}
if (!is.null(captions_data())) {
threshold <- input$frequency_threshold
updated_data <- remove_low_frequency_captions(captions_data(), threshold)
captions_data(updated_data)
# キャプションの頻度を更新
updated_caption_frequency <- get_caption_frequency(captions_data())
caption_frequency(updated_caption_frequency)
}
update_log("Low frequency captions removed.")
})
# Remove Captions from All Images
observeEvent(input$remove_from_all, {
if (operation_mode() != "Normal") {
showNotification("Another operation is in progress. Please finish or cancel it first.", type = "error")
return()
}
# 指定されたキャプションをすべての画像から削除
target_caption <- input$edit_caption
updated_data <- captions_data()
unique_images <- unique(updated_data$image_path[updated_data$caption == target_caption])
for (image in unique_images) {
updated_data <- remove_caption_and_adjust_order(updated_data, image, target_caption)
}
captions_data(updated_data)
# キャプションの頻度を更新
updated_caption_frequency <- get_caption_frequency(captions_data())
caption_frequency(updated_caption_frequency)
update_log("Caption removed from all images.")
})
# Remode/Add Single/Add all/move captionの処理
showOrderInput <- reactiveVal(FALSE)
showTextInput <- reactiveVal(FALSE)
observeEvent(input$remove_single, {
if (operation_mode() != "Normal") {
showNotification("Another operation is in progress. Please finish or cancel it first.", type = "error")
return()
}
target_caption <- input$edit_caption
if (target_caption %in% captions_data()$caption[captions_data()$image_path == input$selected_image]) {
updated_data <- remove_caption_and_adjust_order(captions_data(), input$selected_image, target_caption)
captions_data(updated_data)
update_log("Caption removed from this image.")
} else {
showNotification("Error: Caption not found in the selected image.", type = "error")
}
})
observeEvent(input$add_single, {
if (operation_mode() != "Normal") {
showNotification("Another operation is in progress. Please finish or cancel it first.", type = "error")
return()
}
operation_mode("add_single")
showOrderInput(TRUE)
update_log("ADD SINGLE MODE.")
})
observeEvent(input$add_to_all, {
if (operation_mode() != "Normal") {
showNotification("Another operation is in progress. Please finish or cancel it first.", type = "error")
return()
}
operation_mode("add_all")
showOrderInput(TRUE)
update_log("ADD ALL MODE.")
})
observeEvent(input$move_caption, {
if (operation_mode() != "Normal") {
showNotification("Another operation is in progress. Please finish or cancel it first.", type = "error")
return()
}
target_caption <- input$edit_caption
if (target_caption %in% captions_data()$caption[captions_data()$image_path == input$selected_image]) {
operation_mode("move_caption")
showOrderInput(TRUE)
update_log("MOVE CAPTION SINGLE MODE.")
} else {
showNotification("Error: Caption not found in the selected image.", type = "error")
}
})
observeEvent(input$move_caption_all, {
if (operation_mode() != "Normal") {
showNotification("Another operation is in progress. Please finish or cancel it first.", type = "error")
return()
}
operation_mode("move_caption_all")
showOrderInput(TRUE)
update_log("MOVE CAPTION ALL MODE.")
})
observeEvent(input$confirm_order, {
switch(operation_mode(),
"add_single" = {
# Add to Singleの処理
target_caption <- input$edit_caption
target_order <- max(1, floor(input$target_order))
updated_data <- add_caption_at_order(captions_data(), input$selected_image, target_caption, target_order)
captions_data(updated_data)
update_log("Caption added to target order.")
},
"add_all" = {
target_caption <- input$edit_caption
target_order <- target_order <- max(1, floor(input$target_order))
updated_data <- captions_data()
unique_images <- unique(updated_data$image_path)
for (image in unique_images) {
updated_data <- add_caption_at_order(updated_data, image, target_caption, target_order)
}
captions_data(updated_data)
update_log("Caption added to all images.")
},
"move_caption" = {
# Move Captionの処理
target_caption <- input$edit_caption
target_order <- target_order <- max(1, floor(input$target_order))
updated_data <- move_caption_order(captions_data(), input$selected_image, target_caption, target_order)
captions_data(updated_data)
update_log("Caption moved to target order.")
},
"move_caption_all" = {
# Move Caption Allの処理
target_caption <- input$edit_caption
target_order <- target_order <- max(1, floor(input$target_order))
updated_data <- captions_data()
unique_images <- unique(updated_data$image_path[updated_data$caption == target_caption])
for (image in unique_images) {
updated_data <- move_caption_order(updated_data, image, target_caption, target_order)
}
captions_data(updated_data)
update_log("Caption moved to target order from all images.")
}
)
# キャプションの頻度を更新
updated_caption_frequency <- get_caption_frequency(captions_data())
caption_frequency(updated_caption_frequency)
operation_mode("Normal")
showOrderInput(FALSE)
})
observeEvent(input$cancel_order, {
operation_mode("Normal")
showOrderInput(FALSE)
update_log("Operation was canceled.")
})
observeEvent(input$cancel_text, {
operation_mode("Normal")
showTextInput(FALSE)
update_log("Operation was canceled.")
})
output$showOrderInput <- reactive({
showOrderInput()
})
outputOptions(output, "showOrderInput", suspendWhenHidden=FALSE)
output$showTextInput <- reactive({
showTextInput()
})
outputOptions(output, "showTextInput", suspendWhenHidden=FALSE)
# Direct Remove MODE
observeEvent(input$clicked_caption, {
target_caption <- input$clicked_caption
# 単一画像のキャプションを削除
updated_data <- remove_caption_and_adjust_order(captions_data(), input$selected_image, target_caption)
captions_data(updated_data)
# キャプションの頻度を更新
updated_caption_frequency <- get_caption_frequency(captions_data())
caption_frequency(updated_caption_frequency)
update_log("Caption removed from this image.")
})
observeEvent(input$clicked_caption_frequency, {
target_caption <- input$clicked_caption_frequency
# 指定されたキャプションをすべての画像から削除
updated_data <- captions_data()
unique_images <- unique(updated_data$image_path[updated_data$caption == target_caption])
for (image in unique_images) {
updated_data <- remove_caption_and_adjust_order(updated_data, image, target_caption)
}
captions_data(updated_data)
# キャプションの頻度を更新
updated_caption_frequency <- get_caption_frequency(captions_data())
caption_frequency(updated_caption_frequency)
update_log("Caption removed from all images.")
})
# yes/no選択dialog
show_confirmation_dialog <- function() {
showModal(
modalDialog(
title = "Confirmation",
"Are you sure?",
footer = tagList(
modalButton("No"),
actionButton("yes_button", "Yes")
)
)
)
}
# リアクティブな変数を作成して、ユーザーの選択を保存
user_choice <- reactiveVal(NULL)
# ユーザーの選択に基づいてリアクティブな値を更新
observeEvent(input$yes_button, {
user_choice("Yes")
removeModal()
})
## Restore captions: calling confirmation dialog
observeEvent(input$restore_captions, {
show_confirmation_dialog()
observeEvent(user_choice(), {
if (user_choice() == "Yes") {
captions_data(temp_captions_data())
# キャプションの頻度を更新
updated_caption_frequency <- get_caption_frequency(captions_data())
caption_frequency(updated_caption_frequency)
}
# リアクティブな変数をリセット
user_choice(NULL)
}, ignoreNULL = TRUE)
})
## 書き出し
observeEvent(input$output_captions, {
show_confirmation_dialog()
observeEvent(user_choice(), {
if (user_choice() == "Yes") {
# 出力ディレクトリを確認し、存在しない場合は作成
if (!dir.exists("./output")) {
dir.create("./output")
}
# 各画像に対応するキャプションをCSV形式で書き出す
unique_images <- unique(captions_data()$image_path)
for (image_path in unique_images) {
# ファイル名を生成
image_name <- basename(image_path)
output_filename <- paste0("./output/", tools::file_path_sans_ext(image_name), ".txt")
# キャプションをCSV形式で取得
csv_captions <- capture.output(print_image_captions_as_csv(captions_data(), image_path))
# データをファイルに書き出す
writeLines(csv_captions, con = output_filename)
}
# ログに書き出し完了のメッセージを追加
update_log("Captions data written to ./output/ directory.")
}
# リアクティブな変数をリセット
user_choice(NULL)
}, ignoreNULL = TRUE)
})
# レンダリング
output$image_display <- renderImage({
list(src = file.path(input$selected_image), alt = "Selected Image", width = "80%")
}, deleteFile = FALSE)
output$image_captions <- renderUI({
if (!is.null(captions_data())) {
selected_image_path <- input$selected_image
captions <- filter(captions_data(), image_path == selected_image_path) %>%
arrange(caption_order) %>%
pull(caption)
# キャプションをリンク付きのHTMLに変換
linked_captions <- lapply(captions, function(caption) {
if (!is.null(searched_caption()) && grepl(searched_caption(), caption)) {
sprintf("<a href='javascript:void(0);' onclick='handleCaptionClick(\"%s\");' style='background-color: yellow;'>%s</a>", caption, caption)
} else {
sprintf("<a href='javascript:void(0);' onclick='handleCaptionClick(\"%s\");'>%s</a>", caption, caption)
}
})
HTML(paste(linked_captions, collapse = ", "))
}
})
output$log_output <- renderText({
log_text()
})
output$selected_image_captions <- renderTable({
if (!is.null(captions_data())) {
selected_image_path <- input$selected_image
selected_captions <- filter(captions_data(), image_path == selected_image_path) %>%
select(caption, caption_order) %>%
arrange(caption_order)
# キャプションの並び替え
sorted_captions <- sort_captions_by_search(selected_captions, searched_caption())
# caption列の各エントリにリンクを追加
sorted_captions$caption <- sprintf("<a href='javascript:void(0);' onclick='handleCaptionClick(\"%s\", \"image\");'>%s</a>", sorted_captions$caption, sorted_captions$caption)
return(sorted_captions)
}
}, sanitize.text.function = function(x) x)
output$caption_frequency_table_with_links <- renderTable({
if (!is.null(caption_frequency())) {
freq_data <- caption_frequency()
# キャプションの並び替え
sorted_freq_data <- sort_captions_by_search(freq_data, searched_caption())
# caption列の各エントリにリンクを追加
sorted_freq_data$caption <- sprintf("<a href='javascript:void(0);' onclick='handleCaptionClick(\"%s\", \"frequency\");'>%s</a>", sorted_freq_data$caption, sorted_freq_data$caption)
return(sorted_freq_data)
}
}, sanitize.text.function = function(x) x)
output$relation_database_display <- renderTable({
db <- relation_database()
# relation_databaseが空でない場合のみリンクを作成
if (nrow(db) > 0) {
db$remove <- sprintf("<a href='javascript:void(0);' onclick='handleCaptionClick(\"%s\", \"remove_RDB\");'>Remove</a>", 1:nrow(db))
}
db
}, sanitize.text.function = function(x) x) # HTMLをエスケープしないようにする
}
# アプリを実行
shinyApp(ui = ui, server = server)