# 5. Questions, Answers & the Data Model

This section directly answers the question: **"How do we render a question, and where does the question text come from?"**

## 5.1 The headline fact: there is no question bank in this codebase

This package does not store, own, or look up questionnaire questions. There is no database, no question-ID → question-text mapping, no survey-definition file anywhere in the repository. **Every question's title text and its answer value arrive together, pre-rendered, as flat `{title, value}` pairs inside the JSON payload the host application sends in.** This package's job is purely to lay that text out on the page — not to know what the questions mean, score them, or validate them.

This matters operationally: if a question's wording is wrong in a PDF, the fix is almost never in this repository — it's in whatever upstream system assembled the `Profile`/`profileInfo` array before base64-encoding it and calling this library.

## 5.2 Where question/answer data lives, per pipeline

### Core "Medicus" lab report — no questions at all

This pipeline's domain model is biomarkers/panels/insights/doctor-notes/signatures (see [doc 4.A](04-report-pipelines.md#4a-core-medicus-lab-report)). Grepping the entire core renderer for "question" or "answer" returns nothing. If you need to document or debug question rendering, **you are in the wrong pipeline** — go to the wellbeing report instead.

### Wellbeing report — `data.Profile` (the actual Q&A) and `data.profileInfo` (metadata)

Two separate arrays, both flat `{title, value}` lists, both only meaningfully populated when `data.IsDoctor === true`:

```js
// testing-reports/mediclinic/doctor-data.js — a real fixture used by test.js
{
  "IsDoctor": true,
  "Profile": [
    { "title": "What is your gender?", "value": "Male" },
    { "title": "When is your birthday?", "value": "51 years" },
    { "title": "What is your height?", "value": "178 cm" },
    { "title": "Do you have any of the following conditions?", "value": "Asthma" },
    { "title": "Do you have a family history of any condition?",
      "value": "Diabetes: No one | High blood pressure: No one" },
    { "title": "Over the last 2 weeks, how often have you been bothered by feeling down, depressed, or hopeless?",
      "value": "More than half the days" },
    // ... a full PHQ-9 / GAD-7 style clinical questionnaire, plus lifestyle questions
  ]
}
```

- **`data.Profile`** — the real questionnaire content (PHQ‑9/GAD‑7-style clinical questions plus lifestyle questions). Rendered by `renderDoctorDetails(doctorData)` in `lib/wellbeing_report_generator.js`: each item becomes a two-column row — `.question-title` for `item.title`, `.details-value` for `item.value` — under a "Quiz Answers" header, injected into `.doctor-details` in the page DOM. **Neither the question title nor the answer value is HTML-escaped here** — see the security note in 5.4.
- **`data.profileInfo`** — patient/report metadata (LAB, Reference, Patient name, Report date, Date of birth, Weight, Height, Gender, BMI, Blood Pressure, Waist Circumference), *not* questionnaire answers. Split by array index: the first 4 entries go through `generateHeaderInfo()` (whose output is actually **dead** — its target `.report-info` is commented out of every brand's `header.html`), the rest go through `renderPatientTable()`, which *does* escape the label (not the value) and is injected live into `.patient-table`.
- Numeric wellbeing **scores** (as opposed to raw Q&A text) come from a different part of the payload — `data.wellbeing.calculation` / `data.wellbeing.partsScore` — pre-computed upstream; this package does not calculate PHQ‑9/GAD‑7 scores itself, it only displays whatever score value it's handed.

### `lib/big_integral_questionnaire.js` — a cautionary example, not a real data source

As covered in [doc 4.B.1](04-report-pipelines.md#4b1-libbig_integral_questionnairejs--read-this-before-assuming-its-a-real-feature), this module currently renders **100% hardcoded dummy data** (`getDummyData()` — 11 fixed rows, `result: 0` for all of them, a fake patient name). It is not wired to any real answer data in the payload today. Do not use it as a reference for "how question rendering should work" — use `renderDoctorDetails`/`data.Profile` instead.

### Corporate report and SanusX — no free-text Q&A

Neither of these two pipelines renders question/answer pairs in the `data.Profile` sense. The corporate report works with pre-aggregated `data.elements[]` (population-level summaries, chart values, gender breakdowns — see [doc 4.C](04-report-pipelines.md#4c-corporate-report)). SanusX works with pre-computed scores and flags (`highestAvatarScorePartId`, `isPredictiveElement` — see [doc 4.D](04-report-pipelines.md#4d-sanusx-report)). Both assume all interpretation/scoring already happened upstream.

### "SmartReport" biomarker notes — a different kind of "note," not a question

When a wellbeing report is merged with lab data (`data.SR`, see [doc 4.B](04-report-pipelines.md#4b-wellbeing-report--optional-merged-smartreport)), individual biomarkers can carry `doctorNote`/`biomarkerNote`/`insight` entries. These are clinician-authored free-text notes attached to a specific lab value — a different concept from a questionnaire answer — rendered via `renderReportItems`/`renderNoteV3` in `lib/template.js`, not via `renderDoctorDetails`.

## 5.3 Summary

- **`data.Profile`** (Wellbeing) — real questionnaire Q&A (title + value pairs). Rendered by `renderDoctorDetails` → `.doctor-details`. **Not** HTML-escaped.
- **`data.profileInfo`** (Wellbeing) — patient/report metadata, not Q&A. Rendered by `renderPatientTable` (live) / `generateHeaderInfo` (dead code). Label is escaped; value is not.
- **`data.wellbeing.calculation` / `.partsScore`** (Wellbeing) — pre-computed numeric scores. Rendered as score bars/gauges. N/A for escaping (numeric).
- **`big_integral_questionnaire.js` internal `getDummyData()`** (Wellbeing, Maison Sante only) — hardcoded demo rows, **not real answers**. Rendered by `renderTable`.
- **`data.SR.items[].biomarkerNote` / `.doctorNote` / `.insight`** (Wellbeing, SmartReport merge) — clinician notes on a lab value. Rendered by `renderReportItems`/`renderNoteV3` (`lib/template.js`). Escaping varies.
- **`data.elements[]`** (Corporate) — pre-aggregated population analytics. Rendered by `renderBarChart` and table builders.
- **`data.PartScoresHighestValues`, `data.insights.tips`** (SanusX) — pre-computed scores/flags. Rendered by the avatar/predictions/tips sections.

## 5.4 Security note: unescaped answer text

`renderDoctorDetails` (wellbeing `data.Profile`) injects `item.value` — and, in one further case, `item.title` — as raw HTML with no escaping. If any upstream system ever allows free-text answers containing HTML/script-like content to flow into this field unsanitized, that content will be injected verbatim into the rendered page before Puppeteer snapshots it to PDF. Because report generation happens server-side inside a headless Chrome instance (not a browser the end user controls), this is not a classic reflected-XSS-in-the-browser risk to the *end user*, but it is a real risk if payload data can come from a less-trusted source than the doctor/clinician — e.g. if patient-entered free text were ever passed straight through into `Profile[].value` without sanitization upstream. If you're auditing security boundaries, treat "who is allowed to populate `data.Profile`, and is it sanitized before it reaches this library" as an open question to raise with the host application team — this library does not sanitize it.
