From 15624e038e0f368d2be4c9a76ace77da4d673fdd Mon Sep 17 00:00:00 2001 From: Peter Amstutz Date: Thu, 16 Jul 2020 14:21:40 -0400 Subject: Improve upload form layout. Arvados-DCO-1.1-Signed-off-by: Peter Amstutz --- bh20simplewebuploader/templates/footer.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'bh20simplewebuploader/templates') diff --git a/bh20simplewebuploader/templates/footer.html b/bh20simplewebuploader/templates/footer.html index 37a6b64..5a1f3c9 100644 --- a/bh20simplewebuploader/templates/footer.html +++ b/bh20simplewebuploader/templates/footer.html @@ -21,11 +21,14 @@
- +
+
+ +
-- cgit v1.2.3 From d34374f0e822edd1539ea5de6f8522f2b761de3f Mon Sep 17 00:00:00 2001 From: Peter Amstutz Date: Thu, 16 Jul 2020 14:48:22 -0400 Subject: Improve uploader reporting. Arvados-DCO-1.1-Signed-off-by: Peter Amstutz --- bh20sequploader/main.py | 22 ++++++++++------------ bh20simplewebuploader/main.py | 4 ++-- bh20simplewebuploader/templates/error.html | 2 +- bh20simplewebuploader/templates/success.html | 2 +- 4 files changed, 14 insertions(+), 16 deletions(-) (limited to 'bh20simplewebuploader/templates') diff --git a/bh20sequploader/main.py b/bh20sequploader/main.py index f744a8c..6049bf9 100644 --- a/bh20sequploader/main.py +++ b/bh20sequploader/main.py @@ -29,11 +29,10 @@ def qc_stuff(metadata, sequence_p1, sequence_p2, do_qc=True): try: log.debug("Checking metadata" if do_qc else "Skipping metadata check") if do_qc and not qc_metadata(metadata.name): - log.warning("Failed metadata qc") + log.warning("Failed metadata QC") failed = True except Exception as e: - log.debug(e) - print(e) + log.exception("Failed metadata QC") failed = True target = [] @@ -45,8 +44,7 @@ def qc_stuff(metadata, sequence_p1, sequence_p2, do_qc=True): target[0] = ("reads_1."+target[0][0][6:], target[0][1]) target[1] = ("reads_2."+target[1][0][6:], target[0][1]) except Exception as e: - log.debug(e) - print(e) + log.exception("Failed sequence QC") failed = True if failed: @@ -82,7 +80,7 @@ def main(): seqlabel = target[0][1] if args.validate: - print("Valid") + log.info("Valid") exit(0) col = arvados.collection.Collection(api_client=api) @@ -91,10 +89,10 @@ def main(): if args.sequence_p2: upload_sequence(col, target[1], args.sequence_p2) - print("Reading metadata") + log.info("Reading metadata") with col.open("metadata.yaml", "w") as f: r = args.metadata.read(65536) - print(r[0:20]) + log.info(r[0:20]) while r: f.write(r) r = args.metadata.read(65536) @@ -118,7 +116,7 @@ def main(): ["portable_data_hash", "=", col.portable_data_hash()]]).execute() if dup["items"]: # This exact collection has been uploaded before. - print("Duplicate of %s" % ([d["uuid"] for d in dup["items"]])) + log.error("Duplicate of %s" % ([d["uuid"] for d in dup["items"]])) exit(1) if args.trusted: @@ -131,9 +129,9 @@ def main(): (seqlabel, properties['upload_user'], properties['upload_ip']), properties=properties, ensure_unique_name=True) - print("Saved to %s" % col.manifest_locator()) - - print("Done") + log.info("Saved to %s" % col.manifest_locator()) + log.info("Done") + exit(0) if __name__ == "__main__": main() diff --git a/bh20simplewebuploader/main.py b/bh20simplewebuploader/main.py index 62b68d9..c814f30 100644 --- a/bh20simplewebuploader/main.py +++ b/bh20simplewebuploader/main.py @@ -445,12 +445,12 @@ def receive_files(): if result.returncode != 0: # It didn't work. Complain. - error_message="Uploader returned value {} and said:".format(result.returncode) + str(result.stderr.decode('utf-8')) + error_message="Uploader returned value {} and said:\n".format(result.returncode) + str(result.stderr.decode('utf-8')) print(error_message, file=sys.stderr) return (render_template('error.html', error_message=error_message), 403) else: # It worked. Say so. - return render_template('success.html', log=result.stdout.decode('utf-8', errors='replace')) + return render_template('success.html', log=result.stderr.decode('utf-8', errors='replace')) finally: shutil.rmtree(dest_dir) diff --git a/bh20simplewebuploader/templates/error.html b/bh20simplewebuploader/templates/error.html index b1d9402..fc08aed 100644 --- a/bh20simplewebuploader/templates/error.html +++ b/bh20simplewebuploader/templates/error.html @@ -15,7 +15,7 @@

- Click here to try again. + Click here to try again.


diff --git a/bh20simplewebuploader/templates/success.html b/bh20simplewebuploader/templates/success.html index 9f0987c..c2302fa 100644 --- a/bh20simplewebuploader/templates/success.html +++ b/bh20simplewebuploader/templates/success.html @@ -9,7 +9,7 @@

Upload Successful


- Your files have been uploaded. They should soon appear as output of the Public SARS-CoV-2 Sequence Resource. + Your files have been uploaded. You can track their QC status, once validated they will be part of the Public SARS-CoV-2 Sequence Resource.

The upload log was: -- cgit v1.2.3 From d49f6b5e11a41a51cb257bbafdcba410544f8486 Mon Sep 17 00:00:00 2001 From: Peter Amstutz Date: Thu, 16 Jul 2020 16:27:32 -0400 Subject: Add "validated" and "running workflows" tables to status Arvados-DCO-1.1-Signed-off-by: Peter Amstutz --- bh20seqanalyzer/main.py | 12 +-- bh20simplewebuploader/main.py | 112 +++++++++++++++++++------ bh20simplewebuploader/templates/status.html | 3 +- bh20simplewebuploader/templates/validated.html | 17 ++++ 4 files changed, 114 insertions(+), 30 deletions(-) create mode 100644 bh20simplewebuploader/templates/validated.html (limited to 'bh20simplewebuploader/templates') diff --git a/bh20seqanalyzer/main.py b/bh20seqanalyzer/main.py index f18a93a..b3a439d 100644 --- a/bh20seqanalyzer/main.py +++ b/bh20seqanalyzer/main.py @@ -240,24 +240,26 @@ class SeqAnalyzer: def move_fastq_to_fasta_results(self): projects = self.api.groups().list(filters=[['owner_uuid', '=', self.fastq_project], ["properties.moved_output", "!=", True]], - order="created_at desc",).execute() + order="created_at asc",).execute() for p in projects["items"]: wf = self.get_workflow_output_from_project(p["uuid"]) if not wf: continue logging.info("Moving completed fastq2fasta result %s back to uploader project", wf["output_uuid"]) - self.api.collections().update(uuid=wf["output_uuid"], - body={"owner_uuid": self.uploader_project}).execute() col = arvados.collection.Collection(wf["output_uuid"], api_client=self.api, keep_client=self.keepclient) with col.open("metadata.yaml") as md: metadata_content = ruamel.yaml.round_trip_load(md) + colprop = col.get_properties() + colprop["sequence_label"] = metadata_content["sample"]["sample_id"] + self.api.collections().update(uuid=wf["output_uuid"], + body={"owner_uuid": self.uploader_project, + "properties": colprop}).execute() + p["properties"]["moved_output"] = True - p["properties"]["sequence_label"] = metadata_content["sample"]["sample_id"] self.api.groups().update(uuid=p["uuid"], body={"properties": p["properties"]}).execute() - break def upload_schema(self): diff --git a/bh20simplewebuploader/main.py b/bh20simplewebuploader/main.py index c814f30..7dd07fe 100644 --- a/bh20simplewebuploader/main.py +++ b/bh20simplewebuploader/main.py @@ -144,6 +144,28 @@ def generate_form(components, options): optional = False is_list = False + # It may have documentation + docstring = field.get('doc', None) + + # See if it has a more info/what goes here URL + predicate = field.get('jsonldPredicate', {}) + # Predicate may be a URL, a dict with a URL in _id, maybe a + # dict with a URL in _type, or a dict with _id and _type but no + # URLs anywhere. Some of these may not technically be allowed + # by the format, but if they occur, we might as well try to + # handle them. + if isinstance(predicate, str): + if is_iri(predicate): + ref_iri = predicate + else: + # Assume it's a dict. Look at the fields we know about. + for field in ['_id', 'type']: + field_value = predicate.get(field, None) + if isinstance(field_value, str) and is_iri(field_value) and ref_iri is None: + # Take the first URL-looking thing we find + ref_iri = field_value + break + if isinstance(field_type, MutableSequence): if field_type[0] == "null" and len(field_type) == 2: optional = True @@ -152,28 +174,6 @@ def generate_form(components, options): raise Exception("Can't handle it") if isinstance(field_type, MutableMapping): - # It may have documentation - docstring = field_type.get('doc', None) - - # See if it has a more info/what goes here URL - predicate = field_type.get('jsonldPredicate', {}) - # Predicate may be a URL, a dict with a URL in _id, maybe a - # dict with a URL in _type, or a dict with _id and _type but no - # URLs anywhere. Some of these may not technically be allowed - # by the format, but if they occur, we might as well try to - # handle them. - if isinstance(predicate, str): - if is_iri(predicate): - ref_iri = predicate - else: - # Assume it's a dict. Look at the fields we know about. - for field in ['_id', 'type']: - field_value = predicate.get(field, None) - if isinstance(field_value, str) and is_iri(field_value) and ref_iri is None: - # Take the first URL-looking thing we find - ref_iri = field_value - break - if field_type["type"] == "array": # Now replace the field type with the actual type string is_list = True @@ -525,6 +525,54 @@ def rejected_table(output, items): """) +def workflows_table(output, items): + output.write( +""" + + + + + + + +""") + for r in items: + output.write("") + try: + sid = r["mounts"]["/var/lib/cwl/cwl.input.json"]["content"]["sample_id"] + output.write("" % Markup.escape(r["name"])) + output.write("" % Markup.escape(sid)) + output.write("" % Markup.escape(r["created_at"])) + output.write("" % (r["uuid"], r["uuid"])) + except: + pass + output.write("") + output.write( +""" +
NameSample idStartedContainer request
%s%s%s%s
+""") + +def validated_table(output, items): + output.write( +""" + + + + + +""") + for r in items: + try: + output.write("") + output.write("" % (r["uuid"], r["uuid"])) + output.write("" % Markup.escape(r["properties"].get("sequence_label"))) + output.write("") + except: + pass + output.write( +""" +
CollectionSequence label
%s%s
+""") @app.route('/status') def status_page(): @@ -545,22 +593,39 @@ def status_page(): prop["uuid"] = p["uuid"] status[prop["status"]] = status.get(prop["status"], 0) + 1 + workflows = arvados.util.list_all(api.container_requests().list, + filters=[["name", "in", ["fastq2fasta.cwl"]], ["state", "=", "Committed"]], + order="created_at asc") + output = io.StringIO() validated = api.collections().list(filters=[["owner_uuid", "=", VALIDATED_PROJECT]], limit=1).execute() status["passed"] = validated["items_available"] - for s in (("passed", "/download"), ("pending", "#pending"), ("rejected", "#rejected")): + for s in (("passed", "/validated"), ("pending", "#pending"), ("rejected", "#rejected")): output.write("

%s sequences QC %s

" % (s[1], status.get(s[0], 0), s[0])) + output.write("

%s analysis workflows running

" % ('#workflows', len(workflows))) + output.write("

Pending

") pending_table(output, out) output.write("

Rejected

") rejected_table(output, out) + output.write("

Running Workflows

") + workflows_table(output, workflows) + return render_template('status.html', table=Markup(output.getvalue()), menu='STATUS') +@app.route('/validated') +def validated_page(): + api = arvados.api(host=ARVADOS_API, token=ANONYMOUS_TOKEN, insecure=True) + output = io.StringIO() + validated = arvados.util.list_all(api.collections().list, filters=[["owner_uuid", "=", VALIDATED_PROJECT]]) + validated_table(output, validated) + return render_template('validated.html', table=Markup(output.getvalue()), menu='STATUS') + @app.route('/demo') def demo_page(): return render_template('demo.html',menu='DEMO') @@ -585,7 +650,6 @@ def map_page(): return render_template('map.html',menu='DEMO') - ## Dynamic API functions starting here ## This is quick and dirty for now, just to get something out and demonstrate the queries ## Feel free to rename the functions/endpoints, feel free to process result so we get nicer JSON diff --git a/bh20simplewebuploader/templates/status.html b/bh20simplewebuploader/templates/status.html index a1cf28f..e89437e 100644 --- a/bh20simplewebuploader/templates/status.html +++ b/bh20simplewebuploader/templates/status.html @@ -7,7 +7,8 @@

Sequence upload processing status

-
+
+ {{ table }}
diff --git a/bh20simplewebuploader/templates/validated.html b/bh20simplewebuploader/templates/validated.html new file mode 100644 index 0000000..cee94bd --- /dev/null +++ b/bh20simplewebuploader/templates/validated.html @@ -0,0 +1,17 @@ + + + {% include 'header.html' %} + + {% include 'banner.html' %} + {% include 'menu.html' %} + +

Validated sequences

+ +
+ {{ table }} +
+ +{% include 'footer.html' %} + + + -- cgit v1.2.3