#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
isoenum_webgui.routes
~~~~~~~~~~~~~~~~~~~~~
Isotopic enumerator web interface routes.
"""
import csv
import io
import json
from flask import render_template
from flask import request
from flask import redirect
from flask import url_for
from flask import jsonify
from flask import flash
from pyexcel.exceptions import FileTypeNotSupported
from isoenum_webgui import app
from isoenum_webgui.forms import FileForm
from isoenum_webgui.proc import generate_table
from isoenum_webgui.proc import generate_nmr
from isoenum_webgui.proc import update_record
from isoenum_webgui.proc import create_initial_record
from isoenum_webgui.proc import create_empty_record
from . import RECORDS
from . import HEADER
from . import CSV_HEADER
from . import NMR_TYPES
from . import EXAMPLE_PROJECT
[docs]@app.route("/", methods=["GET", "POST"])
@app.route("/home", methods=["GET", "POST"])
def home():
"""Home."""
file_input_form = FileForm()
if file_input_form.validate_on_submit():
if RECORDS:
RECORDS.clear()
try:
json_str = request.files["file"].read()
RECORDS.update(json.loads(json_str))
except json.decoder.JSONDecodeError:
try:
table_data = request.get_array(field_name="file")
if len(table_data[0]) == 1 and table_data[0][0].lower() != "base identifier":
table_header = ["Base Identifier"]
else:
table_header = table_data.pop(0)
for table_row in table_data:
record = create_initial_record(header=table_header, row=table_row)
RECORDS[record["record_id"]] = record
for i in generate_table(records=RECORDS):
progress_percentage = "{0:.0%}".format(i / len(RECORDS))
except FileTypeNotSupported:
flash("Invalid file", "danger")
return render_template("home.html", file_input_form=file_input_form)
except Exception:
flash("Invalid file", "danger")
return render_template("home.html", file_input_form=file_input_form)
return redirect(url_for("table"))
return render_template("home.html", file_input_form=file_input_form)
[docs]@app.route("/example", methods=["GET", "POST"])
def example_project():
"""Example project."""
if RECORDS:
RECORDS.clear()
RECORDS.update(EXAMPLE_PROJECT)
return redirect(url_for("table"))
[docs]@app.route("/table", methods=["GET", "POST"])
def table():
"""Display base and representative InChI."""
if request.method == "POST" and request.form.get("nmr-inchi-table-data"):
nmr_experiment_type = request.form.get("select-nmr-experiment")
generate_nmr(
nmr_experiment_type=nmr_experiment_type, records=RECORDS
)
return redirect(url_for("nmrtable", nmr_type=nmr_experiment_type))
return render_template("table.html", table_header=HEADER, table_data=RECORDS)
[docs]@app.route("/nmrtable", methods=["GET", "POST"])
def nmrtable():
"""Display NMR-specific InChI."""
nmr_experiment_type = request.args.get("nmr_type", "1D-1H")
nmr_experiment_type = NMR_TYPES[nmr_experiment_type]
return render_template(
"nmrtable.html",
table_header=HEADER,
table_data=RECORDS,
nmr_experiment_type=nmr_experiment_type)
[docs]@app.route("/update_record", methods=["POST"])
def update():
"""Update record."""
record_id = request.form.get("record_id", "")
new_record = update_record(record=request.form)
RECORDS[record_id].update(new_record)
return jsonify(RECORDS[record_id])
[docs]@app.route("/add_record", methods=["POST"])
def add():
"""Add empty record."""
record = create_empty_record()
RECORDS[record["record_id"]] = record
return jsonify({"record_id": record["record_id"]})
[docs]@app.route("/remove_record", methods=["POST"])
def remove():
"""Remove record."""
record_id = request.form.get("record_id", "")
if record_id:
try:
RECORDS.pop(record_id)
except KeyError:
raise KeyError
else:
raise KeyError
return jsonify({"record_id": record_id, "success": True})
[docs]@app.route("/molfile/<record_id>/<record_type>", methods=["GET"])
def display_molfile(record_id, record_type):
"""Display Molfile."""
if record_type == "repr":
svg = RECORDS[record_id]["Repr SVG"]
molfile = RECORDS[record_id]["Repr Molfile"]
elif record_type == "base":
svg = RECORDS[record_id]["Base SVG"]
molfile = RECORDS[record_id]["Base Molfile"]
else:
raise ValueError("Unknown record type")
return render_template("molfile.html", svg=svg, molfile=molfile)
[docs]@app.route("/export_json", methods=["GET"])
def export_json():
"""Export as JSON."""
response = app.response_class(
response=json.dumps(RECORDS, indent=4),
status=201,
mimetype="application/json",
headers={"Content-Disposition": "attachment;filename=records.json"},
)
return response
[docs]@app.route("/export_csv", methods=["GET"])
def export_csv():
"""Export as CSV."""
textio = io.StringIO()
csv_writer = csv.writer(textio)
header = CSV_HEADER["table"]
csv_writer.writerow(header)
for record in RECORDS.values():
row = [record[title] for title in header]
csv_writer.writerow(row)
response = app.response_class(
response=textio.getvalue(),
status=201,
mimetype="text/csv",
headers={"Content-Disposition": "attachment;filename=records.csv"},
)
return response
[docs]@app.route("/export_nmr_csv", methods=["GET", "POST"])
def export_nmr_csv():
"""Export NMR-specific InChI as CSV."""
nmr_experiment_type = request.args.get("nmr_type")
selected_rows = request.form.get("nmr-csv-data")
if selected_rows:
row_ids = selected_rows.split(",")
textio = io.StringIO()
csv_writer = csv.writer(textio)
header = CSV_HEADER["nmrtable"]
csv_writer.writerow(header)
for row_id in row_ids:
record_id, _ = row_id.split("_")
record_name = RECORDS[record_id]["Name"]
base_inchi = RECORDS[record_id]["Base Identifier"]
repr_inchi = RECORDS[record_id]["Repr Identifier"]
descr = " + ".join(RECORDS[record_id]["NMR"][nmr_experiment_type][repr_inchi][row_id]["descr"])
nmr_inchi = RECORDS[record_id]["NMR"][nmr_experiment_type][repr_inchi][row_id]["inchi"]
row = [record_name, base_inchi, repr_inchi, descr, nmr_inchi]
csv_writer.writerow(row)
response = app.response_class(
response=textio.getvalue(),
status=201,
mimetype="text/csv",
headers={"Content-Disposition": "attachment;filename=records.csv"},
)
return response
else:
flash("Please select NMR-specific InChI", "danger")
return redirect(url_for("nmrtable", nmr_type=nmr_experiment_type))
[docs]@app.route("/about")
def about():
"""About page."""
return render_template("about.html")
[docs]@app.after_request
def after_request(response):
"""Do not cache responses."""
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate, max-age=0"
return response