Webapp: evidence pack date range, US month labels, hardware gap rows

Evidence pack takes ?from=&to= (inclusive, Eastern time) with From/To
pickers and quick ranges; ZIP is named ENT-056_<site>_<from>_to_<to>.
Month keys show as 'September 2026'. Hardware gap rows on the to-do
use the shared one-line grid with chips for the missing pack or BIOS.
This commit is contained in:
cproudlock
2026-10-14 15:08:36 -04:00
parent a54df7ebd9
commit 2dbaaded44
4 changed files with 116 additions and 15 deletions

View File

@@ -694,6 +694,8 @@ def blancco_reports():
this_month=datetime.now(tz).strftime("%Y-%m"),
site=config.BLANCCO_SITE,
last_month=blancco_evidence.previous_month(),
pack_from=blancco_evidence.month_bounds(blancco_evidence.previous_month())[0].date().isoformat(),
pack_to=(blancco_evidence.month_bounds(blancco_evidence.previous_month())[1] - timedelta(days=1)).date().isoformat(),
todo=blancco_evidence.todo_counts(),
)
@@ -789,7 +791,27 @@ def blancco_cloud_status():
@app.route("/reports/evidence")
def blancco_evidence_pack():
"""CMMC evidence pack (ENT-056/158): one ZIP per site and month."""
"""CMMC evidence pack (ENT-056/158) for a site. Either ?from=&to= (dates,
YYYY-MM-DD, inclusive, Eastern time) or ?month=YYYY-MM (the monthly pack)."""
from datetime import date as _date
first, last = request.args.get("from"), request.args.get("to")
if first or last:
try:
first, last = _date.fromisoformat(first or ""), _date.fromisoformat(last or "")
except ValueError:
flash("Pick a start and an end date for the evidence pack.", "danger")
return redirect(url_for("blancco_reports"))
if last < first:
first, last = last, first
if (last - first).days > 400:
flash("An evidence pack covers at most about a year (400 days).", "danger")
return redirect(url_for("blancco_reports"))
out = tempfile.SpooledTemporaryFile(max_size=64 * 1024 * 1024)
count = blancco_evidence.build_evidence_zip(None, out, first=first, last=last)
out.seek(0)
audit("REPORT_EVIDENCE", f"{first.isoformat()}..{last.isoformat()} {count} reports")
return send_file(out, mimetype="application/zip", as_attachment=True,
download_name=blancco_evidence.evidence_range_name(first, last) + ".zip")
month = request.args.get("month") or blancco_evidence.previous_month()
if not re.fullmatch(r"20\d\d-(0[1-9]|1[0-2])", month):
abort(400)
@@ -1822,6 +1844,27 @@ def tojson_ordered(value):
return json.dumps(value, ensure_ascii=False)
@app.template_filter("short_date")
def short_date(value):
"""ISO timestamp -> '14 Oct' in the site's time zone; '' if unreadable."""
try:
dt = datetime.fromisoformat(str(value).replace("Z", "+00:00"))
except ValueError:
return ""
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.astimezone(blancco_evidence._tz()).strftime("%d %b")
@app.template_filter("month_label")
def month_label(value):
"""'2026-09' -> 'September 2026'; anything else unchanged."""
try:
return datetime.strptime(str(value), "%Y-%m").strftime("%B %Y")
except ValueError:
return value
# Report page: why an erasure failed, straight from the report.
app.jinja_env.globals["blancco_failure_reason"] = blancco_report.failure_reason