OwenStOnge commited on
Commit
3ff2290
·
verified ·
1 Parent(s): 90d4723

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +819 -5
app.R CHANGED
@@ -210,6 +210,822 @@ merge_with_bat_tracking <- function(csv_data, bat_tracking_data) {
210
  ))
211
  }
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  # UI
214
  ui <- fluidPage(
215
  tags$head(
@@ -1561,13 +2377,11 @@ removeModal()
1561
 
1562
  result <- tryCatch({
1563
  if (input$scrape_source == "pbp") {
1564
- scrape_pbp(input$start_date, input$end_date)
1565
  } else if (input$scrape_source == "pos") {
1566
- scrape_positional(input$start_date, input$end_date)
1567
  } else if (input$scrape_source == "ncaa") {
1568
- # placeholder — put your NCAA scraper here
1569
- scrape_status_msg("NCAA scraper not yet implemented.")
1570
- NULL
1571
  }
1572
  }, error = function(e) {
1573
  scrape_status_msg(paste("Error:", e$message))
 
210
  ))
211
  }
212
 
213
+
214
+ ##SCRAPER FUNCTIONS
215
+
216
+ ftp_server <- "ftp.trackmanbaseball.com"
217
+ username <- "FTP_USER"
218
+ password <- "FTP_PASSWORD"
219
+ ftp_base_dir <- "/v3"
220
+
221
+
222
+ #Trackman pitch by pitch
223
+ scrape_trackman_pbp <- function(start_date, end_date) {
224
+
225
+ dates <- as.character(seq.Date(as.Date(start_date), as.Date(end_date), by = "day"))
226
+ all_data <- data.frame()
227
+
228
+ for (date in dates) {
229
+ year <- format(as.Date(date), "%Y")
230
+ month <- format(as.Date(date), "%m")
231
+ day <- format(as.Date(date), "%d")
232
+
233
+ ftp_dir <- paste0(ftp_base_dir, "/", year, "/", month, "/", day, "/CSV")
234
+ ftp_url <- paste0("ftp://", username, ":", password, "@", ftp_server, ftp_dir, "/")
235
+
236
+ # Get list of files for this day
237
+ file_list <- tryCatch({
238
+ getURL(ftp_url, ftp.use.epsv = FALSE, dirlistonly = TRUE)
239
+ }, error = function(e) { NULL })
240
+
241
+ if (is.null(file_list)) next
242
+
243
+ file_list <- unlist(strsplit(file_list, "\n"))
244
+
245
+ # Filter to verified/unverified CSVs (prefer verified)
246
+ pattern_verified <- "(\\d{8})-(.+)-\\d+\\.csv$"
247
+ pattern_unverified <- "(\\d{8})-(.+)-\\d+_unverified\\.csv$"
248
+
249
+ verified <- grep(pattern_verified, file_list, value = TRUE)
250
+ unverified <- grep(pattern_unverified, file_list, value = TRUE)
251
+
252
+ verified_stadiums <- unique(str_match(verified, pattern_verified)[, 2])
253
+ unverified_stadiums <- str_match(unverified, pattern_unverified)[, 2]
254
+
255
+ files_to_read <- c(
256
+ verified,
257
+ unverified[!unverified_stadiums %in% verified_stadiums]
258
+ )
259
+
260
+ # Read each file directly into memory
261
+ for (file in files_to_read) {
262
+ file_url <- paste0(ftp_url, URLencode(trimws(file)))
263
+
264
+ tryCatch({
265
+ csv_text <- getURL(file_url)
266
+ rows <- read_csv(I(csv_text), col_types = cols(.default = "c"), show_col_types = FALSE)
267
+
268
+ if ("PlayResult" %in% names(rows)) {
269
+ all_data <- bind_rows(all_data, rows)
270
+ }
271
+ }, error = function(e) {
272
+ message("Failed: ", file, " - ", e$message)
273
+ })
274
+ }
275
+
276
+ message("Done with ", date)
277
+ }
278
+
279
+ # Deduplicate
280
+ all_data %>%
281
+ distinct(PitchUID, .keep_all = TRUE)
282
+ }
283
+
284
+
285
+
286
+ #trackman positional
287
+ scrape_trackman_positional <- function(start_date, end_date) {
288
+
289
+ dates <- as.character(seq.Date(as.Date(start_date), as.Date(end_date), by = "day"))
290
+ all_data <- data.frame()
291
+
292
+ for (date in dates) {
293
+ year <- format(as.Date(date), "%Y")
294
+ month <- format(as.Date(date), "%m")
295
+ day <- format(as.Date(date), "%d")
296
+
297
+ ftp_dir <- paste0(ftp_base_dir, "/", year, "/", month, "/", day, "/CSV")
298
+ ftp_url <- paste0("ftp://", username, ":", password, "@", ftp_server, ftp_dir, "/")
299
+
300
+ # Get list of files for this day
301
+ file_list <- tryCatch({
302
+ getURL(ftp_url, ftp.use.epsv = FALSE, dirlistonly = TRUE)
303
+ }, error = function(e) { NULL })
304
+
305
+ if (is.null(file_list)) next
306
+
307
+ file_list <- unlist(strsplit(file_list, "\n"))
308
+
309
+ # Filter to verified/unverified CSVs (prefer verified)
310
+ pattern_verified <- "(\\d{8})-(.+)-\\d+_playerpositioning_FHC\\.csv$"
311
+ pattern_unverified <- "(\\d{8})-(.+)-\\d+_unverified_playerpositioning_FHC\\.csv$"
312
+
313
+ verified <- grep(pattern_verified, file_list, value = TRUE)
314
+ unverified <- grep(pattern_unverified, file_list, value = TRUE)
315
+
316
+ verified_stadiums <- unique(str_match(verified, pattern_verified)[, 2])
317
+ unverified_stadiums <- str_match(unverified, pattern_unverified)[, 2]
318
+
319
+ files_to_read <- c(
320
+ verified,
321
+ unverified[!unverified_stadiums %in% verified_stadiums]
322
+ )
323
+
324
+ # Read each file directly into memory
325
+ for (file in files_to_read) {
326
+ file_url <- paste0(ftp_url, URLencode(trimws(file)))
327
+
328
+ tryCatch({
329
+ csv_text <- getURL(file_url)
330
+ rows <- read_csv(I(csv_text), col_types = cols(.default = "c"), show_col_types = FALSE)
331
+
332
+ if ("PlayResult" %in% names(rows)) {
333
+ all_data <- bind_rows(all_data, rows)
334
+ }
335
+ }, error = function(e) {
336
+ message("Failed: ", file, " - ", e$message)
337
+ })
338
+ }
339
+
340
+ message("Done with ", date)
341
+ }
342
+
343
+ # Deduplicate
344
+ all_data %>%
345
+ distinct(PitchUID, .keep_all = TRUE)
346
+ }
347
+
348
+
349
+
350
+
351
+ #Next section is a large section of functions from the pbp parser github to parse ncaa pbp data to get base states
352
+ stripwhite <- function(x) gsub("\\s*$", "", gsub("^\\s*", "", x))
353
+
354
+ strip_punc <- function(x){
355
+ x=stripwhite(x)
356
+ x=ifelse(str_sub(x,-1)=='.',gsub("\\.", "", x),x)
357
+ return(x)}
358
+
359
+
360
+
361
+ ##########################################################
362
+ # Functions for parsing
363
+
364
+ inn_end = function(top_inn){
365
+ m=length(top_inn)
366
+ inn_end=integer(m)
367
+ for (i in 1:(m-1)){
368
+ inn_end[i]=ifelse(top_inn[i]!=top_inn[i+1], 1,0)
369
+ }
370
+ inn_end[m]=1
371
+ return(inn_end)
372
+ }
373
+
374
+
375
+ game_end = function(game_id){
376
+ m=length(game_id)
377
+ game_end=integer(m)
378
+ for (i in 2:m){
379
+ if (game_id[i]!=game_id[i-1]){
380
+ game_end[i-1]=1
381
+ }
382
+ game_end[m]=1
383
+ }
384
+ return(game_end)
385
+ }
386
+
387
+
388
+
389
+
390
+ runs_on_play= function(a_txt, h_txt, a_score,h_score){
391
+ m=length(a_txt)
392
+ runs_on_play=integer(m)
393
+ runs_on_play[1]=a_score[1]
394
+ for (i in 2:m){
395
+ runs_on_play[i]=case_when(
396
+ a_txt[i]=='' ~ as.integer(h_score[i]-h_score[i-1]),
397
+ a_txt[i]!='' ~ as.integer(a_score[i]-a_score[i-1])
398
+ )
399
+ }
400
+ return(runs_on_play)
401
+ }
402
+
403
+
404
+ r1_name = function(bat_text, bat_name, r1_text, r1_name, inn_end, game_end, sub_in, sub_out){
405
+ m=length(bat_text)
406
+ r1_name= character(m)
407
+ for (i in 2:m){
408
+ if (isTRUE(inn_end[i-1]==0 & game_end[i-1]==0)) {
409
+ r1_name[i]=case_when(
410
+ sub_out[i-1]!=''&sub_out[i-1]==stripwhite(r1_name[i-1])~sub_in[i-1],
411
+ (str_detect(bat_text[i-1], '(singled|walked|hit by pitch|reached)') == TRUE) & (str_detect(bat_text[i-1], '(doubled|tripled|homered|advanced|scored|out|stole)') == FALSE) ~ bat_name[i-1],
412
+ (str_detect(bat_text[i-1], '(reached first)') == TRUE) & (str_detect(bat_text[i-1], '(struck out)') == TRUE) ~ bat_name[i-1],
413
+ (r1_text[i-1]==''|(str_detect(r1_text[i-1], '(advanced to second|stole second|advanced to third|stole third|scored|out)') == FALSE)) & (str_detect(bat_text[i-1], '(double play|advanced to second|stole second|advanced to third|stole third|scored|caught stealing|picked off|homered)') == FALSE) ~ r1_name[i-1],
414
+ (str_detect(bat_text[i-1], '(singled|doubled|tripled|advanced to second|stole second|advanced to third|stole third|scored|homered|out at second c to)') == FALSE) & (str_detect(r1_text[i-1], '(advanced to third|stole third|scored|out at third)') == TRUE) & stripwhite(gsub('((advanced to second|stole second|stole third|advanced to third|scored|out).*$)', '', r1_text[i-1]))!=stripwhite(gsub('((singled|reached).*$)', '', r1_name[i-1])) ~ r1_name[i-1],
415
+ r1_text[i-1]=='' & stripwhite(gsub('((advanced to second|stole second|stole third|advanced to third|scored|out|failed|Failed|picked off).*$)', '', bat_text[i-1]))!=stripwhite(r1_name[i-1]) ~ r1_name[i-1]
416
+ )}}
417
+ return(stripwhite(r1_name))
418
+ }
419
+
420
+
421
+ r2_name = function(bat_text, bat_name, r1_text, r1_name, r2_text, r2_name, inn_end, game_end, sub_in, sub_out){
422
+ m=length(bat_text)
423
+ r2_name= character(m)
424
+ for (i in 2:m){
425
+ if (isTRUE(inn_end[i-1]==0 & game_end[i-1]==0)) {
426
+ r2_name[i]=case_when(
427
+ sub_out[i-1]!=''&sub_out[i-1]==stripwhite(r2_name[i-1])~sub_in[i-1],
428
+ ((str_detect(bat_text[i-1], '(doubled|advanced to second|stole second)') == TRUE) & (str_detect(bat_text[i-1], '(advanced to third|scored|out|stole third)') == FALSE)) ~ stripwhite(gsub('((doubled|advanced to second|stole second).*$)', '', bat_text[i-1])),
429
+ ((str_detect(r1_text[i-1], '(advanced to second|stole second)') == TRUE) & (str_detect(r1_text[i-1], '(advanced to third|scored|out|stole third)') == FALSE)) ~ stripwhite(gsub('((advanced to second|stole second).*$)', '', r1_text[i-1])),
430
+ r2_text[i-1]=='' & stripwhite(gsub('((stole third|advanced to third|scored|out).*$)', '', r1_text[i-1]))!=stripwhite(r2_name[i-1]) & (str_detect(bat_text[i-1], '(advanced to third|stole third|scored|picked off|caught stealing)') == FALSE) ~ r2_name[i-1],
431
+ r2_text[i-1]=='' & stripwhite(gsub('((out on the play).*$)', '', r1_text[i-1]))!=stripwhite(r2_name[i-1]) & (str_detect(bat_text[i-1], '(double play)') == TRUE) ~ r2_name[i-1],
432
+ r1_text[i-1]=='' & (str_detect(bat_text[i-1], '(stole third|advanced to third|scored|picked off|homered|caught stealing)') == FALSE) ~ r2_name[i-1],
433
+ sub_out[i-1]!=''&sub_out[i-1]==stripwhite(r2_name[i-1])~sub_in[i-1]
434
+ )
435
+ r2_name[i]=stripwhite(gsub('((singled|reached).*$)', '', r2_name[i]))
436
+ }
437
+ }
438
+ return(stripwhite(r2_name))
439
+ }
440
+
441
+
442
+ r3_name = function(bat_text, bat_name, r1_text, r1_name, r2_text, r2_name, r3_text, r3_name, inn_end, game_end, sub_in, sub_out){
443
+ m=length(bat_text)
444
+ r3_name= character(m)
445
+ for (i in 2:m){
446
+ if (isTRUE(inn_end[i-1]==0 & game_end[i-1]==0)) {
447
+ r3_name[i]=case_when(
448
+ sub_out[i-1]!=''&sub_out[i-1]==stripwhite(r3_name[i-1])~sub_in[i-1],
449
+ ((str_detect(bat_text[i-1], '(tripled|advanced to third|stole third)') == TRUE) & (str_detect(bat_text[i-1], '(scored|out)') == FALSE)) ~ stripwhite(gsub('((tripled|advanced to third|stole third).*$)', '', bat_text[i-1])),
450
+ ((str_detect(r1_text[i-1], '(advanced to third|stole third)') == TRUE) & (str_detect(r1_text[i-1], '(scored|out)') == FALSE)) ~ stripwhite(gsub('((advanced to third|stole third).*$)', '', r1_text[i-1])),
451
+ ((str_detect(r2_text[i-1], '(advanced to third|stole third)') == TRUE) & (str_detect(r2_text[i-1], '(scored|out)') == FALSE)) ~ stripwhite(gsub('((advanced to third|stole third).*$)', '', r2_text[i-1])),
452
+ r1_text[i-1]=='' & (str_detect(bat_text[i-1], '(scored|stole home|homered)') == FALSE) ~ r3_name[i-1],
453
+ r2_text[i-1]=='' & stripwhite(gsub('((scored|stole home|out).*$)', '', r1_text[i-1]))!=stripwhite(r3_name[i-1]) & (str_detect(bat_text[i-1], '(scored|stole home)') == FALSE) ~ r3_name[i-1],
454
+ r3_text[i-1]=='' & (str_detect(r2_text[i-1], '(scored|stole home|out)') == FALSE) & (str_detect(r1_text[i-1], '(scored|stole home|out)') == FALSE) & (str_detect(bat_text[i-1], '(scored|stole home)') == FALSE) ~ r3_name[i-1])
455
+ r3_name[i]=stripwhite(gsub('((singled|doubled|reached|advanced|stole|failed|Failed|picked off).*$)', '', r3_name[i]))
456
+ }
457
+ }
458
+ return(stripwhite(r3_name))
459
+ }
460
+
461
+
462
+
463
+
464
+
465
+ new_game=function(game_end){
466
+ m = length(game_end)
467
+ new_game=integer(m)
468
+ new_game[1]=1
469
+ for (i in 2:m){
470
+ new_game[i]=game_end[i-1]
471
+ }
472
+ return(new_game)
473
+ }
474
+
475
+ new_inn=function(inn_end){
476
+ m = length(inn_end)
477
+ new_inn=integer(m)
478
+ new_inn[1]=1
479
+ for (i in 2:m){
480
+ new_inn[i]=inn_end[i-1]
481
+ }
482
+ return(new_inn)
483
+ }
484
+
485
+
486
+ outs_before= function(outs_on_play, new_game, new_inn){
487
+ m=length(outs_on_play)
488
+ inn_outs=integer(m)
489
+ for (i in 2:m){
490
+ if (isTRUE(new_game[i] == 0 & new_inn[i] == 0)) {
491
+ inn_outs[i]=((inn_outs[i-1]+outs_on_play[i-1]) %% 3)
492
+ }
493
+ }
494
+ return(inn_outs)
495
+ }
496
+
497
+
498
+
499
+
500
+
501
+
502
+
503
+ score_before=function(new_game, runs_on_play, top_inning, home_team=1){
504
+ m=length(new_game)
505
+ home_score_before=integer(m)
506
+ away_score_before=integer(m)
507
+ for (i in 2:m){
508
+ home_score_before[i]= case_when(
509
+ new_game[i]==0 & top_inning[i-1]==0 ~ as.numeric(home_score_before[i-1]+runs_on_play[i-1]),
510
+ new_game[i]==0 & top_inning[i-1]==1 ~ as.numeric(home_score_before[i-1]),
511
+ TRUE ~ 0)
512
+
513
+ away_score_before[i]= case_when(
514
+ new_game[i]==0 & top_inning[i-1]==1 ~ as.numeric(away_score_before[i-1]+runs_on_play[i-1]),
515
+ new_game[i]==0 & top_inning[i-1]==0 ~ as.numeric(away_score_before[i-1]),
516
+ TRUE ~ 0)
517
+ }
518
+ if(home_team==1){
519
+ return(home_score_before)
520
+ }
521
+ else{return(away_score_before)}
522
+
523
+ }
524
+
525
+ runs_play=function(home_score, away_score, home_score_before, away_score_before, top_inn){
526
+ n=length(homescore)
527
+ runs_play=integer(n)
528
+ for (i in 2:n){
529
+ case_when(top_inn[i]==0 ~ homescore[i]-homescore_before[i])
530
+ if (top_inn[i]==0){
531
+ runs_play[i]=homescore[i]-homescore_before[i]
532
+ } else{
533
+ runs_play[i]=roadscore[i]-roadscore_before[i]
534
+ }
535
+ }
536
+ return(runs_play)
537
+ }
538
+
539
+
540
+ runs_this_inn=function(end_inn, runs_on_play){
541
+ m=length(end_inn)
542
+ runs=integer(m)
543
+ endinnloc=c(0,grep(1,end_inn))
544
+ numinns=length(endinnloc)
545
+
546
+
547
+ for (j in 2:numinns){
548
+ for (k in (endinnloc[j-1]+1):endinnloc[j]){
549
+ runs[k]=sum(runs_on_play[(endinnloc[j-1]+1):endinnloc[j]])
550
+ }
551
+ }
552
+ return(runs)
553
+ }
554
+
555
+
556
+ runs_rest_of_inn=function(end_inn, runs_on_play, runs_this_inn){
557
+ m=length(end_inn)
558
+ runs=integer(m)
559
+
560
+ endinnloc=c(0,grep(1,end_inn))
561
+ numinns=length(endinnloc)
562
+
563
+ for (j in 2:numinns){
564
+ for (k in (endinnloc[j-1]+1):endinnloc[j]){
565
+ runs[k]=runs_this_inn[k]-sum(runs_on_play[(endinnloc[j-1]+1):(k)])
566
+ }
567
+ }
568
+ runs=runs+runs_on_play
569
+ return(runs)
570
+ }
571
+
572
+
573
+
574
+
575
+
576
+ bat_order_id = function(new_game, top_inn, bat_name){
577
+ m = length(top_inn)
578
+
579
+ batorder = rep(NA_character_, m)
580
+
581
+ newgameloc = c(grep(1, new_game), (m+1))
582
+ numgames = length(newgameloc)
583
+
584
+ for (j in 2:numgames){
585
+ kk = 0
586
+ jj = 0
587
+ for (i in newgameloc[j-1]:(newgameloc[j]-1)){
588
+
589
+ if (!is.na(top_inn[i]) && !is.na(bat_name[i]) &&
590
+ top_inn[i] == 1 && bat_name[i] != ''){
591
+
592
+ batorder[i] = (kk %% 9) + 1
593
+ kk = kk + 1
594
+
595
+ } else if (!is.na(top_inn[i]) && !is.na(bat_name[i]) &&
596
+ top_inn[i] == 0 && bat_name[i] != ''){
597
+
598
+ batorder[i] = (jj %% 9) + 1
599
+ jj = jj + 1
600
+
601
+ } else {
602
+ batorder[i] = '' # leave empty if NA or no name
603
+ }
604
+ }
605
+ }
606
+ return(batorder)
607
+ }
608
+
609
+
610
+
611
+ bat_order_fill=function(bat_order, end_game){
612
+ m=length(bat_order)
613
+ for (i in (m):2){
614
+ if(is.na(bat_order[i-1])==TRUE & end_game[i-1]==0){
615
+ bat_order[i-1]=bat_order[i]
616
+ }
617
+ }
618
+
619
+ for (i in 2:m){
620
+ if(is.na(bat_order[i])==TRUE){
621
+ bat_order[i]=bat_order[i-1]
622
+ }
623
+ }
624
+ return(bat_order)
625
+ }
626
+ ##########################################################
627
+
628
+
629
+
630
+
631
+ ncaa_parse=function(pbp_data_frame){
632
+
633
+ pbp_data_frame=pbp_data_frame%>%
634
+ mutate(
635
+ tmp_text=paste(away_text, home_text),
636
+ # #
637
+ sub_fl=case_when(
638
+ str_detect(tmp_text, '(singled|doubled|tripled|homered|walked|reached|struck out|grounded|flied|lined|popped| hit|infield fly|infield fly|out|double play|triple play)')==TRUE & str_detect(tmp_text, c('pinch hit'))==FALSE ~ 0,
639
+ str_detect(tmp_text, c('to (p|c|1b|2b|3b|ss|lf|rf|cf|dh)'))==TRUE ~ 1,
640
+ str_detect(tmp_text, c('pinch hit'))==TRUE ~ 1,
641
+ str_detect(tmp_text, c('pinch ran'))==TRUE ~ 1,
642
+ TRUE ~ 0),
643
+
644
+ # Split the text up
645
+ bat_text=gsub('(;|3a|:).*$','', tmp_text),
646
+
647
+ r1_text=case_when(
648
+ str_detect(tmp_text, '(;|3a|:)')==TRUE ~ stripwhite(gsub('^.*?(;|3a|:)','',tmp_text)),
649
+ TRUE~''),
650
+
651
+ r2_text=case_when(
652
+ str_detect(r1_text, '(;|3a|:)')==TRUE ~ stripwhite(gsub('^.*?(;|3a|:)','',r1_text)),
653
+ TRUE~''),
654
+
655
+ r3_text=case_when(
656
+ str_detect(r2_text, '(;|3a|:)')==TRUE ~ stripwhite(gsub('^.*?(;|3a|:)','',r2_text)),
657
+ TRUE~''),
658
+
659
+ r2_text=stripwhite(gsub('(;|3a|:).*$','',r2_text)),
660
+
661
+ r1_text=stripwhite(gsub('(;|3a|:).*$','',r1_text)),
662
+
663
+ # Event code: same as retrosheet
664
+ event_cd=case_when(
665
+ sub_fl==1 ~ 1,
666
+ str_sub(stripwhite(tmp_text),1,1)=='(' ~ 1,
667
+ str_detect(tmp_text, '(hitting out of turn| for |No play|halted|delay|postponed|ejected|suspended|coach|sunny|review|challenged|HC|\\*\\*)') == TRUE ~ 1,
668
+ str_detect(tmp_text,'struck out') == TRUE ~ 3,
669
+ str_detect(tmp_text,'stole') == TRUE ~ 4,
670
+ (str_detect(tmp_text,'(caught stealing|out at second c to|out at third c to)') == TRUE) & (str_detect(tmp_text,'(bunt|grounded)') == FALSE) ~ 6,
671
+ str_detect(tmp_text,'picked off') == TRUE ~ 8,
672
+ str_detect(tmp_text,'wild pitch') == TRUE ~ 9,
673
+ str_detect(tmp_text,'passed ball') == TRUE ~ 10,
674
+ str_detect(tmp_text,'balk') == TRUE ~ 11,
675
+ str_detect(tmp_text,'Dropped foul') == TRUE ~ 13,
676
+ str_detect(tmp_text,'walked') == TRUE ~ 14,
677
+ str_detect(tmp_text,'hit by pitch') == TRUE ~ 16,
678
+ str_detect(tmp_text,'interference') == TRUE ~ 17,
679
+ str_detect(tmp_text,'error') == TRUE ~ 18,
680
+ str_detect(tmp_text,'muffed') == TRUE ~ 18,
681
+ str_detect(tmp_text,'dropped') == TRUE ~ 18,
682
+ str_detect(tmp_text,'fielder\'s choice') == TRUE ~ 19,
683
+ str_detect(tmp_text,'singled') == TRUE ~ 20,
684
+ str_detect(tmp_text,'doubled') == TRUE ~ 21,
685
+ str_detect(tmp_text,'tripled') == TRUE ~ 22,
686
+ str_detect(tmp_text,'homered') == TRUE ~ 23,
687
+ str_detect(tmp_text, '(flied out|grounded out|popped|fouled out|lined out| infield fly|double play|triple play|out at (first|second|third|home))') == TRUE ~ 2,
688
+ str_detect(tmp_text, 'advanced') == TRUE ~ 12,
689
+ TRUE ~ 0),
690
+
691
+
692
+ # Bat name
693
+ bat_name= case_when(
694
+ event_cd %in% c(0,1)~'',
695
+ str_detect(bat_text, '(Batter|Runner\'s interference)')==TRUE ~'',
696
+ str_detect(bat_text, '(walked|singled|doubled|tripled|reached|struck out|grounded out)')==FALSE & str_detect(bat_text, '(advanced|caught stealing|stole|picked off|out at (first|second|third|home)|tagged out)')==TRUE ~ '',
697
+ str_detect(bat_text, '(singled|doubled|tripled|homered|walked|reached|struck out|grounded|flied|lined|popped|hit | out |fouled out|pinch hit|infield fly|intentionally walked|was intentionally walked|fouled into double play)')==TRUE ~ gsub('((singled|doubled|tripled|homered|walked|reached|struck out|grounded|flied|lined|popped|hit | out |fouled out|pinch hit|infield fly|intentionally walked|was intentionally walked|fouled into double play).*$)', '', bat_text),
698
+ str_detect(stripwhite(r1_text), 'caught stealing c to (2b|3b), double play.')==TRUE ~ bat_text,
699
+ TRUE ~ ''),
700
+
701
+ # Sub in
702
+ sub_in= case_when(
703
+ sub_fl==1&str_detect(bat_text, 'to (p|c|1b|2b|3b|ss|lf|rf|cf|dh)')==TRUE ~ stripwhite(gsub('(to (p|c|1b|2b|3b|ss|lf|rf|cf|dh).*$)', '', bat_text)),
704
+ sub_fl==1&str_detect(bat_text, 'pinch ran for')==TRUE ~ stripwhite(gsub('pinch ran for.*$', '', bat_text)),
705
+ sub_fl==1&str_detect(bat_text, 'pinch hit for')==TRUE ~ stripwhite(gsub('pinch hit for.*$', '', bat_text)),
706
+ TRUE ~ ''),
707
+
708
+ # Sub out
709
+ sub_out= case_when(
710
+ sub_fl==1&str_detect(bat_text, 'to (p|c|1b|2b|3b|ss|lf|rf|cf|dh) for')==TRUE ~ gsub('^.*to (p|c|1b|2b|3b|ss|lf|rf|cf|dh) for', '', bat_text),
711
+ sub_fl==1&str_detect(bat_text, 'pinch ran for')==TRUE ~ gsub('^.*pinch ran for', '', bat_text),
712
+ sub_fl==1&str_detect(bat_text, 'pinch hit')==TRUE ~ gsub('^.*pinch hit for', '', bat_text),
713
+ TRUE ~ ''),
714
+ # Clean sub out
715
+ sub_out=strip_punc(sub_out),
716
+
717
+
718
+ # Game end
719
+ game_end = game_end(game_id),
720
+
721
+ # New game
722
+ new_game=new_game(game_end),
723
+
724
+ # Top inning
725
+ top_inning=ifelse(away_text=='', 0,1),
726
+ # End of inning
727
+ inn_end = inn_end(top_inning),
728
+ # Runner names
729
+ r1_name=r1_name(bat_text, bat_name, r1_text, r1_name, inn_end, game_end, sub_in, sub_out),
730
+ r2_name =r2_name(bat_text, bat_name, r1_text, r1_name, r2_text, r2_name, inn_end, game_end, sub_in, sub_out),
731
+ r3_name =r3_name(bat_text, bat_name, r1_text, r1_name, r2_text, r2_name, r3_text, r3_name, inn_end, game_end, sub_in, sub_out),
732
+ # Clean runner names
733
+ r1_name=replace(r1_name,is.na(r1_name),''),
734
+ r2_name=replace(r2_name,is.na(r2_name),''),
735
+ r3_name=replace(r3_name,is.na(r3_name),''),
736
+
737
+
738
+
739
+
740
+
741
+
742
+ # Fix repeat bat names
743
+ bat_name=case_when(
744
+ bat_name!='' & stripwhite(bat_name)==stripwhite(r1_name)~ '',
745
+ bat_name!='' & stripwhite(bat_name)==stripwhite(r2_name)~ '',
746
+ bat_name!='' & stripwhite(bat_name)==stripwhite(r3_name)~ '',
747
+ TRUE ~ bat_name),
748
+
749
+ #
750
+ outs_on_play=case_when(
751
+ event_cd %in% c(0,1) ~ 0,
752
+ str_count(bat_text, 'triple play') == 1 ~ 3,
753
+ str_count(bat_text, 'double play') == 1 ~ 2,
754
+ (str_detect(bat_text, '( out|popped)') == TRUE) & (str_detect(bat_text, '(reached)') == TRUE) ~ 0,
755
+ # 1 out
756
+ ((str_detect(bat_text, '( out |popped|infield fly)') == TRUE) & (str_detect(r1_text, '( out |popped)')==FALSE) & (str_detect(r2_text, '( out |popped)')==FALSE) &(str_detect(r3_text, '( out |popped)')==FALSE)) |
757
+ ((str_detect(bat_text, '( out |popped|infield fly)') == FALSE) & (str_detect(r1_text, '( out |popped)')==TRUE) & (str_detect(r2_text, '( out |popped)')==FALSE) &(str_detect(r3_text, '( out |popped)')==FALSE)) |
758
+ ((str_detect(bat_text, '( out |popped|infield fly)') == FALSE) & (str_detect(r1_text, '( out |popped)')==FALSE) & (str_detect(r2_text, '( out |popped)')==TRUE) &(str_detect(r3_text, '( out |popped)')==FALSE)) |
759
+ ((str_detect(bat_text, '( out |popped|infield fly)') == FALSE) & (str_detect(r1_text, '( out |popped)')==FALSE) & (str_detect(r2_text, '( out |popped)')==FALSE) &(str_detect(r3_text, '( out |popped)')==TRUE)) ~ 1,
760
+ # 2 outs
761
+ ((str_detect(bat_text, '( out |popped|infield fly)') == TRUE) & (str_detect(r1_text, '( out |popped)')==TRUE) & (str_detect(r2_text, '( out |popped)')==FALSE) &(str_detect(r3_text, '( out |popped)')==FALSE)) |
762
+ ((str_detect(bat_text, '( out |popped|infield fly)') == TRUE) & (str_detect(r1_text, '( out |popped)')==FALSE) & (str_detect(r2_text, '( out |popped)')==TRUE) &(str_detect(r3_text, '( out |popped)')==FALSE)) |
763
+ ((str_detect(bat_text, '( out |popped|infield fly)') == TRUE) & (str_detect(r1_text, '( out |popped)')==FALSE) & (str_detect(r2_text, '( out |popped)')==FALSE) &(str_detect(r3_text, '( out |popped)')==TRUE)) |
764
+ ((str_detect(bat_text, '( out |popped|infield fly)') == FALSE) & (str_detect(r1_text, '( out |popped)')==TRUE) & (str_detect(r2_text, '( out |popped)')==TRUE) &(str_detect(r3_text, '( out |popped)')==FALSE)) |
765
+ ((str_detect(bat_text, '( out |popped|infield fly)') == FALSE) & (str_detect(r1_text, '( out |popped)')==TRUE) & (str_detect(r2_text, '( out |popped)')==FALSE) &(str_detect(r3_text, '( out |popped)')==TRUE)) |
766
+ ((str_detect(bat_text, '( out |popped|infield fly)') == FALSE) & (str_detect(r1_text, '( out |popped)')==FALSE) & (str_detect(r2_text, '( out |popped)')==TRUE) &(str_detect(r3_text, '( out |popped)')==TRUE)) ~ 2,
767
+ # 3 outs
768
+ ((str_detect(bat_text, '( out |popped|infield fly)') == TRUE) & (str_detect(r1_text, '( out |popped)')==TRUE) & (str_detect(r2_text, '( out |popped)')==TRUE) &(str_detect(r3_text, '( out |popped)')==FALSE)) |
769
+ ((str_detect(bat_text, '( out |popped|infield fly)') == TRUE) & (str_detect(r1_text, '( out |popped)')==FALSE) & (str_detect(r2_text, '( out |popped)')==TRUE) &(str_detect(r3_text, '( out |popped)')==TRUE)) |
770
+ ((str_detect(bat_text, '( out |popped|infield fly)') == TRUE) & (str_detect(r1_text, '( out |popped)')==TRUE) & (str_detect(r2_text, '( out |popped)')==FALSE) &(str_detect(r3_text, '( out |popped)')==TRUE)) |
771
+ ((str_detect(bat_text, '( out |popped)') == FALSE) & (str_detect(r1_text, '( out |popped)')==TRUE) & (str_detect(r2_text, '( out |popped)')==TRUE) &(str_detect(r3_text, '( out |popped)')==TRUE)) ~ 3,
772
+ TRUE ~ 0),
773
+
774
+ # New inning
775
+ new_inn=new_inn(inn_end),
776
+ # Outs before
777
+ outs_before=outs_before(outs_on_play, new_game, new_inn),
778
+ # Outs after
779
+ outs_after=outs_before+outs_on_play,
780
+
781
+ # Base code
782
+ base_cd_before=case_when(
783
+ stripwhite(r1_name)!='' & r2_name=='' & r3_name=='' ~ 1,
784
+ r1_name=='' & r2_name!='' & r3_name=='' ~ 2,
785
+ r1_name!='' & r2_name!='' & r3_name=='' ~ 3,
786
+ r1_name=='' & r2_name=='' & r3_name!='' ~ 4,
787
+ r1_name!='' & r2_name=='' & r3_name!='' ~ 5,
788
+ r1_name=='' & r2_name!='' & r3_name!='' ~ 6,
789
+ r1_name!='' & r2_name!='' & r3_name!='' ~ 7,
790
+ TRUE~0),
791
+
792
+ # Batting order
793
+ bat_order=bat_order_id(new_game, top_inning, bat_name),
794
+
795
+ # Hit type
796
+ hit_type=case_when(
797
+ event_cd==3 ~ 'K',
798
+ str_detect(bat_text,'(bunt)')==TRUE ~ 'B',
799
+ str_detect(bat_text, '(bunt)')==FALSE & str_detect(bat_text, '(SAC)')==TRUE & str_detect(bat_text, '(flied|popped)')==FALSE ~ 'B',
800
+ str_detect(bat_text,'(grounded out|(p|3b|2b|ss|1b) to (p|3b|2b|ss|1b|c))')==TRUE ~ 'GO',
801
+ str_detect(bat_text,'(flied|fouled out to (lf|rf))')==TRUE ~ 'FO',
802
+ str_detect(bat_text,'(lined)')==TRUE ~ 'LO',
803
+ str_detect(bat_text,'(popped|infield fly|fouled out to (p|3b|2b|ss|1b|c))')==TRUE ~ 'PO',
804
+ TRUE ~ '' ),
805
+
806
+ # Runs on play
807
+ runs_on_play=(as.numeric(str_count(tmp_text, '(advanced to home)'))+as.numeric(str_count(tmp_text, '(scored)')) + as.numeric(str_count(tmp_text, '(homered)')) + as.numeric(str_count(tmp_text, '(stole home)'))-as.numeric(str_count(tmp_text, '(scored, scored)'))),
808
+
809
+ # Away score
810
+ away_score_before=score_before(new_game, runs_on_play, top_inning, home_team=0),
811
+
812
+ # Home score
813
+ home_score_before=score_before(new_game, runs_on_play, top_inning, home_team=1),
814
+
815
+ # # Away score after
816
+ away_score_after=case_when(
817
+ top_inning==1 ~away_score_before+ runs_on_play,
818
+ TRUE ~ away_score_before),
819
+
820
+ # # Home score after
821
+ home_score_after=case_when(
822
+ top_inning==0 ~home_score_before+ runs_on_play,
823
+ TRUE ~ home_score_before),
824
+
825
+ # Runs this inning
826
+ runs_this_inn=runs_this_inn(inn_end, runs_on_play),
827
+
828
+ # Runs rest of inning
829
+ runs_roi=runs_rest_of_inn(inn_end,runs_on_play, runs_this_inn),
830
+
831
+ # Intentional walk
832
+ int_bb_fl=case_when(
833
+ str_detect(tmp_text,'intentionally ') == TRUE ~ 1,
834
+ TRUE ~ 0
835
+ ),
836
+
837
+ # Sac bunts
838
+ sh_fl=case_when(
839
+ str_detect(bat_text, '(SAC)')==TRUE & str_detect(bat_text, '(flied|popped)')==FALSE ~ 1,
840
+ TRUE~0),
841
+
842
+ # Sac flys
843
+ sf_fl=case_when(
844
+ str_detect(bat_text, '(SAC)')==TRUE & str_detect(bat_text, '(flied|popped)')==TRUE ~ 1,
845
+ str_detect(bat_text, '(SAC)')==FALSE & str_detect(bat_text, '(flied|popped)')==TRUE & str_detect(bat_text, '(RBI)')==TRUE~1,
846
+ TRUE~0 )
847
+ )
848
+
849
+
850
+ pbp_data_frame=pbp_data_frame%>%
851
+ mutate(bat_order=bat_order_fill(bat_order, game_end))
852
+
853
+ return(pbp_data_frame)
854
+
855
+ }
856
+
857
+ prefixes <- c("St\\.", "Mc", "De", "Di", "Van", "Von")
858
+
859
+ #Get NCAA Game IDs
860
+ get_ncaa_schedule <- function(date) {
861
+
862
+ date <- as.Date(date)
863
+ api_url <- sprintf(
864
+ "https://ncaa-api.henrygd.me/scoreboard/baseball/d1/%04d/%02d/%02d/all-conf",
865
+ as.integer(format(date, "%Y")),
866
+ as.integer(format(date, "%m")),
867
+ as.integer(format(date, "%d"))
868
+ )
869
+
870
+
871
+ res <- GET(api_url, user_agent("Mozilla/5.0"))
872
+ stop_for_status(res)
873
+
874
+ dat <- fromJSON(content(res, "text", encoding = "UTF-8"), flatten = TRUE)
875
+
876
+
877
+
878
+ dat$games <- dat$games %>%
879
+ tidyr::unnest(game.away.conferences) %>%
880
+ rename(away_conference = conferenceName) %>%
881
+ dplyr::select(-conferenceSeo) %>%
882
+ tidyr::unnest(game.home.conferences) %>%
883
+ rename(home_conference = conferenceName)
884
+
885
+ tibble(
886
+ Date = dat$games$game.startDate,
887
+ GameID = dat$games$game.url %>%
888
+ str_extract("(?<=/game/)\\d+"),
889
+ HomeTeam = dat$games$game.home.names.short,
890
+ AwayTeam = dat$games$game.away.names.short,
891
+ StartTime = dat$games$game.startTime,
892
+ HomeScore = dat$games$game.home.score,
893
+ AwayScore = dat$games$game.away.score,
894
+ HomeRecord = dat$games$game.home.description,
895
+ AwayRecord = dat$games$game.away.description,
896
+ HomeConference = dat$games$home_conference,
897
+ AwayConference = dat$games$away_conference
898
+ )
899
+ }
900
+
901
+
902
+ #Scrapes NCAA PBP based on game IDs
903
+ get_ncaa_pbp <- function(game_id){
904
+
905
+ url <- sprintf("https://ncaa-api.henrygd.me/game/%s/play-by-play", game_id)
906
+ res <- GET(url)
907
+ stop_for_status(res)
908
+
909
+ dat <- fromJSON(content(res, "text", encoding = "UTF-8"), flatten = TRUE)
910
+
911
+ teams_df <- dat$teams
912
+
913
+ team_home <- teams_df %>% filter(isHome == TRUE) %>% pull(nameShort)
914
+ team_visitor <- teams_df %>% filter(isHome == FALSE) %>% pull(nameShort)
915
+
916
+ home_id <- teams_df %>% filter(isHome == TRUE) %>% pull(teamId)
917
+ visitor_id <- teams_df %>% filter(isHome == FALSE) %>% pull(teamId)
918
+
919
+ home_name_id <- teams_df %>%
920
+ filter(isHome == TRUE) %>%
921
+ mutate(name_id = paste0(toupper(str_sub(nameShort, 1, 3)), "_", toupper(str_sub(teamName, 1, 3)))) %>%
922
+ pull(name_id)
923
+
924
+ away_name_id <- teams_df %>%
925
+ filter(isHome == FALSE) %>%
926
+ mutate(name_id = paste0(toupper(str_sub(nameShort, 1, 3)), "_", toupper(str_sub(teamName, 1, 3)))) %>%
927
+ pull(name_id)
928
+
929
+ pbp <- dat$periods %>%
930
+ unnest(playbyplayStats, names_sep = "_") %>%
931
+ unnest(playbyplayStats_plays, names_sep = "_") %>%
932
+ mutate(
933
+ game_id = game_id,
934
+ away_team = team_visitor,
935
+ home_team = team_home,
936
+ PlayNumber = row_number(),
937
+ BatterTeam = ifelse(playbyplayStats_teamId == home_id, team_home, team_visitor),
938
+ PitcherTeam = ifelse(playbyplayStats_teamId == home_id, team_visitor, team_home),
939
+ away_text = ifelse(playbyplayStats_teamId == visitor_id, playbyplayStats_plays_playText, ""),
940
+ home_text = ifelse(playbyplayStats_teamId == home_id, playbyplayStats_plays_playText, ""),
941
+ Score = ifelse(is.na(playbyplayStats_plays_homeScore) & is.na(playbyplayStats_plays_visitorScore),
942
+ "",
943
+ paste0(playbyplayStats_plays_visitorScore, "-", playbyplayStats_plays_homeScore)),
944
+ home_name_id = home_name_id,
945
+ away_name_id = away_name_id
946
+ ) %>%
947
+ dplyr::select(game_id, Inning = periodNumber, PlayNumber, BatterTeam, PitcherTeam,
948
+ away_team, home_team, away_text, home_text, Score, home_name_id, away_name_id)
949
+
950
+ return(pbp)
951
+ }
952
+
953
+
954
+ scrape_clean_ncaa_pbp <- function(start_date, end_date) {
955
+
956
+ games_list <- tibble()
957
+ counter <- 0
958
+
959
+ dates_list <- seq(as.Date(start_date), as.Date(end_date), by = "day")
960
+
961
+
962
+ #Get Schedule Data for Game IDs
963
+ for (date in dates_list) {
964
+
965
+ counter <- counter + 1
966
+ message(sprintf(
967
+ "%5.1f%% | %d / %d | %s",
968
+ 100 * counter / length(dates_list), counter, length(dates_list), as.Date(date)
969
+ ))
970
+
971
+ new_game_ids <- tryCatch(
972
+ get_ncaa_schedule(date),
973
+ error = function(e) NULL
974
+ )
975
+
976
+ if (is.null(new_game_ids) || nrow(new_game_ids) == 0) {
977
+ message(" -- no games, skipping")
978
+ next
979
+ }
980
+
981
+ new_game_ids <- new_game_ids %>%
982
+ distinct(GameID, .keep_all = TRUE)
983
+ games_list <- rbind(games_list, new_game_ids)
984
+
985
+ Sys.sleep(0.2)
986
+ }
987
+
988
+ df <- tibble()
989
+ counter <- 0
990
+
991
+ #Get PBP Data
992
+ for (i in seq_len(nrow(games_list))){
993
+
994
+ counter <- counter + 1
995
+
996
+ new_data <- tryCatch(
997
+ get_ncaa_pbp(games_list$GameID[i]),
998
+ error = function(e) NULL
999
+ )
1000
+
1001
+ if (is.null(new_data) || nrow(new_data) == 0) {
1002
+ message(" -- no data, skipping")
1003
+ next
1004
+ }
1005
+
1006
+ new_data <- new_data %>%
1007
+ mutate(Date = games_list$Date[i])
1008
+
1009
+ df <- rbind(new_data, df)
1010
+
1011
+
1012
+
1013
+ message(sprintf(
1014
+ "%5.1f%% | %d / %d | %s",
1015
+ 100 * counter / length(unique(games_list$GameID)), counter, length(unique(games_list$GameID)), games_list$GameID[i]
1016
+ ))
1017
+
1018
+ Sys.sleep(0.2)
1019
+
1020
+ }
1021
+
1022
+ pbp <- ncaa_parse(df2)
1023
+
1024
+ }
1025
+
1026
+
1027
+
1028
+
1029
  # UI
1030
  ui <- fluidPage(
1031
  tags$head(
 
2377
 
2378
  result <- tryCatch({
2379
  if (input$scrape_source == "pbp") {
2380
+ scrape_trackman_pbp(input$start_date, input$end_date)
2381
  } else if (input$scrape_source == "pos") {
2382
+ scrape_trackman_positional(input$start_date, input$end_date)
2383
  } else if (input$scrape_source == "ncaa") {
2384
+ scrape_clean_ncaa_pbp(input$start_date, input$end_date)
 
 
2385
  }
2386
  }, error = function(e) {
2387
  scrape_status_msg(paste("Error:", e$message))