From 6326357250711650bc8b4c0b90f8c0b94d72c39e Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Fri, 29 Mar 2024 13:14:14 +0300 Subject: Add UI to select/create tissue. --- qc_app/upload/rqtl2.py | 104 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 3 deletions(-) (limited to 'qc_app/upload') diff --git a/qc_app/upload/rqtl2.py b/qc_app/upload/rqtl2.py index c05f675..7a53756 100644 --- a/qc_app/upload/rqtl2.py +++ b/qc_app/upload/rqtl2.py @@ -28,9 +28,9 @@ from qc_app.files import save_file, fullpath from qc_app.dbinsert import species as all_species from qc_app.db_utils import with_db_connection, database_connection -from qc_app.db.tissues import all_tissues, tissue_by_id from qc_app.db.platforms import platform_by_id, platforms_by_species from qc_app.db.averaging import averaging_methods, averaging_method_by_id +from qc_app.db.tissues import all_tissues, tissue_by_id, create_new_tissue from qc_app.db import ( species_by_id, save_population, @@ -65,6 +65,7 @@ def select_species(): flash("Invalid species or no species selected!", "alert-error error-rqtl2") return redirect(url_for("upload.rqtl2.select_species")) + @rqtl2.route("/upload/species//select-population", methods=["GET", "POST"]) def select_population(species_id: int): @@ -93,6 +94,7 @@ def select_population(species_id: int): species_id=species["SpeciesId"], population_id=population["InbredSetId"])) + @rqtl2.route("/upload/species//create-population", methods=["POST"]) def create_population(species_id: int): @@ -129,9 +131,11 @@ def create_population(species_id: int): pgsrc="create-population"), code=307) + class __RequestError__(Exception): #pylint: disable=[invalid-name] """Internal class to avoid pylint's `too-many-return-statements` error.""" + @rqtl2.route(("/upload/species//population/" "/rqtl2-bundle"), methods=["GET", "POST"]) @@ -197,6 +201,7 @@ def upload_rqtl2_bundle(species_id: int, population_id: int): return redirect(url_for( "upload.rqtl2.rqtl2_bundle_qc_status", jobid=jobid)) + @rqtl2.route("/upload/species/rqtl2-bundle/qc-status/", methods=["GET", "POST"]) def rqtl2_bundle_qc_status(jobid: UUID): @@ -255,15 +260,18 @@ def rqtl2_bundle_qc_status(jobid: UUID): except jobs.JobNotFound: return render_template("rqtl2/no-such-job.html", jobid=jobid) + def form_errors(formargs, *errorcheckers) -> Iterable[tuple[str, Response]]: """Retrieve all errors from the form inputs""" return (checker(formargs) for checker in errorcheckers) + def redirect_on_error(flaskroute, **kwargs): """Utility to redirect on error""" return redirect(url_for(flaskroute, **kwargs, pgsrc="error"), code=(307 if request.method == "POST" else 302)) + def check_species(conn: mdb.Connection, formargs: dict) -> Optional[ tuple[str, Response]]: """ @@ -280,6 +288,7 @@ def check_species(conn: mdb.Connection, formargs: dict) -> Optional[ return None + def check_population(conn: mdb.Connection, formargs: dict, species_id) -> Optional[tuple[str, Response]]: @@ -299,6 +308,7 @@ def check_population(conn: mdb.Connection, return None + def check_r_qtl2_bundle(formargs: dict, species_id, population_id) -> Optional[tuple[str, Response]]: @@ -315,6 +325,7 @@ def check_r_qtl2_bundle(formargs: dict, return None + def check_geno_dataset(formargs: dict, species_id, population_id) -> Optional[tuple[str, Response]]: @@ -328,6 +339,7 @@ def check_geno_dataset(formargs: dict, return None + def check_probe_study(formargs: dict, species_id, population_id) -> Optional[tuple[str, Response]]: @@ -340,6 +352,7 @@ def check_probe_study(formargs: dict, return None + def check_probe_dataset(formargs: dict, species_id, population_id) -> Optional[tuple[str, Response]]: @@ -352,6 +365,7 @@ def check_probe_dataset(formargs: dict, return None + def with_errors(conn: mdb.Connection, endpointthunk: Callable, *checkerstrings): """Run 'endpointthunk' with error checking.""" formargs = {**dict(request.args), **dict(request.form)} @@ -378,6 +392,7 @@ def with_errors(conn: mdb.Connection, endpointthunk: Callable, *checkerstrings): return endpointthunk() + @rqtl2.route(("/upload/species//population/" "/rqtl2-bundle/select-geno-dataset"), methods=["POST"]) @@ -406,6 +421,7 @@ def select_geno_dataset(species_id: int, population_id: int): return with_errors( conn, __thunk__, "species", "population", "rqtl2_bundle_file", "geno_dataset") + @rqtl2.route(("/upload/species//population/" "/rqtl2-bundle/create-geno-dataset"), methods=["POST"]) @@ -457,6 +473,71 @@ def create_geno_dataset(species_id: int, population_id: int): return with_errors(conn, __thunk__, "species", "population", "rqtl2_bundle_file") + +@rqtl2.route(("/upload/species//population/" + "/rqtl2-bundle/select-tissue"), + methods=["POST"]) +def select_tissue(species_id: int, population_id: int): + """Select from existing tissues.""" + with database_connection(app.config["SQL_URI"]) as conn: + def __thunk__(): + if not bool(request.form.get("tissueid", "").strip()): + flash("Invalid tissue selection!", + "alert-error error-select-tissue error-rqtl2") + + return redirect(url_for("upload.rqtl2.select_dataset_info", + species_id=species_id, + population_id=population_id, + pgsrc="upload.rqtl2.select_geno_dataset"), + code=307) + + return with_errors( + conn, __thunk__, "species", "population", "rqtl2_bundle_file", "geno_dataset") + +@rqtl2.route(("/upload/species//population/" + "/rqtl2-bundle/create-tissue"), + methods=["POST"]) +def create_tissue(species_id: int, population_id: int): + """Add new tissue, organ or biological material to the system.""" + form = request.form + datasetinfopage = redirect( + url_for("upload.rqtl2.select_dataset_info", + species_id=species_id, + population_id=population_id, + pgsrc="upload.rqtl2.select_geno_dataset"), + code=307) + with database_connection(app.config["SQL_URI"]) as conn: + tissuename = form.get("tissuename", "").strip() + tissueshortname = form.get("tissueshortname", "").strip() + if not bool(tissuename): + flash("Organ/Tissue name MUST be provided.", + "alert-error error-create-tissue error-rqtl2") + return datasetinfopage + + if not bool(tissueshortname): + flash("Organ/Tissue short name MUST be provided.", + "alert-error error-create-tissue error-rqtl2") + return datasetinfopage + + try: + tissue = create_new_tissue(conn, tissuename, tissueshortname) + flash("Tissue created successfully!", "alert-success") + return render_template( + "rqtl2/create-tissue-success.html", + species=species_by_id(conn, species_id), + population=population_by_species_and_id( + conn, species_id, population_id), + rqtl2_bundle_file=request.form["rqtl2_bundle_file"], + geno_dataset=geno_dataset_by_id( + conn, + int(request.form["geno-dataset-id"])), + tissue=tissue) + except mdb.IntegrityError as _ierr: + flash("Tissue/Organ with that short name already exists!", + "alert-error error-create-tissue error-rqtl2") + return datasetinfopage + + @rqtl2.route(("/upload/species//population/" "/rqtl2-bundle/select-probeset-study"), methods=["POST"]) @@ -477,6 +558,7 @@ def select_probeset_study(species_id: int, population_id: int): "rqtl2_bundle_file", "geno_dataset", "probeset_study") + @rqtl2.route(("/upload/species//population/" "/rqtl2-bundle/select-probeset-dataset"), methods=["POST"]) @@ -498,6 +580,7 @@ def select_probeset_dataset(species_id: int, population_id: int): "rqtl2_bundle_file", "geno_dataset", "probeset_study", "probeset_dataset") + @rqtl2.route(("/upload/species//population/" "/rqtl2-bundle/create-probeset-study"), methods=["POST"]) @@ -540,6 +623,7 @@ def create_probeset_study(species_id: int, population_id: int): return with_errors(conn, __thunk__, "species", "population", "rqtl2_bundle_file", "geno_dataset") + @rqtl2.route(("/upload/species//population/" "/rqtl2-bundle/create-probeset-dataset"), methods=["POST"]) @@ -597,6 +681,7 @@ def create_probeset_dataset(species_id: int, population_id: int):#pylint: disabl "rqtl2_bundle_file", "geno_dataset", "probeset_study") + @rqtl2.route(("/upload/species//population/" "/rqtl2-bundle/dataset-info"), methods=["POST"]) @@ -625,6 +710,19 @@ def select_dataset_info(species_id: int, population_id: int): geno_dataset = geno_dataset_by_id(conn, int(form["geno-dataset-id"])) if "pheno" in cdata and not bool(form.get("probe-study-id")): + tissue = tissue_by_id(conn, form.get("tissueid", "").strip()) + if not bool(tissue): + return render_template( + "rqtl2/select-tissue.html", + species=species, + population=population, + rqtl2_bundle_file=thefile.name, + geno_dataset=geno_dataset, + studies=probeset_studies_by_species_and_population( + conn, species_id, population_id), + platforms=platforms_by_species(conn, species_id), + tissues=all_tissues(conn)) + return render_template( "rqtl2/select-probeset-study-id.html", species=species, @@ -634,7 +732,7 @@ def select_dataset_info(species_id: int, population_id: int): studies=probeset_studies_by_species_and_population( conn, species_id, population_id), platforms=platforms_by_species(conn, species_id), - tissues=all_tissues(conn)) + tissue=tissue) probeset_study = probeset_study_by_id( conn, int(form["probe-study-id"])) @@ -666,7 +764,6 @@ def select_dataset_info(species_id: int, population_id: int): "rqtl2_bundle_file") - @rqtl2.route(("/upload/species//population/" "/rqtl2-bundle/confirm-bundle-details"), methods=["POST"]) @@ -719,6 +816,7 @@ def confirm_bundle_details(species_id: int, population_id: int): "rqtl2_bundle_file", "geno_dataset", "probeset_study", "probeset_dataset") + @rqtl2.route("/status/") def rqtl2_processing_status(jobid: UUID): """Retrieve the status of the job processing the uploaded R/qtl2 bundle.""" -- cgit v1.2.3