diff --git a/Pulumi.cape-cod-dev.yaml b/Pulumi.cape-cod-dev.yaml index ddbf0e4..eb5f117 100644 --- a/Pulumi.cape-cod-dev.yaml +++ b/Pulumi.cape-cod-dev.yaml @@ -1311,7 +1311,7 @@ config: - s3 - name: "cape-frontend" short_name: "cfe" - image: "ami-07fae65036e62d931" + image: "ami-08143e7b201031ca6" public_ip: False instance_type: "t3a.medium" subnet_types: diff --git a/assets/api/capi/capi-openapi-301.yaml.j2 b/assets/api/capi/capi-openapi-301.yaml.j2 index db4e25f..4aa2e07 100644 --- a/assets/api/capi/capi-openapi-301.yaml.j2 +++ b/assets/api/capi/capi-openapi-301.yaml.j2 @@ -915,10 +915,27 @@ paths: schema: type: object description: - A mapping of report name to the report HTML. - Empty when the sample has no reports. + A mapping of report name to an object with + the report HTML and the time it was + generated. Empty when the sample has no + reports. additionalProperties: - type: string + type: object + properties: + createdAt: + type: string + format: date-time + description: + RFC 3339 / ISO 8601 UTC time the + report was generated, parsable + by the JavaScript Date + constructor. + body: + type: string + description: The report HTML. + required: + - createdAt + - body "500": description: "Server Error - Unable to fetch reports." diff --git a/assets/api/capi/handlers/get_reports.py b/assets/api/capi/handlers/get_reports.py index a9974d2..6f831c1 100644 --- a/assets/api/capi/handlers/get_reports.py +++ b/assets/api/capi/handlers/get_reports.py @@ -4,18 +4,26 @@ bucket under `reports//.html` (e.g. the RABiTS report at `reports//rabits.html`). This handler lists those objects for a given sample and returns a mapping of report name (the object file name without the -`.html` suffix) to the report HTML, e.g.: +`.html` suffix) to an object holding the report HTML and the time it was +generated (the object's last-modified time), e.g.: - {"rabits": "...", "bactopia": "..."} + { + "rabits": {"createdAt": "2024-06-01T12:34:56Z", "body": "..."}, + "bactopia": {"createdAt": "2024-06-02T09:00:00Z", "body": "..."} + } -so the front end can render every available report (e.g. as an accordion) -without any further per-report requests. A sample with no reports (or an unknown -sample) returns an empty object. +so the front end can render every available report (e.g. as an accordion) and +sort/label them by generation time without any further per-report requests. A +sample with no reports (or an unknown sample) returns an empty object. + +`createdAt` is an RFC 3339 / ISO 8601 UTC timestamp, directly parsable by +`new Date(...)` in JavaScript/TypeScript. """ import json import logging import os +from datetime import timezone import boto3 from botocore.exceptions import ClientError @@ -30,6 +38,10 @@ REPORTS_PREFIX = "reports" REPORT_SUFFIX = ".html" +# keys of the per-report object returned for each report. +REPORT_CREATED_AT_KEY = "createdAt" +REPORT_BODY_KEY = "body" + # name of the env var (set by the private swimlane api wiring) holding the name # of the artifacts bucket that reports are stored in. REPORTS_BUCKET_ENV_VAR = "REPORTS_BUCKET" @@ -44,7 +56,9 @@ def get_sample_reports(bucket, sample_id): Returns: A mapping of report name (object file name without the `.html` suffix) - to the report HTML string. Empty when the sample has no reports. + to an object of the form `{"createdAt": , "body": }`, + where `createdAt` is the object's last-modified time. Empty when the + sample has no reports. """ s3_client = boto3.client("s3") prefix = f"{REPORTS_PREFIX}/{sample_id}/" @@ -65,7 +79,18 @@ def get_sample_reports(bucket, sample_id): .read() .decode("utf-8") ) - reports[name[: -len(REPORT_SUFFIX)]] = report_html + # s3 last-modified is a tz-aware utc datetime; emit rfc 3339 with a + # `Z` suffix so it parses directly in javascript/typescript. + created_at = ( + obj["LastModified"] + .astimezone(timezone.utc) + .isoformat() + .replace("+00:00", "Z") + ) + reports[name[: -len(REPORT_SUFFIX)]] = { + REPORT_CREATED_AT_KEY: created_at, + REPORT_BODY_KEY: report_html, + } return reports diff --git a/assets/etl/etl_caerbannog_results.py b/assets/etl/etl_caerbannog_results.py index 56260a5..84d9e2f 100644 --- a/assets/etl/etl_caerbannog_results.py +++ b/assets/etl/etl_caerbannog_results.py @@ -292,58 +292,195 @@ def stoplight_report_rows(stoplight_rows): # TEMPORARY (TODO(#363)) self-contained HTML report template. The two table -# blocks are duplicated from the bactopia report template -# (assets/report/bactopia-single-sample-analysis/template.html.j2, commit -# 3251ccd); the surrounding //