diff options
| author | Frederick Muriuki Muriithi | 2025-12-16 13:30:46 -0600 |
|---|---|---|
| committer | Frederick Muriuki Muriithi | 2025-12-16 13:32:36 -0600 |
| commit | 98993dc0be542b377e62c97031f29f63e83f7ed4 (patch) | |
| tree | 4ee9cb7a6e467b65f4e7d001fd40cc821a972466 | |
| parent | e94474760d20a145a9242680ca908579f2729efc (diff) | |
| download | gn-uploader-98993dc0be542b377e62c97031f29f63e83f7ed4.tar.gz | |
Add "Streamlined UI" templates for phenotype upload sub-features.
| -rw-r--r-- | uploader/background_jobs.py | 17 | ||||
| -rw-r--r-- | uploader/phenotypes/views.py | 10 | ||||
| -rw-r--r-- | uploader/templates/background-jobs/sui-default-success-page.html | 17 | ||||
| -rw-r--r-- | uploader/templates/jobs/sui-job-error.html | 17 | ||||
| -rw-r--r-- | uploader/templates/jobs/sui-job-not-found.html | 11 | ||||
| -rw-r--r-- | uploader/templates/jobs/sui-job-status.html | 24 | ||||
| -rw-r--r-- | uploader/templates/phenotypes/sui-job-status.html | 140 | ||||
| -rw-r--r-- | uploader/templates/phenotypes/sui-load-phenotypes-success.html | 26 | ||||
| -rw-r--r-- | uploader/templates/phenotypes/sui-review-job-data.html | 121 |
9 files changed, 374 insertions, 9 deletions
diff --git a/uploader/background_jobs.py b/uploader/background_jobs.py index d33c498..a9c0345 100644 --- a/uploader/background_jobs.py +++ b/uploader/background_jobs.py @@ -5,7 +5,7 @@ from typing import Callable from functools import partial from flask import ( - url_for, + request, redirect, Response, Blueprint, @@ -16,6 +16,7 @@ from gn_libs import jobs from gn_libs import sqlite3 from gn_libs.jobs.jobs import JobNotFound +from uploader.flask_extensions import url_for from uploader.authorisation import require_login background_jobs_bp = Blueprint("background-jobs", __name__) @@ -76,7 +77,9 @@ def handler(job: dict, handler_type: str) -> HandlerType: ).get(handler_type) if bool(_handler): return _handler(job) - return render_template("background-jobs/default-success-page.html", job=job) + _sui = "sui-" if request.args.get("streamlined_ui") else "" + return render_template(f"background-jobs/{_sui}default-success-page.html", + job=job) error_handler = partial(handler, handler_type="error") @@ -87,6 +90,7 @@ success_handler = partial(handler, handler_type="success") @require_login def job_status(job_id: uuid.UUID): """View the job status.""" + _sui = "sui-" if request.args.get("streamlined_ui") else "" with sqlite3.connection(app.config["ASYNCHRONOUS_JOBS_SQLITE_DB"]) as conn: try: job = jobs.job(conn, job_id, fulldetails=True) @@ -99,10 +103,10 @@ def job_status(job_id: uuid.UUID): if status == "completed": return success_handler(job) - return render_template("jobs/job-status.html", job=job) + return render_template(f"jobs/{_sui}job-status.html", job=job) except JobNotFound as _jnf: return render_template( - "jobs/job-not-found.html", + f"jobs/{_sui}job-not-found.html", job_id=job_id) @@ -110,9 +114,10 @@ def job_status(job_id: uuid.UUID): @require_login def job_error(job_id: uuid.UUID): """Handle job errors in a generic manner.""" + _sui = "sui-" if request.args.get("streamlined_ui") else "" with sqlite3.connection(app.config["ASYNCHRONOUS_JOBS_SQLITE_DB"]) as conn: try: job = jobs.job(conn, job_id, fulldetails=True) - return render_template("jobs/job-error.html", job=job) + return render_template(f"jobs/{_sui}job-error.html", job=job) except JobNotFound as _jnf: - return render_template("jobs/job-not-found.html", job_id=job_id) + return render_template(f"jobs/{_sui}job-not-found.html", job_id=job_id) diff --git a/uploader/phenotypes/views.py b/uploader/phenotypes/views.py index c4fd7fd..364fc79 100644 --- a/uploader/phenotypes/views.py +++ b/uploader/phenotypes/views.py @@ -513,7 +513,9 @@ def job_status( job = jobs.job(rconn, jobs.jobsnamespace(), str(job_id)) except jobs.JobNotFound as _jnf: job = None - return render_template("phenotypes/job-status.html", + + _sui = "sui-" if bool(request.args.get("streamlined_ui")) else "" + return render_template(f"phenotypes/{_sui}job-status.html", species=species, population=population, dataset=dataset, @@ -593,7 +595,8 @@ def review_job_data( for filetype,meta in metadata.items() } _job_metadata = json.loads(job["job-metadata"]) - return render_template("phenotypes/review-job-data.html", + _sui = "sui-" if bool(request.args.get("streamlined_ui")) else "" + return render_template(f"phenotypes/{_sui}review-job-data.html", species=species, population=population, dataset=dataset, @@ -983,7 +986,8 @@ def load_data_success( _publication["Authors"], (_publication["Title"] or "")) if item != "") - return render_template("phenotypes/load-phenotypes-success.html", + _sui="sui-" if request.args.get("streamlined_ui") else "" + return render_template(f"phenotypes/{_sui}load-phenotypes-success.html", species=species, population=population, dataset=dataset, diff --git a/uploader/templates/background-jobs/sui-default-success-page.html b/uploader/templates/background-jobs/sui-default-success-page.html new file mode 100644 index 0000000..5732456 --- /dev/null +++ b/uploader/templates/background-jobs/sui-default-success-page.html @@ -0,0 +1,17 @@ +{%extends "phenotypes/base.html"%} +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}Background Jobs: Success{%endblock%} + +{%block pagetitle%}Background Jobs: Success{%endblock%} + +{%block contents%} +{{flash_all_messages()}} + +<div class="row"> + <p>Job <strong>{{job.job_id}}</strong>, + {%if job.get("metadata", {}).get("job-type")%} + of type '<em>{{job.metadata["job-type"]}}</em> + {%endif%}' completed successfully.</p> +</div> +{%endblock%} diff --git a/uploader/templates/jobs/sui-job-error.html b/uploader/templates/jobs/sui-job-error.html new file mode 100644 index 0000000..1a839a6 --- /dev/null +++ b/uploader/templates/jobs/sui-job-error.html @@ -0,0 +1,17 @@ +{%extends "sui-base.html"%} + +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}Background Jobs: Error{%endblock%} + +{%block pagetitle%}Background Jobs: Error{%endblock%} + +{%block contents%} + +<h1>Background Jobs: Error</h1> +<p>Job <strong>{{job["job_id"]}}</strong> failed!</p> +<p>The error details are in the "STDERR" section below.</p> + +<h2>STDERR</h2> +<pre>{{job["stderr"]}}</pre> +{%endblock%} diff --git a/uploader/templates/jobs/sui-job-not-found.html b/uploader/templates/jobs/sui-job-not-found.html new file mode 100644 index 0000000..96c8586 --- /dev/null +++ b/uploader/templates/jobs/sui-job-not-found.html @@ -0,0 +1,11 @@ +{%extends "sui-base.html"%} + +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}Background Jobs{%endblock%} + +{%block pagetitle%}Background Jobs{%endblock%} + +{%block contents%} +<p>Could not find job with ID: {{job_id}}</p> +{%endblock%} diff --git a/uploader/templates/jobs/sui-job-status.html b/uploader/templates/jobs/sui-job-status.html new file mode 100644 index 0000000..fc5e532 --- /dev/null +++ b/uploader/templates/jobs/sui-job-status.html @@ -0,0 +1,24 @@ +{%extends "sui-base.html"%} + +{%from "flash_messages.html" import flash_all_messages%} + +{%block extrameta%} +<meta http-equiv="refresh" content="5" /> +{%endblock%} + +{%block title%}Background Jobs{%endblock%} + +{%block pagetitle%}Background Jobs{%endblock%} + +{%block contents%} + +<p>Status: {{job["metadata"]["status"]}}</p> +<p>Job Type: {{job["metadata"]["job-type"]}}</p> + +<h2>STDOUT</h2> +<pre>{{job["stdout"]}}</pre> + +<h2>STDERR</h2> +<pre>{{job["stderr"]}}</pre> + +{%endblock%} diff --git a/uploader/templates/phenotypes/sui-job-status.html b/uploader/templates/phenotypes/sui-job-status.html new file mode 100644 index 0000000..bca87d5 --- /dev/null +++ b/uploader/templates/phenotypes/sui-job-status.html @@ -0,0 +1,140 @@ +{%extends "phenotypes/sui-base.html"%} +{%from "cli-output.html" import cli_output%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "macro-table-pagination.html" import table_pagination%} + +{%block extrameta%} +{%if job and job.status not in ("success", "completed:success", "error", "completed:error")%} +<meta http-equiv="refresh" content="5" /> +{%endif%} +{%endblock%} + +{%block title%}Phenotypes{%endblock%} + +{%block pagetitle%}Phenotypes{%endblock%} + +{%block contents%} + +{%if job%} +<div class="row"> + <h2 class="heading">{{dataset.FullName}} ({{dataset.Name}})</h2> + <h3 class="subheading">upload progress</h3> +</div> +<div class="row" style="overflow:scroll;"> + <p><strong>Process Status:</strong> {{job.status}}</p> + {%if metadata%} + <table class="table table-responsive"> + <thead> + <tr> + <th>File</th> + <th>Status</th> + <th>Lines Processed</th> + <th>Total Errors</th> + </tr> + </thead> + + <tbody> + {%for file,meta in metadata.items()%} + <tr> + <td>{{file}}</td> + <td>{{meta.status}}</td> + <td>{{meta.linecount}}</td> + <td>{{meta["total-errors"]}}</td> + </tr> + {%endfor%} + </tbody> + </table> + {%endif%} +</div> + +<div class="row"> + {%if job.status in ("completed:success", "success")%} + <p> + {%if errors | length == 0%} + <a href="{{url_for('species.populations.phenotypes.review_job_data', + species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id, + job_id=job_id)}}" + class="btn btn-primary" + title="Continue to process data">Continue</a> + {%else%} + <span class="text-muted" + disabled="disabled" + style="border: solid 2px;border-radius: 5px;padding: 0.3em;"> + Cannot continue due to errors. Please fix the errors first. + </span> + {%endif%} + </p> + {%endif%} +</div> + +<h3 class="subheading">upload errors</h3> +<div class="row" style="max-height: 20em; overflow: scroll;"> + {%if errors | length == 0 %} + <p class="text-info"> + <span class="glyphicon glyphicon-info-sign"></span> + No errors found so far + </p> + {%else%} + <table class="table table-responsive"> + <thead style="position: sticky; top: 0; background: white;"> + <tr> + <th>File</th> + <th>Row</th> + <th>Column</th> + <th>Value</th> + <th>Message</th> + </tr> + </thead> + + <tbody style="font-size: 0.9em;"> + {%for error in errors%} + <tr> + <td>{{error.filename}}</td> + <td>{{error.rowtitle}}</td> + <td>{{error.coltitle}}</td> + <td>{%if error.cellvalue is not none and error.cellvalue | length > 25%} + {{error.cellvalue[0:24]}}… + {%else%} + {{error.cellvalue}} + {%endif%} + </td> + <td> + {%if error.message | length > 250 %} + {{error.message[0:249]}}… + {%else%} + {{error.message}} + {%endif%} + </td> + </tr> + {%endfor%} + </tbody> + </table> + {%endif%} +</div> + +<div class="row"> + {{cli_output(job, "stdout")}} +</div> + +<div class="row"> + {{cli_output(job, "stderr")}} +</div> + +{%else%} +<div class="row"> + <h3 class="text-danger">No Such Job</h3> + <p>Could not find a job with the ID: {{job_id}}</p> + <p> + Please go back to + <a href="{{url_for('species.populations.phenotypes.view_dataset', + species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id)}}" + title="'{{dataset.Name}}' dataset page"> + the '{{dataset.Name}}' dataset page</a> + to upload new phenotypes or edit existing ones.</p> +</div> +{%endif%} +{%endblock%} diff --git a/uploader/templates/phenotypes/sui-load-phenotypes-success.html b/uploader/templates/phenotypes/sui-load-phenotypes-success.html new file mode 100644 index 0000000..dff0682 --- /dev/null +++ b/uploader/templates/phenotypes/sui-load-phenotypes-success.html @@ -0,0 +1,26 @@ +{%extends "phenotypes/sui-base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "macro-table-pagination.html" import table_pagination%} + +{%block title%}Phenotypes{%endblock%} + +{%block pagetitle%}Phenotypes{%endblock%} + +{%block contents%} +{{super()}} + +<div class="row"> + <p>You have successfully loaded + <!-- maybe indicate the number of phenotypes here? -->your + new phenotypes into the database.</p> + <!-- TODO: Maybe notify user that they have sole access. --> + <!-- TODO: Maybe provide a link to go to GeneNetwork to view the data. --> + <p>View your data + <a href="{{search_page_uri}}" + target="_blank">on GeneNetwork2</a>. + You might need to login to GeneNetwork2 to view specific traits.</p> +</div> +{%endblock%} + + +{%block more_javascript%}{%endblock%} diff --git a/uploader/templates/phenotypes/sui-review-job-data.html b/uploader/templates/phenotypes/sui-review-job-data.html new file mode 100644 index 0000000..ea4183d --- /dev/null +++ b/uploader/templates/phenotypes/sui-review-job-data.html @@ -0,0 +1,121 @@ +{%extends "phenotypes/sui-base.html"%} +{%from "cli-output.html" import cli_output%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "macro-table-pagination.html" import table_pagination%} +{%from "phenotypes/macro-display-pheno-dataset-card.html" import display_pheno_dataset_card%} + +{%block extrameta%} +{%if not job%} +<meta http-equiv="refresh" + content="20; url={{url_for('species.populations.phenotypes.view_dataset', species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id)}}" /> +{%endif%} +{%endblock%} + +{%block title%}Phenotypes{%endblock%} + +{%block pagetitle%}Phenotypes{%endblock%} + +{%block lvl4_breadcrumbs%} +<li {%if activelink=="add-phenotypes"%} + class="breadcrumb-item active" + {%else%} + class="breadcrumb-item" + {%endif%}> + <a href="{{url_for('species.populations.phenotypes.add_phenotypes', + species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id)}}">View Datasets</a> +</li> +{%endblock%} + +{%block contents%} + +{%if job%} +<div class="row"> + <h3 class="heading">Data Review</h3> + <p class="text-info"><strong> + The data has <em>NOT</em> been added/saved yet. Review the details below + and click "Continue" to save the data.</strong></p> + <p>The “<strong>{{dataset.FullName}}</strong>” dataset from the + “<strong>{{population.FullName}}</strong>” population of the + species “<strong>{{species.SpeciesName}} ({{species.FullName}})</strong>” + will be updated as follows:</p> + + <ul> + {%if publication%} + <li>All {{summary.get("pheno", {}).get("total-data-rows", "0")}} phenotypes + are linked to the following publication: + <ul> + <li><strong>Publication Title:</strong> + {{publication.Title or "—"}}</li> + <li><strong>Author(s):</strong> + {{publication.Authors or "—"}}</li> + </ul> + </li> + {%endif%} + {%for ftype in ("phenocovar", "pheno", "phenose", "phenonum")%} + {%if summary.get(ftype, False)%} + <li>A total of {{summary[ftype]["number-of-files"]}} files will be processed + adding {%if ftype == "phenocovar"%}(possibly){%endif%} + {{summary[ftype]["total-data-rows"]}} new + {%if ftype == "phenocovar"%} + phenotypes + {%else%} + {{summary[ftype]["description"]}} rows + {%endif%} + to the database. + </li> + {%endif%} + {%endfor%} + </ul> + + <form id="frm-review-phenotype-data" + method="POST" + action="{{url_for('species.populations.phenotypes.load_data_to_database', + species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id)}}"> + <input type="hidden" name="data-qc-job-id" value="{{job.jobid}}" /> + <input type="submit" + value="continue" + class="btn btn-primary" /> + </form> +</div> +{%else%} +<div class="row"> + <h4 class="subheading">Invalid Job</h3> + <p class="text-danger"> + Could not find a job with the ID: <strong>{{job_id}}.</p> + <p>You will be redirected in + <span id="countdown-element" class="text-info">20</span> second(s)</p> + <p class="text-muted"> + <small> + If you are not redirected, please + <a href="{{url_for( + 'species.populations.phenotypes.view_dataset', + species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id)}}">click here</a> to continue + </small> + </p> +</div> +{%endif%} +{%endblock%} + + +{%block javascript%} +<script type="text/javascript"> + $(document).ready(function() { + var countdown = 20; + var countdown_element = $("#countdown-element"); + if(countdown_element.length === 1) { + intv = window.setInterval(function() { + countdown = countdown - 1; + countdown_element.html(countdown); + }, 1000); + } + }); +</script> +{%endblock%} |
