From 53ff8af0843942d83dff9fd5b95d1ae98e80fe27 Mon Sep 17 00:00:00 2001
From: Peter Amstutz
Date: Thu, 16 Jul 2020 11:48:35 -0400
Subject: Refactor analysis code into a class
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz
---
bh20simplewebuploader/main.py | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
(limited to 'bh20simplewebuploader/main.py')
diff --git a/bh20simplewebuploader/main.py b/bh20simplewebuploader/main.py
index 8089883..3173d60 100644
--- a/bh20simplewebuploader/main.py
+++ b/bh20simplewebuploader/main.py
@@ -479,10 +479,13 @@ def pending_table(output, items):
for r in items:
if r["status"] != "pending":
continue
- output.write("")
- output.write("%s | " % (r["uuid"], r["uuid"]))
- output.write("%s | " % Markup.escape(r["sequence_label"]))
- output.write("
")
+ try:
+ output.write("")
+ output.write("%s | " % (r["uuid"], r["uuid"]))
+ output.write("%s | " % Markup.escape(r.get("sequence_label")))
+ output.write("
")
+ except:
+ pass
output.write(
"""
@@ -497,13 +500,16 @@ def rejected_table(output, items):
Errors |
""")
for r in items:
- if r["status"] != "rejected":
- continue
- output.write("")
- output.write("%s | " % (r["uuid"], r["uuid"]))
- output.write("%s | " % Markup.escape(r["sequence_label"]))
- output.write("%s | " % Markup.escape("\n".join(r.get("errors", []))))
- output.write("
")
+ try:
+ if r["status"] != "rejected":
+ continue
+ output.write("")
+ output.write("%s | " % (r["uuid"], r["uuid"]))
+ output.write("%s | " % Markup.escape(r.get("sequence_label")))
+ output.write("%s | " % Markup.escape("\n".join(r.get("errors", []))))
+ output.write("
")
+ except:
+ pass
output.write(
"""
--
cgit v1.2.3
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/main.py | 86 ++++++++++++++++-------------
bh20simplewebuploader/static/main.css | 17 ++++--
bh20simplewebuploader/templates/footer.html | 5 +-
3 files changed, 64 insertions(+), 44 deletions(-)
(limited to 'bh20simplewebuploader/main.py')
diff --git a/bh20simplewebuploader/main.py b/bh20simplewebuploader/main.py
index 3173d60..62b68d9 100644
--- a/bh20simplewebuploader/main.py
+++ b/bh20simplewebuploader/main.py
@@ -8,7 +8,7 @@ import os
import sys
import re
import string
-import yaml
+import ruamel.yaml as yaml
import pkg_resources
from flask import Flask, request, redirect, send_file, send_from_directory, render_template, jsonify
import os.path
@@ -16,6 +16,9 @@ import requests
import io
import arvados
from markupsafe import Markup
+from schema_salad.sourceline import add_lc_filename
+from schema_salad.schema import shortname
+from typing import MutableSequence, MutableMapping
ARVADOS_API = 'lugli.arvadosapi.com'
ANONYMOUS_TOKEN = '5o42qdxpxp5cj15jqjf7vnxx5xduhm4ret703suuoa3ivfglfh'
@@ -47,6 +50,8 @@ def type_to_heading(type_name):
Turn a type name like "sampleSchema" from the metadata schema into a human-readable heading.
"""
+ type_name = shortname(type_name)
+
print(type_name,file=sys.stderr)
# Remove camel case
decamel = re.sub('([A-Z])', r' \1', type_name)
@@ -78,7 +83,7 @@ def is_iri(string):
return string.startswith('http')
-def generate_form(schema, options):
+def generate_form(components, options):
"""
Linearize the schema into a list of dicts.
@@ -101,9 +106,6 @@ def generate_form(schema, options):
IRI.
"""
- # Get the list of form components, one of which is the root
- components = schema.get('$graph', [])
-
# Find the root
root_name = None
# And also index components by type name
@@ -131,14 +133,25 @@ def generate_form(schema, options):
# First make a heading, if we aren't the very root of the form
yield {'heading': type_to_heading(type_name)}
- for field_name, field_type in by_name.get(type_name, {}).get('fields', {}).items():
+ for field in by_name.get(type_name, {}).get('fields', []):
+ field_name = shortname(field["name"])
+ field_type = field["type"]
# For each field
ref_iri = None
docstring = None
- if not isinstance(field_type, str):
- # If the type isn't a string
+ optional = False
+ is_list = False
+
+ if isinstance(field_type, MutableSequence):
+ if field_type[0] == "null" and len(field_type) == 2:
+ optional = True
+ field_type = field_type[1]
+ else:
+ raise Exception("Can't handle it")
+
+ if isinstance(field_type, MutableMapping):
# It may have documentation
docstring = field_type.get('doc', None)
@@ -161,25 +174,13 @@ def generate_form(schema, options):
ref_iri = field_value
break
-
- # Now overwrite the field type with the actual type string
- field_type = field_type.get('type', '')
-
- # Decide if the field is optional (type ends in ?)
- optional = False
- if field_type.endswith('?'):
- # It's optional
- optional = True
- # Drop the ?
- field_type = field_type[:-1]
-
- # Decide if the field is a list (type ends in [])
- is_list = False
- if field_type.endswith('[]'):
- # It's a list
- is_list = True
- # Reduce to the normal type
- field_type = field_type[:-2]
+ if field_type["type"] == "array":
+ # Now replace the field type with the actual type string
+ is_list = True
+ field_type = field_type.get('items', '')
+ else:
+ field_type = field_type.get('type', '')
+ pass
if field_type in by_name:
# This is a subrecord. We need to recurse
@@ -227,15 +228,24 @@ def generate_form(schema, options):
return list(walk_fields(root_name))
-# At startup, we need to load the metadata schema from the uploader module, so we can make a form for it
-if os.path.isfile("bh20sequploader/bh20seq-schema.yml"):
- METADATA_SCHEMA = yaml.safe_load(open("bh20sequploader/bh20seq-schema.yml","r").read())
- METADATA_OPTION_DEFINITIONS = yaml.safe_load(open("bh20sequploader/bh20seq-options.yml","r").read())
-else:
- METADATA_SCHEMA = yaml.safe_load(pkg_resources.resource_stream("bh20sequploader", "bh20seq-schema.yml"))
- METADATA_OPTION_DEFINITIONS = yaml.safe_load(pkg_resources.resource_stream("bh20sequploader", "bh20seq-options.yml"))
-# print(METADATA_SCHEMA,file=sys.stderr)
-FORM_ITEMS = generate_form(METADATA_SCHEMA, METADATA_OPTION_DEFINITIONS)
+import schema_salad.schema
+def load_schema_generate_form():
+ # At startup, we need to load the metadata schema from the uploader module, so we can make a form for it
+ if os.path.isfile("bh20sequploader/bh20seq-schema.yml"):
+ METADATA_SCHEMA = yaml.round_trip_load(open("bh20sequploader/bh20seq-schema.yml","r").read())
+ METADATA_OPTION_DEFINITIONS = yaml.safe_load(open("bh20sequploader/bh20seq-options.yml","r").read())
+ else:
+ METADATA_SCHEMA = yaml.round_trip_load(pkg_resources.resource_stream("bh20sequploader", "bh20seq-schema.yml"))
+ METADATA_OPTION_DEFINITIONS = yaml.safe_load(pkg_resources.resource_stream("bh20sequploader", "bh20seq-options.yml"))
+
+ METADATA_SCHEMA["name"] = "bh20seq-schema.yml"
+ add_lc_filename(METADATA_SCHEMA, "bh20seq-schema.yml")
+ metaschema_names, _metaschema_doc, metaschema_loader = schema_salad.schema.get_metaschema()
+ schema_doc, schema_metadata = metaschema_loader.resolve_ref(METADATA_SCHEMA, "")
+
+ return generate_form(schema_doc, METADATA_OPTION_DEFINITIONS)
+
+FORM_ITEMS = load_schema_generate_form()
@app.route('/')
def send_home():
@@ -543,10 +553,10 @@ def status_page():
for s in (("passed", "/download"), ("pending", "#pending"), ("rejected", "#rejected")):
output.write("%s sequences QC %s
" % (s[1], status.get(s[0], 0), s[0]))
- output.write("Pending
")
+ output.write("Pending
")
pending_table(output, out)
- output.write("Rejected
")
+ output.write("Rejected
")
rejected_table(output, out)
return render_template('status.html', table=Markup(output.getvalue()), menu='STATUS')
diff --git a/bh20simplewebuploader/static/main.css b/bh20simplewebuploader/static/main.css
index 47fb408..b28ee9c 100644
--- a/bh20simplewebuploader/static/main.css
+++ b/bh20simplewebuploader/static/main.css
@@ -178,7 +178,7 @@ span.dropt:hover {text-decoration: none; background: #ffffff; z-index: 6; }
.about {
display: grid;
- grid-template-columns: repeat(2, 1fr);
+ grid-template-columns: 1fr 1fr;
grid-auto-flow: row;
}
@@ -229,7 +229,7 @@ a {
#metadata_fill_form {
column-count: 4;
margin-top: 0.5em;
- column-width: 250px;
+ column-width: 15em;
}
.record, .record .field-group, .record .field-group .field {
@@ -238,6 +238,8 @@ a {
-webkit-column-break-inside: avoid; /* Chrome, Safari, Opera */
page-break-inside: avoid; /* Firefox */
break-inside: avoid;
+ display: block;
+ width: 90%;
}
.record {
@@ -258,6 +260,10 @@ a {
width: max-content;
}
+.control {
+ width: 100%;
+}
+
.filter-options {
width: 100%;
}
@@ -304,9 +310,10 @@ footer {
}
.sponsors img {
- width: 80%;
- display:block;
- margin:auto;
+ width: auto;
+ display: block;
+ margin: auto;
+ height: 4em;
}
.loader {
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/main.py')
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.