chat / server.R
WeMWish's picture
Fix OAuth infinite redirect loop
3610a6a
# server.R
library(shiny)
library(readxl)
library(DT)
library(dplyr)
# Source the warning overlay and long operations code
source("warning_overlay.R", local = TRUE)
source("long_operations.R", local = TRUE)
source("auth/hf_oauth.R", local = TRUE)
source("utils/supabase_r.R", local = TRUE)
# setwd("/Users/audrey/Downloads/research/ckweb/Tcellstates")
# Define server logic
function(input, output, session) {
# --- START: Environment Variable Diagnostic ---
print("=== Environment Variables Diagnostic ===")
all_env <- Sys.getenv()
# Print only relevant environment variables (filter sensitive ones)
relevant_vars <- c("SUPABASE_URL", "SUPABASE_KEY", "OAUTH_CLIENT_ID", "OAUTH_CLIENT_SECRET",
"SPACE_HOST", "SPACE_ID", "HF_TOKEN")
for (var_name in relevant_vars) {
var_value <- Sys.getenv(var_name, unset = "NOT_SET")
if (var_name %in% c("SUPABASE_KEY", "OAUTH_CLIENT_SECRET", "HF_TOKEN")) {
# Mask sensitive values
print(paste(var_name, ":", if(var_value == "NOT_SET") "NOT_SET" else "[REDACTED]"))
} else {
print(paste(var_name, ":", var_value))
}
}
print("========================================")
# --- END: Environment Variable Diagnostic ---
# --- START: OAuth and Supabase Initialization ---
oauth_config <- initialize_oauth()
supabase_client <- initialize_supabase()
# --- END: OAuth and Supabase Initialization ---
# --- START: TaijiChat R Callback for Python Agent Thoughts ---
python_agent_thought_callback <- function(thought_message_from_python) {
# Attempt to explicitly convert to R character and clean up
thought_message_text <- tryCatch({
as.character(thought_message_from_python)[1] # Take the first element after converting
}, error = function(e) {
print(paste("R Callback: Error converting thought to character:", e$message))
return(NULL)
})
if (!is.null(thought_message_text) && is.character(thought_message_text) && length(thought_message_text) == 1 && nzchar(trimws(thought_message_text))) {
# print(paste("R Callback: Valid thought from Python -", thought_message_text)) # For R console debugging
session$sendCustomMessage(type = "agent_new_thought", message = list(text = trimws(thought_message_text)))
} else {
# Log the original and potentially converted type for better debugging
print(paste("R Callback: Received invalid or empty thought. Original type:", class(thought_message_from_python), ", Value:", thought_message_from_python, ", Converted text:", thought_message_text))
}
}
# --- END: TaijiChat R Callback for Python Agent Thoughts ---
# --- START: TaijiChat Agent Initialization ---
# This assumes manager_agent_module is globally available from ui.R's sourcing.
# ui.R does: manager_agent_module <- reticulate::import("agents.manager_agent")
api_key_val <- NULL
# First try to get API key from environment variable
api_key_val <- Sys.getenv("OPENAI_API_KEY")
# If environment variable is not set, try reading from file
if (api_key_val == "") {
tryCatch({
api_key_content <- readLines("api_key.txt", warn = FALSE)
if (length(api_key_content) > 0 && nzchar(trimws(api_key_content[1]))) {
api_key_val <- trimws(api_key_content[1])
print("TaijiChat: API key successfully read from file in server.R.")
} else {
warning("TaijiChat: api_key.txt is empty or not found. LLM features may be disabled.")
print("TaijiChat: api_key.txt is empty or not found.")
}
}, error = function(e) {
warning(paste("TaijiChat: Error reading api_key.txt in server.R:", e$message))
print(paste("TaijiChat: Error reading api_key.txt:", e$message))
})
} else {
print("TaijiChat: API key successfully read from environment variable.")
}
py_openai_client_instance <- NULL
if (!is.null(api_key_val)) {
tryCatch({
# Ensure reticulate is configured to use the correct Python environment
if (reticulate::py_available(initialize = TRUE)) {
openai_py_module <- reticulate::import("openai", convert = FALSE) # convert=FALSE for raw Python objects
py_openai_client_instance <- openai_py_module$OpenAI(api_key = api_key_val)
print("TaijiChat: Python OpenAI client initialized successfully in server.R via reticulate.")
} else {
warning("TaijiChat: Python (reticulate) not available or not initialized. Cannot create OpenAI client.")
print("TaijiChat: Python (reticulate) not available. Cannot create OpenAI client.")
}
}, error = function(e) {
warning(paste("TaijiChat: Failed to initialize Python OpenAI client in server.R:", e$message))
print(paste("TaijiChat: Failed to initialize Python OpenAI client:", e$message))
py_openai_client_instance <- NULL
})
} else {
print("TaijiChat: API key is NULL, skipping Python OpenAI client initialization.")
}
rv_agent_instance <- reactiveVal(NULL)
# Attempt to create the agent instance once.
current_manager_agent_module <- NULL
tryCatch({
# Force reload the module to ensure we get the latest version
print("TaijiChat: Attempting to reload Python modules to ensure latest version...")
reticulate::py_run_string("
import sys
import importlib
if 'agents.manager_agent' in sys.modules:
print('Reloading agents.manager_agent module...')
importlib.reload(sys.modules['agents.manager_agent'])
")
current_manager_agent_module <- reticulate::import("agents.manager_agent", convert = FALSE)
if (is.null(current_manager_agent_module)) {
warning("TaijiChat: reticulate::import('agents.manager_agent') returned NULL in server.R.")
print("TaijiChat: reticulate::import('agents.manager_agent') returned NULL in server.R.")
} else {
print("TaijiChat: Successfully imported/retrieved 'agents.manager_agent' module in server.R.")
}
}, error = function(e) {
warning(paste("TaijiChat: Failed to import agents.manager_agent in server.R:", e$message))
print(paste("TaijiChat: Failed to import agents.manager_agent in server.R:", e$message))
})
if (!is.null(current_manager_agent_module)) {
# Module is available, now try to instantiate the agent
if (!is.null(py_openai_client_instance)) {
tryCatch({
supabase_py_client <- if (!is.null(supabase_client)) supabase_client else NULL
agent_inst <- current_manager_agent_module$ManagerAgent(
openai_client = py_openai_client_instance,
r_callback_fn = python_agent_thought_callback,
supabase_client = supabase_py_client,
user_id = NULL,
hf_user_id = NULL
)
rv_agent_instance(agent_inst)
print("TaijiChat: Python ManagerAgent instance created in server.R using pre-initialized client and R callback.")
}, error = function(e) {
warning(paste("TaijiChat: Failed to instantiate ManagerAgent in server.R with client & callback:", e$message))
print(paste("TaijiChat: Failed to instantiate ManagerAgent with client & callback:", e$message))
})
} else if (!is.null(api_key_val)) { # Try with API key if client object failed but key exists
tryCatch({
supabase_py_client <- if (!is.null(supabase_client)) supabase_client else NULL
agent_inst <- current_manager_agent_module$ManagerAgent(
openai_api_key = api_key_val,
r_callback_fn = python_agent_thought_callback,
supabase_client = supabase_py_client,
user_id = NULL,
hf_user_id = NULL
)
rv_agent_instance(agent_inst)
print("TaijiChat: Python ManagerAgent instance created in server.R with API key and R callback (client to be init by Python).")
}, error = function(e) {
warning(paste("TaijiChat: Failed to instantiate ManagerAgent with API key & callback in server.R:", e$message))
print(paste("TaijiChat: Failed to instantiate ManagerAgent with API key & callback:", e$message))
})
} else {
# Neither client nor API key is available for the agent
warning("TaijiChat: Cannot create ManagerAgent instance: OpenAI client/API key not available for agent constructor.")
print("TaijiChat: Cannot create ManagerAgent: OpenAI client/API key not available for agent constructor.")
}
} else {
# Module itself could not be imported/retrieved
warning("TaijiChat: agents.manager_agent module is NULL after import attempt. Agent not created.")
print("TaijiChat: agents.manager_agent module is NULL after import attempt. Agent not created.")
}
# --- END: TaijiChat Agent Initialization ---
# --- START: Authentication Handlers ---
# Handle login button click - generate OAuth URL (only once)
observeEvent(input$hfSignInBtn, {
req(input$hfSignInBtn)
# Check if we're in OAuth callback (code in URL)
query_string <- parseQueryString(session$clientData$url_search)
if (!is.null(query_string$code)) {
print("OAuth: In callback, skipping button redirect")
return()
}
if (!oauth_config$enabled) {
print("OAuth: Not enabled")
return()
}
print(paste("OAuth: Button clicked, count:", input$hfSignInBtn))
# Generate random state for CSRF protection
state <- generate_oauth_state()
session$userData$oauth_state <- state
# Get base URL - use SPACE_HOST if available (HF Spaces), otherwise use client URL
space_host <- Sys.getenv("SPACE_HOST", "")
if (space_host != "") {
redirect_uri <- paste0("https://", space_host)
} else {
redirect_uri <- paste0(session$clientData$url_protocol, "//",
session$clientData$url_hostname,
if (session$clientData$url_port != "") paste0(":", session$clientData$url_port) else "")
}
print(paste("OAuth: Redirect URI:", redirect_uri))
# Generate authorization URL
auth_url <- get_authorization_url(oauth_config, redirect_uri, state)
if (!is.null(auth_url)) {
print(paste("OAuth: Redirecting to:", auth_url))
# Send redirect message to JavaScript
session$sendCustomMessage(type = 'redirect_to_oauth', message = list(url = auth_url))
}
}, ignoreInit = TRUE, once = TRUE)
# Send initial auth state on session start
# Don't send if we're in OAuth callback (will be handled after auth completes)
observe({
isolate({
# Check if we're in OAuth callback by looking at URL parameters
query_string <- parseQueryString(session$clientData$url_search)
if (is.null(query_string$code)) {
# No OAuth code in URL, show login overlay
session$sendCustomMessage('auth_state', list(authenticated = FALSE))
}
})
})
# OAuth callback handler
observeEvent(input$oauth_code, {
if (!oauth_config$enabled || is.null(supabase_client)) {
print("OAuth: Not enabled or Supabase not configured")
return()
}
tryCatch({
# Get redirect URI - must match exactly what was used in authorization request
space_host <- Sys.getenv("SPACE_HOST", "")
if (space_host != "") {
redirect_uri <- paste0("https://", space_host)
} else {
redirect_uri <- paste0(session$clientData$url_protocol, "//",
session$clientData$url_hostname,
if (session$clientData$url_port != "") paste0(":", session$clientData$url_port) else "")
}
print(paste("OAuth: Token exchange using redirect_uri:", redirect_uri))
# Exchange code for token
token_result <- exchange_code_for_token(oauth_config, input$oauth_code, redirect_uri)
if (is.null(token_result)) {
print("OAuth: Failed to exchange code for token")
return()
}
# Get user info from HF
user_info <- get_user_info(token_result$access_token)
if (is.null(user_info)) {
print("OAuth: Failed to get user info")
return()
}
# Create or retrieve user in Supabase
supabase_user <- get_or_create_user(
supabase_client,
user_info$hf_user_id,
user_info$hf_username,
user_info$email
)
if (is.null(supabase_user)) {
print("OAuth: Failed to create/retrieve user in Supabase")
return()
}
# Store in session
session$userData$hf_user <- user_info
session$userData$supabase_user <- supabase_user
session$userData$access_token <- token_result$access_token
print(paste("OAuth: User authenticated -", user_info$hf_username))
# Hide login overlay
session$sendCustomMessage('auth_state', list(authenticated = TRUE))
# Show user info with quota
tokens_used <- supabase_user$tokens_used %||% 0
token_quota <- supabase_user$token_quota %||% 100000
quota_text <- paste(tokens_used, "/", token_quota)
session$sendCustomMessage('update_user_info', list(
username = user_info$hf_username,
quota = quota_text
))
}, error = function(e) {
print(paste("OAuth: Error in callback handler -", e$message))
})
})
# Logout handler
observeEvent(input$logoutBtn, {
logout_user(session)
session$sendCustomMessage('auth_state', list(authenticated = FALSE))
print("OAuth: User logged out")
})
# --- END: Authentication Handlers ---
# Server logic for home tab
output$home <- renderText({
"Welcome to the Home page"
})
observeEvent(input$read_now, {
# Trigger a redirect using JavaScript
session$sendCustomMessage(type = "redirect",
message = "https://doi.org/10.1101/2023.01.03.522354")
})
# NEW READ EXCEL FILE FOR TRANSPOSED DATASET
new_read_excel_file <- function(path) {
df <- read_excel(path)
colnames(df)[1] <- "Regulator Names"
# Transpose the dataframe
df_transposed <- as.data.frame(t(df))
# Fix the column names of the transposed dataframe (optional)
colnames(df_transposed) <- df_transposed[1, ] # Set first row as column names
df_transposed <- df_transposed[-1, ] # Remove the first row which is now used as column names
return(df_transposed)
}
# # NEW FILTER FUNCTION FOR TRANSPOSED DATASET
# new_filter_data <- function(df, keyword) {
#
# # Find the columns whose names contain the keyword
# matching_columns <- grepl(keyword, colnames(df), ignore.case = TRUE)
#
# # Check if any matching columns exist
# if (sum(matching_columns) == 0) {
# # If no matching columns are found, return an empty dataframe with proper structure
# return(data.frame())
# }
#
# # Subset the dataframe, ensuring the result is always a dataframe
# filtered_df <- df[, matching_columns, drop = FALSE]
#
# return(filtered_df)
# }
#NEW FILTER FUNCTION FOR MULTIPLE GENE SEARCH
# new_filter_data <- function(df, keywords) {
# # Split the keywords by commas and remove any leading/trailing whitespace
# keyword_list <- strsplit(keywords, ",")[[1]]
# keyword_list <- trimws(keyword_list)
#
# # Initialize an empty logical vector for matching columns
# matching_columns <- rep(FALSE, ncol(df))
#
# # Loop through each keyword and update the matching_columns vector
# for (keyword in keyword_list) {
# matching_columns <- matching_columns | grepl(keyword, colnames(df), ignore.case = TRUE)
# }
#
# # Check if any matching columns exist
# if (sum(matching_columns) == 0) {
# # If no matching columns are found, return an empty dataframe with proper structure
# return(data.frame())
# }
#
# # Subset the dataframe, ensuring the result is always a dataframe
# filtered_df <- df[, matching_columns, drop = FALSE]
#
# return(filtered_df)
# }
new_filter_data <- function(df, keywords) {
# If no keywords are provided, return the full dataset
if (is.null(keywords) || keywords == "") {
return(df)
}
# Split the keywords by commas and remove any leading/trailing whitespace
keyword_list <- strsplit(keywords, ",")[[1]]
keyword_list <- trimws(keyword_list) # Remove leading/trailing spaces
# Initialize an empty logical vector for matching columns
matching_columns <- rep(FALSE, ncol(df))
# Loop through each keyword and update the matching_columns vector
for (keyword in keyword_list) {
matching_columns <- matching_columns | grepl(keyword, colnames(df), ignore.case = TRUE)
}
# Check if any matching columns exist
if (sum(matching_columns) == 0) {
# If no matching columns are found, return an empty dataframe
return(data.frame())
}
# Subset the dataframe, ensuring the result is always a dataframe
filtered_df <- df[, matching_columns, drop = FALSE]
return(filtered_df)
}
# TESTING FUNCTIONS
#NEW ALL DATA SEARCH
data <- reactive({
new_read_excel_file("www/tablePagerank/Table_TF PageRank Scores for Audrey.xlsx")
})
# Track the current column page
column_page <- reactiveVal(1)
# Reset the column page when the search input changes for main page data
observeEvent(input$search_input, {
column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked
observeEvent(input$next_btn, {
current_page <- column_page()
total_cols <- ncol(new_filter_data(data(), input$search_input))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
column_page(current_page + 1)
}
# new_page <- column_page() + 1
# column_page(new_page)
})
observeEvent(input$prev_btn, {
current_page <- column_page()
column_page(max(current_page - 1, 1))
# new_page <- max(column_page() - 1, 1) # Ensure the page does not go below 1
# column_page(new_page)
})
# # OLD: Reactive for filtering the data based on input$search_input
# new_filtered_data <- reactive({
#
# df <- new_filter_data(data(), input$search_input)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # Ensure start_col is within the number of columns
# if (start_col > total_cols) {
# return(df) # Return the filtered dataframe as-is if start_col exceeds the number of columns
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
# return(df_subset)
# })
#NEW FILTER DATA TO INCLUDE ADDITIONAL DESCRIPTION ROW + WORKS WITH SEARCHING INDIVIDUAL GENES
new_filtered_data <- reactive({
# Filter the data and determine the total number of columns
df <- new_filter_data(data(), input$search_input)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold)
style <- "<h5 style='font-weight: bold;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Combine the data with the description and styled "Cell state data" row
df_subset <- rbind(df_subset[1:2, , drop = FALSE], description_row, df_subset[-(1:2), , drop = FALSE])
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 2; // Adjust this index to the row you want highlighted
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
# Rendering the filtered data table and showing all rows
output$table <- renderDT({
new_filtered_data()
}, options = list(pageLength = nrow(new_filtered_data()), dom = 't'))
#NEW NAIVE SEARCH
naive_data <- reactive({
new_read_excel_file("www/tablePagerank/Naive.xlsx")
})
# Track the current column page for naive data
naive_column_page <- reactiveVal(1)
# Reset the column page when the search input changes for naive data
observeEvent(input$search_input_naive, {
naive_column_page(1) # Reset to page 1 when a new search is made
})
# Reset the column page when the search input changes for naive data
observeEvent(input$search_input_naive, {
naive_column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked for naive data
observeEvent(input$naive_next_btn, {
current_page <- naive_column_page()
total_cols <- ncol(new_filter_data(naive_data(), input$search_input_naive))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
naive_column_page(current_page + 1)
}
# new_page <- naive_column_page() + 1
# naive_column_page(new_page)
})
observeEvent(input$naive_prev_btn, {
current_page <- naive_column_page()
naive_column_page(max(current_page - 1, 1))
# new_page <- max(naive_column_page() - 1, 1) # Ensure the page does not go below 1
# naive_column_page(new_page)
})
#OLD FILTERED DATA
# filtered_naive_data <- reactive({
#
# df <- new_filter_data(naive_data(), input$search_input_naive)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (naive_column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # Ensure start_col is within the number of columns
# if (start_col > total_cols) {
# return(df) # Return the filtered dataframe as-is if start_col exceeds the number of columns
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
# return(df_subset)
# })
#NEW FILTERED DATA
filtered_naive_data <- reactive({
# Filter the naive data and determine the total number of columns
df <- new_filter_data(naive_data(), input$search_input_naive)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (naive_column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows, moving them to the first row
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold with white font and background color)
style <- "<h5 style='font-weight: bold; color: white;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Move the description row to the first row of the subset
df_subset <- rbind(description_row, df_subset) # Move this styled row to the first position
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 0; // Apply styling to the first row
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
output$table_naive <- renderDT({
filtered_naive_data()
}, options = list(pageLength = nrow(filtered_naive_data()), dom = 't'))
#NEW TE SEARCH
te_data <- reactive({
new_read_excel_file("www/tablePagerank/TE.xlsx")
})
# Track the current column page for "te" data
te_column_page <- reactiveVal(1)
# Reset the column page when the search input changes for "te" data
observeEvent(input$search_input_te, {
te_column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked for "te" data
observeEvent(input$te_next_btn, {
current_page <- te_column_page()
total_cols <- ncol(new_filter_data(te_data(), input$search_input_te))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
te_column_page(current_page + 1)
}
})
observeEvent(input$te_prev_btn, {
current_page <- te_column_page()
te_column_page(max(current_page - 1, 1)) # Ensure the page does not go below 1
})
#OLD: Reactive for filtering and paginating the "te" page data
# filtered_te_data <- reactive({
# df <- new_filter_data(te_data(), input$search_input_te)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (te_column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # If start_col exceeds the total number of columns, return the last valid subset
# if (start_col > total_cols) {
# start_col <- (ceiling(total_cols / 4) - 1) * 4 + 1 # Set start_col to the last valid page
# end_col <- total_cols # End with the last column
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
#
# return(df_subset)
# })
#NEW FILTERED DATA
filtered_te_data <- reactive({
# Filter the TE data and determine the total number of columns
df <- new_filter_data(te_data(), input$search_input_te)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (te_column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows, moving them to the first row
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold with white font and background color)
style <- "<h5 style='font-weight: bold; color: white;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Move the description row to the first row of the subset
df_subset <- rbind(description_row, df_subset) # Move this styled row to the first position
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 0; // Apply styling to the first row
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
# Rendering the filtered "te" page data table and showing all rows
output$table_te <- renderDT({
filtered_te_data() # Use the filtered data with pagination logic applied
}, options = list(
pageLength = nrow(filtered_te_data()), # Show all rows
dom = 't', # Remove the row length dropdown
scrollX = TRUE # Enable horizontal scrolling if needed
))
#NEW MP SEARCH
mp_data <- reactive({
new_read_excel_file("www/tablePagerank/MP.xlsx")
})
# Track the current column page for "mp" data
mp_column_page <- reactiveVal(1)
# Reset the column page when the search input changes for "mp" data
observeEvent(input$search_input_mp, {
mp_column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked for "mp" data
observeEvent(input$mp_next_btn, {
current_page <- mp_column_page()
total_cols <- ncol(new_filter_data(mp_data(), input$search_input_mp))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
mp_column_page(current_page + 1)
}
})
observeEvent(input$mp_prev_btn, {
current_page <- mp_column_page()
mp_column_page(max(current_page - 1, 1)) # Ensure the page does not go below 1
})
#OLD: Reactive for filtering and paginating the "mp" page data
# filtered_mp_data <- reactive({
# df <- new_filter_data(mp_data(), input$search_input_mp)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (mp_column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # If start_col exceeds the total number of columns, return the last valid subset
# if (start_col > total_cols) {
# start_col <- (ceiling(total_cols / 4) - 1) * 4 + 1 # Set start_col to the last valid page
# end_col <- total_cols # End with the last column
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
#
# return(df_subset)
# })
#NEW FILTERED DATA
filtered_mp_data <- reactive({
# Filter the MP data and determine the total number of columns
df <- new_filter_data(mp_data(), input$search_input_mp)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (mp_column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows, moving them to the first row
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold with white font and background color)
style <- "<h5 style='font-weight: bold; color: white;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Move the description row to the first row of the subset
df_subset <- rbind(description_row, df_subset) # Move this styled row to the first position
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 0; // Apply styling to the first row
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
# Rendering the filtered "mp" page data table and showing all rows
output$table_mp <- renderDT({
filtered_mp_data() # Use the filtered data with pagination logic applied
}, options = list(
pageLength = nrow(filtered_mp_data()), # Show all rows
dom = 't', # Remove the row length dropdown
scrollX = TRUE # Enable horizontal scrolling if needed
))
#NEW TCM SEARCH
tcm_data <- reactive({
new_read_excel_file("www/tablePagerank/TCM.xlsx")
})
# Track the current column page for "tcm" data
tcm_column_page <- reactiveVal(1)
# Reset the column page when the search input changes for "tcm" data
observeEvent(input$search_input_tcm, {
tcm_column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked for "tcm" data
observeEvent(input$tcm_next_btn, {
current_page <- tcm_column_page()
total_cols <- ncol(new_filter_data(tcm_data(), input$search_input_tcm))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
tcm_column_page(current_page + 1)
}
})
observeEvent(input$tcm_prev_btn, {
current_page <- tcm_column_page()
tcm_column_page(max(current_page - 1, 1)) # Ensure the page does not go below 1
})
#OLD: Reactive for filtering and paginating the "tcm" page data
# filtered_tcm_data <- reactive({
# df <- new_filter_data(tcm_data(), input$search_input_tcm)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (tcm_column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # If start_col exceeds the total number of columns, return the last valid subset
# if (start_col > total_cols) {
# start_col <- (ceiling(total_cols / 4) - 1) * 4 + 1 # Set start_col to the last valid page
# end_col <- total_cols # End with the last column
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
#
# return(df_subset)
# })
#NEW FILTERED DATA
filtered_tcm_data <- reactive({
# Filter the TCM data and determine the total number of columns
df <- new_filter_data(tcm_data(), input$search_input_tcm)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (tcm_column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows, moving them to the first row
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold with white font and background color)
style <- "<h5 style='font-weight: bold; color: white;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Move the description row to the first row of the subset
df_subset <- rbind(description_row, df_subset) # Move this styled row to the first position
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 0; // Apply styling to the first row
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
# Rendering the filtered "tcm" page data table and showing all rows
output$table_tcm <- renderDT({
filtered_tcm_data() # Use the filtered data with pagination logic applied
}, options = list(
pageLength = nrow(filtered_tcm_data()), # Show all rows
dom = 't', # Remove the row length dropdown
scrollX = TRUE # Enable horizontal scrolling if needed
))
#NEW TEM SEARCH
tem_data <- reactive({
new_read_excel_file("www/tablePagerank/TEM.xlsx")
})
# Track the current column page for "tem" data
tem_column_page <- reactiveVal(1)
# Reset the column page when the search input changes for "tem" data
observeEvent(input$search_input_tem, {
tem_column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked for "tem" data
observeEvent(input$tem_next_btn, {
current_page <- tem_column_page()
total_cols <- ncol(new_filter_data(tem_data(), input$search_input_tem))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
tem_column_page(current_page + 1)
}
})
observeEvent(input$tem_prev_btn, {
current_page <- tem_column_page()
tem_column_page(max(current_page - 1, 1)) # Ensure the page does not go below 1
})
#OLD: Reactive for filtering and paginating the "tem" page data
# filtered_tem_data <- reactive({
# df <- new_filter_data(tem_data(), input$search_input_tem)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (tem_column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # If start_col exceeds the total number of columns, return the last valid subset
# if (start_col > total_cols) {
# start_col <- (ceiling(total_cols / 4) - 1) * 4 + 1 # Set start_col to the last valid page
# end_col <- total_cols # End with the last column
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
#
# return(df_subset)
# })
#NEW FILTERED DATA
filtered_tem_data <- reactive({
# Filter the TEM data and determine the total number of columns
df <- new_filter_data(tem_data(), input$search_input_tem)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (tem_column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows, moving them to the first row
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold with white font and background color)
style <- "<h5 style='font-weight: bold; color: white;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Move the description row to the first row of the subset
df_subset <- rbind(description_row, df_subset) # Move this styled row to the first position
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 0; // Apply styling to the first row
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
# Rendering the filtered "tem" page data table and showing all rows
output$table_tem <- renderDT({
filtered_tem_data() # Use the filtered data with pagination logic applied
}, options = list(
pageLength = nrow(filtered_tem_data()), # Show all rows
dom = 't', # Remove the row length dropdown
scrollX = TRUE # Enable horizontal scrolling if needed
))
#NEW TRM SEARCH
trm_data <- reactive({
new_read_excel_file("www/tablePagerank/TRM.xlsx")
})
# Track the current column page for "trmm" data
trm_column_page <- reactiveVal(1)
# Reset the column page when the search input changes for "trmm" data
observeEvent(input$search_input_trm, {
trm_column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked for "trmm" data
observeEvent(input$trm_next_btn, {
current_page <- trm_column_page()
total_cols <- ncol(new_filter_data(trm_data(), input$search_input_trm))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
trm_column_page(current_page + 1)
}
})
observeEvent(input$trm_prev_btn, {
current_page <- trm_column_page()
trm_column_page(max(current_page - 1, 1)) # Ensure the page does not go below 1
})
#OLD: Reactive for filtering and paginating the "trmm" page data
# filtered_trm_data <- reactive({
# df <- new_filter_data(trm_data(), input$search_input_trm)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (trm_column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # If start_col exceeds the total number of columns, return the last valid subset
# if (start_col > total_cols) {
# start_col <- (ceiling(total_cols / 4) - 1) * 4 + 1 # Set start_col to the last valid page
# end_col <- total_cols # End with the last column
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
#
# return(df_subset)
# })
#NEW FILTERED DATA
filtered_trm_data <- reactive({
# Filter the TRM data and determine the total number of columns
df <- new_filter_data(trm_data(), input$search_input_trm)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (trm_column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows, moving them to the first row
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold with white font and background color)
style <- "<h5 style='font-weight: bold; color: white;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Move the description row to the first row of the subset
df_subset <- rbind(description_row, df_subset) # Move this styled row to the first position
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 0; // Apply styling to the first row
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
# Rendering the filtered "trmm" page data table and showing all rows
output$table_trm <- renderDT({
filtered_trm_data() # Use the filtered data with pagination logic applied
}, options = list(
pageLength = nrow(filtered_trm_data()), # Show all rows
dom = 't', # Remove the row length dropdown
scrollX = TRUE # Enable horizontal scrolling if needed
))
#NEW TEX PROG SEARCH
texprog_data <- reactive({
new_read_excel_file("www/tablePagerank/TEXprog.xlsx")
})
# Track the current column page for "texprog" data
texprog_column_page <- reactiveVal(1)
# Reset the column page when the search input changes for "texprog" data
observeEvent(input$search_input_texprog, {
texprog_column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked for "texprog" data
observeEvent(input$texprog_next_btn, {
current_page <- texprog_column_page()
total_cols <- ncol(new_filter_data(texprog_data(), input$search_input_texprog))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
texprog_column_page(current_page + 1)
}
})
observeEvent(input$texprog_prev_btn, {
current_page <- texprog_column_page()
texprog_column_page(max(current_page - 1, 1)) # Ensure the page does not go below 1
})
#OLD: Reactive for filtering and paginating the "texprog" page data
# filtered_texprog_data <- reactive({
# df <- new_filter_data(texprog_data(), input$search_input_texprog)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (texprog_column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # If start_col exceeds the total number of columns, return the last valid subset
# if (start_col > total_cols) {
# start_col <- (ceiling(total_cols / 4) - 1) * 4 + 1 # Set start_col to the last valid page
# end_col <- total_cols # End with the last column
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
#
# return(df_subset)
# })
#NEW FILTERED DATA
filtered_texprog_data <- reactive({
# Filter the TEXprog data and determine the total number of columns
df <- new_filter_data(texprog_data(), input$search_input_texprog)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (texprog_column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows, moving them to the first row
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold with white font and background color)
style <- "<h5 style='font-weight: bold; color: white;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Move the description row to the first row of the subset
df_subset <- rbind(description_row, df_subset) # Move this styled row to the first position
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 0; // Apply styling to the first row
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
# Rendering the filtered "texprog" page data table and showing all rows
output$table_texprog <- renderDT({
filtered_texprog_data() # Use the filtered data with pagination logic applied
}, options = list(
pageLength = nrow(filtered_texprog_data()), # Show all rows
dom = 't', # Remove the row length dropdown
scrollX = TRUE # Enable horizontal scrolling if needed
))
#NEW TEX EFF LIKE SEARCH
texefflike_data <- reactive({
new_read_excel_file("www/tablePagerank/TEXeff.xlsx")
})
# Track the current column page for "texefflike" data
texefflike_column_page <- reactiveVal(1)
# Reset the column page when the search input changes for "texefflike" data
observeEvent(input$search_input_texefflike, {
texefflike_column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked for "texefflike" data
observeEvent(input$texefflike_next_btn, {
current_page <- texefflike_column_page()
total_cols <- ncol(new_filter_data(texefflike_data(), input$search_input_texefflike))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
texefflike_column_page(current_page + 1)
}
})
observeEvent(input$texefflike_prev_btn, {
current_page <- texefflike_column_page()
texefflike_column_page(max(current_page - 1, 1)) # Ensure the page does not go below 1
})
#OLD: Reactive for filtering and paginating the "texefflike" page data
# filtered_texefflike_data <- reactive({
# df <- new_filter_data(texefflike_data(), input$search_input_texefflike)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (texefflike_column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # If start_col exceeds the total number of columns, return the last valid subset
# if (start_col > total_cols) {
# start_col <- (ceiling(total_cols / 4) - 1) * 4 + 1 # Set start_col to the last valid page
# end_col <- total_cols # End with the last column
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
#
# return(df_subset)
# })
#NEW FILTERED DATA
filtered_texefflike_data <- reactive({
# Filter the TEXefflike data and determine the total number of columns
df <- new_filter_data(texefflike_data(), input$search_input_texefflike)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (texefflike_column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows, moving them to the first row
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold with white font and background color)
style <- "<h5 style='font-weight: bold; color: white;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Move the description row to the first row of the subset
df_subset <- rbind(description_row, df_subset) # Move this styled row to the first position
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 0; // Apply styling to the first row
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
# Rendering the filtered "texefflike" page data table and showing all rows
output$table_texefflike <- renderDT({
filtered_texefflike_data() # Use the filtered data with pagination logic applied
}, options = list(
pageLength = nrow(filtered_texefflike_data()), # Show all rows
dom = 't', # Remove the row length dropdown
scrollX = TRUE # Enable horizontal scrolling if needed
))
#NEW TEX TERM SEARCH
texterm_data <- reactive({
new_read_excel_file("www/tablePagerank/TEXterm.xlsx")
})
# Track the current column page for "texterm" data
texterm_column_page <- reactiveVal(1)
# Reset the column page when the search input changes for "texterm" data
observeEvent(input$search_input_texterm, {
texterm_column_page(1) # Reset to page 1 when a new search is made
})
# Update the column page when buttons are clicked for "texterm" data
observeEvent(input$texterm_next_btn, {
current_page <- texterm_column_page()
total_cols <- ncol(new_filter_data(texterm_data(), input$search_input_texterm))
# Calculate the maximum possible number of pages
max_page <- ceiling(total_cols / 4)
# Move to the next page, but do not exceed the max_page
if (current_page < max_page) {
texterm_column_page(current_page + 1)
}
})
observeEvent(input$texterm_prev_btn, {
current_page <- texterm_column_page()
texterm_column_page(max(current_page - 1, 1)) # Ensure the page does not go below 1
})
#OLD: Reactive for filtering and paginating the "texterm" page data
# filtered_texterm_data <- reactive({
# df <- new_filter_data(texterm_data(), input$search_input_texterm)
#
# # Get the total number of columns in the filtered dataframe
# total_cols <- ncol(df)
#
# # Ensure there are columns after filtering
# if (total_cols == 0) {
# return(data.frame()) # Return an empty dataframe if no columns match the search
# }
#
# # Define the start and end index for columns based on the current page
# start_col <- (texterm_column_page() - 1) * 4 + 1
# end_col <- min(start_col + 3, total_cols) # Show up to 4 columns
#
# # If start_col exceeds the total number of columns, return the last valid subset
# if (start_col > total_cols) {
# start_col <- (ceiling(total_cols / 4) - 1) * 4 + 1 # Set start_col to the last valid page
# end_col <- total_cols # End with the last column
# }
#
# # Subset the columns for the current page
# df_subset <- df[, start_col:end_col, drop = FALSE]
#
# return(df_subset)
# })
#NEW FILTERED DATA
filtered_texterm_data <- reactive({
# Filter the TEXterm data and determine the total number of columns
df <- new_filter_data(texterm_data(), input$search_input_texterm)
total_cols <- ncol(df)
# Return empty dataframe if no columns match the search
if (total_cols == 0) return(data.frame())
# Define the column range for the current page
start_col <- (texterm_column_page() - 1) * 4 + 1
end_col <- min(start_col + 3, total_cols)
# Return filtered data if start_col exceeds the total columns
if (start_col > total_cols) return(df)
# Subset the dataframe for the current page
df_subset <- df[, start_col:end_col, drop = FALSE]
# Apply HTML formatting to description and "Cell state data" rows, moving them to the first row
if (nrow(df_subset) >= 2) {
description_row <- data.frame(
matrix(NA, nrow = 1, ncol = ncol(df_subset), dimnames = list(NULL, colnames(df_subset)))
)
# Define the HTML style for the description row (making "TF activity score" bold with white font and background color)
style <- "<h5 style='font-weight: bold; color: white;'>TF activity score</h5>"
# Apply style to both description and "Cell state data"
description_row[1, ] <- style
rownames(description_row)[1] <- "Cell state data"
# Move the description row to the first row of the subset
df_subset <- rbind(description_row, df_subset) # Move this styled row to the first position
}
# Return the data with HTML rendering enabled, show all 45 rows, and remove the search box
DT::datatable(df_subset, escape = FALSE, options = list(
pageLength = 45,
lengthChange = FALSE,
searching = FALSE, # Remove the search box
rowCallback = JS(
"function(row, data, index) {
var highlightedRow = 0; // Apply styling to the first row
if (index === highlightedRow) {
$('td', row).css({
'background-color': '#95a5a6',
'color': 'white',
'font-weight': 'bold'
});
}
}"
)
))
})
# Rendering the filtered "texterm" page data table and showing all rows
output$table_texterm <- renderDT({
filtered_texterm_data() # Use the filtered data with pagination logic applied
}, options = list(
pageLength = nrow(filtered_texterm_data()), # Show all rows
dom = 't', # Remove the row length dropdown
scrollX = TRUE # Enable horizontal scrolling if needed
))
# # ORIGINAL READ EXCEL FILE
# read_excel_file <- function(path) {
# df <- read_excel(path)
# colnames(df)[1] <- "Regulator Names"
# return(df)
# }
#
# # ORIGINAL FILTER FUNCTION
# filter_data <- function(data, keyword) {
# if (is.null(keyword) || is.na(keyword) || keyword == "") {
# return(data)
# } else {
# keyword <- tolower(keyword)
# return(data[apply(data, 1, function(x) any(grepl(keyword, tolower(x)))), ])
# }
# }
#
# #ORIGINAL DISPLAY TABLE FUNCTION
# # Main page data
# data <- reactive({
# read_excel_file("www/tablePagerank/Table_TF PageRank Scores for Audrey.xlsx")
# })
#
# filtered_data <- reactive({
# filter_data(data(), input$search_input)
# })
#
# output$table <- renderDT({
# filtered_data()
# })
# Server logic for naive tab
output$naive <- renderText({
"TF Activity Score: Naive"
})
# OLD NAIVE DATA TABLE
# naive_data <- reactive({
# read_excel_file("www/tablePagerank/Naive.xlsx")
# })
#
# filtered_naive_data <- reactive({
# filter_data(naive_data(), input$search_input_naive)
# })
#
# output$table_naive <- renderDT({
# filtered_naive_data()
# })
# Server logic for TE tab
output$te <- renderText({
"TF Activity Score: TE"
})
# OLD TE DATA TABLE
# te_data <- reactive({
# read_excel_file("www/tablePagerank/TE.xlsx")
# })
#
# filtered_te_data <- reactive({
# filter_data(te_data(), input$search_input_te)
# })
#
# output$table_te <- renderDT({
# filtered_te_data()
# })
# Server logic for MP tab
output$mp <- renderText({
"TF Activity Score: MP"
})
# OLD MP DATA
# mp_data <- reactive({
# read_excel_file("www/tablePagerank/MP.xlsx")
# })
#
# filtered_mp_data <- reactive({
# filter_data(mp_data(), input$search_input_mp)
# })
#
# output$table_mp <- renderDT({
# filtered_mp_data()
# })
# Server logic for T CM tab
output$tcm <- renderText({
"TF Activity Score: T CM"
})
# OLD TCM DATA
# tcm_data <- reactive({
# read_excel_file("www/tablePagerank/TCM.xlsx")
# })
#
# filtered_tcm_data <- reactive({
# filter_data(tcm_data(), input$search_input_tcm)
# })
#
# output$table_tcm <- renderDT({
# filtered_tcm_data()
# })
# Server logic for T EM tab
output$tem <- renderText({
"TF Activity Score: T EM"
})
# OLD TEM DATA
# tem_data <- reactive({
# read_excel_file("www/tablePagerank/TEM.xlsx")
# })
#
# filtered_tem_data <- reactive({
# filter_data(tem_data(), input$search_input_tem)
# })
#
# output$table_tem <- renderDT({
# filtered_tem_data()
# })
#
# # Server logic for T RM tab
# output$trm <- renderText({
# "TF Activity Score: T RM"
# })
# OLD TRM DATA
# trm_data <- reactive({
# read_excel_file("www/tablePagerank/TRM.xlsx")
# })
#
# filtered_trm_data <- reactive({
# filter_data(trm_data(), input$search_input_trm)
# })
#
# output$table_trm <- renderDT({
# filtered_trm_data()
# })
# Server logic for Tex Prog tab
output$texprog <- renderText({
"TF Activity Score: Tex Prog"
})
# OLD TEX PROG DATA
# texprog_data <- reactive({
# read_excel_file("www/tablePagerank/TEXprog.xlsx")
# })
#
# filtered_texprog_data <- reactive({
# filter_data(texprog_data(), input$search_input_texprog)
# })
#
# output$table_texprog <- renderDT({
# filtered_texprog_data()
# })
# Server logic for Tex Eff-like tab
output$texefflike <- renderText({
"TF Activity Score: Tex Eff-like"
})
# OLD TEX EFF LIKE DATA
# texefflike_data <- reactive({
# read_excel_file("www/tablePagerank/TEXeff.xlsx")
# })
#
# filtered_texefflike_data <- reactive({
# filter_data(texefflike_data(), input$search_input_texefflike)
# })
#
# output$table_texefflike <- renderDT({
# filtered_texefflike_data()
# })
# Server logic for Tex Term tab
output$texterm <- renderText({
"TF Activity Score: Tex Term"
})
# OLD TEX TERM DATA
# texterm_data <- reactive({
# read_excel_file("www/tablePagerank/TEXterm.xlsx")
# })
#
# filtered_texterm_data <- reactive({
# filter_data(texterm_data(), input$search_input_texterm)
# })
#
# output$table_texterm <- renderDT({
# filtered_texterm_data()
# })
#click image code for TF Wave Analysis
observeEvent(input$c1_link, {
updateNavbarPage(session, "mainNav", selected = "c1")
})
observeEvent(input$c2_link, {
updateNavbarPage(session, "mainNav", selected = "c2")
})
observeEvent(input$c3_link, {
updateNavbarPage(session, "mainNav", selected = "c3")
})
observeEvent(input$c4_link, {
updateNavbarPage(session, "mainNav", selected = "c4")
})
observeEvent(input$c5_link, {
updateNavbarPage(session, "mainNav", selected = "c5")
})
observeEvent(input$c6_link, {
updateNavbarPage(session, "mainNav", selected = "c6")
})
observeEvent(input$c7_link, {
updateNavbarPage(session, "mainNav", selected = "c7")
})
# Handle specific image part clicks to navigate to the subpages
observeEvent(input$to_tfcat, {
updateNavbarPage(session, "mainNav", selected = "tfcatpage")
})
observeEvent(input$to_tfwave, {
updateNavbarPage(session, "mainNav", selected = "overview")
})
observeEvent(input$to_tfnet, {
updateNavbarPage(session, "mainNav", selected = "tfnetpage")
})
# SEARCH FOR A TF AND FIND OUT WHICH WAVE THEY ARE APART OF
# Function to read the file and assign proper column names
read_searchwave_file <- function(path) {
df <- read_excel(path) # Read the Excel file
colnames(df) <- c("Wave1", "Wave2", "Wave3", "Wave4", "Wave5", "Wave6", "Wave7") # Customize as needed
return(df)
return(df)
}
# Load data reactively
searchwavedata <- reactive({
# Provide the path to your Excel file
read_searchwave_file("www/waveanalysis/searchtfwaves.xlsx") # Path to your file
})
# Reactive function to search and filter the data based on the wave search input
filtered_wave_data <- reactive({
# Get the search input
search_term <- input$search_input_wave
# If no search term, return the entire dataset in the desired transposed format
if (is.null(search_term) || search_term == "") {
# Initialize an empty list to store the gene names for each wave
wave_genes <- list()
# Iterate over each wave column and get the associated genes
for (col in colnames(searchwavedata())) {
wave_genes[[col]] <- searchwavedata()[[col]] # Store all gene names for each wave
}
# Create a data frame with waves as columns and genes as rows
result_df <- data.frame(wave_genes)
# Return the transposed data frame with wave numbers as column headers and genes as rows
return(result_df)
}
# Initialize an empty list to store the result
result <- list()
# Iterate through each column and check if the search term exists
for (col in colnames(searchwavedata())) {
# Filter genes for each column where the search term matches
matching_genes <- searchwavedata()[[col]][grepl(search_term, searchwavedata()[[col]], ignore.case = TRUE)]
# If there are matching genes, store them
if (length(matching_genes) > 0) {
result[[col]] <- paste(matching_genes, collapse = ", ") # Combine matching genes as a string
}
}
# If no results are found, return an empty dataframe
if (length(result) == 0) {
return(data.frame(Wave = character(0), Gene = character(0))) # Return empty data frame if nothing matches
}
# Convert the result list to a data frame
result_df <- data.frame(
Wave = names(result), # Column names as 'Wave'
Gene = unlist(result), # Concatenated list of matching gene names
stringsAsFactors = FALSE
)
# Remove any duplicate wave names (keep only one occurrence per wave)
result_df <- result_df[!duplicated(result_df$Wave), ]
# Remove rows where Gene column is NA or empty
result_df <- result_df[!is.na(result_df$Gene) & result_df$Gene != "", ]
# Create a matrix with wave names as columns and gene names as rows
transposed_df <- matrix(unlist(result_df$Gene), nrow = 1) # Convert the gene names to a row
# Set the column names to the wave numbers, replacing "Wave 1" instead of "wave.1"
colnames(transposed_df) <- paste("Wave", seq_along(result_df$Wave))
# Return the transposed result
return(as.data.frame(transposed_df))
})
# Render the table output based on the filtered and transposed data
output$table_wave <- renderDT({
# Get the filtered and transposed data
df <- filtered_wave_data()
# Render the data table without row names and disable the default search box
datatable(df, options = list(searching = FALSE), rownames = FALSE)
})
# # Function to read Excel files
# read_regulator_file <- function(path) {
# df <- read_excel(path)
# colnames(df)[1] <- " " # Adjust column name as needed
# return(df)
# }
#
# # Load data initially
# tfregulated_data <- reactive({
# read_regulator_file("www/networkanalysis/comp_log2FC_RegulatedData_TRMTEXterm.xlsx")
# })
#
# # Filtered data based on search input
# filtered_tfregulated_data <- reactive({
# req(tfregulated_data()) # Ensure tfregulated_data() is available
# if (is.null(input$search_tfregulated_data) || input$search_tfregulated_data == "") {
# return(tfregulated_data())
# } else {
# # Perform filtering based on input$search_tfregulated_data
# # Example filtering logic:
# # filtered_data <- tfregulated_data() %>%
# # filter(...) # Add your filtering logic here
# # return(filtered_data)
# # Replace the above with your actual filtering logic
# return(tfregulated_data()) # Placeholder for now
# }
# })
#
# # Render the DataTable
# output$table_tfregulated_data <- renderDT({
# datatable(filtered_tfregulated_data())
# })
# ORIGINALLY SEARCH TRM TEXterm COORELATION
# Load Excel file when the app starts
data_tftfimage <- read_excel("www/TFcorintextrm/TF-TFcorTRMTEX.xlsx")
# Reactive function to filter the data based on the search input (case-insensitive)
filtered_data_tftfimage <- reactive({
req(input$search) # Ensure search input is available
# Convert both the column and search input to lowercase for case-insensitive comparison
data_filtered <- data_tftfimage[tolower(data_tftfimage[["TF Name"]]) == tolower(input$search), ]
return(data_filtered)
})
# Render the first column (Gene Names) as clickable links
output$gene_list_table <- renderUI({
tagList(
lapply(data_tftfimage[[1]], function(gene_name) { # Assuming the first column contains gene names
tags$div(
actionLink(
inputId = paste0("gene_", gene_name), # Unique input ID for each gene
label = gene_name
),
style = "margin-bottom: 10px;" # Add spacing between links
)
})
)
})
# Generate dynamic observers for each gene link
lapply(data_tftfimage[[1]], function(gene_name) {
observeEvent(input[[paste0("gene_", gene_name)]], {
# Find the row corresponding to the clicked gene
selected_gene_data <- data_tftfimage[data_tftfimage[[1]] == gene_name, ]
img_src <- selected_gene_data[["TF Merged Graph Path"]] # Replace with the actual image column name
# Update the image gallery output with the selected gene's image
output$image_gallery <- renderUI({
if (!is.null(img_src) && nchar(img_src) > 0) {
tags$div(
style = "text-align: center;",
tags$img(src = img_src, style = "max-width: 100%; height: auto;"),
tags$p(gene_name) # Optionally display the gene name below the image
)
} else {
"No image available for the selected gene."
}
})
})
})
# Event to handle search functionality (unchanged from your original code)
observeEvent(input$search_btn, {
output$result_table <- renderTable({
if (nrow(filtered_data_tftfimage()) > 0) {
filtered_data_tftfimage()[, -which(names(filtered_data_tftfimage()) == "TF Merged Graph Path")] # Show all columns except the image path
} else {
NULL # If no results, display nothing
}
})
output$image_gallery <- renderUI({
if (nrow(filtered_data_tftfimage()) > 0) {
image_list <- lapply(1:nrow(filtered_data_tftfimage()), function(i) {
img_src <- filtered_data_tftfimage()[[ "TF Merged Graph Path" ]][i]
tags$div(
style = "text-align: center; margin-bottom: 20px;",
tags$img(src = img_src, style = "max-width: 100%; height: auto;")
)
})
do.call(tags$div, image_list)
} else {
"TF not found. Please search for a valid TF."
}
})
})
#tf communities
# Read the first Excel file directly from the specified path
datatrm <- read_excel("www/tfcommunities/trmcommunities.xlsx")
# Read the second Excel file directly from the specified path
datatex <- read_excel("www/tfcommunities/texcommunities.xlsx")
# Render the first table without the search button
output$trmcom <- renderDT({
datatable(
datatrm,
options = list(
lengthChange = FALSE,
pageLength = 5,
searching = FALSE # Disable the search button
)
)
})
# Render the second table without the search button
output$texcom <- renderDT({
datatable(
datatex,
options = list(
lengthChange = FALSE,
pageLength = 5,
searching = FALSE # Disable the search button
)
)
})
# Load your multiomics file
multiexcel_data <- read_excel("www/multi-omicsdata.xlsx")
# #Render data table + hyperlinks author's name to DOI
# output$multiomicsdatatable <- renderDT({
# # Transform the "author" column to contain hyperlinks using the "DOI" column
# multiexcel_data <- multiexcel_data %>%
# mutate(
# Author = paste0(
# "<a href='",
# DOI, # Column with the full DOI URLs
# "' target='_blank'>",
# Author, # Column with the display text (e.g., author name)
# "</a>"
# )
# ) %>%
# select(-DOI) # Remove the "DOI" column after linking it to the "author" column
#
# # Dynamically remove empty columns ("18", "19", etc.)
# multiexcel_data <- multiexcel_data %>%
# select(where(~ !all(is.na(.)) & !all(. == ""))) # Keep only non-empty columns
#
# # Render the data table with HTML content and no row names
# datatable(multiexcel_data,
# options = list(searching = FALSE),
# rownames = FALSE,
# escape = FALSE) # Allow HTML rendering
# })
#Render and hyperlink table + edit size so that everything fits into webpage
output$multiomicsdatatable <- renderDT({
# Transform the "author" column to contain hyperlinks using the "DOI" column
multiexcel_data <- multiexcel_data %>%
mutate(
Author = paste0(
"<a href='",
DOI, # Column with the full DOI URLs
"' target='_blank'>",
Author, # Column with the display text (e.g., author name)
"</a>"
)
) %>%
select(-DOI) # Remove the "DOI" column after linking it to the "author" column
# Dynamically remove empty columns ("18", "19", etc.)
multiexcel_data <- multiexcel_data %>%
select(where(~ !all(is.na(.)) & !all(. == ""))) # Keep only non-empty columns
# Render the data table with fit-to-page options
datatable(
multiexcel_data,
options = list(
autoWidth = TRUE, # Adjust column widths automatically
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 10 # Limit rows displayed per page (adjustable)
),
rownames = FALSE,
escape = FALSE # Allow HTML rendering for links
)
})
# --- START: TaijiChat Message Handling ---
chat_history <- reactiveVal(list()) # Stores list of lists: list(role="user/assistant", content="message")
observeEvent(input$user_chat_message, {
req(input$user_chat_message)
user_message_text <- trimws(input$user_chat_message)
print(paste("TaijiChat: Received user_chat_message -", user_message_text))
if (nzchar(user_message_text)) {
# Check authentication (implement OAuth later in ui.R)
# For now, system works without auth, but logs as "anonymous"
current_user <- session$userData$hf_user
hf_user_id <- if (!is.null(current_user)) current_user$hf_user_id else "anonymous"
# Check quota before processing
if (!is.null(supabase_client) && !is.null(current_user)) {
quota_result <- check_user_quota(supabase_client, hf_user_id)
if (!quota_result$has_quota) {
session$sendCustomMessage(type = "agent_response", message = list(
text = paste("Token quota exceeded. Used:", quota_result$tokens_used, "Remaining: 0")
))
return()
}
}
current_hist <- chat_history()
updated_hist_user <- append(current_hist, list(list(role = "user", content = user_message_text)))
chat_history(updated_hist_user)
agent_instance_val <- rv_agent_instance()
# Set user context in agent
if (!is.null(agent_instance_val) && !is.null(current_user)) {
supabase_user <- session$userData$supabase_user
agent_instance_val$set_user_context(
user_id = supabase_user$id,
hf_user_id = hf_user_id
)
}
if (!is.null(agent_instance_val)) {
# Ensure history is a list of R named lists, then r_to_py will convert to list of Python dicts
py_hist_for_agent <- lapply(updated_hist_user, function(turn) {
list(role = turn$role, content = turn$content)
})
# py_hist_for_agent_converted <- reticulate::r_to_py(py_hist_for_agent)
# Send a "Thinking..." message to UI before long computation
session$sendCustomMessage(type = "agent_thinking_started", message = list(text = "Thinking..."))
tryCatch({
print(paste("TaijiChat: Sending to Python agent - Query:", user_message_text))
# For debugging, convert history to JSON string to see its structure if needed
# hist_json_debug <- jsonlite::toJSON(py_hist_for_agent, auto_unbox = TRUE)
# print(paste("TaijiChat: Conversation history (JSON for debug):", hist_json_debug))
# Get literature search preference (default to FALSE if not set)
literature_enabled <- if (is.null(input$literature_search_enabled)) FALSE else input$literature_search_enabled
print(paste("TaijiChat: Literature search enabled:", literature_enabled))
# Call Python agent method with literature preference
# The process_single_query method in Python expects history as a list of dicts.
# reticulate::r_to_py should handle the conversion of the list of R named lists.
agent_reply_py <- agent_instance_val$process_single_query_with_preferences(
user_query_text = user_message_text,
conversation_history_from_r = py_hist_for_agent, # Pass the R list of lists
literature_enabled = literature_enabled
)
# Explicitly convert potential Python object to R character string
agent_reply_text <- as.character(agent_reply_py)
print(paste("TaijiChat: Received from Python agent -", agent_reply_text))
# Check if this is an image response
if (startsWith(agent_reply_text, "TAIJICHAT_IMAGE_RESPONSE:")) {
# Extract the JSON part - START AFTER THE COLON IN THE PREFIX
json_str <- substr(agent_reply_text, 26, nchar(agent_reply_text))
# Debug the JSON string
print(paste("Attempting to parse JSON:", json_str))
# Try parsing in a safer way
tryCatch({
# Remove any leading/trailing whitespace and ensure proper JSON format
json_str <- trimws(json_str)
# Try to parse the JSON
image_info <- NULL
if (nchar(json_str) > 0) {
# Attempt to parse using various approaches
image_info <- tryCatch({
jsonlite::fromJSON(json_str)
}, error = function(e1) {
tryCatch({
# Try unescaping first
jsonlite::fromJSON(gsub('\\\\"', '"', json_str))
}, error = function(e2) {
NULL
})
})
}
if (!is.null(image_info) && !is.null(image_info$image_path)) {
# Debug image path
image_path_original <- image_info$image_path
image_path_normalized <- sub("^www/", "", image_info$image_path)
# Check if file exists
file_exists_check <- file.exists(image_path_original)
print(paste("Image path debug - Original:", image_path_original,
"Normalized:", image_path_normalized,
"File exists:", file_exists_check))
# Add a special marker to the message to trigger image display in UI
image_html <- paste0(
'<div class="chat-image-container">',
'<img src="', image_path_normalized, '" class="chat-image-preview" onclick="showFullImage(\'',
image_path_normalized, '\')">',
'</div>'
)
# Get original response or use a default
original_response <- ifelse(!is.null(image_info$original_response),
image_info$original_response,
"I've analyzed this image.")
# Combine the image HTML with the original text response
enhanced_reply <- paste0(
image_html,
"<br>",
original_response
)
agent_reply_text <- enhanced_reply
# Send a custom message to ensure the image display script is active
session$sendCustomMessage(type = "activate_image_viewer", message = list())
} else {
warning("Failed to extract image path from JSON")
agent_reply_text <- paste("I analyzed an image but had trouble displaying it. Here's what I found:",
gsub("TAIJICHAT_IMAGE_RESPONSE:.*", "", agent_reply_text))
}
}, error = function(e) {
warning(paste("JSON parsing error:", e$message, "- JSON string:", json_str))
print(paste("JSON parsing error:", e$message, "- JSON string:", json_str))
# Just use the original text in case of parsing error
agent_reply_text <- paste("I analyzed an image but had trouble displaying it. Here's what I found:",
substr(agent_reply_text, 25, nchar(agent_reply_text)))
})
}
final_hist <- append(updated_hist_user, list(list(role = "assistant", content = agent_reply_text)))
chat_history(final_hist)
session$sendCustomMessage(type = "agent_chat_response", message = list(text = agent_reply_text))
# Update quota display after successful query
if (!is.null(supabase_client) && !is.null(current_user)) {
quota_result <- check_user_quota(supabase_client, hf_user_id)
tokens_used <- quota_result$tokens_used
token_quota <- session$userData$supabase_user$token_quota %||% 100000
quota_text <- paste(tokens_used, "/", token_quota)
session$sendCustomMessage('update_quota', list(quota = quota_text))
}
}, error = function(e) {
error_message <- paste("TaijiChat: Error calling Python agent or processing response:", e$message)
warning(error_message)
print(error_message)
session$sendCustomMessage(type = "agent_chat_response", message = list(text = paste("Sorry, an error occurred with the agent.")))
})
} else {
warning("TaijiChat: Agent instance is NULL. Cannot process chat message.")
print("TaijiChat: Agent instance is NULL. Cannot process chat message.")
session$sendCustomMessage(type = "agent_chat_response", message = list(text = "The chat agent is not available. Please check server logs."))
}
} else {
print("TaijiChat: Received empty user_chat_message.")
}
})
# --- END: TaijiChat Message Handling ---
#Render and hyperlink table + edit size so that everything fits into webpage
output$multiomicsdatatable <- renderDT({
# Transform the "author" column to contain hyperlinks using the "DOI" column
multiexcel_data <- multiexcel_data %>%
mutate(
Author = paste0(
"<a href='",
DOI, # Column with the full DOI URLs
"' target='_blank'>",
Author, # Column with the display text (e.g., author name)
"</a>"
)
) %>%
select(-DOI) # Remove the "DOI" column after linking it to the "author" column
# Dynamically remove empty columns ("18", "19", etc.)
multiexcel_data <- multiexcel_data %>%
select(where(~ !all(is.na(.)) & !all(. == ""))) # Keep only non-empty columns
# Render the data table with fit-to-page options
datatable(
multiexcel_data,
options = list(
autoWidth = TRUE, # Adjust column widths automatically
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 10 # Limit rows displayed per page (adjustable)
),
rownames = FALSE,
escape = FALSE # Allow HTML rendering for links
)
})
# Update reactive expressions to use warning overlay
output$tfData <- renderDT({
withWarningOverlayReactive(session, {
# Your existing reactive code here
# This will automatically show the warning overlay for long-running operations
}, "get_processed_tf_data")
})
}
# # ############################## CODE GRAVEYARD ##############################