Skip to content

Commit

Permalink
webserver
Browse files Browse the repository at this point in the history
  • Loading branch information
tongzhouxu committed May 29, 2024
1 parent 4ef60f0 commit 4c33f8c
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 3 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
.vscode/
/__pycache__/
mashpit/__pycache__/
mashpit/webserver.py
mashpit/static/
mashpit/templates/
mashpit/test/__pycache__/
build/
dist/
Expand Down
6 changes: 6 additions & 0 deletions mashpit/static/bootstrap.min.css

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions mashpit/static/bootstrap.min.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions mashpit/static/jquery-3.6.0.min.js

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions mashpit/templates/base.html
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>
39 changes: 39 additions & 0 deletions mashpit/templates/loading.html
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 %}

65 changes: 65 additions & 0 deletions mashpit/templates/result.html
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 %}
35 changes: 35 additions & 0 deletions mashpit/templates/upload.html
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 %}
108 changes: 108 additions & 0 deletions mashpit/webserver.py
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)


0 comments on commit 4c33f8c

Please sign in to comment.