Skip to content

render_report_html crashes with KeyError when processor output is missing expected keys #38

@Devguru-codes

Description

@Devguru-codes

Location

apps/backend/app/utils/report_template.py:1-3

Description

render_report_html directly accesses data["missing_parameters"], data["basic_report"], and data["extended_report"] without .get(). The processor returns missing_required_parameters (not missing_parameters) and asl_parameters as a dict (not list of tuples). This causes KeyError when generating PDFs.

Reproduction (on main branch)

Automated Results on main branch

# Test Status Detail
T1 Template renders with all keys present PASS
T2 Handles absent 'missing_parameters' FAIL KeyError: 'missing_parameters'
T3 Handles absent 'basic_report' FAIL KeyError: 'basic_report'
T4 Works with processor's actual output format FAIL KeyError: 'missing_parameters'

Summary: PASSED=1 FAILED=3

API Script on main

ATTEMPTING render_report_html(processor_output):
CRASHED — KeyError: 'missing_parameters'
>> VERDICT: Bug is PRESENT.

Code to reproduce this -

"""Bug 6 reproduction: report_template.py expects keys that may not exist in report data"""
import sys, os, json
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'apps', 'backend'))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'package', 'src'))

from app.utils.report_template import render_report_html

results = []

# T1: Template works with ALL expected keys present
try:
    data_full = {
        "asl_parameters": [("TR", "4500 ms"), ("TE", "12 ms")],
        "missing_parameters": ["LabelingDuration", "PostLabelingDelay"],
        "basic_report": "Basic ASL report text",
        "extended_report": "Extended ASL report text"
    }
    html = render_report_html(data_full)
    ok1 = "<html>" in html and "TR" in html
    results.append({"test": "T1: Template renders with all keys present", "pass": ok1, "detail": ""})
except Exception as e:
    results.append({"test": "T1: Template renders with all keys present", "pass": False, "detail": str(e)})

# T2: Template should NOT crash when `missing_parameters` key is absent
try:
    data_no_missing = {
        "asl_parameters": [("TR", "4500 ms")],
        "basic_report": "Basic report",
        "extended_report": "Extended report"
    }
    html = render_report_html(data_no_missing)
    results.append({"test": "T2: Template handles absent 'missing_parameters' gracefully",
                    "pass": True, "detail": ""})
except KeyError as e:
    results.append({"test": "T2: Template handles absent 'missing_parameters' gracefully",
                    "pass": False, "detail": f"KeyError: {e}"})

# T3: Template should NOT crash when `basic_report` key is absent
try:
    data_no_basic = {
        "asl_parameters": [("TR", "4500 ms")],
        "missing_parameters": []
    }
    html = render_report_html(data_no_basic)
    results.append({"test": "T3: Template handles absent 'basic_report' gracefully",
                    "pass": True, "detail": ""})
except KeyError as e:
    results.append({"test": "T3: Template handles absent 'basic_report' gracefully",
                    "pass": False, "detail": f"KeyError: {e}"})

# T4: Template should work with processor's actual output keys
#     Processor returns `missing_required_parameters` (not `missing_parameters`)
try:
    processor_output = {
        "asl_parameters": {"TR": "4500 ms", "TE": "12 ms"},  # dict not list of tuples
        "missing_required_parameters": {"LabelingDuration": "ms"},
        "basic_report": "Basic report text",
        "extended_report": "Extended report text"
    }
    html = render_report_html(processor_output)
    ok4 = "<html>" in html
    results.append({"test": "T4: Template works with processor's actual output format",
                    "pass": ok4, "detail": ""})
except Exception as e:
    results.append({"test": "T4: Template works with processor's actual output format",
                    "pass": False, "detail": f"{type(e).__name__}: {e}"})

# Print results
passed = sum(1 for r in results if r["pass"])
failed = sum(1 for r in results if not r["pass"])
print(f"PASSED={passed} FAILED={failed}")
for r in results:
    status = "PASS" if r["pass"] else "FAIL"
    detail = f" -- {r['detail']}" if r["detail"] else ""
    print(f"  [{status}] {r['test']}{detail}")

with open(os.path.join(os.path.dirname(__file__), 'results.json'), 'w') as f:
    json.dump({"passed": passed, "failed": failed, "results": results}, f, indent=2)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions