From e689d873c4f5c0c9c456c9173c7946452f0b505d Mon Sep 17 00:00:00 2001 From: xnought Date: Fri, 5 Apr 2024 16:50:48 -0700 Subject: [PATCH 1/4] feat: delete TMAlign files after computing --- backend/src/api/protein.py | 27 ++++++++++----- backend/src/tmalign.py | 68 +++++++++++++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/backend/src/api/protein.py b/backend/src/api/protein.py index 73f7517e..11783006 100644 --- a/backend/src/api/protein.py +++ b/backend/src/api/protein.py @@ -9,7 +9,7 @@ from ..api_types import ProteinEntry, UploadBody, UploadError, EditBody, CamelModel from ..auth import requiresAuthentication -from ..tmalign import tm_align +from ..tmalign import tm_align_return from io import BytesIO from fastapi import APIRouter from fastapi.responses import FileResponse, StreamingResponse @@ -97,6 +97,14 @@ def pdb_to_fasta(pdb: PDB): return ">{}\n{}".format(pdb.name, "".join(pdb.amino_acids())) +def str_as_file_stream(input_str: str, filename_as: str) -> StreamingResponse: + return StreamingResponse( + BytesIO(input_str.encode()), + media_type="text/plain", + headers={"Content-Disposition": f"attachment; filename={filename_as}"}, + ) + + """ ENDPOINTS TODO: add the other protein types here instead of in api_types.py """ @@ -137,7 +145,7 @@ def get_protein_entry(protein_name: str): with Database() as db: try: query = """SELECT proteins.name, - proteins.description, + proteins.description, proteins.length, proteins.mass, proteins.content, @@ -356,13 +364,16 @@ def edit_protein_entry(body: EditBody, req: Request): # /pdb with two attributes returns both PDBs, superimposed and with different colors. @router.get("/protein/pdb/{proteinA:str}/{proteinB:str}") def align_proteins(proteinA: str, proteinB: str): - try: - pdbA = stored_pdb_file_name(proteinA) - pdbB = stored_pdb_file_name(proteinB) - - file = tm_align(proteinA, pdbA, proteinB, pdbB) + if not protein_name_found(proteinA) or not protein_name_found(proteinB): + raise HTTPException( + status_code=404, detail="One of the proteins provided is not found in DB" + ) - return FileResponse(file, filename=proteinA + "_" + proteinB + ".pdb") + try: + filepath_pdbA = stored_pdb_file_name(proteinA) + filepath_pdbB = stored_pdb_file_name(proteinB) + superimposed_pdb = tm_align_return(filepath_pdbA, filepath_pdbB) + return str_as_file_stream(superimposed_pdb, f"{proteinA}_{proteinB}.pdb") except Exception as e: log.error(e) raise HTTPException(status_code=500, detail=str(e)) diff --git a/backend/src/tmalign.py b/backend/src/tmalign.py index 3790a329..ad0f727d 100644 --- a/backend/src/tmalign.py +++ b/backend/src/tmalign.py @@ -20,10 +20,36 @@ def assert_tmalign_installed(): ) -def parse_pdb(filepath: str) -> list[str]: - with open(filepath, "r") as f: - lines = f.readlines() - return lines +temp_dirs_active = 0 + + +class UniqueTempDir: + """ + on opening scope will create directory of the given name + on closing scope will delete directory of the given name + uses the global `active_caches` above to create a unique dir name + """ + + def __init__(self, base_path): + self.base_path = base_path + + def __enter__(self): + global temp_dirs_active + temp_dirs_active += 1 + self.temp_dir = os.path.join(self.base_path, f"temp_dir_{temp_dirs_active}") + + # create the directory (and override existing one if exists) + bash_cmd("rm -rf " + self.temp_dir) + bash_cmd(f"mkdir {self.temp_dir}") + + return self.temp_dir + + def __exit__(self, *args): + global temp_dirs_active + + # get rid of the temp directory + temp_dirs_active -= 1 + bash_cmd("rm -rf " + self.temp_dir) def tm_align( @@ -73,3 +99,37 @@ def tm_align( raise e return desired_file + + +def tm_align_return(pdbA: str, pdbB: str) -> str: + """ + Description: + Returns two overlaid, aligned, and colored PDB structures in a single PDB file. + The ones without extensions appear to be PDB files. + + Params: + pdbA: + The filepath of the first protein. + pdbB: + The filepath of the second protein. + + Returns: the str contents of the pdbA superimposed on pdbB with TMAlgin + """ + + assert_tmalign_installed() + + with UniqueTempDir(base_path=TMALIGN_LOCATION) as temp_dir_path: + try: + output_location = os.path.join(temp_dir_path, "output") + cmd = f"{TMALIGN_EXECUTABLE} {pdbA} {pdbB} -o {output_location}" + bash_cmd(cmd) + + tmalign_pdb_path = f"{output_location}_all_atm" + + with open(tmalign_pdb_path, "r") as tmalign_pdb_file: + tmalign_pdb_file_str = tmalign_pdb_file.read() + return tmalign_pdb_file_str + + except Exception as e: + log.warn(e) + raise e From 8ddc53038f00d744a3738198dae504e4467ae9a9 Mon Sep 17 00:00:00 2001 From: xnought Date: Fri, 5 Apr 2024 16:53:45 -0700 Subject: [PATCH 2/4] ./run.sh gen_api --- backend/src/api/protein.py | 2 +- frontend/src/lib/openapi/services/DefaultService.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/api/protein.py b/backend/src/api/protein.py index 11783006..63a65787 100644 --- a/backend/src/api/protein.py +++ b/backend/src/api/protein.py @@ -362,7 +362,7 @@ def edit_protein_entry(body: EditBody, req: Request): # /pdb with two attributes returns both PDBs, superimposed and with different colors. -@router.get("/protein/pdb/{proteinA:str}/{proteinB:str}") +@router.get("/protein/pdb/{proteinA:str}/{proteinB:str}", response_model=str) def align_proteins(proteinA: str, proteinB: str): if not protein_name_found(proteinA) or not protein_name_found(proteinB): raise HTTPException( diff --git a/frontend/src/lib/openapi/services/DefaultService.ts b/frontend/src/lib/openapi/services/DefaultService.ts index bed97ba9..5332f3b2 100644 --- a/frontend/src/lib/openapi/services/DefaultService.ts +++ b/frontend/src/lib/openapi/services/DefaultService.ts @@ -256,13 +256,13 @@ export class DefaultService { * Align Proteins * @param proteinA * @param proteinB - * @returns any Successful Response + * @returns string Successful Response * @throws ApiError */ public static alignProteins( proteinA: string, proteinB: string, - ): CancelablePromise { + ): CancelablePromise { return __request(OpenAPI, { method: 'GET', url: '/protein/pdb/{proteinA}/{proteinB}', From 39197682d9180043e0790cb2ecdc4a81eeecf83e Mon Sep 17 00:00:00 2001 From: xnought Date: Fri, 5 Apr 2024 17:50:10 -0700 Subject: [PATCH 3/4] feat: style alignment --- frontend/src/Router.svelte | 6 +- frontend/src/lib/Molstar.svelte | 3 +- frontend/src/lib/SimilarProteins.svelte | 18 ++-- frontend/src/routes/Align.svelte | 105 ++++++++++++++++++++++++ frontend/src/routes/Compare.svelte | 73 ---------------- frontend/src/routes/TMAlignEntry.svelte | 44 ++++++++++ 6 files changed, 164 insertions(+), 85 deletions(-) create mode 100644 frontend/src/routes/Align.svelte delete mode 100644 frontend/src/routes/Compare.svelte create mode 100644 frontend/src/routes/TMAlignEntry.svelte diff --git a/frontend/src/Router.svelte b/frontend/src/Router.svelte index 3c0016ad..5b07cdf2 100644 --- a/frontend/src/Router.svelte +++ b/frontend/src/Router.svelte @@ -10,7 +10,7 @@ import Edit from "./routes/Edit.svelte"; import Tutorials from "./routes/Tutorials.svelte"; import ForceUploadThumbnails from "./routes/ForceUploadThumbnails.svelte"; - import Compare from "./routes/Compare.svelte"; + import Align from "./routes/Align.svelte"; @@ -28,7 +28,9 @@ > - + diff --git a/frontend/src/lib/Molstar.svelte b/frontend/src/lib/Molstar.svelte index 6456c48e..bd45d188 100644 --- a/frontend/src/lib/Molstar.svelte +++ b/frontend/src/lib/Molstar.svelte @@ -7,6 +7,7 @@ export let binary = false; export let width = 500; export let height = 500; + export let hideControls = true; let divEl: HTMLDivElement; async function render() { @@ -28,7 +29,7 @@ alphafoldView: true, reactive: true, sequencePanel: true, - hideControls: true, + hideControls, hideCanvasControls: ["animation"], }); } diff --git a/frontend/src/lib/SimilarProteins.svelte b/frontend/src/lib/SimilarProteins.svelte index e4e3acd5..d11c86d5 100644 --- a/frontend/src/lib/SimilarProteins.svelte +++ b/frontend/src/lib/SimilarProteins.svelte @@ -44,8 +44,8 @@ Name E-Value Prob. Match - Region of Similarity - TMAlign + Region of Similarity + TMAlign {#each similarProteins as protein, i} @@ -79,7 +79,7 @@ -
+
-
+
Compare Align @@ -135,7 +135,7 @@ a { display: flex; gap: 1px; - align-items: center; + region-items: center; } /* width */ ::-webkit-scrollbar { @@ -166,12 +166,12 @@ .prob-cell { width: 120px; } - .align-cell { + .region-cell { width: 290px; padding-left: 10px; padding-right: 30px; } - .compare-cell { + .align-cell { width: 100px; } diff --git a/frontend/src/routes/Align.svelte b/frontend/src/routes/Align.svelte new file mode 100644 index 00000000..58eebe7d --- /dev/null +++ b/frontend/src/routes/Align.svelte @@ -0,0 +1,105 @@ + + + + Venome Protein {entryA ? entryA.name : ""} + + +
+
+ {#if entryA && entryB} +
+
+

Align

+

+ Aligned the following structures with TMAlign +

+
+ + +
+ +
+
+
+ +
+ {:else if !error} + +

+ {:else if error} + +

Error

+ {/if} +
+
+ + diff --git a/frontend/src/routes/Compare.svelte b/frontend/src/routes/Compare.svelte deleted file mode 100644 index 324f5a04..00000000 --- a/frontend/src/routes/Compare.svelte +++ /dev/null @@ -1,73 +0,0 @@ - - - - Venome Protein {entry ? entry.name : ""} - - -
- {#if entry} -
- -

Comparing Proteins

- -
- {proteinA} and {proteinB} -
- -
- {:else if !error} - -

- {:else if error} - -

Error

-

Could not find a protein with the id {urlId}

- {/if} -
- - diff --git a/frontend/src/routes/TMAlignEntry.svelte b/frontend/src/routes/TMAlignEntry.svelte new file mode 100644 index 00000000..477f70b1 --- /dev/null +++ b/frontend/src/routes/TMAlignEntry.svelte @@ -0,0 +1,44 @@ + + + + +
+ {entry.description} +
+
+ Length: {entry.length} residues +
+
+ Mass: {numberWithCommas(entry.mass, 0)} Da +
+
+ + From 9886aba000a4e8a697952b01a35cfe9a35400c43 Mon Sep 17 00:00:00 2001 From: xnought Date: Sat, 6 Apr 2024 14:42:54 -0700 Subject: [PATCH 4/4] feat: also add species name --- frontend/src/routes/TMAlignEntry.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/routes/TMAlignEntry.svelte b/frontend/src/routes/TMAlignEntry.svelte index 477f70b1..e35f8b5b 100644 --- a/frontend/src/routes/TMAlignEntry.svelte +++ b/frontend/src/routes/TMAlignEntry.svelte @@ -24,6 +24,9 @@
{entry.description}
+
+ Organism: {entry.speciesName} +
Length: {entry.length} residues