aboutsummaryrefslogtreecommitdiff
path: root/qc_app
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2024-06-12 12:48:56 -0500
committerFrederick Muriuki Muriithi2024-06-12 12:48:56 -0500
commit5e96d27f3d96c84fc5a15d7040843b379b701d20 (patch)
treead283df6ef65ed66f800d79446f04b19c2d08768 /qc_app
parent3abf09f5d5a0ab7fb3b1ca2be981c364ef68a8cb (diff)
downloadgn-uploader-5e96d27f3d96c84fc5a15d7040843b379b701d20.tar.gz
Make URI and UI correspond to each other.
Formerly, the URI and UI were not corresponding to each other, e.g. the URI /upload/samples/select_species would display the UI for selecting/creating the population. This was very confusing. This commit fixes that. The commit also adds in user input validation to catch input errors.
Diffstat (limited to 'qc_app')
-rw-r--r--qc_app/samples.py240
-rw-r--r--qc_app/templates/index.html19
-rw-r--r--qc_app/templates/samples/select-population.html6
-rw-r--r--qc_app/templates/samples/select-species.html29
-rw-r--r--qc_app/templates/samples/upload-samples.html6
5 files changed, 178 insertions, 122 deletions
diff --git a/qc_app/samples.py b/qc_app/samples.py
index e7be458..804f262 100644
--- a/qc_app/samples.py
+++ b/qc_app/samples.py
@@ -22,7 +22,7 @@ from functional_tools import take
from qc_app import jobs
from qc_app.files import save_file
-from qc_app.input_validation import is_empty_input, is_integer_input
+from qc_app.input_validation import is_integer_input
from qc_app.db_utils import (
with_db_connection,
database_connection,
@@ -31,13 +31,18 @@ from qc_app.db import (
species_by_id,
save_population,
population_by_id,
- populations_by_species)
+ populations_by_species,
+ species as fetch_species)
samples = Blueprint("samples", __name__)
-@samples.route("/upload/species", methods=["POST"])
+@samples.route("/upload/species", methods=["GET", "POST"])
def select_species():
"""Select the species."""
+ if request.method == "GET":
+ return render_template("samples/select-species.html",
+ species=with_db_connection(fetch_species))
+
index_page = redirect(url_for("entry.upload_file"))
species_id = request.form.get("species_id")
if bool(species_id):
@@ -45,23 +50,31 @@ def select_species():
species = with_db_connection(
lambda conn: species_by_id(conn, species_id))
if bool(species):
- return render_template(
- "samples/select-population.html",
- species=species,
- populations=with_db_connection(
- lambda conn: populations_by_species(conn, species_id)))
+ return redirect(url_for(
+ "samples.select_population", species_id=species_id))
flash("Invalid species selected!", "alert-error")
flash("You need to select a species", "alert-error")
return index_page
-@samples.route("/upload/create-population", methods=["POST"])
-def create_population():
+@samples.route("/upload/species/<int:species_id>/create-population",
+ methods=["POST"])
+def create_population(species_id: int):
"""Create new grouping/population."""
+ if not is_integer_input(species_id):
+ flash("You did not provide a valid species. Please select one to "
+ "continue.",
+ "alert-danger")
+ return redirect(url_for("samples.select_species"))
+ species = with_db_connection(lambda conn: species_by_id(conn, species_id))
+ if not bool(species):
+ flash("Species with given ID was not found.", "alert-danger")
+ return redirect(url_for("samples.select_species"))
+
species_page = redirect(url_for("samples.select_species"), code=307)
with database_connection(app.config["SQL_URI"]) as conn:
- species = species_by_id(conn, request.form.get("species_id"))
- pop_name = request.form.get("inbredset_name").strip()
- pop_fullname = request.form.get("inbredset_fullname").strip()
+ species = species_by_id(conn, species_id)
+ pop_name = request.form.get("inbredset_name", "").strip()
+ pop_fullname = request.form.get("inbredset_fullname", "").strip()
if not bool(species):
flash("Invalid species!", "alert-error error-create-population")
@@ -81,33 +94,50 @@ def create_population():
})
flash("Grouping/Population created successfully.", "alert-success")
- return render_template(
- "samples/upload-samples.html",
- species=species,
- population=with_db_connection(
- lambda conn: population_by_id(conn, pop["population_id"])))
+ return redirect(url_for("samples.upload_samples",
+ species_id=species_id,
+ population_id=pop["population_id"]))
-@samples.route("/upload/select-population", methods=["POST"])
-def select_population():
+@samples.route("/upload/species/<int:species_id>/population",
+ methods=["GET", "POST"])
+def select_population(species_id: int):
"""Select from existing groupings/populations."""
- species_page = redirect(url_for("samples.select_species"), code=307)
- with database_connection(app.config["SQL_URI"]) as conn:
- species = species_by_id(conn, request.form.get("species_id"))
- pop_id = int(request.form.get("inbredset_id"))
- population = with_db_connection(lambda conn: population_by_id(conn, pop_id))
-
+ if not is_integer_input(species_id):
+ flash("You did not provide a valid species. Please select one to "
+ "continue.",
+ "alert-danger")
+ return redirect(url_for("samples.select_species"))
+ species = with_db_connection(lambda conn: species_by_id(conn, species_id))
if not bool(species):
- flash("Invalid species!", "alert-error error-select-population")
- return species_page
+ flash("Species with given ID was not found.", "alert-danger")
+ return redirect(url_for("samples.select_species"))
+ if request.method == "GET":
+ return render_template(
+ "samples/select-population.html",
+ species=species,
+ populations=with_db_connection(
+ lambda conn: populations_by_species(conn, species_id)))
+
+ population_page = redirect(url_for(
+ "samples.select_population", species_id=species_id), code=307)
+ _population_id = request.form.get("inbredset_id")
+ if not is_integer_input(_population_id):
+ flash("You did not provide a valid population. Please select one to "
+ "continue.",
+ "alert-danger")
+ return population_page
+ population = with_db_connection(
+ lambda conn: population_by_id(conn, _population_id))
if not bool(population):
flash("Invalid grouping/population!",
"alert-error error-select-population")
- return species_page
+ return population_page
- return render_template("samples/upload-samples.html",
- species=species,
- population=population)
+ return redirect(url_for("samples.upload_samples",
+ species_id=species_id,
+ population_id=_population_id),
+ code=307)
def read_samples_file(filepath, separator: str, firstlineheading: bool, **kwargs) -> Iterator[dict]:
"""Read the samples file."""
@@ -201,77 +231,83 @@ def build_sample_upload_job(# pylint: disable=[too-many-arguments]
f"--quotechar={quotechar}"
] + (["--firstlineheading"] if firstlineheading else [])
-@samples.route("/upload/samples", methods=["POST"])
-def upload_samples():
+@samples.route("/upload/species/<int:species_id>/populations/<int:population_id>/samples",
+ methods=["GET", "POST"])
+def upload_samples(species_id: int, population_id: int):#pylint: disable=[too-many-return-statements]
"""Upload the samples."""
- samples_uploads_page = redirect(url_for("samples.select_population"),
- code=307)
-
- with database_connection(app.config["SQL_URI"]) as conn:
- _speciesid = request.form.get("species_id")
- if is_integer_input(_speciesid):
- flash("You did not provide a valid species. Please select one to "
- "continue.",
- "alert-danger")
- return redirect(url_for("entry.upload_file"))
- species = species_by_id(conn, _speciesid)
- if not bool(species):
- flash("Invalid species!", "alert-error")
- return samples_uploads_page
-
- _population_id = request.form.get("inbredset_id")
- if not is_integer_input(_population_id):
- flash("You did not provide a valid population. Please select one "
- "to continue.",
- "alert-danger")
- return redirect("samples.select_species", code=307)
- population = with_db_connection(
- lambda conn: population_by_id(
- conn, int(_population_id)))
- if not bool(population):
- flash("Invalid grouping/population!", "alert-error")
- return samples_uploads_page
-
- try:
- samples_file = save_file(request.files["samples_file"],
- Path(app.config["UPLOAD_FOLDER"]))
- except AssertionError:
- flash("You need to provide a file with the samples data.",
- "alert-error")
- return samples_uploads_page
-
- firstlineheading = (request.form.get("first_line_heading") == "on")
-
- separator = request.form.get("separator")
- if separator == "other":
- separator = request.form.get("other_separator")
- if not bool(separator):
- flash("You need to provide a separator character.", "alert-error")
- return samples_uploads_page
-
- quotechar = (request.form.get("field_delimiter", '"') or '"')
-
- redisuri = app.config["REDIS_URL"]
- with Redis.from_url(redisuri, decode_responses=True) as rconn:
- the_job = jobs.launch_job(
- jobs.initialise_job(
- rconn,
- jobs.jobsnamespace(),
- str(uuid.uuid4()),
- build_sample_upload_job(
- species["SpeciesId"],
- population["InbredSetId"],
- samples_file,
- separator,
- firstlineheading,
- quotechar),
- "samples_upload",
- app.config["JOBS_TTL_SECONDS"],
- {"job_name": f"Samples Upload: {samples_file.name}"}),
- redisuri,
- f"{app.config['UPLOAD_FOLDER']}/job_errors")
- return redirect(url_for(
- "samples.upload_status", job_id=the_job["jobid"]))
+ samples_uploads_page = redirect(url_for("samples.upload_samples",
+ species_id=species_id,
+ population_id=population_id))
+ if not is_integer_input(species_id):
+ flash("You did not provide a valid species. Please select one to "
+ "continue.",
+ "alert-danger")
+ return redirect(url_for("samples.select_species"))
+ species = with_db_connection(lambda conn: species_by_id(conn, species_id))
+ if not bool(species):
+ flash("Species with given ID was not found.", "alert-danger")
+ return redirect(url_for("samples.select_species"))
+
+ if not is_integer_input(population_id):
+ flash("You did not provide a valid population. Please select one "
+ "to continue.",
+ "alert-danger")
+ return redirect(url_for("samples.select_population",
+ species_id=species_id),
+ code=307)
+ population = with_db_connection(
+ lambda conn: population_by_id(conn, int(population_id)))
+ if not bool(population):
+ flash("Invalid grouping/population!", "alert-error")
+ return redirect(url_for("samples.select_population",
+ species_id=species_id),
+ code=307)
+
+ if request.method == "GET" or request.files.get("samples_file") is None:
+ return render_template("samples/upload-samples.html",
+ species=species,
+ population=population)
+
+ try:
+ samples_file = save_file(request.files["samples_file"],
+ Path(app.config["UPLOAD_FOLDER"]))
+ except AssertionError:
+ flash("You need to provide a file with the samples data.",
+ "alert-error")
+ return samples_uploads_page
+
+ firstlineheading = (request.form.get("first_line_heading") == "on")
+
+ separator = request.form.get("separator", ",")
+ if separator == "other":
+ separator = request.form.get("other_separator", ",")
+ if not bool(separator):
+ flash("You need to provide a separator character.", "alert-error")
+ return samples_uploads_page
+
+ quotechar = (request.form.get("field_delimiter", '"') or '"')
+
+ redisuri = app.config["REDIS_URL"]
+ with Redis.from_url(redisuri, decode_responses=True) as rconn:
+ the_job = jobs.launch_job(
+ jobs.initialise_job(
+ rconn,
+ jobs.jobsnamespace(),
+ str(uuid.uuid4()),
+ build_sample_upload_job(
+ species["SpeciesId"],
+ population["InbredSetId"],
+ samples_file,
+ separator,
+ firstlineheading,
+ quotechar),
+ "samples_upload",
+ app.config["JOBS_TTL_SECONDS"],
+ {"job_name": f"Samples Upload: {samples_file.name}"}),
+ redisuri,
+ f"{app.config['UPLOAD_FOLDER']}/job_errors")
+ return redirect(url_for(
+ "samples.upload_status", job_id=the_job["jobid"]))
@samples.route("/upload/status/<uuid:job_id>", methods=["GET"])
def upload_status(job_id: uuid.UUID):
diff --git a/qc_app/templates/index.html b/qc_app/templates/index.html
index 588133a..6e98849 100644
--- a/qc_app/templates/index.html
+++ b/qc_app/templates/index.html
@@ -114,22 +114,9 @@
<p>This section gives you the opportunity to upload any missing samples</p>
</div>
-<form method="POST" action="{{url_for('samples.select_species')}}">
- <legend class="heading">upload samples</legend>
- <fieldset>
- <label for="select_species02">Species</label>
- <select id="select_species02" name="species_id" required="required">
- <option value="">Select species</option>
- {%for spec in species%}
- <option value="{{spec.SpeciesId}}">{{spec.MenuName}}</option>
- {%endfor%}
- </select>
- </fieldset>
-
- <fieldset>
- <input type="submit" value="submit" class="btn btn-main form-col-2" />
- </fieldset>
-</form>
+<a href={{url_for("samples.select_species")}}
+ title="Upload samples/cases/individuals for your data">
+ upload Samples/Cases</a>
{%endblock%}
diff --git a/qc_app/templates/samples/select-population.html b/qc_app/templates/samples/select-population.html
index 24decb4..4bb4cc1 100644
--- a/qc_app/templates/samples/select-population.html
+++ b/qc_app/templates/samples/select-population.html
@@ -15,7 +15,8 @@
<hr />
-<form method="POST" action="{{url_for('samples.select_population')}}">
+<form method="POST" action="{{url_for('samples.select_population',
+ species_id=species.SpeciesId)}}">
<legend class="heading">select grouping/population</legend>
{{flash_messages("error-select-population")}}
@@ -43,7 +44,8 @@
<p style="color:#FE3535; padding-left:20em; font-weight:bolder;">OR</p>
-<form method="POST" action="{{url_for('samples.create_population')}}">
+<form method="POST" action="{{url_for('samples.create_population',
+ species_id=species.SpeciesId)}}">
<legend class="heading">create new grouping/population</legend>
{{flash_messages("error-create-population")}}
diff --git a/qc_app/templates/samples/select-species.html b/qc_app/templates/samples/select-species.html
new file mode 100644
index 0000000..6aa4781
--- /dev/null
+++ b/qc_app/templates/samples/select-species.html
@@ -0,0 +1,29 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}Select Grouping/Population{%endblock%}
+
+{%block contents%}
+<h2 class="heading">upload samples/cases</h2>
+
+<p>We need to know what species your data belongs to.</p>
+
+{{flash_all_messages()}}
+
+<form method="POST" action="{{url_for('samples.select_species')}}">
+ <legend class="heading">upload samples</legend>
+ <fieldset>
+ <label for="select_species02">Species</label>
+ <select id="select_species02" name="species_id" required="required">
+ <option value="">Select species</option>
+ {%for spec in species%}
+ <option value="{{spec.SpeciesId}}">{{spec.MenuName}}</option>
+ {%endfor%}
+ </select>
+ </fieldset>
+
+ <fieldset>
+ <input type="submit" value="submit" class="btn btn-main form-col-2" />
+ </fieldset>
+</form>
+{%endblock%}
diff --git a/qc_app/templates/samples/upload-samples.html b/qc_app/templates/samples/upload-samples.html
index 5d1ec4c..d04df61 100644
--- a/qc_app/templates/samples/upload-samples.html
+++ b/qc_app/templates/samples/upload-samples.html
@@ -40,7 +40,9 @@
<form id="form-samples"
method="POST"
- action="{{url_for('samples.upload_samples')}}"
+ action="{{url_for('samples.upload_samples',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
enctype="multipart/form-data">
<legend class="heading">upload samples</legend>
<fieldset>
@@ -69,7 +71,7 @@
name="separator"
required="required"
class="form-col-2">
- <option value="">Select separator for your file</option>
+ <option value="">Select separator for your file: (default is comma)</option>
<option value="&#x0009;">TAB</option>
<option value="&#x0020;">Space</option>
<option value=",">Comma</option>