1+ from fastapi import FastAPI , HTTPException
2+ from pydantic import BaseModel
3+ import subprocess
4+ import os
5+ from uuid import uuid4
6+ import shutil
7+
8+ app = FastAPI ()
9+
10+ # Define a model for the input parameters
11+ class MMSeqsParams (BaseModel ):
12+ query : str # The query sequence
13+ database : str
14+ output : str # The output directory
15+ sensitivity : float = 7.5 # Sensitivity parameter for mmseqs2
16+ threads : int = 4 # Number of threads to use
17+ blast_format : bool = True # Option to convert to BLAST+ format
18+
19+ # Dictionary to keep track of running jobs and results
20+ job_results = {}
21+
22+ def create_fastas_file_from_seq (seq , filename ):
23+ with open (filename , 'w' ) as file :
24+ file .write (f">seq\n { seq } \n " )
25+
26+ def create_queryDB_from_seq (filename ):
27+ # this will create a db from a single sequence file
28+ # the command is mmseqs createdb <input> <output>
29+ # the output should be a file with the same name as the input but with the extension .db
30+
31+ command = [
32+ "mmseqs" , "createdb" ,
33+ filename ,
34+ filename .replace ('fasta' , '' ) + ".db"
35+ ]
36+
37+ try :
38+ subprocess .run (command , check = True )
39+
40+ except subprocess .CalledProcessError as e :
41+ raise HTTPException (status_code = 600 , detail = str (e ))
42+
43+
44+ @app .get ("/" )
45+ async def read_root ():
46+ return {"message" : "Welcome to the MMSeqs2 API!" }
47+
48+ @app .post ("/run_mmseqs" )
49+ async def run_mmseqs (params : MMSeqsParams ):
50+ # Create a unique job id
51+ job_id = str (uuid4 ())
52+ output_dir = f"/tmp/{ job_id } "
53+
54+ # Prepare the output directory
55+ os .makedirs (output_dir , exist_ok = True )
56+
57+ # Prepare paths
58+ result_m8_path = os .path .join (output_dir , "result.m8" )
59+ result_tsv_path = os .path .join (output_dir , "result.tsv" )
60+
61+ # Create the FASTA file
62+ path_query = os .path .join (output_dir , "query.fasta" )
63+ path_queryDB = path_query .replace ('fasta' , '' ) + ".db"
64+ create_fastas_file_from_seq (params .query , path_query )
65+ create_queryDB_from_seq (path_query )
66+
67+ # Run the mmseqs2 search command
68+ command = [
69+ "mmseqs" , "search" ,
70+ path_queryDB ,
71+ params .database ,
72+ os .path .join (output_dir , "result" ),
73+ output_dir ,
74+ "--threads" , str (params .threads ),
75+ "--sensitivity" , str (params .sensitivity )
76+ ]
77+
78+ try :
79+ # Execute mmseqs search
80+ subprocess .run (command , check = True )
81+
82+ # Convert the results to BLAST+ format if requested
83+ if params .blast_format :
84+ # mmseqs convertalis queryDB targetDB resultDB resultDB.m8
85+ # Convert to BLAST tabular format (BLAST m8 format)
86+ convert_command = [
87+ "mmseqs" , "convertalis" ,
88+ params .query ,
89+ params .database ,
90+ os .path .join (output_dir , "result" ),
91+ result_m8_path ,
92+ ]
93+ subprocess .run (convert_command , check = True )
94+
95+ # Store the result path for m8 format
96+ job_results [job_id ] = {
97+ "status" : "completed" ,
98+ "result_path" : result_m8_path
99+ }
100+ else :
101+ # Store the result path for standard mmseqs2 output (TSV format)
102+ job_results [job_id ] = {
103+ "status" : "completed" ,
104+ "result_path" : result_tsv_path
105+ }
106+
107+ return {"job_id" : job_id }
108+ except subprocess .CalledProcessError as e :
109+ raise HTTPException (status_code = 500 , detail = f"mmseqs2 failed: { str (e )} " )
110+
111+ @app .get ("/results/{job_id}" )
112+ async def get_results (job_id : str ):
113+ # Check if the job exists
114+ if job_id not in job_results :
115+ raise HTTPException (status_code = 404 , detail = "Job not found" )
116+
117+ # Get the result path
118+ result = job_results [job_id ]
119+
120+ # Read and return the result (assuming it's a text file you want to read and return)
121+ result_file = result ["result_path" ]
122+ if os .path .exists (result_file ):
123+ with open (result_file , "r" ) as file :
124+ data = file .read ()
125+ return {"status" : result ["status" ], "results" : data }
126+ else :
127+ raise HTTPException (status_code = 404 , detail = "Result file not found" )
128+
129+
130+ if __name__ == '__main__' :
131+ import uvicorn
132+
133+ uvicorn .run ("app:app" , host = "0.0.0.0" , port = 8000 , reload = True )
0 commit comments