about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--qc_app/files.py20
-rw-r--r--qc_app/samples.py95
-rw-r--r--qc_app/templates/samples/upload-samples.html2
3 files changed, 115 insertions, 2 deletions
diff --git a/qc_app/files.py b/qc_app/files.py
new file mode 100644
index 0000000..6485b27
--- /dev/null
+++ b/qc_app/files.py
@@ -0,0 +1,20 @@
+"""Utilities to deal with uploaded files."""
+from pathlib import Path
+from typing import Union
+
+from werkzeug.utils import secure_filename
+from flask import (
+    request,
+    current_app as app)
+
+def save_file(key: str, upload_dir: Path) -> Union[Path, bool]:
+    """Save the uploaded file and return the path."""
+    if not bool(request.files.get(key)):
+        return False
+    filename = Path(secure_filename(request.files[key].filename))
+    if not upload_dir.exists():
+        upload_dir.mkdir()
+
+    filepath = Path(upload_dir, filename)
+    request.files["samples_file"].save(filepath)
+    return filepath
diff --git a/qc_app/samples.py b/qc_app/samples.py
index cc745ca..27fdad3 100644
--- a/qc_app/samples.py
+++ b/qc_app/samples.py
@@ -1,9 +1,22 @@
 """Code regarding samples"""
+import csv
+from pathlib import Path
+from typing import Iterator
+
 import MySQLdb as mdb
 from MySQLdb.cursors import DictCursor
 from flask import (
-    flash, request, url_for, redirect, Blueprint, render_template, current_app as app)
+    flash,
+    request,
+    url_for,
+    redirect,
+    Blueprint,
+    render_template,
+    current_app as app)
+
+from quality_control.parsing import take
 
+from .files import save_file
 from .db_utils import with_db_connection
 from .dbinsert import species_by_id, groups_by_species
 
@@ -106,3 +119,83 @@ def select_population():
     return render_template("samples/upload-samples.html",
                            species=species,
                            population=population)
+
+def read_samples_file(filepath, separator: str, **kwargs) -> Iterator[dict]:
+    """Read the samples file."""
+    with open(filepath, "r", encoding="utf-8") as inputfile:
+        reader = csv.DictReader(
+            inputfile,
+            fieldnames=("Name", "Name2", "Symbol", "Alias"),
+            delimiter=separator,
+            quotechar=kwargs.get("quotechar"))
+        for row in reader:
+            yield row
+
+def save_samples_data(conn: mdb.Connection, file_data: Iterator[dict]):
+    """Save the samples to DB."""
+    with conn.cursor() as cursor:
+        while True:
+            cursor.executemany(
+                "INSERT INTO Strain(Name, Name2, SpeciesId, Symbol, Alias) "
+                "VALUES("
+                "    %(Name)s, %(Name2)s, %(SpeciesId)s, %(Symbol)s, %(Alias)s"
+                ")",
+                tuple(take(file_data, 10000)))
+
+def cross_reference_samples(conn: mdb.Connection,
+                            population_id: int,
+                            strain_names: tuple[str, ...]):
+    """Link samples to their population."""
+    with conn.cursor(cursorclass=DictCursor) as cursor:
+        params_str = ", ".join(["%s"] * len(strain_names))
+        cursor.execute(
+            "SELECT Id FROM Strain WHERE (Name, SpeciesId) IN "
+            f"{params_str}",
+            tuple((name, species["SpeciesId"]) for name in strain_names))
+        strain_ids = (sid for sid in cursor.fetchall())
+        cursor.execute(
+            "SELECT MAX(OrderId) AS loid FROM StrainXRef WHERE InbredSetId=%s",
+            (population_id,))
+        last_order_id = cursor.fetchone()["loid"]
+        cursor.executemany(
+            "INSERT INTO StrainXRef(InbredSetId, StrainId, OrderId) "
+            "VALUES (%(pop_id)s, %(strain_id)s, %(order_id)s)",
+            tuple({
+                "pop_id": population_id,
+                "strain_id": strain_id,
+                "order_id": order_id
+            } for order_id, strain_id in
+                  enumerate(strain_ids, start=(last_order_id+10))))
+
+@samples.route("/upload/samples", methods=["POST"])
+def upload_samples():
+    """Upload the samples."""
+    samples_uploads_page = redirect(url_for("samples.select_population"),
+                                    code=307)
+
+    species = species_by_id(request.form.get("species_id"))
+    if not bool(species):
+        flash("Invalid species!", "alert-error")
+        return samples_uploads_page
+
+    population = with_db_connection(
+        lambda conn: population_by_id(
+            conn, int(request.form.get("inbredset_id"))))
+    if not bool(population):
+        flash("Invalid grouping/population!", "alert-error")
+        return samples_uploads_page
+
+    samples_file = save_file("samples_file", Path(app.config["UPLOAD_FOLDER"]))
+    if not bool(samples_file):
+        flash("You need to provide a file with the samples data.")
+        return samples_uploads_page
+
+    def __insert_samples__(conn: mdb.Connection):
+        save_samples_data(conn, read_samples_file(samples_file))
+        cross_reference_samples(
+            conn,
+            population["InbredSetId"],
+            tuple(row["Name"] for row in read_samples_file(samples_file)))
+
+    with_db_connection(__insert_samples__)
+    return "SUCCESS: Respond with a better UI than this."
diff --git a/qc_app/templates/samples/upload-samples.html b/qc_app/templates/samples/upload-samples.html
index 23dc8a8..5d1ec4c 100644
--- a/qc_app/templates/samples/upload-samples.html
+++ b/qc_app/templates/samples/upload-samples.html
@@ -40,7 +40,7 @@
 
 <form id="form-samples"
       method="POST"
-      action="#"
+      action="{{url_for('samples.upload_samples')}}"
       enctype="multipart/form-data">
   <legend class="heading">upload samples</legend>
   <fieldset>