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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions