-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathserver.R
More file actions
1622 lines (1296 loc) · 58.9 KB
/
server.R
File metadata and controls
1622 lines (1296 loc) · 58.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Required packages
library(shiny)
library(shinyjs)
library(rhandsontable)
library(stringr)
library(stringi)
library(Biostrings)
library(rentrez)
library(rlist)
library(DT)
library(plyr)
library(Rcpp)
library(curl)
library(httr)
library(jsonlite)
library(xml2)
# Required supporting files
source("apeShiftFunctions.R")
source("genbankAccessoryFunctions.R")
source("menthu2.0AccessoryFunctions.R")
source("required2.0Functions_1.R")
source("targetAccessoryFunctions2.0.R")
source("ensemblAccessoryFunctions.R")
shinyServer(function(input, output, session){
########################################################################################################################################################################
###################Global Variables#####################################################################################################################################
########################################################################################################################################################################
# Storage for final results so that it is accessible to download handler and submit buttons
results <<- 0
# Empty data frame for displaying when users are inputting exon information
dfEmpty <<- data.frame(Exon_Num = rep(0, 5),
exonStart = rep(0, 5),
exonEnd = rep(0, 5),
stringsAsFactors = FALSE)
pamEmpty <<- data.frame(PAM_Sequence = rep("NNN", 1),
DSB_Position = rep(0, 1),
Overhang_Length = rep(0, 1),
stringsAsFactors = FALSE)
# Example data frame for when the paste gene seq example is selected
dfExample <<- data.frame(Exon_Num = c(1, 2, 3, 4, 5),
exonStart = c(1, 101, 201, 301, 401),
exonEnd = c(100, 200, 300, 400, 500),
stringsAsFactors = FALSE)
# Example data frame for when examples are selected
pamExample <<- data.frame(PAM_Sequence = c("NG", "TTN"),
DSB_Position = c(-3, 18),
Overhang_Length = c(0, 5),
stringsAsFactors = FALSE)
# Reactive values
rValues <- reactiveValues(downloadF = FALSE, # Flag for displaying download button (for copy/paste gene seq)
downloadFGB = FALSE, # Flag for displaying download button (for genbank)
filtOpsGB = FALSE, # Flag for displaying filter options after calculation completion
filtOpsGS = FALSE, # Flag for displaying filter options after calculation completion
filtOpsE = FALSE, # Flag for displaying filter options after calculation completion
geneSeqResultsFlag = FALSE, # Flag for displaying gene seq results table
genbankResultsFlag = FALSE, # Flag for displaying genbank results table
rhFrame = dfEmpty, # Slot to hold exon information data frame
pamFrame = pamEmpty, # Slot to hold custom nuclease information
resetVal = FALSE, # For if the reset button has been clicked
geneSeqError = 0, # Error messages for geneSeq submission
downloadFEns = FALSE, # Flag for displaying download button (for Ensembl)
ensemblResultsFlag = FALSE, # Flag for displaying Ensembl results table
validExonListFlag = TRUE
)
# Load Ensembl ID table
ensIds <<- readRDS("2018-09-21_ensIds.RDS")
# Flag for displaying example table when clicking example geneSeq link
geneSeqExampleFlag <<- FALSE
########################################################################################################################################################################
##################Validation Checks#####################################################################################################################################
########################################################################################################################################################################
####Make sure GenBank/RefSeq ID is properly formatted####
validRefSeqGBGenbankId <- reactive({
if(input$genbankId != ""){
#Let RefSeq RNA accessions through
if( stringr::str_detect(input$genbankId, regex("^(NM|NR|XM|XR)_[0-9]{6}", ignore_case = TRUE))){
#Catch RefSeq protein accesssions
} else if(stringr::str_detect(input$genbankId, regex("^(AP|NP|YP|XP|WP)_[0-9]{6}", ignore_case = TRUE))){
shiny::validate(
need(1 == 2,
paste0("Error: This ID matches the RefSeq protein accession format. ",
"Please submit an accession corresponding to a DNA sequence.")))
# Catch ginormous genomic region files; disabled for running locally
} else if(stringr::str_detect(input$genbankId, regex("^(AC|NC|NG|NT|NW|NZ)_[0-9]{6}", ignore_case = TRUE))){
#shiny::validate(
# need(1 == 2,
# paste0("Error: This ID matches a RefSeq complete genomic molecule, incomplete genomic region, contig, ",
# "scaffold, or complete genome. We do not currently support any of these reference types due to ",
# "issues surrounding exon identification. You can use a GenBank or RefSeq nucleotide entry ",
# "corresponding to your gene of interest."))
#)
# Catch GenBank protein accessions
} else if(stringr::str_detect(input$genbankId, regex("^[A-Z]{3}[0-9]{5}", ignore_case = TRUE))){
shiny::validate(
need(1 == 2,
paste0("Error: This ID matches GenBank protein accession format. ",
"Please submit an accession corresponding to a DNA sequence.")))
# Catch anything else not conforming to input types
} else {
shiny::validate(
need(((( stringr::str_detect(input$genbankId, regex("^[a-zA-Z]{2}[0-9]{6}", ignore_case = TRUE))) |
(stringr::str_detect(input$genbankId, regex("^[a-zA-Z]{1}[0-9]{5}", ignore_case = TRUE)))) |
(stringr::str_detect(input$genbankId, regex("^(AC|NC|NG|NT|NW|NZ)_[0-9]{6}\\.[0-9]", ignore_case = TRUE)))) |
( stringr::str_detect(input$genbankId, regex("^[a-zA-Z]{4}[0-9]{8,10}", ignore_case = TRUE))),
paste0("Error: The entered ID does not match a known Genbank NUCLEOTIDE or RefSeq NUCLEOTIDE ID format.",
"Please check your submitted accession to make sure you are using a NUCLEOTIDE entry.")))
}
}
})
####Validate Ensembl accession format####
validEnsemblId <- reactive({
# Check that the input is not empty
if((input$inputType == 3) & input$ensemblId != ""){
# If gene input...
if(getEnsemblIdType(input$ensemblId) == "gene"){
shiny::validate(
need(1 == 2,
paste0("Error: The input Ensembl accession appears to be an Ensembl gene. ",
"Ensembl genes can have several associated transcripts; please use the Ensembl transcript ID instead of the gene.")
)
)
# If NOT gene input...
} else {
shiny::validate(
need(ensemblIdSpecies(input$ensemblId, ensIds, bool = TRUE),
"Error: Input Ensembl ID does not match a known Ensembl species or Ensembl ID format. Please check your input ID."
)
)
}
}
})
# Check if the Ensembl ID actually exists
# If we can make contact with Ensembl
ensemblIdExists <- reactive({
if((input$inputType == 3) & input$ensemblId != ""){
if(is.null(validEnsemblId())){
if(isEnsemblUp()){
shiny::validate(
need(!grepl("not found", lookupEnsemblInfo(input$ensemblId)),
"Error: The input Ensembl ID was not found in Ensembl's database. Please check your input ID."
)
)
# If we can't contact Ensembl
} else {
shiny::validate(
need(1 == 2,
"Warning: Ensembl is not responding to our requests. Please try again in a few minutes."
)
)
}
}
}
})
####Validate copy/paste sequence input####
validGeneSeq <- reactive({
#If the input type is copy/paste gene seq and text has been entered
if((input$geneSeq != "") &&
(input$inputType == 2)){
shiny::validate(
#Check for fasta format
need(!stringr::str_detect(input$geneSeq, "[>]"),
"Error: Input DNA sequence appears to be in FASTA format. Please paste your sequence without the fasta header."),
#Check for not DNA input
need(!stringr::str_detect(input$geneSeq, "[^ACGTacgt0-9\\s\\n]"),
paste0("Error: Input DNA sequence contains non-standard nucleotides. ",
"Allowed nucleotides are A, C, G, and T.")),
# Prevent users from submitting too short sequences
need(nchar(input$geneSeq) >= 80,
paste0("The DNA sequence has <80 nucleotides. MENTHU requires at least 40 nucleotides upstream",
"and 40 nucleotides downstream of the DSB site in order to properly calculate the MENTHU score."))#,
#Prevent users from blowing up the server
#need(nchar(input$geneSeq) < 5000,
# paste0("The DNA sequence has >5000 nucleotides. For sequences of this size, ",
# "please use the local version of MENTHU, which can be accessed via the 'Tools and Downloads' tab."))
)
} else if(input$inputType == 2) {
# Prevent running on empty submission
shiny::validate(
need(input$geneSeq != "", "")
)
}
})
# Valid PAM - make sure user selects at least one target type
validPAM <- reactive({
shiny::validate(
need((length(input$casType) > 0) | (input$talenOp == 1) | (input$customCutOpt == 1),
"Error: No nuclease selected. Please select a Cas type and/or a custom PAM scheme and/or the TALEN input option.")
)
})
# Valid TALEN inputs
validTalen <- reactive({
shiny::validate(
# Make sure that the arm length min is less than max
need(input$armin <= input$armax,
"Error: Maximum TALEN arm length must be greater than or equal to minimum TALEN arm length."),
# Limit arm length to 15-18 nt
need((input$armin >= 15),
"Error: Minimum TALEN arm length is 15 nucleotides."),
need((input$armax <= 18),
"Error: Maximum TALEN arm length is 18 nucleotides.")
)
})
# Valid custom PAM inputs
validCustomPam <- reactive({
# If the custom PAM is selected, make sure it only has IUPAC nucleotides or separating characters
if(input$customPamSeq != ""){
if(stringr::str_detect(input$customPamSeq, "[^ACGTRYSWKMBDHVNacgtryswkmbdhvn\\s,]")){
shiny::validate(
need(1 == 2,
paste0("Error: Non-allowed characters detected. ",
"Only standard nucleotide (A, C, G, T), IUPAC extended ",
"nucleotide symbols (R, Y, S, W, K, M, B, D, H, V, N), ",
"and separating characters (spaces and commas) allowed."))
)
} else {
# Check for all 'N' PAMs, single nucleotide PAMs, or PAMs consisting solely of Ns and a single nucleotide; disabled for running locally
#disallowed <- "^[N]+[ACGTRYSWKMBDHV]{1}$|^[N]+[ACGTRYSWKMBDHV]{1}[N]+$|^[ACGTRYSWKMBDHVN]{1}$|^[N]+$"
#disallowedCheck <- grepl(disallowed, pamStitch("", input$customPamSeq), ignore.case = TRUE, perl = TRUE)
#disallowedExist <- is.element(TRUE, unlist(disallowedCheck))
#if(disallowedExist){
# shiny::validate(
# need(1 == 2,
# paste0("Error: Due to computational limitations, we currently do not accept custom PAMs consisting",
# "solely of 'N', single nucleotide PAMs, or PAMs consisting of solely of 'N's and a single nucleotide."))
# )
#}
}
}
})
# Valid custom cut sites
validCustomCutSites <- reactive({
if(input$cutSite != ""){
shiny::validate(
need(!stringr::str_detect(input$cutSite, "[^0-9\\s,-]"),
"Error: Only numbers, dashes, commas, and spaces allowed.")
)
}
})
# Valid custom overhangs
validOverhangs <- reactive({
if(input$overhang != ""){
shiny::validate(
need(!stringr::str_detect(input$overhang, "[^0-9\\s,-]"),
"Error: Only numbers, dashes, commas, and spaces allowed.")
)
}
})
# Check that the number of custom PAMs matches the number of custom cutSites
validMatchCustomInputLength <- reactive({
if((input$customPamSeq != "") &&
(input$customCutOpt != "")){
shiny::validate(
need(length(as.character(distStitch("", input$cutSite))) == length(pamStitch("", input$customPamSeq)) &&
length(as.character(distStitch("", input$cutSite))) == length(as.character(ohStitch("", input$overhang))),
paste0("Error: The number of custom PAM sequences, custom DSB sites, and overhang lengths does not match. ",
"Please make sure that each PAM has one distance to its cut site and overhang length specified."))
)
}
})
# Check to make sure that exon list inputs are interpretable
validExonList <- reactive({
if(input$exonTargetType == 3 && input$exonTargetList != ""){
if(!rValues$validExonListFlag){
shiny::validate(
need(1 == 2,
paste0("Error: Some or all of the exons in this list are not found in the input accession. ",
"Please check the exon numbering of your accession (ESPECIALLY if you're using Ensembl), ",
"as GenBank, RefSeq, and Ensembl may have inconsistent exon numbering. "))
)
}
}
})
########################################################
##############PRINT VALIDATION RESULTS##################
########################################################
output$validgenbankid <- renderText({validRefSeqGBGenbankId() })
output$genbankidexists <- renderText({ })
output$validensemblid <- renderText({validEnsemblId() })
output$ensemblidexists <- renderText({ensemblIdExists() })
output$validgeneseq <- renderText({validGeneSeq() })
output$validpam <- renderText({validPAM() })
output$validtalen <- renderText({validTalen() })
output$validcustompam <- renderText({validCustomPam() })
output$validcustomcutsites <- renderText({validCustomCutSites() })
output$validoverhangs <- renderText({validOverhangs() })
output$validmatchcustominputlength <- renderText({validMatchCustomInputLength()})
output$geneseqerrors <- renderText({geneSeqErrors() })
output$validexonlist <- renderText({validExonList() })
#output$validexoninfo <- renderText({validExonInfo() })
########################################################################################################################################################################
##################Bookmark Functions####################################################################################################################################
########################################################################################################################################################################
observeEvent(input$bookmarkGS, {session$doBookmark()})
observeEvent(input$bookmarkGB, {session$doBookmark()})
observeEvent(input$bookmarkE, {session$doBookmark()})
########################################################################################################################################################################
#####################UI Rendering#######################################################################################################################################
########################################################################################################################################################################
# If there are no PAMs in the target sequence
geneSeqErrors <- reactive({
rValues$geneSeqError
if(rValues$geneSeqError == 0){
""
} else if(rValues$geneSeqError == 1){
"Error: No targets corresponding to the PAM(s) detected."
}
})
# TODO - do we need this function for TALENs?
###########################################################
######### Rendering functions for exon input table ########
######### ROLL FOR INITIATIVE ###########
###########################################################
# , \ / , #
# / \ )\__/( / \ #
# / \ (_\ /_) / \ #
# ____/_____\__\@ @/___/_____\____ #
# | |\../| | #
# | \VV/ | #
# | Here thar be | #
# | DARGONS!! | #
# |_________________________________| #
# | /\ / \\ \ /\ | #
# | / V )) V \ | #
# |/ ` // ' \| #
# ` V ' #
###########################################################
# Dragon ASCII art from #
# https://www.asciiart.eu/mythology/dragons #
###########################################################
# But for realsies do not touch any of this; it literally #
# took months to get lines 397-435 working properly. #
###########################################################
#### Render function for exon input table
renderTable <- reactive({
exonOutDF <- NULL
if(!is.null(input$exonInfo)){
exonOutDF <- hot_to_r(input$exonInfo)
} else if(!is.null(isolate(rValues$rhFrame))){
exonOutDF <- isolate(rValues$rhFrame)
}
if(!is.null(exonOutDF)){
rValues$rhFrame <- exonOutDF
}
return(exonOutDF)
}) %>% debounce(1000)
#### Render exon input table
output$exonInfo <- renderRHandsontable({
# Action buttons that this function is dependent on
input$reset
input$exampleGeneSeq
if(isolate(rValues$resetVal)){
exonOutDF <- rValues$rhFrame
} else {
exonOutDF <- renderTable()
rValues$resetVal <- FALSE
}
if(!is.null(exonOutDF)){
rhandsontable(exonOutDF) %>%
hot_context_menu(allowRowEdit = TRUE, allowColEdit = FALSE) %>%
hot_col("Exon_Num", format = "0") %>%
hot_col("exonStart", format = "0") %>%
hot_col("exonEnd", format = "0")
}
})
#### Render table for nucleases ####
renderTableNuc <- reactive({
pamOutDF <- NULL
if(!is.null(input$pamTable)){
pamOutDF <- hot_to_r(input$pamTable)
} else if(!is.null(isolate(rValues$pamFrame))){
pamOutDF <- isolate(rValues$pamFrame)
}
if(!is.null(pamOutDF)){
rValues$pamFrame <- pamOutDF
}
return(pamOutDF)
}) %>% debounce(1000)
#### Render more nuclease table ####
output$pamTable <- renderRHandsontable({
# Action buttons that this function is dependent on
input$reset
input$exampleGeneSeq
input$exampleGenBank
input$exampleEnsembl
if(isolate(rValues$resetVal)){
pamOutDF <- rValues$pamFrame
} else {
pamOutDF <- renderTableNuc()
rValues$resetVal <- FALSE
}
if(!is.null(pamOutDF)){
rhandsontable(pamOutDF) %>%
hot_context_menu(allowRowEdit = TRUE, allowColEdit = FALSE) %>%
hot_col("PAM_Sequence", format = "N") %>%
hot_col("DSB_Position", format = 0) %>%
hot_col("Overhang_Length", format = 0)
}
})
##########################################################
#################### END DARGONS #########################
##########################################################
##########################################################
################# Download Stuff #########################
##########################################################
# Download button for copy/paste results
output$downOutGS <- renderUI({
if(rValues$downloadF){
downloadButton("downRes", "Download Results")
} else {
""
}
})
# Download button for genbank results
output$downOutGB <- renderUI({
if(rValues$downloadFGB){
downloadButton("downRes", "Download Results")
} else {
""
}
})
# Download button for Ensembl results
output$downOutEns <- renderUI({
if(rValues$downloadFEns){
downloadButton("downRes", "Download Results")
} else {
""
}
})
# Download handler code
output$downRes <- downloadHandler(
filename = function(){
#Name file in the form "YYYY-MM-DD_HH-MM-SS_InputID_targets.csv
if(input$inputType == 1){
# For GenBank/Refseq
# Tag the file if the outputs were filtered
if(input$t7OptGB || input$thresholdGB || !input$inFrameGB || !input$outFrameGB){
filt <- "filtered"
} else {
filt <- ""
}
# Generate the file name
paste0(gsub("CDT", "", gsub(" ", "_", Sys.time())), "_", input$genbankId, "_MENTHU_targets_", filt, ".csv")
} else if(input$inputType == 3){
# For Ensembl
# Tag the file if the outputs were filtered
if(input$t7OptE || input$thresholdE || !input$inFrameE || !input$outFrameE){
filt <- "filtered"
} else {
filt <- ""
}
# Generate the file name
paste0(gsub("CDT", "", gsub(" ", "_", Sys.time())), "_", input$ensemblId, "_MENTHU_targets_", filt, ".csv")
} else {
# For copy/paste
# Tag the file if the outputs were filtered
if(input$t7OptGS || input$thresholdGS || !input$inFrameGS || !input$outFrameGS){
filt <- "filtered"
} else {
filt <- ""
}
# Generate the file name
paste0(gsub("CDT", "", gsub(" ", "_", Sys.time())), "_custom_seq_MENTHU_targets_", filt, ".csv")
}
},
content = function(file){
# Apply filters to the download
if(input$inputType == 1){
resOut <- filterResults(results, input$t7OptGB, input$thresholdGB, input$inFrameGB, input$outFrameGB)
} else if(input$inputType == 2){
resOut <- filterResults(results, input$t7OptGS, input$thresholdGS, input$inFrameGS, input$outFrameGS)
} else if(input$inputType == 3){
resOut <- filterResults(results, input$t7OptE, input$thresholdE, input$inFrameE, input$outFrameE)
}
# Check to make sure there's actually output to download
# TODO allow users to remove PAMs from download
if(resOut[[1]]){
# Remove HTML comments
resOut[[2]]$Target_Sequence <- gsub("<strong>|</strong>", "", resOut[[2]]$Target_Sequence, ignore.case = TRUE, perl = TRUE)
# Output the file
write.table(resOut[[2]], file, row.names = FALSE, col.names = TRUE, append = FALSE, quote = FALSE, sep = ",")
} else {
}
}
)
######################################################################################################################################################################
################# UI FILTER OPTIONS ##################################################################################################################################
######################################################################################################################################################################
# Display filter conditional panel when output is finished calculating
output$filtOpsGS <- reactive({return(rValues$filtOpsGS)})
output$filtOpsGB <- reactive({return(rValues$filtOpsGB)})
output$filtOpsE <- reactive({return(rValues$filtOpsE )})
# Make sure the filter options stay awake when hidden
outputOptions(output, "filtOpsGS", suspendWhenHidden = FALSE)
outputOptions(output, "filtOpsGB", suspendWhenHidden = FALSE)
outputOptions(output, "filtOpsE", suspendWhenHidden = FALSE)
#####################################################################################################################################################################
############################# RESULT OUTPUT #########################################################################################################################
#####################################################################################################################################################################
# Output function for copy/paste results
output$geneSeqResults <- renderUI({
rValues$geneSeqResultsFlag
input$inputType
if(rValues$geneSeqResultsFlag && input$inputType == "2"){
out <- filterResults(results, input$t7OptGS, input$thresholdGS, input$inFrameGS, input$outFrameGS)
if(out[[1]]){
output$placeholder <- DT::renderDataTable(out[[2]],
options = list(scrollX = TRUE),
rownames = FALSE,
escape = FALSE)
DT::dataTableOutput("placeholder")
} else {
"No sites satisfy the selected filters."
}
} else {
""
}
})
# Output function for Genbank results
output$genbankResults <- renderUI({
rValues$genbankResultsFlag
input$inputType
if(rValues$genbankResultsFlag && input$inputType == "1"){
out <- filterResults(results, input$t7OptGB, input$thresholdGB, input$inFrameGB, input$outFrameGB)
if(out[[1]]){
output$placeholder <- DT::renderDataTable(out[[2]],
options = list(scrollX = TRUE),
rownames = FALSE,
escape = FALSE)
DT::dataTableOutput("placeholder")
} else {
"No sites satisfy the selected filters."
}
} else {
""
}
})
# Output function for Ensembl results
output$ensemblResults <- renderUI({
rValues$ensemblResultsFlag
input$inputType
if(rValues$ensemblResultsFlag && input$inputType == "3"){
out <- filterResults(results, input$t7OptE, input$thresholdE, input$inFrameE, input$outFrameE)
if(out[[1]]){
output$placeholder <- DT::renderDataTable(out[[2]],
options = list(scrollX = TRUE),
rownames = FALSE,
escape = FALSE)
DT::dataTableOutput("placeholder")
} else {
"No sites satisfy the selected filters."
}
} else {
""
}
})
########################################################################################################################################################################
#################Submission Handling####################################################################################################################################
########################################################################################################################################################################
########################################################################################################################################################################
############### GenBank/RefSeq Code#####################################################################################################################################
########################################################################################################################################################################
observeEvent(input$genbankSubmit,{
resetOutputs()
results <<- 0
# Run checks for okay PAM/TALEN input
if(input$talenOp == 1){
if(is.null(validTalen())){
talFlag <- 1 # TALEN input is good
} else {
talFlag <- 2 # Problems with input TALEN
}
} else {
talFlag <- 0 # TALENs not selected
}
# Check for valid custom PAMs
# Set custom PAM to throw a problem unless corrected
cusPamFlag <- 2
# Determine custom PAM cut site validation flag
if(input$customCutOpt == 1){
if(is.null(validCustomPam()) &&
is.null(validCustomCutSites())){
cusPamFlag <- 1 # Custom PAM cut sites are valid
} else {
cusPamFlag <- 2 # Custom PAM cut sites are NOT valid
}
} else {
cusPamFlag <- 0 # Custom PAM cut sites are not used
}
# Check for valid overhang flags
# Set custom overhang to throw a flag unless corrected
cusOhFlag <- 2
# Determine the overhang validation falg
if(input$customCutOpt == 1){
if(is.null(validCustomPam()) &&
is.null(validOverhangs())){
cusOhFlag <- 1 # Custom overhangs are valid
} else {
cusOhFlag <- 2 # Custom overhangs are NOT valid
}
} else {
cusOhFlag <- 0 # Custom overhangs are not used
}
# Check to make sure that there are equal numbers of PAMs, cut sites, and overhangs
lenMatch <- 2
if(input$customCutOpt == 1){
if(is.null(validMatchCustomInputLength())){
lenMatch <- 1 # Correct number
} else {
lenMatch <- 2 # Incorrect number
}
} else {
lenMatch <- 0 # Custom PAMs aren't used
}
# Prevent the whole shebang from running without okay inputs
if( is.null(validRefSeqGBGenbankId()) && # Check Genbank ID is okay
is.null(validPAM()) && # Check that one of the input options is selected
(cusPamFlag != 2) && # Check if custom PAMs are used, and if they are okay
(cusOhFlag != 2) && # Check that the custom overhang flag is okay
(talFlag != 2) && # Check if TALENs are used, and if they are okay
(lenMatch != 2)){ # Check the PAM/cutsite/overhang numbers
# Create a new progress object
progress <- shiny::Progress$new()
# Make sure the progress option closes regardless of calculation outcome
on.exit(progress$close())
# Set the progress message to display at beginning of calculations
progress$set(message = "Progress:", value = 0)
talenList <- ""
# If talen options are used, get the spacer min/max values
if(input$talenOp == 1){
if(input$spacer == 0){
spamin <- 14
spamax <- 14
} else if(input$spacer == 1){
spamin <- 16
spamax <- 16
} else {
spamin <- 14
spamax <- 16
}
talenList <- list(input$armin, input$armax, spamin, spamax)
}
# Update progress
progress$set(detail = "Retrieving GenBank entry...", value = 0.1)
# Try to pull genbank entry associated with accession
# Get the GenBank sequence with exon/intron information
gba <- input$genbankId
# TODO is all this still necessary?
endOfTry <<- FALSE
gbFlag <<- FALSE
gbhFlag <<- FALSE
#Try to retrieve the Genbank file
if(endOfTry == FALSE){
tryCatch({
#info <- suppressWarnings(wonkyGenBankHandler(gba))
info <- suppressWarnings(getGenbankFile(gba))
endOfTry <<- TRUE
gbhFlag <<- TRUE # Flag to indicate wonkyGenBankHandler was required
gbFlag <<- FALSE # Flag to indicate readGenBank failed #deprecated; to remove
}, error = function(err){
}
)
}
# Figure out which exons to target
if(endOfTry){
if(input$exonTargetType == 0){
exonStuff <- 1
} else if(input$exonTargetType == 1){
exonStuff <- input$exonBegPercentage / 100
} else if(input$exonTargetType == 2){
exonStuff <- input$exonEndPercentage / 100
} else if(input$exonTargetType == 3){
exonStuff <- input$exonTargetList
}
if(!is.null(input$casType)){
preGen <- getPreGenPamList(input$casType)
}
# Handle cut distances and PAMs for input to calculateMENTHUGeneSeqGenBank
if(input$customCutOpt == 1){ # If customs pams in use
suppressWarnings(if(!is.null(input$casType)){ # If pre-made PAMs in use
pams <- pamStitch( preGen$pamList, input$customPamSeq)
cutDistances <- distStitch(preGen$cutDist, input$cutSite)
overhangs <- ohStitch( preGen$ohList, input$overhang)
} else { # If no pre-made PAMs
pams <- pamStitch( "", input$customPamSeq)
cutDistances <- distStitch("", input$cutSite)
overhangs <- ohStitch( "", input$overhang)
})
} else if(length(input$casType) > 0) { # If no custom pams AND preGen used
pams <- preGen$pamList
cutDistances <- preGen$cutDist
overhangs <- preGen$ohList
# If only TALENs are used
} else {
pams <- NULL
cutDistances <- NULL
overhangs <- NULL
}
if(input$talenOp == 1){
talFlag <- TRUE
} else {
talFlag <- FALSE
}
#Calculate the MENTHU score
stuff <- calculateMENTHUGeneSeqGenBank(pams, cutDistances, overhangs, wiggle = TRUE, wiggleRoom = 39, talenList, gbFlag, gbhFlag,
info, input$firstExon, input$exonTargetType, exonStuff, progress)
# Statistics on the number of targets detected
output$genbankhits <- renderUI({
HTML(paste(
paste0("Number of target sites detected: ", stuff[[2]]),
paste0("Number of target sites with sufficient sequence context to calculate score: ", stuff[[3]]),
paste0("Number of target sites with 3bp microhomology arms within 5bp of each other: ", stuff[[4]]),
paste0("Number of target sites with score above 1.5 threshold: ", stuff[[5]]),
paste0("Number of target sites satisfying 3bp mh and threshold constraints: ", stuff[[6]]),
sep = "<br>"
))
})
results <<- stuff[[1]]
# Set flags to display output-dependent UI elements
rValues$filtOpsGB <- TRUE # Show filtering options when there is output to filter
rValues$genbankResultsFlag <- TRUE # Display the results table
rValues$downloadFGB <- TRUE # Make the GenBank download button visible
# Order the result table from largest menthuScore to smallest, and drop 0s
results <<- results[which(results$MENTHU_Score > 0), ]
results <<- results[order(-results$MENTHU_Score) , ]
} else {
# Output error if no genbank file is found
output$genbankIdOutcome <- renderText(paste0("Error: Accession '", input$genbankId, "' was not found in database."))
}
}
})
########################################################################################################################################################################
############### Ensembl Code############################################################################################################################################
########################################################################################################################################################################
observeEvent(input$ensemblSubmit,{
resetOutputs()
results <<- 0
#timeX <- Sys.time()
# Set TALEN flag to throw error without correction
talFlag <- 2
#Run checks for okay PAM/TALEN input
if(input$talenOp == 1){
if(is.null(validTalen())){
talFlag <- 1 # Input TALEN is valid
} else {
talFlag <- 2 # Input TALEN is NOT valid
}
} else {
talFlag <- 0 # TALENs not selected
}
# Check that all the custom PAM stuff is okay
cusPamFlag <- 2
cusOhFlag <- 2
lenMatch <- 2
if(input$customCutOpt == 1){
# Check that custom DSBs are OK
# Flag as not valid unless fixed
if(is.null(validCustomPam()) &&
is.null(validCustomCutSites())){
cusPamFlag <- 1 # Custom DSBs are valid
} else {
cusPamFlag <- 2 # Custom DSBs are NOT valid
}
# Check that custom overhangs are OK
# Flag as not valid unless fixed
if(is.null(validCustomPam()) &&
is.null(validOverhangs())){
cusOhFlag <- 1 # Custom overhangs are valid
} else {
cusOhFlag <- 2 # Custom overhangs are NOT valid
}
# Check that there are correct number of custom PAM sequences, DSBs, and overhangs
# Flag as not valid unless fixed
if(is.null(validMatchCustomInputLength())){
lenMatch <- 1 # Custom PAM inputs are correctly matched
} else {
lenMatch <- 2 # Custom PAM inputs are NOT correctly matched
}
} else {
cusPamFlag <- 0 # Custom DSBs are not used
cusOhFlag <- 0 # Custom overhangs are not used
lenMatch <- 0 # Custom PAMs are not used
}
# Prevent the whole shebang from running without okay inputs
if(is.null(validEnsemblId()) && # Check Ensembl ID is okay
is.null(ensemblIdExists()) && # Make sure the ID actually exists
is.null(validPAM()) && # Check that one of the input options is selected
(cusPamFlag != 2) && # Check if custom PAMs are used, and if they are okay
(cusOhFlag != 2) && # Check that the overhangs are ok
(talFlag != 2) && # Check if TALENs are used, and if they are okay
(lenMatch != 2)){
# Create a new progress object
progress <- shiny::Progress$new()
# Make sure the progress option closes regardless of calculation outcome
on.exit(progress$close())
# Set the progress message to display at beginning of calculations
progress$set(message = "Progress:", value = 0)
talenList <- ""
# Check if TALENs are used, and set spacer min/max
if(input$talenOp == 1){
if(input$spacer == 0){
spamin <- 14
spamax <- 14
} else if(input$spacer == 1){
spamin <- 16
spamax <- 16
} else {
spamin <- 14
spamax <- 16