Medical Report Generator

System Business Logic Documentation

📋 This document explains how the medical report generator works

Table of Contents

  1. How It Works
  2. Supported Test Types
  3. Region/Lab Detection
  4. Excel File Requirements
  5. Report Generation Rules
  6. File Naming Rules
  7. Date Handling
  8. Multi-Test Support
  9. Adding a New Test Type
  10. Adding a New Region/Lab

How It Works

Step-by-Step Process

  1. Upload Excel File
  2. You upload an Excel file (.xlsx) with patient information
  3. The system validates the file has all required columns

  4. Process Each Patient

  5. For each row in the Excel file, the system:

    • Reads patient information
    • Identifies which test(s) were requested
    • Selects the correct template for each test
    • Generates individual reports
  6. Download Reports

  7. All reports are packaged into a ZIP file
  8. Each report is a Word document (.docx)
  9. Reports maintain all template formatting and styling

What Happens to Your Data

  1. Read row One row of the uploaded workbook app.process_xlsx_and_generate_reports
  2. Validate Required columns and required values xlsx_parser.validate_xlsx
  3. Transform Spreadsheet columns to report fields xlsx_parser.transform_xlsx_row
  4. Detect lab From the health facility name template_selector.detect_region_from_health_facility
  5. Detect assays One row may request several template_selector.detect_test_types one row, many reports
  6. Select template Falls back to the default lab if absent template_selector.get_template_path_with_fallback
  7. Fill in Replace the template's sample values app.generate_report
  8. Name the file Assay, patient, report id template_selector.generate_report_filename
  9. Bundle One timestamped ZIP for the batch app.generate
How one workbook row becomes one or more reports. Each stage names the function that performs it.

Important: One patient can generate multiple reports if they have multiple test types!


Supported Test Types

The system recognizes these test patterns in the "Xét nghiệm" column:

Which report template exists for each assay and lab
Assay Sư Vạn Hạnh default HN Gia An Thủ Đức JIO Health Medic SG
HPV HPV Genotyping HPV_SG.docx is present HPV_HN.docx is present HPV_GIAAN.docx is present HPV_TĐ.docx is present HPV_JIO.docx is present HPV_MDSG.docx is present
STD Sexually Transmissible Disease STD_SG.docx is present STD_HN.docx is present STD_GIAAN.docx is present STD_TĐ.docx is present STD_JIO.docx is present STD_MDSG.docx is present
GBS PCR GBS (Group B Streptococcus) GBS_SG.docx is present GBS_HN.docx is present GBS_GIAAN.docx is present GBS_TĐ.docx is present GBS_JIO.docx is present GBS_MDSG.docx is present
CTNG Neisseria gonorrhoeae-Chlamydia trachomatis DNA CTNG_SG.docx is present CTNG_HN.docx is present CTNG_GIAAN.docx is present CTNG_TĐ.docx is present CTNG_JIO.docx is present CTNG_MDSG.docx is present
HBV HBV DNA HBV_SG.docx is present HBV_HN.docx is present HBV_GIAAN.docx is present HBV_TĐ.docx is present HBV_JIO.docx is present HBV_MDSG.docx is present
HCV HCV RNA Realtime HCV_SG.docx is present HCV_HN.docx is present HCV_GIAAN.docx is present HCV_TĐ.docx is present HCV_JIO.docx is present HCV_MDSG.docx is present
Read a filename straight off the grid: row code, underscore, column code, .docx. All 36 templates are present.

Each assay's detection pattern is shown beneath its code in the grid above.


Region/Lab Detection

The system automatically selects the appropriate regional template based on the health facility name in the "Bác sĩ" column.

Testing the health facility name, in this order

  1. contains TÂM AN HÀ NỘI HN HN
  2. contains BV ĐA KHOA VẠN GIA AN or BV Đa Khoa Vạn Gia An GIAAN Gia An
  3. contains Tâm An Thủ Đức TĐ Thủ Đức
  4. contains C- Mother Clinic or Home Visit JIO JIO Health
  5. contains PKĐK MEDIC SÀI GÒN MDSG Medic SG
anything else SG Sư Vạn Hạnh
The first test that matches wins, so order matters. Matching ignores upper and lower case and looks for the text anywhere in the value. The last row is not a test: it is what you get when nothing matched.

Examples

"Bác sĩ" Value Detected Region Template Used
"BS. Phòng Khám ABC" SG HPV_SG.docx
"TÂM AN ĐÀ NẴNG" SG HPV_SG.docx
"TÂM AN HÀ NỘI" HN HPV_HN.docx
"BV ĐA KHOA VẠN GIA AN" GIAAN HPV_GIAAN.docx
"Tâm An Thủ Đức" TĐ HPV_TĐ.docx
"TÂM AN THỦ ĐỨC" TĐ HPV_TĐ.docx
"C- Mother Clinic" JIO HPV_JIO.docx
"Home Visit" JIO HPV_JIO.docx
"PKĐK MEDIC SÀI GÒN" MDSG HPV_MDSG.docx

Important: - Facility names are matched ignoring upper/lower case, anywhere in the value: "Tâm An Thủ Đức" and "TÂM AN THỦ ĐỨC" both detect as TĐ. - Each region may have different placeholder values and formatting in their templates. - JIO region special behavior: When PID is present, it is prefixed to the patient name in reports (e.g., "PTS 01 - NGUYỄN VĂN A") and included in filenames.


Test Type Examples

What You Write in "Xét nghiệm" Reports Generated
HPV Genotyping: 1 HPV report
Sexually Transmissible Disease: 1 STD report
HPV Genotyping:x, STD: 2 reports (HPV + STD)
HBV DNA:, HCV RNA Realtime: 2 reports (HBV + HCV)

Note: If the system doesn't recognize the test type, that row will be skipped with a warning.


Excel File Requirements

  • Ngày ĐK Read into the report Date received
  • STT Must have a value Report number
  • PID Read into the report Patient ID
  • Họ tên Must have a value Patient name
  • Chẩn đoán Read into the report Diagnosis
  • G.Tính Column must exist Sex
  • Năm sinh Read into the report Year of birth
  • Tuổi Column must exist Nothing reads it
  • SĐT Read into the report Phone, appended to the address
  • Địa chỉ Column must exist Address
  • Bác sĩ Column must exist Health facility
  • Xét nghiệm Read into the report Which assays to run
  • Kết luận Accepted and ignored Nothing reads it
  • KQ Read into the report Result value (HBV and HCV)
Only must have a value and column must exist are enforced when a workbook is validated. A column marked accepted and ignored may be present, but nothing reads it.

Two columns behave in a way the names do not suggest:

KQ holds the result value, and is used for HBV and HCV reports.


Report Generation Rules

Rule 1: One Test Type = One Report

Each test type generates a separate report file.

Example: - Patient has: HPV Genotyping:, STD: - System generates: 2 separate Word documents

Rule 2: Report Uses Correct Template

The system automatically selects the correct template based on: 1. Test type (from "Xét nghiệm" column) 2. Region/Lab (from "Bác sĩ" column)

Examples: - HPV test + TÂM AN HÀ NỘI → Uses HPV_HN.docx - STD test + C- Mother Clinic → Uses STD_JIO.docx - GBS test + Other facility → Uses GBS_SG.docx (default)

Rule 3: Patient Information is Filled In

The system replaces placeholder values in the template with actual patient data: - Report ID (from STT column) - Patient Name - JIO region only: If PID is present, patient name becomes {PID} - {Name} (e.g., "PTS 01 - NGUYỄN VĂN A") - All other regions: Patient name as-is - Health Facility/Doctor - Birth Year - Sex - Address - All regions: If phone number (SĐT) is present, it's appended to address - With address: {Address} - SĐT: {Phone} (e.g., "123 Đường ABC, Quận 1 - SĐT: 0901234567") - Without address: SĐT: {Phone} (e.g., "SĐT: 0901234567") - Phone numbers are stored as strings to preserve leading zeros (e.g., 0901234567) - Registration Date - Report Date

The same in all 36 templates

  • {{DATE_RECEIVED}} date received
  • {{REPORT_DATE}} report date
  • {{ADDRESS}} address
  • {{DIAGNOSIS}} diagnosis

Different in every template

These have no token. The placeholder is the template's own sample text, which generation replaces. In HPV for SG, the report number is found by searching the document for 9851.

Because a per-template placeholder is literal sample text, changing that text in a .docx without changing the configuration silently stops the replacement from happening.

Rule 4: Dates are Formatted Consistently

All dates in reports follow the format: DD/MM/YYYY

Example: 24/01/2026


File Naming Rules

Generated report files follow this naming pattern:

HPV Assay PTS 01 Patient ID only when the lab is JIO and the PID is not empty NGUYỄN VĂN A Patient name 9851 Report number .docx

With the patient ID: HPV - PTS 01 - NGUYỄN VĂN A - 9851.docx
Without it: HPV - NGUYỄN VĂN A - 9851.docx

ZIP File Name

medical_reports_{TIMESTAMP}.zip

Example: medical_reports_20260126_143055.zip


Date Handling

Date received
Ngày ĐK Taken straight from the workbook column.
Report date
The date picker on the upload page Applies to every report in the batch. Today Used when the picker is left empty.
240126-9797
240126-9797 The tracking number's date half is parsed and then discarded. The registration date comes from Ngày ĐK.
Every date printed on a report is formatted DD/MM/YYYY.

"Ngày ĐK" must be a valid date — Excel's date format is fine.


Multi-Test Support

One row, assay column reads

HPV Genotyping:x, Sexually Transmissible Disease:
  • HPV - NGUYỄN VĂN A - 9851.docx
  • STD - NGUYỄN VĂN A - 9851.docx
One row requesting 2 assays produces 2 reports, all sharing the same patient details and report number. Patient information is never duplicated in the workbook.

Benefits


Common Scenarios

Scenario 1: Standard Single Test

Input: - 10 patients, each with one test type

Output: - 10 report files in ZIP

Scenario 2: Mixed Test Types

Input: - 5 patients with HPV - 3 patients with STD - 2 patients with both HPV and STD

Output: - 12 report files in ZIP (5 + 3 + 4)

Scenario 3: Batch with Custom Report Date

Input: - Upload 20 patients - Select report date: 20/01/2026

Output: - All 20 reports use 20/01/2026 as the report date - Registration dates remain from "Ngày ĐK" column


Error Handling

What Gets Skipped

The system will skip rows if: - Patient name (Họ tên) is empty - STT is empty - Test type is not recognized - Template file doesn't exist (even after fallback to SG)

Note: If a region-specific template is missing, the system automatically falls back to the SG (Sư Vạn Hạnh) template.

What You'll See


Best Practices

1. Prepare Your Excel File

2. Verify Before Upload

3. Choose Report Date Wisely

4. Review Generated Files


JIO Region Special Behaviors

The JIO Health lab has unique requirements for PID (Patient ID) handling.

PID in Report Content

For JIO region only, when PID is present: - Patient name in the report becomes: {PID} - {Name} - Example: If PID is "PTS 01" and name is "NGUYỄN VĂN A", the report will show "PTS 01 - NGUYỄN VĂN A"

For every other region: - Patient name appears as-is, without PID prefix - Example: "NGUYỄN VĂN A"

PID in Filename

For JIO region only, when PID is present: - Filename includes PID: {TEST_TYPE} - {PID} - {PATIENT_NAME} - {REPORT_ID}.docx - Example: HPV - PTS 01 - NGUYỄN VĂN A - 9851.docx

For every other region: - Filename does NOT include PID: {TEST_TYPE} - {PATIENT_NAME} - {REPORT_ID}.docx - Example: HPV - NGUYỄN VĂN A - 9851.docx

Summary Table

Region PID Column Patient Name in Report Filename Format
JIO PTS 01 PTS 01 - NGUYỄN VĂN A HPV - PTS 01 - NGUYỄN VĂN A - 9851.docx
JIO (empty) NGUYỄN VĂN A HPV - NGUYỄN VĂN A - 9851.docx
All except JIO PTS 01 NGUYỄN VĂN A HPV - NGUYỄN VĂN A - 9851.docx
All except JIO (empty) NGUYỄN VĂN A HPV - NGUYỄN VĂN A - 9851.docx

Summary

Key Points to Remember

  1. ✅ One Excel row can generate multiple reports
  2. ✅ Test type in "Xét nghiệm" determines which test template to use
  3. ✅ Region/Lab in "Bác sĩ" determines which regional template to use
  4. ✅ System supports 6 labs: Sư Vạn Hạnh (SG), HN, Gia An, Thủ Đức, JIO Health, and Medic SG
  5. ✅ Report date can be customized or defaults to today
  6. ✅ File names include test type, patient ID, name, and report ID
  7. ✅ All reports are packaged in a timestamped ZIP file
  8. ✅ Empty rows and unknown test types are skipped
  9. ✅ Missing regional templates automatically fall back to SG templates

Support

If you encounter issues: - Check your Excel file has all required columns - Verify test type names match exactly - Ensure patient names and STT are not empty - Review the file statistics table before generating - Check the "By Lab & Test Type" table to verify region detection - If wrong templates are used, verify the "Bác sĩ" column contains correct facility names



Reference — configuration

Adding a lab

  • 1 entry in the lab mapping
  • 6 placeholder blocks, one per assay
  • 6 template files

Adding an assay

  • 1 entry in the assay configuration
  • 6 placeholder blocks, one per lab
  • 6 template files
Counts follow the current 6 assays and 6 labs, so they stay right as either grows. Only the default lab's template is strictly required — a missing one falls back to it.

Adding a New Test Type

Adding a new test type requires configuration and template files for each region.

Step 1: Add Configuration

Edit template_selector.py and add an entry to the TEST_TYPES dictionary:

TEST_TYPES = {
    # ... existing types ...
    "NEW": {
        "pattern": "New Test Name",  # Pattern to match in "Xét nghiệm" column
        "template_placeholders": {
            "SG": {
                "report_id_placeholder": "1234",
                "patient_name_placeholder": "SAMPLE NAME",
                "health_facility_placeholder": "Sample Facility",
                "age_placeholder": "1990",
                "sex_placeholder": "Nam",
            },
            "HN": {
                "report_id_placeholder": "5678",
                "patient_name_placeholder": "NGUYEN VAN A",
                "health_facility_placeholder": "TÂM AN HÀ NỘI",
                "age_placeholder": "1985",
                "sex_placeholder": "Nam",
            },
            # Add entries for the other regions as needed (see REGION_LABELS in template_selector.py for the full list)
        },
        "default_result": None,  # Or region-specific dict if needed
    },
}

Step 2: Add Template Files

Every file follows the same naming rule: {CODE}_{LAB}.docx.

test_templates/NEW_SG.docx       # required -- every other lab falls back to this one
test_templates/NEW_{LAB}.docx    # optional, one per other lab

See the adding-checklist figure above for how many files that is today.

Replace NEW with your test code (e.g., HIV, COVID, etc.), and {LAB} with a lab code from the matrix above.

Note: If a region-specific template doesn't exist, the system automatically falls back to the SG template.

Configuration Fields

Field Required Description
pattern Yes The exact text to search for in "Xét nghiệm" column
template_placeholders Yes Placeholder values in the template to replace
default_result No Default result text (used for HBV/HCV type tests)

Verification

After adding a new test type: 1. Run tests: pytest tests/test_template_selector.py 2. Start server and test with sample data 3. Verify the new test type appears in /api/config endpoint


Adding a New Region/Lab

Adding support for a new regional lab requires updating configuration and creating templates.

Step 1: Add Region Mapping

Edit template_selector.py and add an entry to the REGION_MAPPINGS dictionary:

REGION_MAPPINGS = {
    # ... existing regions ...
    "NEW_REGION": ["Pattern 1", "Pattern 2"],  # Add your region here
}

The patterns use substring matching (contains) and ignore upper/lower case, so one spelling per lab is enough.

Step 2: Add Region Placeholders

For each test type in TEST_TYPES, add placeholder configuration for the new region:

"HPV": {
    "pattern": "HPV Genotyping",
    "template_placeholders": {
        # ... existing regions ...
        "NEW_REGION": {
            "report_id_placeholder": "9999",
            "patient_name_placeholder": "SAMPLE NAME",
            "health_facility_placeholder": "New Facility Name",
            "age_placeholder": "1990",
            "sex_placeholder": "Nam",
        },
    },
    "default_result": None,
},

Repeat for all 6 test types: HPV, STD, GBS, CTNG, HBV, HCV.

Step 3: Create Template Files

Same naming rule, the other way round: {CODE}_{LAB}.docx, one per assay.

test_templates/{CODE}_NEW_REGION.docx

See the adding-checklist figure above for how many that is today.

Step 4: Add the Region Label (Required)

Edit template_selector.py and add an entry to the REGION_LABELS dict -- the human-readable name shown in the template matrix, the detection ladder, and served to the frontend by /api/config:

REGION_LABELS = {
    # ... existing labels ...
    "NEW_REGION": "Friendly Name",
}

This is not optional: test_every_region_has_a_label fails until every region has one.

Optionally, also add a line to the "Supported labs" help text on the index page (templates/index.html):

<li><strong>NEW_REGION</strong> - When "Bác sĩ" contains "Pattern 1" or "Pattern 2"</li>

Step 5: Update Default Results (if applicable)

If the region has different default result text for HBV/HCV tests:

"HBV": {
    # ... existing config ...
    "default_result": {
        "SG": "Dưới ngưỡng phát hiện (Không có nghĩa là âm tính)",
        "HN": "Dưới ngưỡng phát hiện",
        "NEW_REGION": "Your custom text here",
    },
},

Verification

After adding a new region: 1. Test region detection with sample "Bác sĩ" values 2. Verify correct templates are selected 3. Generate test reports to ensure all placeholders work 4. Check the statistics table shows the new region correctly


Last Updated: 2026-01-27