-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4ef60f0
commit 4c33f8c
Showing
9 changed files
with
291 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> | ||
<title>{% block title %}{% endblock %}</title> | ||
<link rel="stylesheet" href="static/bootstrap.min.css" /> | ||
<style> | ||
{% block css %}{% endblock %} | ||
</style> | ||
</head> | ||
<body class="d-flex flex-column min-vh-100"> | ||
<div class="flex-grow-1"> | ||
{% block content %}{% endblock %} | ||
</div> | ||
<div class="footer sticky-footer"> | ||
<div class="container text-center"> | ||
<p class="text-muted"> | ||
Copyright 2024 | ||
<a href="http://denglab.info" class="text-muted">Deng Lab</a> | ||
</p> | ||
</div> | ||
</div> | ||
<script src="static/bootstrap.min.js"></script> | ||
<script src="static/jquery-3.6.0.min.js"></script> | ||
{% block scripts %}{% endblock %} | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
{% extends "base.html" %} | ||
{% block title %}Mashpit{% endblock %} | ||
{% block content %} | ||
<div class="container"> | ||
<h1> | ||
<strong>Mashpit</strong> | ||
<br /> | ||
<small class="text-muted fw-light"> | ||
<span style="font-size: 0.5em;">A sketch-based survilliance database</span> | ||
</small> | ||
</h1> | ||
<br /> | ||
<div class="container d-flex align-items-center"> | ||
<h1 class="me-3" style="font-family: Arial, sans-serif">Loading...</h1> | ||
<div class="spinner-border" role="status"></div> | ||
</div> | ||
</div> | ||
{% endblock %} | ||
|
||
{% block scripts %} | ||
<script> | ||
$(document).ready(function() { | ||
// Check the classification status every 2 seconds | ||
setInterval(function() { | ||
$.get('/status', function(data) { | ||
if (data === 'complete') { | ||
// Redirect to the result page if the classification is complete | ||
window.location.href = '/result'; | ||
} else { | ||
// Update the status message | ||
$('#status').text(data); | ||
|
||
} | ||
}); | ||
}, 2000); | ||
}); | ||
</script> | ||
{% endblock %} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
{% extends "base.html" %} | ||
|
||
{% block title %}Mashpit{% endblock %} | ||
{% block css %} | ||
<style> | ||
.full-width-table { | ||
width: 90%; /* Ensures the table takes up the full width of its container */ | ||
} | ||
.container { | ||
max-width: 80%; /* Adjusts the container width to a larger percentage */ | ||
} | ||
.table thead th { | ||
text-align: left; /* Aligns header content to the left */ | ||
} | ||
.zoomable-image { | ||
width: 20%; /* Adjusts the image width to 20% */ | ||
} | ||
</style> | ||
{% endblock %} | ||
|
||
{% block content %} | ||
<div class="container"> | ||
<h1> | ||
<strong>Mashpit</strong> | ||
<br /> | ||
<small class="text-muted fw-light"> | ||
<span style="font-size: 0.5em;">A sketch-based surveillance database</span> | ||
</small> | ||
</h1> | ||
<br /> | ||
<!-- Tab Navigation --> | ||
<ul class="nav nav-tabs" id="resultTabs" role="tablist"> | ||
<li class="nav-item" role="presentation"> | ||
<button class="nav-link active" id="table-tab" data-bs-toggle="tab" data-bs-target="#table" type="button" role="tab" aria-controls="table" aria-selected="false">Query Result</button> | ||
</li> | ||
<li class="nav-item" role="presentation"> | ||
<button class="nav-link" id="image-tab" data-bs-toggle="tab" data-bs-target="#image" type="button" role="tab" aria-controls="image" aria-selected="true">Mashtree</button> | ||
</li> | ||
|
||
</ul> | ||
<!-- Tab Content --> | ||
<div class="tab-content" id="myTabContent"> | ||
|
||
<div class="tab-pane fade show active" id="table" role="tabpanel" aria-labelledby="table-tab"> | ||
<div class="table-responsive"> | ||
<table class="table table-bordered table-striped table-hover full-width-table"> | ||
{{ df_output_html|safe }} | ||
</table> | ||
</div> | ||
</div> | ||
|
||
<div class="tab-pane fade" id="image" role="tabpanel" aria-labelledby="image-tab"> | ||
<div class="container"> | ||
<div class="row"> | ||
<div class="col"> | ||
<img src="{{ url_for('static', filename=image_path) }}" class="img-fluid" alt="Neighbor-joining tree based on Mash distances" /> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
|
||
</div> | ||
</div> | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
{% extends "base.html" %} | ||
|
||
{% block title %}Mashpit{% endblock %} | ||
{% block content %} | ||
<div class="container"> | ||
<h1> | ||
<strong>Mashpit</strong> | ||
<br /> | ||
<small class="text-muted fw-light"> | ||
<span style="font-size: 0.5em;">A sketch-based survilliance database</span> | ||
</small> | ||
</h1> | ||
<br /> | ||
<div class="row justify-content-start"> | ||
<div class="col-md-6"> | ||
<br /> | ||
<form action="/submit" method="POST" enctype="multipart/form-data"> | ||
<div class="mb-3"> | ||
<label for="formFile_db" class="form-label">Select the database (.db) file</label> | ||
<input class="form-control" type="file" id="formFile_db" name="formFile_db"> | ||
</div> | ||
<div class="mb-3"> | ||
<label for="formFile_sig" class="form-label">Select the database signature (.sig) file</label> | ||
<input class="form-control" type="file" id="formFile_sig" name="formFile_sig"> | ||
</div> | ||
<div class="mb-3"> | ||
<label for="formFile_assembly" class="form-label">Select the query assembly file</label> | ||
<input class="form-control" type="file" id="formFile_assembly" name="formFile_assembly"> | ||
</div> | ||
<button type="submit" class="btn btn-secondary">Submit</button> | ||
</form> | ||
</div> | ||
</div> | ||
</div> | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import os | ||
import threading | ||
import subprocess | ||
import shutil | ||
import pandas as pd | ||
from flask import Flask, render_template, request, redirect, url_for | ||
|
||
app = Flask(__name__, template_folder=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')) | ||
|
||
# Lock for thread-safe operations on global variables | ||
lock = threading.Lock() | ||
|
||
# Global variables to store the output of the classification and status | ||
mashpit_out = [] | ||
mashpit_status = "idle" | ||
|
||
def run_mashpit(asm_file_path): | ||
global mashpit_out | ||
global mashpit_status | ||
try: | ||
with lock: | ||
mashpit_status = "in progress" | ||
upload_folder = os.path.join(app.config['UPLOAD_FOLDER']) | ||
subprocess.run(['mashpit', 'query', asm_file_path, upload_folder], check=True) | ||
query_name = os.path.basename(asm_file_path).split('.')[0] | ||
# move the output to the static folder | ||
static_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static') | ||
# check if file exists and remove it | ||
if os.path.exists(os.path.join(static_folder, f'{query_name}_output.csv')): | ||
os.remove(os.path.join(static_folder, f'{query_name}_output.csv')) | ||
if os.path.exists(os.path.join(static_folder, f'{query_name}_tree.png')): | ||
os.remove(os.path.join(static_folder, f'{query_name}_tree.png')) | ||
shutil.move(f'{query_name}_output.csv', static_folder) | ||
shutil.move(f'{query_name}_tree.png', static_folder) | ||
|
||
df_output = pd.read_csv(os.path.join(static_folder, f'{query_name}_output.csv')) | ||
# remove first column | ||
df_output = df_output.iloc[:, 1:] | ||
# put column 'asm_acc', 'similarity_score' and 'SNP_tree_link' to the front | ||
df_output = df_output[['asm_acc', 'similarity_score', 'SNP_tree_link'] + [col for col in df_output.columns if col not in ['asm_acc', 'similarity_score', 'SNP_tree_link']]] | ||
|
||
image_path = f'{query_name}_tree.png' | ||
df_output_html = df_output.to_html(index=False, classes='table table-striped table-hover') | ||
|
||
with lock: | ||
mashpit_out = [image_path, df_output_html] | ||
mashpit_status = "complete" | ||
|
||
except Exception as e: | ||
with lock: | ||
mashpit_status = f"error: {str(e)}" | ||
|
||
@app.route("/", methods=['GET']) | ||
def upload(): | ||
return render_template('upload.html') | ||
|
||
@app.route("/submit", methods=['POST']) | ||
def submit(): | ||
uploaded_files = request.files | ||
paths = [] | ||
for key in ['formFile_db', 'formFile_sig', 'formFile_assembly']: | ||
file = uploaded_files[key] | ||
file_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) | ||
file.save(file_path) | ||
paths.append(file_path) | ||
|
||
global mashpit_thread | ||
global mashpit_status | ||
|
||
with lock: | ||
mashpit_status = "preparing" | ||
mashpit_thread = threading.Thread(target=run_mashpit, args=(paths[2],)) | ||
mashpit_thread.start() | ||
|
||
return redirect(url_for('loading')) | ||
|
||
@app.route('/loading') | ||
def loading(): | ||
global mashpit_status | ||
with lock: | ||
current_status = mashpit_status | ||
return render_template('loading.html', status=current_status) | ||
|
||
@app.route('/result') | ||
def result(): | ||
global mashpit_out | ||
with lock: | ||
image_path, df_output_html = mashpit_out | ||
return render_template('result.html', image_path=image_path, df_output_html=df_output_html) | ||
|
||
@app.route('/status') | ||
def status(): | ||
global mashpit_status | ||
with lock: | ||
current_status = mashpit_status | ||
return current_status | ||
|
||
def webserver(args): | ||
app.config['UPLOAD_FOLDER'] = 'uploads' | ||
# if the folder exists, clear it | ||
if os.path.exists(app.config['UPLOAD_FOLDER']): | ||
for file in os.listdir(app.config['UPLOAD_FOLDER']): | ||
os.remove(os.path.join(app.config['UPLOAD_FOLDER'], file)) | ||
else: | ||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) | ||
app.run(debug=True) | ||
|
||
|