diff options
author | Frederick Muriuki Muriithi | 2024-06-12 12:48:56 -0500 |
---|---|---|
committer | Frederick Muriuki Muriithi | 2024-06-12 12:48:56 -0500 |
commit | 5e96d27f3d96c84fc5a15d7040843b379b701d20 (patch) | |
tree | ad283df6ef65ed66f800d79446f04b19c2d08768 /qc_app | |
parent | 3abf09f5d5a0ab7fb3b1ca2be981c364ef68a8cb (diff) | |
download | gn-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.py | 240 | ||||
-rw-r--r-- | qc_app/templates/index.html | 19 | ||||
-rw-r--r-- | qc_app/templates/samples/select-population.html | 6 | ||||
-rw-r--r-- | qc_app/templates/samples/select-species.html | 29 | ||||
-rw-r--r-- | qc_app/templates/samples/upload-samples.html | 6 |
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="	">TAB</option> <option value=" ">Space</option> <option value=",">Comma</option> |