Spaces:
Sleeping
Sleeping
Update app.R
Browse files
app.R
CHANGED
|
@@ -4,6 +4,22 @@ library(arrow)
|
|
| 4 |
library(DT)
|
| 5 |
library(ggplot2)
|
| 6 |
library(shinyjs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
# ββ Load functions βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 9 |
load_data <- function(level) {
|
|
@@ -106,6 +122,14 @@ build_pitcher_table <- function(lb, min_pitches, portal_only) {
|
|
| 106 |
`Twitter` = paste0('<a href="https://www.google.com/search?q=',
|
| 107 |
gsub(" ", "+", Name),
|
| 108 |
'+baseball+twitter" target="_blank">🐦 Search</a>'),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
Name = ifelse(HasSinker,
|
| 110 |
paste0('<span style="background:#cce5ff; padding:2px 6px;',
|
| 111 |
'border-radius:3px; font-weight:700;">', Name, '</span>'),
|
|
@@ -113,7 +137,7 @@ build_pitcher_table <- function(lb, min_pitches, portal_only) {
|
|
| 113 |
)
|
| 114 |
if (portal_only) df <- df %>% filter(Portal == "Yes")
|
| 115 |
df %>%
|
| 116 |
-
select(Name, Level, Team, Portal, `Plot`, `Twitter`, HomeState, Throws, Height, Weight,
|
| 117 |
Pitches, ERA, FIP, WHIP, `FB Velo`, `Zone%`, `Strike%`,
|
| 118 |
`K's`, `BB's`, `K/BB`, `Whiff%`, `Chase%`, `Hard Hit%`,
|
| 119 |
`GB%`, `FB%`, `LD%`, `Stuff+`, `Location+`) %>%
|
|
@@ -185,6 +209,11 @@ ui <- fluidPage(
|
|
| 185 |
.plot-btn { background:#002147; color:white; border:none; border-radius:4px;
|
| 186 |
padding:4px 10px; font-size:12px; cursor:pointer; font-weight:600; }
|
| 187 |
.plot-btn:hover { background:#003366; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
table.dataTable tbody td a {
|
| 189 |
color:#002147; font-weight:600; font-size:12px; text-decoration:none;
|
| 190 |
background:#e8f0fe; border-radius:4px; padding:3px 8px; display:inline-block;
|
|
@@ -279,6 +308,38 @@ ui <- fluidPage(
|
|
| 279 |
Shiny.setInputValue('hitter_modal_closed', Math.random(), {priority: 'event'});
|
| 280 |
}
|
| 281 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
|
| 283 |
// update min label
|
| 284 |
Shiny.addCustomMessageHandler('update_label', function(msg) {
|
|
@@ -420,6 +481,65 @@ ui <- fluidPage(
|
|
| 420 |
tags$div("vs. Pitch Type", class = "section-label"),
|
| 421 |
DTOutput("hitter_pitch_table")
|
| 422 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
)
|
| 424 |
)
|
| 425 |
|
|
@@ -580,13 +700,13 @@ make_color_ramp <- function(values, low, high, flip = FALSE,
|
|
| 580 |
scrollY = "65vh",
|
| 581 |
scrollCollapse= TRUE,
|
| 582 |
fixedHeader = TRUE,
|
| 583 |
-
order = list(list(
|
| 584 |
orderMulti = FALSE,
|
| 585 |
dom = "lfrtip",
|
| 586 |
columnDefs = list(
|
| 587 |
list(className = "dt-left", targets = 0:1),
|
| 588 |
-
list(className = "dt-center", targets = 2:
|
| 589 |
-
list(orderable = FALSE, targets = c(4, 5))
|
| 590 |
),
|
| 591 |
initComplete = JS("function(settings, json) {
|
| 592 |
$('#loading_msg').hide();
|
|
@@ -834,6 +954,44 @@ make_color_ramp <- function(values, low, high, flip = FALSE,
|
|
| 834 |
observeEvent(input$hitter_modal_closed, {
|
| 835 |
shinyjs::hide("hitter_modal_wrapper")
|
| 836 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 837 |
|
| 838 |
batter_data <- reactive({
|
| 839 |
req(current_batter(), raw_data())
|
|
|
|
| 4 |
library(DT)
|
| 5 |
library(ggplot2)
|
| 6 |
library(shinyjs)
|
| 7 |
+
library(googlesheets4)
|
| 8 |
+
|
| 9 |
+
# ββ Google Sheets auth βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 10 |
+
gs_key <- Sys.getenv("GS_KEY")
|
| 11 |
+
sheet_id <- Sys.getenv("GS_SHEET_ID")
|
| 12 |
+
|
| 13 |
+
if (nzchar(gs_key)) {
|
| 14 |
+
tryCatch({
|
| 15 |
+
key_file <- tempfile(fileext = ".json")
|
| 16 |
+
writeLines(gs_key, key_file)
|
| 17 |
+
gs4_auth(path = key_file)
|
| 18 |
+
message("Google Sheets auth successful")
|
| 19 |
+
}, error = function(e) {
|
| 20 |
+
message("Google Sheets auth failed: ", e$message)
|
| 21 |
+
})
|
| 22 |
+
}
|
| 23 |
|
| 24 |
# ββ Load functions βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 25 |
load_data <- function(level) {
|
|
|
|
| 122 |
`Twitter` = paste0('<a href="https://www.google.com/search?q=',
|
| 123 |
gsub(" ", "+", Name),
|
| 124 |
'+baseball+twitter" target="_blank">🐦 Search</a>'),
|
| 125 |
+
`Board` = paste0('<button class="board-btn" data-pitcher="',
|
| 126 |
+
gsub('"', '"', Pitcher),
|
| 127 |
+
'" data-name="', gsub('"', '"', Name),
|
| 128 |
+
'" data-team="', gsub('"', '"', ifelse(is.na(Team), "", Team)),
|
| 129 |
+
'" data-throws="', gsub('"', '"', ifelse(is.na(Throws), "", Throws)),
|
| 130 |
+
'" data-height="', gsub('"', '"', ifelse(is.na(Height), "", Height)),
|
| 131 |
+
'" data-weight="', gsub('"', '"', ifelse(is.na(Weight), "", Weight)),
|
| 132 |
+
'">β Add</button>'),
|
| 133 |
Name = ifelse(HasSinker,
|
| 134 |
paste0('<span style="background:#cce5ff; padding:2px 6px;',
|
| 135 |
'border-radius:3px; font-weight:700;">', Name, '</span>'),
|
|
|
|
| 137 |
)
|
| 138 |
if (portal_only) df <- df %>% filter(Portal == "Yes")
|
| 139 |
df %>%
|
| 140 |
+
select(Name, Level, Team, Portal, `Plot`, `Twitter`, `Board`, HomeState, Throws, Height, Weight,
|
| 141 |
Pitches, ERA, FIP, WHIP, `FB Velo`, `Zone%`, `Strike%`,
|
| 142 |
`K's`, `BB's`, `K/BB`, `Whiff%`, `Chase%`, `Hard Hit%`,
|
| 143 |
`GB%`, `FB%`, `LD%`, `Stuff+`, `Location+`) %>%
|
|
|
|
| 209 |
.plot-btn { background:#002147; color:white; border:none; border-radius:4px;
|
| 210 |
padding:4px 10px; font-size:12px; cursor:pointer; font-weight:600; }
|
| 211 |
.plot-btn:hover { background:#003366; }
|
| 212 |
+
.plot-btn:hover { background:#003366; }
|
| 213 |
+
|
| 214 |
+
.board-btn { background:#f5c842; color:#002147; border:none; border-radius:4px;
|
| 215 |
+
padding:4px 10px; font-size:12px; cursor:pointer; font-weight:600; }
|
| 216 |
+
.board-btn:hover { background:#e0b030; }
|
| 217 |
table.dataTable tbody td a {
|
| 218 |
color:#002147; font-weight:600; font-size:12px; text-decoration:none;
|
| 219 |
background:#e8f0fe; border-radius:4px; padding:3px 8px; display:inline-block;
|
|
|
|
| 308 |
Shiny.setInputValue('hitter_modal_closed', Math.random(), {priority: 'event'});
|
| 309 |
}
|
| 310 |
});
|
| 311 |
+
// board modal
|
| 312 |
+
$(document).on('click', '.board-btn', function() {
|
| 313 |
+
var d = $(this).data();
|
| 314 |
+
// parse name from HTML span
|
| 315 |
+
var nameText = $(this).closest('tr').find('td:first button').text().trim();
|
| 316 |
+
var parts = nameText.split(' ');
|
| 317 |
+
$('#board_first').val(parts[0] || '');
|
| 318 |
+
$('#board_last').val(parts.slice(1).join(' ') || '');
|
| 319 |
+
$('#board_school').val(d.team || '');
|
| 320 |
+
$('#board_throws').val(d.throws || '');
|
| 321 |
+
$('#board_height').val(d.height || '');
|
| 322 |
+
$('#board_weight').val(d.weight || '');
|
| 323 |
+
$('#board_position').val('P');
|
| 324 |
+
$('#board_notes').val('');
|
| 325 |
+
$('#board_modal_wrapper').show();
|
| 326 |
+
});
|
| 327 |
+
$(document).on('click', '#board_modal_close, #board_cancel_btn', function() {
|
| 328 |
+
$('#board_modal_wrapper').hide();
|
| 329 |
+
});
|
| 330 |
+
$(document).on('click', '#board_submit_btn', function() {
|
| 331 |
+
Shiny.setInputValue('board_submit', {
|
| 332 |
+
first: $('#board_first').val(),
|
| 333 |
+
last: $('#board_last').val(),
|
| 334 |
+
school: $('#board_school').val(),
|
| 335 |
+
position: $('#board_position').val(),
|
| 336 |
+
height: $('#board_height').val(),
|
| 337 |
+
weight: $('#board_weight').val(),
|
| 338 |
+
throws: $('#board_throws').val(),
|
| 339 |
+
notes: $('#board_notes').val(),
|
| 340 |
+
added_by: $('#board_added_by').val()
|
| 341 |
+
}, {priority: 'event'});
|
| 342 |
+
});
|
| 343 |
|
| 344 |
// update min label
|
| 345 |
Shiny.addCustomMessageHandler('update_label', function(msg) {
|
|
|
|
| 481 |
tags$div("vs. Pitch Type", class = "section-label"),
|
| 482 |
DTOutput("hitter_pitch_table")
|
| 483 |
)
|
| 484 |
+
),
|
| 485 |
+
tags$div(
|
| 486 |
+
id = "board_modal_wrapper", class = "modal-overlay", style = "display:none;",
|
| 487 |
+
tags$div(
|
| 488 |
+
class = "modal-box", style = "width:480px;",
|
| 489 |
+
tags$button("β", id = "board_modal_close", class = "modal-close"),
|
| 490 |
+
tags$p("Add to Scout Board", class = "modal-title"),
|
| 491 |
+
tags$div(style = "display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-bottom:12px;",
|
| 492 |
+
tags$div(
|
| 493 |
+
tags$div("First Name", class = "filter-label"),
|
| 494 |
+
textInput("board_first", label = NULL, width = "100%")
|
| 495 |
+
),
|
| 496 |
+
tags$div(
|
| 497 |
+
tags$div("Last Name", class = "filter-label"),
|
| 498 |
+
textInput("board_last", label = NULL, width = "100%")
|
| 499 |
+
),
|
| 500 |
+
tags$div(
|
| 501 |
+
tags$div("School", class = "filter-label"),
|
| 502 |
+
textInput("board_school", label = NULL, width = "100%")
|
| 503 |
+
),
|
| 504 |
+
tags$div(
|
| 505 |
+
tags$div("Position", class = "filter-label"),
|
| 506 |
+
textInput("board_position", label = NULL, value = "P", width = "100%")
|
| 507 |
+
),
|
| 508 |
+
tags$div(
|
| 509 |
+
tags$div("Height", class = "filter-label"),
|
| 510 |
+
textInput("board_height", label = NULL, width = "100%")
|
| 511 |
+
),
|
| 512 |
+
tags$div(
|
| 513 |
+
tags$div("Weight", class = "filter-label"),
|
| 514 |
+
textInput("board_weight", label = NULL, width = "100%")
|
| 515 |
+
),
|
| 516 |
+
tags$div(
|
| 517 |
+
tags$div("Throws", class = "filter-label"),
|
| 518 |
+
textInput("board_throws", label = NULL, width = "100%")
|
| 519 |
+
),
|
| 520 |
+
tags$div(
|
| 521 |
+
tags$div("Added By", class = "filter-label"),
|
| 522 |
+
textInput("board_added_by", label = NULL, width = "100%")
|
| 523 |
+
)
|
| 524 |
+
),
|
| 525 |
+
tags$div(
|
| 526 |
+
tags$div("Notes", class = "filter-label"),
|
| 527 |
+
tags$textarea(id = "board_notes", style = "width:100%; height:80px;
|
| 528 |
+
border:1px solid #d0d0d0; border-radius:4px; padding:8px;
|
| 529 |
+
font-family:'Segoe UI',Arial,sans-serif; font-size:13px;",
|
| 530 |
+
placeholder = "Scouting notes...")
|
| 531 |
+
),
|
| 532 |
+
tags$br(),
|
| 533 |
+
tags$div(style = "display:flex; gap:10px; justify-content:flex-end;",
|
| 534 |
+
tags$button("Cancel", id = "board_cancel_btn",
|
| 535 |
+
style = "background:#f0f0f0; border:none; border-radius:4px;
|
| 536 |
+
padding:8px 16px; cursor:pointer; font-size:13px;"),
|
| 537 |
+
tags$button("Submit", id = "board_submit_btn",
|
| 538 |
+
style = "background:#002147; color:white; border:none; border-radius:4px;
|
| 539 |
+
padding:8px 16px; cursor:pointer; font-size:13px; font-weight:600;")
|
| 540 |
+
),
|
| 541 |
+
uiOutput("board_confirm_msg")
|
| 542 |
+
)
|
| 543 |
)
|
| 544 |
)
|
| 545 |
|
|
|
|
| 700 |
scrollY = "65vh",
|
| 701 |
scrollCollapse= TRUE,
|
| 702 |
fixedHeader = TRUE,
|
| 703 |
+
order = list(list(11, "desc")),
|
| 704 |
orderMulti = FALSE,
|
| 705 |
dom = "lfrtip",
|
| 706 |
columnDefs = list(
|
| 707 |
list(className = "dt-left", targets = 0:1),
|
| 708 |
+
list(className = "dt-center", targets = 2:27),
|
| 709 |
+
list(orderable = FALSE, targets = c(4, 5, 6))
|
| 710 |
),
|
| 711 |
initComplete = JS("function(settings, json) {
|
| 712 |
$('#loading_msg').hide();
|
|
|
|
| 954 |
observeEvent(input$hitter_modal_closed, {
|
| 955 |
shinyjs::hide("hitter_modal_wrapper")
|
| 956 |
})
|
| 957 |
+
# ββ Scout board submission ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 958 |
+
observeEvent(input$board_submit, {
|
| 959 |
+
req(input$board_submit)
|
| 960 |
+
d <- input$board_submit
|
| 961 |
+
|
| 962 |
+
tryCatch({
|
| 963 |
+
sheet_data <- data.frame(
|
| 964 |
+
First = d$first,
|
| 965 |
+
Last = d$last,
|
| 966 |
+
School = d$school,
|
| 967 |
+
Position = d$position,
|
| 968 |
+
Height = d$height,
|
| 969 |
+
Weight = d$weight,
|
| 970 |
+
Throws = d$throws,
|
| 971 |
+
Notes = d$notes,
|
| 972 |
+
`Added By` = d$added_by,
|
| 973 |
+
`Date Added` = format(Sys.time(), "%Y-%m-%d %H:%M"),
|
| 974 |
+
check.names = FALSE
|
| 975 |
+
)
|
| 976 |
+
|
| 977 |
+
sheet_append(sheet_id, sheet_data)
|
| 978 |
+
|
| 979 |
+
output$board_confirm_msg <- renderUI({
|
| 980 |
+
tags$p("β
Added to Scout Board!",
|
| 981 |
+
style = "color:#00840D; font-weight:700; margin-top:10px; text-align:center;")
|
| 982 |
+
})
|
| 983 |
+
|
| 984 |
+
shinyjs::delay(2000, {
|
| 985 |
+
shinyjs::runjs("$('#board_modal_wrapper').hide();")
|
| 986 |
+
})
|
| 987 |
+
|
| 988 |
+
}, error = function(e) {
|
| 989 |
+
output$board_confirm_msg <- renderUI({
|
| 990 |
+
tags$p(paste("β Error:", e$message),
|
| 991 |
+
style = "color:#E1463E; font-weight:700; margin-top:10px;")
|
| 992 |
+
})
|
| 993 |
+
})
|
| 994 |
+
})
|
| 995 |
|
| 996 |
batter_data <- reactive({
|
| 997 |
req(current_batter(), raw_data())
|