Skip to content

Instantly share code, notes, and snippets.

@hepcat72
Last active May 19, 2021 20:24
Show Gist options
  • Save hepcat72/3319f03f3b9e75ad2fa6e7f3d63c690b to your computer and use it in GitHub Desktop.
Save hepcat72/3319f03f3b9e75ad2fa6e7f3d63c690b to your computer and use it in GitHub Desktop.
Automator services for frequent bioinformatics tasks
--What is this: This is an applescript to use in an automator service that allows you to get the nucleotide and alignment length of a sequence, along with stats on the numbers of individual characters.
--Purpose: Determine the length of a selected nucleotide and alignment string in any application.
--Installation: Create an Automator service, paste this code into a "Run AppleScript" action, and save.
--How to use it: 1. Select a nucleotide string (newlines, numbers, spaces, and any other character are allowed). 2. Right-click the selection and select this service.
--Author: Robert Leach, Genomics Group, Princeton, [email protected]
--Note, this counts "DNA characters"
on run {input, parameters}
tell application "System Events"
set _appname to name of first process whose frontmost is true
end tell
set msg to countNucleotides(input as string)
tell application _appname
display alert "" & msg
end tell
return input
end run
on countNucleotides(dnaStr)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "A"
set aCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "T"
set tCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "G"
set gCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "C"
set cCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "B"
set bCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "D"
set dCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "H"
set hCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "V"
set vCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "R"
set rCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "Y"
set yCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "K"
set kCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "M"
set mCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "S"
set sCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "W"
set wCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "N"
set nCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "-"
set gapCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to astid
set seqTotal to aCount + tCount + gCount + cCount + bCount + dCount + hCount + vCount + rCount + yCount + kCount + mCount + sCount + wCount + nCount
set alnTotal to seqTotal + gapCount
set realTotal to aCount + tCount + gCount + cCount
set ambigTotal to seqTotal - realTotal
set statsstr to "Sequence length: " & (seqTotal as string) & "
Alignment length: " & (alnTotal as string) & "
Discrete Nucleotides: " & (realTotal as string) & "
Ambiguous Nucleotides: " & (ambigTotal as string) & "
Breakdown:
A " & aCount & "
T " & tCount & "
G " & gCount & "
C " & cCount & "
"
if bCount > 0 then
set statsstr to statsstr & " B " & bCount & "
"
end if
if dCount > 0 then
set statsstr to statsstr & " D " & dCount & "
"
end if
if hCount > 0 then
set statsstr to statsstr & " H " & hCount & "
"
end if
if vCount > 0 then
set statsstr to statsstr & " V " & vCount & "
"
end if
if rCount > 0 then
set statsstr to statsstr & " R " & rCount & "
"
end if
if yCount > 0 then
set statsstr to statsstr & " Y " & yCount & "
"
end if
if kCount > 0 then
set statsstr to statsstr & " K " & kCount & "
"
end if
if mCount > 0 then
set statsstr to statsstr & " M " & mCount & "
"
end if
if sCount > 0 then
set statsstr to statsstr & " S " & sCount & "
"
end if
if wCount > 0 then
set statsstr to statsstr & " W " & wCount & "
"
end if
if nCount > 0 then
set statsstr to statsstr & " N " & nCount & "
"
end if
if gapCount > 0 then
set statsstr to statsstr & " - " & gapCount & "
"
end if
return (statsstr)
end countNucleotides
--What is this: This is an applescript to use in an automator service that allows you to get the length of any sequence, not including whitespace characters.
--Purpose: Determine the length of a selected amino acid or any other pseudo-sequence string in any application.
--Installation: Create an Automator service, paste this code into a "Run AppleScript" action, and save.
--How to use it: 1. Select a sequence string. 2. Right-click the selection and select this service.
--Author: Robert Leach, Genomics Group, Princeton, [email protected]
--Note, this counts "non-whitespace characters", i.e. everything except space, tab, newline, & carriage return
on run {input, parameters}
tell application "System Events"
set _appname to name of first process whose frontmost is true
end tell
set selText to item 1 of input as text
set AppleScript's text item delimiters to {space, tab, linefeed, return}
set selText to text items of selText
set AppleScript's text item delimiters to {}
set selText to selText as string
set character_count to count characters of selText
tell application _appname
display alert "" & character_count & " characters"
end tell
return input
end run
--What is this: This is an applescript to use in an automator service that allows you to get the most likely barcode combinations from a series of fastq files. It counts index sequences and looks for an order of magnitude jump in thos counts to guess the barcodes.
--Purpose: Determine the barcodes necessary to split fastq files by sample.
--Installation: Create an Automator service, select "Service receives selected [files of folders]", paste this code into a "Run AppleScript" action, and save as "Guess Barcodes.workflow".
--How to use it: 1. Right-click a fastq file and select this service ("Guess Barcodes"). 2. If no barcodes were found, click the "Edit Params" button and follow instructions
--Author: Robert Leach, Genomics Group, Princeton, [email protected]
--Version: 2.1 (added a setting for 5' or 3' results only and tweaked the order of magnitude threshold to be slightly less stringent)
--Version History:
-- V2.0 Added the ability to count barcode combinations.
-- V1.1 fixed an issue with max_barcodes being 0 not working as intended.
-- V1.0 handled fastq files independently.
on run {input, parameters}
set barcode_length to 8
set max_barcodes to 100
set check_n_seqs to 100000 --Set to 0 to check all or about 100000 to guess based on first million reads
set show_max to 0 --Set to 1 to show max_barcodes per file or 0 to only show the guessed barcodes
set barcodes_at_end to 0
set done to false
set fastqFiles to {}
set validExtensions to {"fq", "fastq", "fastqsanger"}
tell application "Finder"
set delims to AppleScript's text item delimiters
set AppleScript's text item delimiters to "."
repeat with fqfile in input
set theEXT to last item of (text items of ((get name of (fqfile)) as text))
if theEXT is in validExtensions then
set the end of fastqFiles to fqfile
else
display dialog "Extension " & ((name extension of (file fqfile)) as string) & " is not in validExtensions"
end if
end repeat
set AppleScript's text item delimiters to delims
end tell
if (count fastqFiles) is equal to 0 then
display dialog "Please select a file with a .fq, .fastq, or .fastqsanger extension"
return
end if
repeat while done is false
set barcodes to my getBarcodes(barcode_length, max_barcodes, check_n_seqs, show_max, fastqFiles, barcodes_at_end)
tell application "System Events"
set response to (display dialog "Educated barcode guesses:" default answer (barcodes as string) buttons {"Edit Params", "OK"} default button 1 with title "Barcodes")
end tell
if (the button returned of response) is "OK" then
set done to true
else
set {barcode_length, max_barcodes, check_n_seqs, show_max, barcodes_at_end} to my displayMultiDialog("Edit Barcode Search Parameters", "Edit the parameters that were last used on each line below. (Barcodes are determined by abundance - only the most abundant are considered. The cutoff selected is where the abundance jumps by an order of magnitude.)", {"• Barcode Length", "• Max Barcodes to evaluate (0 = all)", "• Number of reads to process", "• Show all considered barcodes (0=No, 1=Yes)", "• Barcodes at 3' end (0=No, 1=Yes)"}, {barcode_length as string, max_barcodes as string, check_n_seqs as string, show_max as string, barcodes_at_end as string})
end if
end repeat
end run
on getBarcodes(barcode_length, max_barcodes, check_n_seqs, show_max, fqfiles, barcodes_at_end)
set check_n_lines to check_n_seqs * 4
set limit_command to "tail -n " & max_barcodes
if max_barcodes < 1 then
set limit_command to "cat"
end if
tell application "Finder"
set filenames to {}
set paste_command to "paste"
repeat with fqfile in fqfiles
set the end of filenames to name of fqfile
if check_n_seqs < 1 then
if (barcodes_at_end as integer) is equal to 0 then
set paste_command to paste_command & " <(cat " & ((quoted form of (POSIX path of fqfile)) as string) & " | awk '(NR - 2) % 4 == 0' | perl -ne 'chomp;print(substr($_,0," & (barcode_length as string) & "),qq(\\n))')"
else
set paste_command to paste_command & " <(cat " & ((quoted form of (POSIX path of fqfile)) as string) & " | awk '(NR - 2) % 4 == 0' | perl -ne 'chomp;print(substr($_,-" & (barcode_length as string) & "),qq(\\n))')"
end if
else
if (barcodes_at_end as integer) is equal to 0 then
set paste_command to paste_command & " <(head -n " & check_n_lines & " " & ((quoted form of (POSIX path of fqfile)) as string) & " | awk '(NR - 2) % 4 == 0' | perl -ne 'chomp;print(substr($_,0," & (barcode_length as string) & "),qq(\\n))')"
else
set paste_command to paste_command & " <(head -n " & check_n_lines & " " & ((quoted form of (POSIX path of fqfile)) as string) & " | awk '(NR - 2) % 4 == 0' | perl -ne 'chomp;print(substr($_,-" & (barcode_length as string) & "),qq(\\n))')"
end if
end if
end repeat
if (barcodes_at_end as integer) is equal to 0 then
set barcodes to "5' barcodes for files:" & return & return
else
set barcodes to "3' barcodes for files:" & return & return
end if
set barcodes to barcodes & tab & my join(return & tab, filenames) & ":" & return & "----------" & return & return
set barcodes to barcodes & (do shell script "/bin/bash -s <<'EOF'" & linefeed & paste_command & " | sort | uniq -c | sort -n | " & limit_command & " | perl -e '$p=0;$o=0;while(<STDIN>){if(/(\\d+)/){$v=$1;if($o>0 && $v>(($o>10?$o-10:$o)*10)){$p=1;if(" & (show_max as string) & "){print(qq(\\n))}}s/\\s*(\\d+)\\s*(.+)\\s*/$2\\t$1\\n/;print if($p || " & (show_max as string) & ");$o=$v;}}if(!$p && !" & (show_max as string) & "){print(qq(No barcodes found. Try Editing params.))}'" & linefeed & "EOF") & return & return
end tell
end getBarcodes
on displayMultiDialog(mytitle, myPrompt, valuePrompts, default_vals)
return (inputItems for valuePrompts given title:mytitle, prompt:myPrompt, defaults:default_vals)
end displayMultiDialog
to inputItems for someItems given title:theTitle, prompt:thePrompt, defaults:theDefaults
(*
displays a dialog for multiple item entry - a carriage return is used between each input item
for each item in someItems, a line of text is displayed in the dialog and a line is reserved for the input
the number of items returned are padded or truncated to match the number of items in someItems
to fit the size of the dialog, items should be limited in length (~30) and number (~15)
parameters - someItems [list/integer]: a list or count of items to get from the dialog
theTitle [boolean/text]: use a default or the given dialog title
thePrompt [boolean/text]: use a default or the given prompt text
returns [list]: a list of the input items
*)
if thePrompt is in {true, false} then -- "with" or "without" prompt
if thePrompt then
set thePrompt to "Input the following items:" & return & return -- default
else
set thePrompt to ""
end if
else -- fix up the prompt a bit
set thePrompt to thePrompt & return & return
end if
if theTitle is in {true, false} then if theTitle then -- "with" or "without" title
set theTitle to "Multiple Input Dialog" -- default
else
set theTitle to ""
end if
if theDefaults is in {false} then -- "with" or "without" prompt
set theDefaults to {}
end if
set theDefaultCount to (count theDefaults)
if class of someItems is integer then -- no item list
set {theCount, someItems} to {someItems, ""}
if thePrompt is not "" then set thePrompt to text 1 thru -2 of thePrompt
else
set theCount to (count someItems)
end if
if theCount is less than 1 then error "inputItems handler: empty input list"
set {theItems, theInput} to {{}, {}}
if theDefaultCount is greater than theCount then error "inputItems handler: Too many default values"
repeat with itemNum from 1 to theCount -- set the number of lines in the input and the defaults
if itemNum is greater than theDefaultCount then
set the end of theInput to ""
if theDefaultCount is greater than 0 then
set item itemNum of someItems to (item itemNum of someItems) & " [\"\"]"
end if
else
set the end of theInput to item itemNum of theDefaults as string
set item itemNum of someItems to (item itemNum of someItems) & " [\"" & (item itemNum of theDefaults) & "\"]"
end if
end repeat
set {tempTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}
set {someItems, theInput} to {someItems as text, theInput as text}
set AppleScript's text item delimiters to tempTID
set theInput to paragraphs of text returned of (display dialog thePrompt & someItems with title theTitle default answer theInput)
repeat with anItem from 1 to theCount -- pad/truncate entered items
try
set the end of theItems to (item anItem of theInput)
on error
set the end of theItems to ""
end try
end repeat
return theItems
end inputItems
on join(myDelimiter, myList)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to myDelimiter
set joinedString to myList as text
set AppleScript's text item delimiters to astid
return joinedString
end join
--What is this: This is an applescript to use in an automator service that allows you to get the reverse complement of selected DNA sequence in a resulting dialog window.
--Purpose: To quickly get the reverse complement of a selected sequence in any app.
--Installation: Create an Automator service, paste this code into a "Run AppleScript" action, and save.
--How to use it: 1. Select the sequence you wish to see the reverse complement of. 2. Right-click the selection and select this service.
--Author: Robert Leach, Genomics Group, Princeton, [email protected]
on run {input, parameters}
tell application "System Events"
set _appname to name of first process whose frontmost is true
end tell
set msg to reverseComplement(input as string)
tell application _appname
display dialog "Reverse Complement:" default answer msg buttons {"OK"} default button 1 with title "Reverse Complement"
end tell
return input
end run
on reverseComplement(dnaStr)
set revStr to reverse of characters of dnaStr as text
set revCompStr to complement(revStr)
return (revCompStr)
end reverseComplement
on complement(dnaStr)
set str_pos to 1
set len to length of dnaStr
set comp to ""
repeat while str_pos ≤ len
set nt to text str_pos thru str_pos of dnaStr
set comp to comp & complementNt(nt)
set str_pos to str_pos + 1
end repeat
return (comp)
end complement
on complementNt(nt)
set compNt to nt
if nt is equal to "A" then
return ("T")
else if nt is equal to "a" then
return ("t")
else if nt is equal to "T" then
return ("A")
else if nt is equal to "t" then
return ("a")
else if nt is equal to "G" then
return ("C")
else if nt is equal to "g" then
return ("c")
else if nt is equal to "C" then
return ("G")
else if nt is equal to "c" then
return ("g")
else if nt is equal to "B" then
return ("V")
else if nt is equal to "b" then
return ("v")
else if nt is equal to "D" then
return ("H")
else if nt is equal to "d" then
return ("h")
else if nt is equal to "H" then
return ("D")
else if nt is equal to "h" then
return ("d")
else if nt is equal to "V" then
return ("B")
else if nt is equal to "v" then
return ("b")
else if nt is equal to "R" then
return ("Y")
else if nt is equal to "r" then
return ("y")
else if nt is equal to "Y" then
return ("R")
else if nt is equal to "y" then
return ("r")
else if nt is equal to "K" then
return ("M")
else if nt is equal to "k" then
return ("m")
else if nt is equal to "M" then
return ("K")
else if nt is equal to "m" then
return ("k")
end if
--Don't need to do S, W, or N, because they complement themselves
--All other characters such as white spaces, numbers, gaps or other stuff will just be returned as-is without error or warning
return (compNt)
end complementNt
--Installation: Create an Automator service, paste this code into a "Run AppleScript" action, and save.
--How to use it: 1. Copy a number indicating the length you want the selection to be. 2. Select the region of text where you want the copied length to be selected. 3. Right-click the selection and select this service.
--NOTES:
--If the contents of the clipboard is not a number, you'll be prompted to enter a length.
--If the selected text is in Terminal.app, the modified selection will display in a dialog window (because the arrow keys are used for the selection modification and that doesn't work in Terminal). Also, if the target selection length is longer than the current selection, an error about selection length will be displayed.
--If input is not supplied, this script will backup the current clipboard, copy the current selection to get the currently selected text, then restore the clipboard.
on run {input, parameters}
try
set debug to false
if input's class is not list then
set input to (my getHighlight(debug))
end if
tell application "System Events"
--See if the clipboard has a number in it to use for the selection length
set checkClipboard to the clipboard as text
if my isNumString(checkClipboard) then
set n to checkClipboard as integer
else
--Get the name of the frontmost application so we can bring the front window back into focus after the dialog window goes away (which surprisingly, is not the default behavior)
set curProc to (name of first process whose frontmost is true)
set n to my getintvalue()
--And this is the only trick I've found to bring any window in an app back into focus
do shell script "open -a '" & (curProc as string) & "'"
delay 0.2
end if
set {selText, nt_count} to (my selectAtLeastAln((input as string), (n as integer), debug))
--Exit if an empty string was returned (indicating an error)
if selText is "" then
return
end if
--Estimate/guess whether it will be quicker to shrink the selection from the end or grow the selection from the beginning
set shrinkit to ((nt_count - n) < n)
--Figure out whether the selection must be lengthened or shrunk
if nt_count is equal to 0 or n is greater than nt_count then
display dialog "The selection length must be greater than the number of characters to select"
return
end if
--See if we're in Terminal
set isTerminal to ((name of first process where it is frontmost) as string) is equal to "Terminal"
if isTerminal is true then
ignoring application responses
display dialog "Length " & n & " is selected below:" default answer selText buttons {"OK"} default button 1 with title "Selected Character Position"
end ignoring
end if
if shrinkit is false then
key code 123
set str_pos to 1
set nt_count to 0
repeat while nt_count < n
key code 124 using {shift down}
set nt to text str_pos thru str_pos of selText
if (my countAlignChars(nt)) > 0 then
set nt_count to nt_count + 1
end if
set str_pos to str_pos + 1
end repeat
else
--Trick to make sure arrow presses affect the right side of the selection instead of the left
set character_count to count characters of selText
key code 124 using {shift down}
key code 123 using {shift down}
if (count characters of selText) < character_count then
key code 124 using {shift down}
end if
set str_pos to character_count
set nt to text str_pos thru str_pos of selText
repeat while nt_count > n or (my countAlignChars(nt)) < 1
key code 123 using {shift down}
if (my countAlignChars(nt)) > 0 then
set nt_count to nt_count - 1
end if
set str_pos to str_pos - 1
set nt to text str_pos thru str_pos of selText
end repeat
end if
delay 1
end tell
on error errstr
display dialog errstr
end try
end run
property mytitle : "Length of text to select"
-- I am asking the user to provide an integer
-- In case the user cancels the dialog, I return «missing value»
on getintvalue()
set dlgmsg to "How many characters would you like to select?:"
try
display dialog dlgmsg default answer "1" buttons {"Cancel", "Enter"} default button 2 with title mytitle
on error
-- User canceled
return missing value
end try
set dlgresult to result
set usrinput to text returned of dlgresult
-- the user did not enter anything...
if usrinput is "" then
my getintvalue()
else
-- let's check if the user entered numbers only
set nums to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}
set invalidchars to ""
repeat with char in usrinput
if char is not in nums then
set invalidchars to invalidchars & char & space
end if
end repeat
-- we found invalid characters
if invalidchars is not "" then
set errmsg to "We found the following characters in the given input. Please enter numbers only." & return & return & invalidchars & return
my dsperrmsg(errmsg, "--")
my getintvalue()
else
-- let's try to transform the user input into an integer
try
set intvalue to usrinput as integer
return intvalue
on error
set errmsg to "We could not coerce the given input into an integer:" & return & return & usrinput & return
my dsperrmsg(errmsg, "--")
my getintvalue()
end try
end if
end if
end getintvalue
-- I am displaying error messages to the user
on dsperrmsg(errmsg, errnum)
tell me
activate
display dialog errmsg & " (" & errnum & ")" buttons {"OK"} default button 1 with title mytitle with icon stop
end tell
end dsperrmsg
-- isNumString :: String -> Bool
on isNumString(s)
try
if class of s is string then
set c to class of (s as number)
c is real or c is integer
else
false
end if
on error
false
end try
end isNumString
on getHighlight(debug)
--Save the current contents of the clipboard
try
set theSpare to the clipboard --as text
on error
set response to (display dialog "Warning: The contents of the clipboard will be lost." buttons {"Cancel", "OK"})
if button returned of response is "OK" then
set theSpare to ""
end if
end try
set the clipboard to ""
--Declare the variable we're going to return
set selecTxt to ""
tell application "System Events"
--Initiate the copy
keystroke "c" using {command down}
--Wait up to 2 seconds for the copy to finish
set done to "no"
set waitnum to 0
set waitInterval to 0.02
set maxwaits to 100
--Repeat while the clipboard contents have not changed
repeat while done = "no"
--Get the contents of the clipboard
try
set selecTxt to the clipboard as text
end try
--See if we're done or need to wait
if waitnum is equal to maxwaits then
set done to "yes"
else if selecTxt is equal to "" then
delay waitInterval
set waitnum to waitnum + 1
else
set done to "yes"
end if
end repeat
if debug is true then
try
display dialog "Copied text: " & (the clipboard as text)
end try
end if
end tell
--Restore the original clipboard contents
set the clipboard to theSpare --as record
if debug is true then
try
display dialog "The clipboard contents have been restored to " & (the clipboard as text)
end try
end if
--Return the highlighted text
return selecTxt
end getHighlight
on countAlignChars(dnaStr)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "A"
set aCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "T"
set tCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "G"
set gCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "C"
set cCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "B"
set bCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "D"
set dCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "H"
set hCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "V"
set vCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "R"
set rCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "Y"
set yCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "K"
set kCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "M"
set mCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "S"
set sCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "W"
set wCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "N"
set nCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "-"
set gapCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to astid
set seqTotal to aCount + tCount + gCount + cCount + bCount + dCount + hCount + vCount + rCount + yCount + kCount + mCount + sCount + wCount + nCount
set alnTotal to seqTotal + gapCount
return (alnTotal as integer)
end countAlignChars
--Uses the down arrow to grab at least n characters of nucleotides and returns the newly selected string and the number of nucleotides it contains
on selectAtLeastAln(initial_str as string, n as integer, debug)
set cur_str to initial_str
set character_count to ((count characters of cur_str) as integer)
set last_character_count to 0
set nt_count to ((my countAlignChars(cur_str)) as integer)
repeat while character_count > last_character_count and nt_count < n
--Add a line to the selection
tell application "System Events" to key code 125 using {shift down}
set cur_str to (my getHighlight(debug))
set last_character_count to character_count
set character_count to ((count characters of cur_str) as integer)
set nt_count to (my countAlignChars(cur_str))
end repeat
if nt_count < n then
display dialog "Error: Unable to select enough sequence. Please make sure to select more sequence than the target length (" & (n as string) & ")."
return ({"", 0})
end if
return ({cur_str as string, nt_count as integer})
end selectAtLeastAln
--What is this: This is an applescript to use in an automator service that allows you to modify the length of a text selection to a value specified (either by a number on the clipboard or a number entered at a prompt).
--Purpose: The initial intent is to use this service to visualize a specific position in a DNA or protein string (although it currently assumes the string is solid sequence characters - a future *separate* version will count only relevant characters).
--Installation: Create an Automator service, paste this code into a "Run AppleScript" action, and save.
--How to use it: 1. Copy a number indicating the length you want the selection to be. 2. Select the region of text where you want the copied length to be selected. 3. Right-click the selection and select this service.
--Author: Robert Leach, Genomics Group, Princeton, [email protected]
--NOTES:
--If the contents of the clipboard is not a number, you'll be prompted to enter a length.
--If the selected text is in Terminal.app, the modified selection will display in a dialog window (because the arrow keys are used for the selection modification and that doesn't work in Terminal). Also, if the target selection length is longer than the current selection, the remainder will be filled in with N's (because this was originally made for DNA strings).
--If input is not supplied, this script will backup the current clipboard, copy the current selection to get the currently selected text, then restore the clipboard.
on run {input, parameters}
try
set debug to false
if input's class is not list then
set input to getHighlight(debug)
end if
tell application "System Events"
--See if the clipboard has a number in it to use for the selection length
set checkClipboard to the clipboard as text
if my isNumString(checkClipboard) then
set n to checkClipboard as integer
else
--Get the name of the frontmost application so we can bring the front window back into focus after the dialog window goes away (which surprisingly, is not the default behavior)
set curProc to (name of first process whose frontmost is true)
set n to my getintvalue()
--And this is the only trick I've found to bring any window in an app back into focus
do shell script "open -a '" & (curProc as string) & "'"
delay 0.2
end if
--Check the length of the selected text passed in
set character_count to count characters of ((input as string) as string)
set lengthen to false
set mod_length to character_count - n
--Figure out whether the selection must be lengthened or shrunk
if character_count is equal to 0 or n is greater than character_count then
set lengthen to true
set mod_length to n - character_count
end if
--See if we're in Terminal
set isTerminal to ((name of first process where it is frontmost) as string) is equal to "Terminal"
if isTerminal is true then
if lengthen is true then
set substr to (input as string)
repeat mod_length times
set substr to substr & "N"
end repeat
else
set substr to text 1 thru n of (input as string)
set substr to substr & (text (n + 1) thru character_count of (input as string))
end if
ignoring application responses
display dialog "Length " & n & " is selected below:" default answer substr buttons {"OK"} default button 1 with title "Selected Character Position"
end ignoring
end if
--Trick to make sure arrow presses affect the right side of the selection instead of the left
if not (character_count is equal to 0 or n is greater than character_count) then
key code 124 using {shift down}
key code 123 using {shift down}
key code 124 using {shift down}
end if
if lengthen is false and n is less than mod_length then
if character_count is greater than 0 then
key code 123
end if
repeat n times
key code 124 using {shift down}
end repeat
else
repeat mod_length times
if lengthen is true then
key code 124 using {shift down}
else
key code 123 using {shift down}
end if
end repeat
end if
delay 1
end tell
on error errstr
display dialog errstr
end try
end run
property mytitle : "Length of text to select"
-- I am asking the user to provide an integer
-- In case the user cancels the dialog, I return «missing value»
on getintvalue()
set dlgmsg to "How many characters would you like to select?:"
try
display dialog dlgmsg default answer "1" buttons {"Cancel", "Enter"} default button 2 with title mytitle
on error
-- User canceled
return missing value
end try
set dlgresult to result
set usrinput to text returned of dlgresult
-- the user did not enter anything...
if usrinput is "" then
my getintvalue()
else
-- let's check if the user entered numbers only
set nums to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}
set invalidchars to ""
repeat with char in usrinput
if char is not in nums then
set invalidchars to invalidchars & char & space
end if
end repeat
-- we found invalid characters
if invalidchars is not "" then
set errmsg to "We found the following characters in the given input. Please enter numbers only." & return & return & invalidchars & return
my dsperrmsg(errmsg, "--")
my getintvalue()
else
-- let's try to transform the user input into an integer
try
set intvalue to usrinput as integer
return intvalue
on error
set errmsg to "We could not coerce the given input into an integer:" & return & return & usrinput & return
my dsperrmsg(errmsg, "--")
my getintvalue()
end try
end if
end if
end getintvalue
-- I am displaying error messages to the user
on dsperrmsg(errmsg, errnum)
tell me
activate
display dialog errmsg & " (" & errnum & ")" buttons {"OK"} default button 1 with title mytitle with icon stop
end tell
end dsperrmsg
-- isNumString :: String -> Bool
on isNumString(s)
try
if class of s is string then
set c to class of (s as number)
c is real or c is integer
else
false
end if
on error
false
end try
end isNumString
on getHighlight(debug)
--Save the current contents of the clipboard
try
set theSpare to the clipboard --as text
on error
set response to (display dialog "Warning: The contents of the clipboard will be lost." buttons {"Cancel", "OK"})
if button returned of response is "OK" then
set theSpare to ""
end if
end try
set the clipboard to ""
--Declare the variable we're going to return
set selecTxt to ""
tell application "System Events"
--Initiate the copy
keystroke "c" using {command down}
--Wait up to 2 seconds for the copy to finish
set done to "no"
set waitnum to 0
set waitInterval to 0.02
set maxwaits to 100
--Repeat while the clipboard contents have not changed
repeat while done = "no"
--Get the contents of the clipboard
try
set selecTxt to the clipboard as text
end try
--See if we're done or need to wait
if waitnum is equal to maxwaits then
set done to "yes"
else if selecTxt is equal to "" then
delay waitInterval
set waitnum to waitnum + 1
else
set done to "yes"
end if
end repeat
if debug is true then
try
display dialog "Copied text: " & (the clipboard as text)
end try
end if
end tell
--Restore the original clipboard contents
set the clipboard to theSpare --as record
if debug is true then
try
display dialog "The clipboard contents have been restored to " & (the clipboard as text)
end try
end if
--Return the highlighted text
return selecTxt
end getHighlight
--Installation: Create an Automator service, paste this code into a "Run AppleScript" action, and save.
--How to use it: 1. Copy a number indicating the length you want the selection to be. 2. Select the region of text where you want the copied length to be selected. 3. Right-click the selection and select this service.
--NOTES:
--If the contents of the clipboard is not a number, you'll be prompted to enter a length.
--If the selected text is in Terminal.app, the modified selection will display in a dialog window (because the arrow keys are used for the selection modification and that doesn't work in Terminal). Also, if the target selection length is longer than the current selection, an error about selection length will be displayed.
--If input is not supplied, this script will backup the current clipboard, copy the current selection to get the currently selected text, then restore the clipboard.
on run {input, parameters}
try
set debug to false
if input's class is not list then
set input to (my getHighlight(debug))
end if
tell application "System Events"
--See if the clipboard has a number in it to use for the selection length
set checkClipboard to the clipboard as text
if my isNumString(checkClipboard) then
set n to checkClipboard as integer
else
--Get the name of the frontmost application so we can bring the front window back into focus after the dialog window goes away (which surprisingly, is not the default behavior)
set curProc to (name of first process whose frontmost is true)
set n to my getintvalue()
--And this is the only trick I've found to bring any window in an app back into focus
do shell script "open -a '" & (curProc as string) & "'"
delay 0.2
end if
set {selText, nt_count} to (my selectAtLeast((input as string), (n as integer), debug))
--Exit if an empty string was returned (indicating an error)
if selText is "" then
return
end if
--Estimate/guess whether it will be quicker to shrink the selection from the end or grow the selection from the beginning
set shrinkit to ((nt_count - n) < n)
--Figure out whether the selection must be lengthened or shrunk
if nt_count is equal to 0 or n is greater than nt_count then
display dialog "The selection length must be greater than the number of characters to select"
return
end if
--See if we're in Terminal
set isTerminal to ((name of first process where it is frontmost) as string) is equal to "Terminal"
if isTerminal is true then
ignoring application responses
display dialog "Length " & n & " is selected below:" default answer selText buttons {"OK"} default button 1 with title "Selected Character Position"
end ignoring
end if
if shrinkit is false then
key code 123
set str_pos to 1
set nt_count to 0
repeat while nt_count < n
key code 124 using {shift down}
set nt to text str_pos thru str_pos of selText
if (my countNucleotides(nt)) > 0 then
set nt_count to nt_count + 1
end if
set str_pos to str_pos + 1
end repeat
else
--Trick to make sure arrow presses affect the right side of the selection instead of the left
set character_count to count characters of selText
key code 124 using {shift down}
key code 123 using {shift down}
if (count characters of selText) < character_count then
key code 124 using {shift down}
end if
set str_pos to character_count
set nt to text str_pos thru str_pos of selText
repeat while nt_count > n or (my countNucleotides(nt)) < 1
key code 123 using {shift down}
if (my countNucleotides(nt)) > 0 then
set nt_count to nt_count - 1
end if
set str_pos to str_pos - 1
set nt to text str_pos thru str_pos of selText
end repeat
end if
delay 1
end tell
on error errstr
display dialog errstr
end try
end run
property mytitle : "Length of text to select"
-- I am asking the user to provide an integer
-- In case the user cancels the dialog, I return «missing value»
on getintvalue()
set dlgmsg to "How many characters would you like to select?:"
try
display dialog dlgmsg default answer "1" buttons {"Cancel", "Enter"} default button 2 with title mytitle
on error
-- User canceled
return missing value
end try
set dlgresult to result
set usrinput to text returned of dlgresult
-- the user did not enter anything...
if usrinput is "" then
my getintvalue()
else
-- let's check if the user entered numbers only
set nums to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}
set invalidchars to ""
repeat with char in usrinput
if char is not in nums then
set invalidchars to invalidchars & char & space
end if
end repeat
-- we found invalid characters
if invalidchars is not "" then
set errmsg to "We found the following characters in the given input. Please enter numbers only." & return & return & invalidchars & return
my dsperrmsg(errmsg, "--")
my getintvalue()
else
-- let's try to transform the user input into an integer
try
set intvalue to usrinput as integer
return intvalue
on error
set errmsg to "We could not coerce the given input into an integer:" & return & return & usrinput & return
my dsperrmsg(errmsg, "--")
my getintvalue()
end try
end if
end if
end getintvalue
-- I am displaying error messages to the user
on dsperrmsg(errmsg, errnum)
tell me
activate
display dialog errmsg & " (" & errnum & ")" buttons {"OK"} default button 1 with title mytitle with icon stop
end tell
end dsperrmsg
-- isNumString :: String -> Bool
on isNumString(s)
try
if class of s is string then
set c to class of (s as number)
c is real or c is integer
else
false
end if
on error
false
end try
end isNumString
on getHighlight(debug)
--Save the current contents of the clipboard
try
set theSpare to the clipboard --as text
on error
set response to (display dialog "Warning: The contents of the clipboard will be lost." buttons {"Cancel", "OK"})
if button returned of response is "OK" then
set theSpare to ""
end if
end try
set the clipboard to ""
--Declare the variable we're going to return
set selecTxt to ""
tell application "System Events"
--Initiate the copy
keystroke "c" using {command down}
--Wait up to 2 seconds for the copy to finish
set done to "no"
set waitnum to 0
set waitInterval to 0.02
set maxwaits to 100
--Repeat while the clipboard contents have not changed
repeat while done = "no"
--Get the contents of the clipboard
try
set selecTxt to the clipboard as text
end try
--See if we're done or need to wait
if waitnum is equal to maxwaits then
set done to "yes"
else if selecTxt is equal to "" then
delay waitInterval
set waitnum to waitnum + 1
else
set done to "yes"
end if
end repeat
if debug is true then
try
display dialog "Copied text: " & (the clipboard as text)
end try
end if
end tell
--Restore the original clipboard contents
set the clipboard to theSpare --as record
if debug is true then
try
display dialog "The clipboard contents have been restored to " & (the clipboard as text)
end try
end if
--Return the highlighted text
return selecTxt
end getHighlight
on countNucleotides(dnaStr)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "A"
set aCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "T"
set tCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "G"
set gCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "C"
set cCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "B"
set bCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "D"
set dCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "H"
set hCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "V"
set vCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "R"
set rCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "Y"
set yCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "K"
set kCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "M"
set mCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "S"
set sCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "W"
set wCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to "N"
set nCount to (count dnaStr's text items) - 1
set AppleScript's text item delimiters to astid
set seqTotal to aCount + tCount + gCount + cCount + bCount + dCount + hCount + vCount + rCount + yCount + kCount + mCount + sCount + wCount + nCount
return (seqTotal as integer)
end countNucleotides
--Uses the down arrow to grab at least n characters of nucleotides and returns the newly selected string and the number of nucleotides it contains
on selectAtLeast(initial_str as string, n as integer, debug)
set cur_str to initial_str
set character_count to ((count characters of cur_str) as integer)
set last_character_count to 0
set nt_count to ((my countNucleotides(cur_str)) as integer)
repeat while character_count > last_character_count and nt_count < n
--Add a line to the selection
tell application "System Events" to key code 125 using {shift down}
set cur_str to (my getHighlight(debug))
set last_character_count to character_count
set character_count to ((count characters of cur_str) as integer)
set nt_count to (my countNucleotides(cur_str))
end repeat
if nt_count < n then
display dialog "Error: Unable to select enough sequence. Please make sure to select more sequence than the target length (" & (n as string) & ")."
return ({"", 0})
end if
return ({cur_str as string, nt_count as integer})
end selectAtLeast
--Installation: Create an Automator service, paste this code into a "Run AppleScript" action, and save.
--How to use it: 1. Copy a number indicating the length you want the selection to be. 2. Select the region of text where you want the copied length to be selected. 3. Right-click the selection and select this service.
--NOTES:
--If the contents of the clipboard is not a number, you'll be prompted to enter a length.
--If the selected text is in Terminal.app, the modified selection will display in a dialog window (because the arrow keys are used for the selection modification and that doesn't work in Terminal). Also, if the target selection length is longer than the current selection, an error about selection length will be displayed.
--If input is not supplied, this script will backup the current clipboard, copy the current selection to get the currently selected text, then restore the clipboard.
on run {input, parameters}
try
set debug to false
if input's class is not list then
set input to (my getHighlight(debug))
end if
tell application "System Events"
--See if the clipboard has a number in it to use for the selection length
set checkClipboard to the clipboard as text
if my isNumString(checkClipboard) then
set n to checkClipboard as integer
else
--Get the name of the frontmost application so we can bring the front window back into focus after the dialog window goes away (which surprisingly, is not the default behavior)
set curProc to (name of first process whose frontmost is true)
set n to my getintvalue()
--And this is the only trick I've found to bring any window in an app back into focus
do shell script "open -a '" & (curProc as string) & "'"
delay 0.2
end if
set {selText, nt_count} to (my selectAtLeastAln((input as string), (n as integer), debug))
--Exit if an empty string was returned (indicating an error)
if selText is "" then
return
end if
--Estimate/guess whether it will be quicker to shrink the selection from the end or grow the selection from the beginning
set shrinkit to ((nt_count - n) < n)
--Figure out whether the selection must be lengthened or shrunk
if nt_count is equal to 0 or n is greater than nt_count then
display dialog "The selection length must be greater than the number of characters to select"
return
end if
--See if we're in Terminal
set isTerminal to ((name of first process where it is frontmost) as string) is equal to "Terminal"
if isTerminal is true then
ignoring application responses
display dialog "Length " & n & " is selected below:" default answer selText buttons {"OK"} default button 1 with title "Selected Character Position"
end ignoring
end if
if shrinkit is false then
key code 123
set str_pos to 1
set nt_count to 0
repeat while nt_count < n
key code 124 using {shift down}
set nt to text str_pos thru str_pos of selText
if (my countSequenceChars(nt)) > 0 then
set nt_count to nt_count + 1
end if
set str_pos to str_pos + 1
end repeat
else
--Trick to make sure arrow presses affect the right side of the selection instead of the left
set character_count to count characters of selText
key code 124 using {shift down}
key code 123 using {shift down}
if (count characters of selText) < character_count then
key code 124 using {shift down}
end if
set str_pos to character_count
set nt to text str_pos thru str_pos of selText
repeat while nt_count > n or (my countSequenceChars(nt)) < 1
key code 123 using {shift down}
if (my countSequenceChars(nt)) > 0 then
set nt_count to nt_count - 1
end if
set str_pos to str_pos - 1
set nt to text str_pos thru str_pos of selText
end repeat
end if
delay 1
end tell
on error errstr
display dialog errstr
end try
end run
property mytitle : "Length of text to select"
-- I am asking the user to provide an integer
-- In case the user cancels the dialog, I return «missing value»
on getintvalue()
set dlgmsg to "How many characters would you like to select?:"
try
display dialog dlgmsg default answer "1" buttons {"Cancel", "Enter"} default button 2 with title mytitle
on error
-- User canceled
return missing value
end try
set dlgresult to result
set usrinput to text returned of dlgresult
-- the user did not enter anything...
if usrinput is "" then
my getintvalue()
else
-- let's check if the user entered numbers only
set nums to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}
set invalidchars to ""
repeat with char in usrinput
if char is not in nums then
set invalidchars to invalidchars & char & space
end if
end repeat
-- we found invalid characters
if invalidchars is not "" then
set errmsg to "We found the following characters in the given input. Please enter numbers only." & return & return & invalidchars & return
my dsperrmsg(errmsg, "--")
my getintvalue()
else
-- let's try to transform the user input into an integer
try
set intvalue to usrinput as integer
return intvalue
on error
set errmsg to "We could not coerce the given input into an integer:" & return & return & usrinput & return
my dsperrmsg(errmsg, "--")
my getintvalue()
end try
end if
end if
end getintvalue
-- I am displaying error messages to the user
on dsperrmsg(errmsg, errnum)
tell me
activate
display dialog errmsg & " (" & errnum & ")" buttons {"OK"} default button 1 with title mytitle with icon stop
end tell
end dsperrmsg
-- isNumString :: String -> Bool
on isNumString(s)
try
if class of s is string then
set c to class of (s as number)
c is real or c is integer
else
false
end if
on error
false
end try
end isNumString
on getHighlight(debug)
--Save the current contents of the clipboard
try
set theSpare to the clipboard --as text
on error
set response to (display dialog "Warning: The contents of the clipboard will be lost." buttons {"Cancel", "OK"})
if button returned of response is "OK" then
set theSpare to ""
end if
end try
set the clipboard to ""
--Declare the variable we're going to return
set selecTxt to ""
tell application "System Events"
--Initiate the copy
keystroke "c" using {command down}
--Wait up to 2 seconds for the copy to finish
set done to "no"
set waitnum to 0
set waitInterval to 0.02
set maxwaits to 100
--Repeat while the clipboard contents have not changed
repeat while done = "no"
--Get the contents of the clipboard
try
set selecTxt to the clipboard as text
end try
--See if we're done or need to wait
if waitnum is equal to maxwaits then
set done to "yes"
else if selecTxt is equal to "" then
delay waitInterval
set waitnum to waitnum + 1
else
set done to "yes"
end if
end repeat
if debug is true then
try
display dialog "Copied text: " & (the clipboard as text)
end try
end if
end tell
--Restore the original clipboard contents
set the clipboard to theSpare --as record
if debug is true then
try
display dialog "The clipboard contents have been restored to " & (the clipboard as text)
end try
end if
--Return the highlighted text
return selecTxt
end getHighlight
on countSequenceChars(seq)
set AppleScript's text item delimiters to {space, tab, linefeed, return}
set selText to text items of seq
set AppleScript's text item delimiters to {}
set selText to selText as string
set len to count characters of selText
return (len as integer)
end countSequenceChars
--Uses the down arrow to grab at least n characters of nucleotides and returns the newly selected string and the number of nucleotides it contains
on selectAtLeastAln(initial_str as string, n as integer, debug)
set cur_str to initial_str
set character_count to ((count characters of cur_str) as integer)
set last_character_count to 0
set nt_count to ((my countSequenceChars(cur_str)) as integer)
repeat while character_count > last_character_count and nt_count < n
--Add a line to the selection
tell application "System Events" to key code 125 using {shift down}
set cur_str to (my getHighlight(debug))
set last_character_count to character_count
set character_count to ((count characters of cur_str) as integer)
set nt_count to (my countSequenceChars(cur_str))
end repeat
if nt_count < n then
display dialog "Error: Unable to select enough sequence. Please make sure to select more sequence than the target length (" & (n as string) & ")."
return ({"", 0})
end if
return ({cur_str as string, nt_count as integer})
end selectAtLeastAln
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment