Skip to content

Commit 60650cf

Browse files
committed
Added new optional walltime parameter, terminating blast queries that remain in WAITING status indefinitely. Changed behaviour of NCBI blast queries to terminate on returning undefined status.
1 parent 4e5d040 commit 60650cf

2 files changed

Lines changed: 53 additions & 30 deletions

File tree

BlastHTTP.cabal

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ name: BlastHTTP
77
-- PVP summary: +-+------- breaking API changes
88
-- | | +----- non-breaking API additions
99
-- | | | +--- code changes with no API change
10-
version: 1.1.0
10+
version: 1.2.0
1111
synopsis: Libary to interface with the NCBI blast REST interface
1212
description: Searches for a provided nucleotide or protein sequence with the NCBI Blast REST service and returns a blast result in xml format as BlastResult datatype.
1313
.
@@ -27,6 +27,9 @@ category: Bioinformatics
2727
build-type: Simple
2828
cabal-version: >=1.8
2929

30+
extra-source-files:
31+
README.md changelog
32+
3033
source-repository head
3134
type: git
3235
location: https://github.com/eggzilla/BlastHTTP

src/Bio/BlastHTTP.hs

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
--
99
-- 2. database: Selects the database to be queried against. Example values are refseq_genomic, nr, est,.. Please consider that the database must be chosen in accordance with the blastprogram. Default value: refseq_genomic. Type: Maybe String
1010
--
11-
-- 3. querySequence: nucleotides or protein sequence, depending on the blast program used. If no sequence is provided an exception as String will be produced. Type: Maybe SeqData
11+
-- 3. querySequences: nucleotides or protein sequences, depending on the blast program used. If no sequence is provided an exception as String will be produced. Type: [Sequence]
1212
--
13-
-- 4. entrezQuery: This argument is optional and will filter the result if provided. Type: Maybe String
13+
-- 4. optionalArguments: This argument is optional and will filter the result if provided. Type: Maybe String
14+
--
15+
-- 5. optionalWalltime: Optional walltime in mircroseconds. If specified, will terminate the query after reaching the timelimit and return Left. Type: Maybe Int
1416
--
1517
-- and returns Either a BlastResult (Right) on success or an exception as String (Left)
1618
--
@@ -37,7 +39,8 @@ data BlastHTTPQuery = BlastHTTPQuery
3739
, program :: Maybe String
3840
, database :: Maybe String
3941
, querySequences :: [Sequence]
40-
, optionalArguments :: Maybe String
42+
, optionalArguments :: Maybe String
43+
, optionalWalltime :: Maybe Int
4144
}
4245
deriving (Show, Eq)
4346

@@ -51,16 +54,15 @@ atId elementId = deep (isElem >>> hasAttrValue "id" (== elementId))
5154

5255
-- | Send query and parse RID from retrieved HTML
5356
startSession :: String -> String -> String -> String -> Maybe String -> IO String
54-
startSession provider' program' database' querySequences' optionalArguments'
55-
| provider' == "ebi" = startSessionEBI program' database' querySequences' optionalArguments'
56-
| otherwise = startSessionNCBI program' database' querySequences' optionalArguments'
57+
startSession provider' program' database' querySequences' optionalArguments'
58+
| provider' == "ebi" = startSessionEBI program' database' querySequences' optionalArguments'
59+
| otherwise = startSessionNCBI program' database' querySequences' optionalArguments'
5760

5861
startSessionEBI :: String -> String -> String -> Maybe String -> IO String
5962
startSessionEBI program' database' querySequences' optionalArguments' = do
6063
requestXml <- withSocketsDo
6164
$ sendQueryEBI program' database' querySequences' optionalArguments'
6265
let requestID = L8.unpack requestXml
63-
--print "EBI - extracted request ID"
6466
return requestID
6567

6668
startSessionNCBI :: String -> String -> String -> Maybe String -> IO String
@@ -72,7 +74,7 @@ startSessionNCBI program' database' querySequences' optionalArguments' = do
7274

7375
-- | Send query with or without optional arguments and return response HTML
7476
sendQueryEBI :: String -> String -> String -> Maybe String -> IO L8.ByteString
75-
sendQueryEBI program' database' querySequences' optionalArguments' = do
77+
sendQueryEBI program' database' querySequences' _ = do
7678
putStrLn "Making HTTP request"
7779
res <- do
7880
--initReq <- parseUrl "http://postcatcher.in/catchers/541811052cb53502000001a7"
@@ -106,7 +108,6 @@ retrieveSessionStatus provider' rid = do
106108
statusXml <- withSocketsDo $ simpleHttp ("http://www.ebi.ac.uk/Tools/services/rest/ncbiblast/status/" ++ rid)
107109
let statusXMLString = L8.unpack statusXml
108110
putStrLn "EBI statusXMLString"
109-
print statusXMLString
110111
return statusXMLString
111112
else do
112113
statusXml <- withSocketsDo $ simpleHttp ("http://www.ncbi.nlm.nih.gov/blast/Blast.cgi?CMD=Get&FORMAT_OBJECT=SearchInfo&RID=" ++ rid)
@@ -127,19 +128,29 @@ retrieveResult provider' rid = do
127128
return (Right resultXML)
128129

129130
-- | Check if job results are ready and then retrieves results
130-
checkSessionStatus :: String -> String -> IO (Either String BlastResult)
131-
checkSessionStatus provider' rid = do
131+
-- If a walltime in microseconds was set query retrieval will termiate after it is consumed and return a Left result
132+
checkSessionStatus :: String -> String -> Maybe Int -> Int -> IO (Either String BlastResult)
133+
checkSessionStatus provider' rid walltime consumedTime = do
132134
threadDelay 120000000
133135
status <- retrieveSessionStatus provider' rid
134-
waitOrRetrieve provider' status rid
135-
136-
waitOrRetrieve :: String -> String -> String -> IO (Either String BlastResult)
137-
waitOrRetrieve provider' status rid
138-
| provider' == "ebi" = waitOrRetrieveEBI status rid
139-
| otherwise = waitOrRetrieveNCBI status rid
140-
141-
waitOrRetrieveEBI :: String -> String -> IO (Either String BlastResult)
142-
waitOrRetrieveEBI status rid
136+
if (isNothing walltime)
137+
then do
138+
waitOrRetrieve provider' status rid walltime consumedTime
139+
else do
140+
if (consumedTime < (fromJust walltime))
141+
then do
142+
waitOrRetrieve provider' status rid walltime (consumedTime + 120000000)
143+
else do
144+
let exceptionMessage = "BLASTHTTP: Query did not return result within walltime"
145+
return (Left exceptionMessage)
146+
147+
waitOrRetrieve :: String -> String -> String -> Maybe Int -> Int -> IO (Either String BlastResult)
148+
waitOrRetrieve provider' status rid walltime consumedTime
149+
| provider' == "ebi" = waitOrRetrieveEBI status rid walltime consumedTime
150+
| otherwise = waitOrRetrieveNCBI status rid walltime consumedTime
151+
152+
waitOrRetrieveEBI :: String -> String -> Maybe Int -> Int -> IO (Either String BlastResult)
153+
waitOrRetrieveEBI status rid walltime consumedTime
143154
| "FINISHED" `isInfixOf` status = retrieveResult "ebi" rid
144155
| "FAILURE" `isInfixOf` status = do
145156
let exceptionMessage = "BLASTHTTP: The EBI blast job failed."
@@ -151,29 +162,34 @@ waitOrRetrieveEBI status rid
151162
let exceptionMessage = "BLASTHTTP: The EBI blast job cannot be found."
152163
return (Left exceptionMessage)
153164
-- RUNNING
154-
| otherwise = checkSessionStatus "ebi" rid
165+
| otherwise = checkSessionStatus "ebi" rid walltime consumedTime
155166

156-
waitOrRetrieveNCBI :: String -> String -> IO (Either String BlastResult)
157-
waitOrRetrieveNCBI status rid
167+
waitOrRetrieveNCBI :: String -> String -> Maybe Int -> Int -> IO (Either String BlastResult)
168+
waitOrRetrieveNCBI status rid walltime consumedTime
158169
| "Status=READY" `isInfixOf` status = retrieveResult "ncbi" rid
159170
| "Status=FAILURE" `isInfixOf` status = do
160171
let exceptionMessage = "Search $rid failed; please report to blast-help at ncbi.nlm.nih.gov.\n"
161172
return (Left exceptionMessage)
162173
| "Status=UNKNOWN" `isInfixOf` status = do
163174
let exceptionMessage = "Search $rid expired.\n"
164175
return (Left exceptionMessage)
165-
| otherwise = checkSessionStatus "ncbi" rid
176+
| "Status=WAITING" `isInfixOf` status = do
177+
checkSessionStatus "ncbi" rid walltime consumedTime
178+
--Unexpected status, return Left
179+
| otherwise = do
180+
let exceptionMessage = "Status has unexpected value " ++ status ++ " - aborting blast search\n"
181+
return (Left exceptionMessage)
166182

167183
-- | Sends Query and retrieves result on reaching READY status, will return exeption message if no query sequence has been provided
168-
performQuery :: String -> String -> String -> [Sequence] -> Maybe String -> IO (Either String BlastResult)
169-
performQuery provider' program' database' querySequences' optionalArgumentMaybe
184+
performQuery :: String -> String -> String -> [Sequence] -> Maybe String -> Maybe Int -> IO (Either String BlastResult)
185+
performQuery provider' program' database' querySequences' optionalArgumentMaybe walltime
170186
| null querySequences' = do
171187
let exceptionMessage = "Error - no query sequence provided"
172188
return (Left exceptionMessage)
173189
| otherwise = do
174190
let sequenceString = urlEncode (concatMap showSequenceString querySequences')
175191
rid <- startSession provider' program' database' sequenceString optionalArgumentMaybe
176-
checkSessionStatus provider' rid
192+
checkSessionStatus provider' rid walltime (0 :: Int)
177193

178194
showSequenceString :: Sequence -> String
179195
showSequenceString fastaSequence = sequenceString
@@ -185,12 +201,16 @@ showSequenceString fastaSequence = sequenceString
185201
-- The querySequence has to be provided, all other parameters are optional and can be set to Nothing
186202
-- optionalArguments is attached to the query as is .e.g: "&ALIGNMENTS=250"
187203
blastHTTP :: BlastHTTPQuery -> IO (Either String BlastResult)
188-
blastHTTP (BlastHTTPQuery provider' program' database' querySequences' optionalArguments') = do
204+
blastHTTP (BlastHTTPQuery provider' program' database' querySequences' optionalArguments' walltime') = do
189205
let defaultProvider = "ncbi"
190206
let defaultProgram = "blastn"
191207
let defaultDatabase = "refseq_genomic"
208+
let defaultWalltime = Nothing
192209
let selectedProvider = fromMaybe defaultProvider provider'
193210
let selectedProgram = fromMaybe defaultProgram program'
194211
let selectedDatabase = fromMaybe defaultDatabase database'
195-
performQuery selectedProvider selectedProgram selectedDatabase querySequences' optionalArguments'
212+
let selectedWalltime = maybe defaultWalltime Just walltime'
213+
--walltime of 1h in microseconds
214+
--let walltime = Just (7200000000 ::Int)
215+
performQuery selectedProvider selectedProgram selectedDatabase querySequences' optionalArguments' selectedWalltime
196216

0 commit comments

Comments
 (0)