diff --git a/SURGERY_PROCEDURE_AUTOCOMPLETE_TESTING.md b/SURGERY_PROCEDURE_AUTOCOMPLETE_TESTING.md
new file mode 100644
index 0000000..df39355
--- /dev/null
+++ b/SURGERY_PROCEDURE_AUTOCOMPLETE_TESTING.md
@@ -0,0 +1,346 @@
+# Surgery/Procedure Autocomplete Testing Checklist
+
+## ✅ Implementation Complete
+
+### **Enhanced Features:**
+1. ✅ Custom dropdown with SNOMED CT and ICD code display
+2. ✅ Arrow key navigation (↑↓)
+3. ✅ Enter/Escape key support
+4. ✅ Visual hover feedback
+5. ✅ Keyboard highlight (blue background)
+6. ✅ Code badges (blue for SNOMED, gray for ICD)
+7. ✅ Master CRUD integration
+
+---
+
+## 🧪 Testing Procedure
+
+### **Test 1: Surgery Autocomplete Basic Flow**
+**Steps:**
+1. Navigate to any IPD discharge form: `http://localhost/Ipd_discharge/ipd_select/{ipd_id}`
+2. Scroll to "Surgery / Procedure / delivery if any" section
+3. Click in the **Surgery name** input field
+4. Type at least 2 characters (e.g., "append")
+
+**Expected Results:**
+- ✅ Dropdown appears below input
+- ✅ Shows matching surgery names from master table
+- ✅ Each item displays:
+ - Surgery name (bold, prominent)
+ - Blue badge with SNOMED/term code (if exists)
+ - Gray badge with ICD code (if exists)
+- ✅ Items are clickable with mouse hover highlighting
+
+**Screenshot:**
+```
+┌─────────────────────────────────────────────┐
+│ Surgery name input: "append" │
+│ ┌───────────────────────────────────────┐ │
+│ │ Appendectomy │ │
+│ │ [SNOMED: 80146002] [ICD: K35.80] │ │
+│ ├───────────────────────────────────────┤ │
+│ │ Appendectomy, laparoscopic │ │
+│ │ [SNOMED: 174041007] [ICD: K35.89] │ │
+│ └───────────────────────────────────────┘ │
+└─────────────────────────────────────────────┘
+```
+
+---
+
+### **Test 2: Keyboard Navigation**
+**Steps:**
+1. Type to show dropdown
+2. Press **Arrow Down** key multiple times
+3. Press **Arrow Up** key
+4. Press **Enter** to select highlighted item
+
+**Expected Results:**
+- ✅ Arrow Down: Moves blue highlight down one item
+- ✅ Arrow Up: Moves blue highlight up one item
+- ✅ Highlighted item has blue background with white text
+- ✅ Highlighted item auto-scrolls into view if needed
+- ✅ Enter: Selects highlighted item and fills input
+- ✅ Dropdown closes after selection
+
+---
+
+### **Test 3: Mouse Selection**
+**Steps:**
+1. Type to show dropdown
+2. Hover mouse over different items
+3. Click on an item
+
+**Expected Results:**
+- ✅ Hover: Item gets light gray background
+- ✅ Click: Input fills with selected surgery name
+- ✅ Dropdown closes
+- ✅ Hidden field `new_surgery_master_id` is populated with master record ID
+
+---
+
+### **Test 4: Escape Key**
+**Steps:**
+1. Type to show dropdown
+2. Press **Escape** key
+
+**Expected Results:**
+- ✅ Dropdown closes immediately
+- ✅ Input value remains unchanged
+
+---
+
+### **Test 5: Procedure Autocomplete** *(Same as Surgery)*
+**Steps:**
+1. Scroll to **Procedure** section (below Surgery)
+2. Type in the **Procedure name** input
+3. Verify dropdown behavior is identical to Surgery
+
+**Expected Results:**
+- ✅ Separate dropdown container (no interference)
+- ✅ Shows procedure terms (filtered by `type='procedure'`)
+- ✅ All keyboard/mouse behaviors work independently
+- ✅ Hidden field `new_procedure_master_id` populated correctly
+
+---
+
+### **Test 6: Master CRUD Integration**
+**Steps:**
+1. Click the **"Master CRUD"** button
+2. Modal should open with Surgery/Procedure master list
+3. Add a new surgery:
+ - Type: Surgery
+ - Name: "Test Surgery ABC"
+ - Code: "TEST001"
+ - ICD Code: "Z99.99"
+ - Click **Save**
+4. Close modal
+5. Type "test" in Surgery name input
+
+**Expected Results:**
+- ✅ Modal opens showing master list
+- ✅ New record saves successfully
+- ✅ Dropdown now shows "Test Surgery ABC" with codes
+- ✅ Can select the new item immediately
+
+---
+
+### **Test 7: Add Row Flow**
+**Steps:**
+1. Use autocomplete to select a surgery
+2. Fill in Date field (optional)
+3. Fill in Remark field (optional)
+4. Click **+ADD** button
+
+**Expected Results:**
+- ✅ New row appears in Surgery table above input
+- ✅ Row shows: Name | Date | Remark | Remove button
+- ✅ Form can be submitted with selected surgery
+- ✅ Surgery record links to master table via `new_surgery_master_id`
+
+---
+
+### **Test 8: Code Display Variations**
+**Test different master records:**
+- Record with both SNOMED and ICD → Shows both badges
+- Record with only SNOMED → Shows blue badge only
+- Record with only ICD → Shows gray badge only
+- Record with no codes → Shows name only (no badges)
+
+**Expected Results:**
+- ✅ Badge display adapts correctly to available data
+- ✅ Layout remains clean and readable
+
+---
+
+### **Test 9: Performance & Responsiveness**
+**Steps:**
+1. Type characters rapidly
+2. Delete and retype
+3. Test with slow network (throttle in DevTools)
+
+**Expected Results:**
+- ✅ Search has 300ms debounce (doesn't spam requests)
+- ✅ Dropdown updates smoothly
+- ✅ No console errors
+- ✅ No JavaScript exceptions
+
+---
+
+### **Test 10: Edge Cases**
+**Steps:**
+1. Type only 1 character → Dropdown should NOT appear
+2. Type 2+ characters → Dropdown appears
+3. Clear input completely → Dropdown hides
+4. Click outside dropdown while open → Closes after 200ms
+5. Type search with no results → Dropdown hides
+
+**Expected Results:**
+- ✅ All edge cases handled gracefully
+- ✅ No errors in console
+- ✅ UI remains responsive
+
+---
+
+## 🐛 Known Issues / Limitations
+
+### ✅ **FIXED Issues:**
+1. ~~Old datalist code conflicting with new dropdown~~ → Removed `bindSurgeryTermLookup` calls
+2. ~~Hidden master_id fields not being populated~~ → Now correctly stores on selection
+3. ~~Dropdown positioning issues~~ → Using `position: relative` parent wrapper
+
+### ⚠️ **Potential Future Enhancements:**
+1. **Fuzzy Search:** Currently exact substring match. Could add fuzzy/phonetic matching.
+2. **Recent Items:** Could cache recently used surgeries for quick access.
+3. **Favorites:** Allow marking frequently used items as favorites.
+4. **Multi-select:** For cases where multiple procedures done simultaneously.
+5. **Search by Code:** Currently searches by name. Could also search by SNOMED/ICD code directly.
+
+---
+
+## 📋 Database Verification
+
+### **Check Master Table:**
+```sql
+-- View all surgeries with codes
+SELECT * FROM ipd_discharge_surgery_master
+WHERE term_type = 'surgery' AND is_active = 1
+ORDER BY term_name;
+
+-- View all procedures with codes
+SELECT * FROM ipd_discharge_surgery_master
+WHERE term_type = 'procedure' AND is_active = 1
+ORDER BY term_name;
+
+-- Check if codes are populated
+SELECT
+ term_type,
+ COUNT(*) as total,
+ SUM(CASE WHEN term_code IS NOT NULL AND term_code != '' THEN 1 ELSE 0 END) as with_snomed,
+ SUM(CASE WHEN icd_code IS NOT NULL AND icd_code != '' THEN 1 ELSE 0 END) as with_icd
+FROM ipd_discharge_surgery_master
+GROUP BY term_type;
+```
+
+### **Check Linked Records:**
+```sql
+-- After adding surgery via form
+SELECT
+ s.surgery_name,
+ s.surgery_date,
+ s.surgery_remark,
+ m.term_name as master_term_name,
+ m.term_code as snomed_code,
+ m.icd_code
+FROM ipd_discharge_surgery s
+LEFT JOIN ipd_discharge_surgery_master m ON m.id = s.surgery_master_id
+WHERE s.ipd_id = {test_ipd_id};
+
+-- Same for procedures
+SELECT
+ p.procedure_name,
+ p.procedure_date,
+ p.procedure_remark,
+ m.term_name as master_term_name,
+ m.term_code as snomed_code,
+ m.icd_code
+FROM ipd_discharge_procedure p
+LEFT JOIN ipd_discharge_surgery_master m ON m.id = p.procedure_master_id
+WHERE p.ipd_id = {test_ipd_id};
+```
+
+---
+
+## 🔧 Troubleshooting
+
+### **Dropdown not appearing:**
+1. Check browser console for JavaScript errors
+2. Verify jQuery is loaded: `console.log(window.jQuery)`
+3. Check network tab for AJAX call to `surgery_master_lookup`
+4. Verify typing at least 2 characters
+
+### **Codes not showing:**
+1. Run database query to check if master records have `term_code` and `icd_code` populated
+2. Check browser DevTools → Network → XHR response to see if codes are in JSON
+3. Verify badge CSS classes are rendering (inspect element)
+
+### **Arrow keys not working:**
+1. Check if input has focus
+2. Verify `keydown` event listener is attached
+3. Check console for JavaScript errors
+4. Test in different browsers
+
+### **Master CRUD not refreshing dropdown:**
+1. After saving in Master CRUD modal, new items should appear immediately
+2. The `fetchMasterRows()` is called after save
+3. Next autocomplete search will fetch updated list from database
+
+---
+
+## ✅ Completion Checklist
+
+### **Code Changes:**
+- [x] HTML: Replaced datalist with custom dropdown divs
+- [x] HTML: Added position-relative wrapper for dropdown positioning
+- [x] JavaScript: Created `initSurgeryProcedureAutocomplete()` function
+- [x] JavaScript: Created `initTermAutocomplete()` with full keyboard support
+- [x] JavaScript: Arrow key navigation with visual highlighting
+- [x] JavaScript: Click and hover event handlers
+- [x] JavaScript: 300ms debounce on input for performance
+- [x] JavaScript: Badge rendering for SNOMED/ICD codes
+- [x] JavaScript: Removed old `bindSurgeryTermLookup` calls from `initSurgeryTools()`
+- [x] CSS: Dropdown styling with Bootstrap classes
+- [x] Integration: Called from `initSurgeryProcedureAutocomplete()` on page load
+
+### **Testing:**
+- [ ] Test 1: Surgery autocomplete basic flow
+- [ ] Test 2: Keyboard navigation (arrows, Enter, Escape)
+- [ ] Test 3: Mouse selection and hover
+- [ ] Test 4: Escape key closes dropdown
+- [ ] Test 5: Procedure autocomplete (independent operation)
+- [ ] Test 6: Master CRUD integration
+- [ ] Test 7: Add row with selected item
+- [ ] Test 8: Code badge display variations
+- [ ] Test 9: Performance and debouncing
+- [ ] Test 10: Edge cases (min length, no results, blur)
+
+### **Verification:**
+- [ ] No JavaScript console errors
+- [ ] No PHP errors in logs
+- [ ] Database records properly linked to master table
+- [ ] SNOMED and ICD codes visible in UI
+- [ ] Works on Chrome/Firefox/Edge
+- [ ] Works on mobile/tablet (touch selection)
+- [ ] Page load time not impacted
+
+---
+
+## 📝 User Acceptance
+
+**Tested By:** _____________
+**Date:** _____________
+**Browser:** _____________
+**Result:** ✅ PASS / ❌ FAIL
+
+**Notes:**
+```
+[Add any observations, issues, or suggestions here]
+```
+
+---
+
+## 🎯 Next Steps (Optional Enhancements)
+
+1. **Search Optimization:** Add full-text search index on `term_name`, `term_code`, `icd_code`
+2. **Code Validation:** Add SNOMED CT validator to verify codes are valid
+3. **ICD Lookup:** Add button to search ICD-10 codes from external API
+4. **Bulk Import:** Allow importing surgery/procedure master from CSV/Excel
+5. **Analytics:** Track most commonly used surgeries for quick access
+6. **Templates:** Create surgery groups/templates (e.g., "C-Section Package")
+7. **ABDM Sync:** Auto-sync selected surgeries to ABDM FHIR bundles
+
+---
+
+**Document Version:** 1.0
+**Created:** 2026-06-06
+**Last Updated:** 2026-06-06
+**Status:** Ready for Testing
diff --git a/app/Views/billing/ipd/discharge_create.php b/app/Views/billing/ipd/discharge_create.php
index 0f35b31..48c8a1f 100644
--- a/app/Views/billing/ipd/discharge_create.php
+++ b/app/Views/billing/ipd/discharge_create.php
@@ -2884,8 +2884,8 @@ $allergyStatusNoKnown = in_array($allergyStatusNormalized, ['no known drug aller
}
function initSurgeryTools(form) {
- bindSurgeryTermLookup(form, 'surgery', 'new_surgery_name', 'discharge_surgery_suggest', 'new_surgery_master_id', 'discharge_surgery_status');
- bindSurgeryTermLookup(form, 'procedure', 'new_procedure_name', 'discharge_procedure_suggest', 'new_procedure_master_id', 'discharge_surgery_status');
+ // Surgery/Procedure autocomplete now handled by initSurgeryProcedureAutocomplete()
+ // Old bindSurgeryTermLookup removed - using custom dropdown instead of datalist
initSurgeryMasterCrud(form);
}
diff --git a/pdf_test_html.html b/pdf_test_html.html
new file mode 100644
index 0000000..b03f7f7
--- /dev/null
+++ b/pdf_test_html.html
@@ -0,0 +1,4255 @@
+
+
+
+
CBC and lipid panel.
', +]; + +$observations = [ + [ + 'test_name' => 'Triglyceride [Mass/volume] in Serum or Plasma', + 'loinc_code' => '2571-8', + 'value_type' => 'quantity', + 'value' => '146', + 'unit' => 'mg/dL', + 'ucum_code' => 'mg/dL', + 'ref_high' => '150', + 'interpretation' => 'N', + ], + [ + 'test_name' => 'Cholesterol in HDL [Mass/volume] in Serum or Plasma', + 'loinc_code' => '2085-9', + 'value_type' => 'quantity', + 'value' => '45', + 'unit' => 'mg/dL', + 'ucum_code' => 'mg/dL', + 'ref_low' => '40', + 'interpretation' => 'N', + ], +]; + +$organization = [ + 'name' => 'XYZ Lab Pvt.Ltd.', + 'hfr_id' => 'IN0510000828', +]; + +$encounter = [ + 'id' => 'LAB-5234342', + 'status' => 'finished', + 'class_code' => 'AMB', + 'period_start' => '2020-07-10T11:45:33+05:30', +]; + +$attachment = [ + 'title' => 'Laboratory report', + 'content_type' => 'application/pdf', + 'data_base64' => base64_encode('%PDF-1.4 mock lab report%'), +]; + +$bundle = $builder->buildLabReportBundle($patient, $diagnosticReport, $observations, null, $organization, $encounter, $attachment); + +$resources = []; +foreach (($bundle['entry'] ?? []) as $entry) { + $r = $entry['resource'] ?? null; + if (is_array($r) && isset($r['resourceType'])) { + $resources[] = $r; + } +} + +$byType = []; +foreach ($resources as $res) { + $type = (string) ($res['resourceType'] ?? ''); + if ($type !== '') { + $byType[$type][] = $res; + } +} + +$composition = $byType['Composition'][0] ?? []; +$diagReport = $byType['DiagnosticReport'][0] ?? []; +$docRefs = $byType['DocumentReference'] ?? []; + +$checks = []; +$checks[] = ['check' => 'Bundle profile DocumentBundle', 'ok' => in_array('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentBundle', (array) (($bundle['meta']['profile'] ?? [])), true)]; +$checks[] = ['check' => 'Composition profile DiagnosticReportRecord', 'ok' => in_array('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportRecord', (array) (($composition['meta']['profile'] ?? [])), true)]; +$checks[] = ['check' => 'Composition section count = 1', 'ok' => count((array) ($composition['section'] ?? [])) === 1]; + +$section = $composition['section'][0] ?? []; +$entries = (array) ($section['entry'] ?? []); +$checks[] = ['check' => 'Section entry count between 1 and 2', 'ok' => count($entries) >= 1 && count($entries) <= 2]; + +$hasTypedDiagReport = false; +$hasTypedDocRef = false; +foreach ($entries as $entry) { + if (($entry['type'] ?? '') === 'DiagnosticReport') { + $hasTypedDiagReport = true; + } + if (($entry['type'] ?? '') === 'DocumentReference') { + $hasTypedDocRef = true; + } +} +$checks[] = ['check' => 'Section has typed DiagnosticReport entry', 'ok' => $hasTypedDiagReport]; +$checks[] = ['check' => 'Section has typed DocumentReference entry (optional but present here)', 'ok' => $hasTypedDocRef]; + +$checks[] = ['check' => 'DiagnosticReport profile DiagnosticReportLab', 'ok' => in_array('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportLab', (array) (($diagReport['meta']['profile'] ?? [])), true)]; +$checks[] = ['check' => 'DiagnosticReport.resultsInterpreter present', 'ok' => count((array) ($diagReport['resultsInterpreter'] ?? [])) >= 1]; +$checks[] = ['check' => 'DiagnosticReport.result present', 'ok' => count((array) ($diagReport['result'] ?? [])) >= 1]; +$checks[] = ['check' => 'DiagnosticReport.conclusion non-empty', 'ok' => trim((string) ($diagReport['conclusion'] ?? '')) !== '']; + +$docRefWithData = false; +foreach ($docRefs as $docRef) { + $content = $docRef['content'][0]['attachment'] ?? []; + if (!empty($content['data'])) { + $docRefWithData = true; + break; + } +} +$checks[] = ['check' => 'At least one DocumentReference has attachment.data', 'ok' => $docRefWithData]; + +$failed = array_values(array_filter($checks, static fn(array $c): bool => empty($c['ok']))); + +echo "ABDM Lab Bundle Check\n"; +echo "====================\n"; +foreach ($checks as $c) { + echo sprintf("[%s] %s\n", $c['ok'] ? 'PASS' : 'FAIL', $c['check']); +} + +echo "\nResource Counts\n"; +echo "---------------\n"; +foreach ($byType as $type => $items) { + echo sprintf("%s: %d\n", $type, count($items)); +} + +file_put_contents(dirname(__DIR__, 2) . '/writable/tmp/abdm_lab_bundle_sample.json', json_encode($bundle, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + +echo "\nSaved sample bundle to writable/tmp/abdm_lab_bundle_sample.json\n"; +if ($failed !== []) { + echo "Remaining gaps found: " . count($failed) . "\n"; + exit(2); +} + +echo "No gaps found in this profile sanity check.\n"; +exit(0); diff --git a/writable/tmp/abdm_lab_bundle_real_check.php b/writable/tmp/abdm_lab_bundle_real_check.php new file mode 100644 index 0000000..5ac68c9 --- /dev/null +++ b/writable/tmp/abdm_lab_bundle_real_check.php @@ -0,0 +1,387 @@ + 'Haematology', + 2 => 'Biochemistry', + 3 => 'Serology', + 4 => 'Microbiology', + 5 => 'Pathology / Cytology', + 6 => 'Radiology', + 7 => 'Urology', + 8 => 'Molecular Diagnostics', + default => '', + }; +} + +function unitToUcum(string $unit): string +{ + $unit = trim($unit); + if ($unit === '') { + return ''; + } + + $map = [ + 'g/dl' => 'g/dL', + 'mg/dl' => 'mg/dL', + 'mg/l' => 'mg/L', + 'mmol/l' => 'mmol/L', + 'umol/l' => 'umol/L', + 'u/l' => 'U/L', + 'iu/l' => 'IU/L', + 'iu/ml' => 'IU/mL', + 'cells/cumm' => '10*3/uL', + 'cells/mm3' => '10*3/uL', + '10^9/l' => '10*9/L', + '10^3/ul' => '10*3/uL', + 'fl' => 'fL', + 'pg' => 'pg', + '%' => '%', + 'sec' => 's', + 'min' => 'min', + 'mmhg' => 'mm[Hg]', + 'meq/l' => 'meq/L', + ]; + + return $map[strtolower($unit)] ?? $unit; +} + +function tableExists(mysqli $mysqli, string $table): bool +{ + $safe = $mysqli->real_escape_string($table); + $res = $mysqli->query("SHOW TABLES LIKE '{$safe}'"); + if (!$res) { + return false; + } + $exists = $res->num_rows > 0; + $res->free(); + return $exists; +} + +$root = dirname(__DIR__, 2); +$env = parseEnvFile($root . '/.env'); + +$host = envVal($env, 'database.default.hostname', '127.0.0.1'); +$user = envVal($env, 'database.default.username', 'root'); +$pass = envVal($env, 'database.default.password', ''); +$db = envVal($env, 'database.default.database', ''); +$port = (int) envVal($env, 'database.default.port', '3306'); + +if ($db === '') { + fwrite(STDERR, "Could not determine database.default.database from .env\n"); + exit(1); +} + +$mysqli = @new mysqli($host, $user, $pass, $db, $port); +if ($mysqli->connect_errno) { + fwrite(STDERR, "DB connection failed: " . $mysqli->connect_error . "\n"); + exit(1); +} +$mysqli->set_charset('utf8mb4'); + +$columns = []; +$colResult = $mysqli->query('SHOW COLUMNS FROM lab_request'); +while ($colResult && ($row = $colResult->fetch_assoc())) { + $columns[] = (string) ($row['Field'] ?? ''); +} +$colResult?->free(); + +$patientIdCol = null; +foreach (['patient_id', 'pid', 'patient_master_id', 'pat_id'] as $candidate) { + if (in_array($candidate, $columns, true)) { + $patientIdCol = $candidate; + break; + } +} + +$patientSelect = $patientIdCol !== null ? ", {$patientIdCol} AS resolved_patient_id" : ', 0 AS resolved_patient_id'; + +$sql = "SELECT id, patient_name, lab_type, charge_id, Report_Data, report_data_Impression, status, reported_time, lab_repo_id {$patientSelect} FROM lab_request WHERE COALESCE(Report_Data, '') <> '' ORDER BY id DESC LIMIT 1"; +$labResult = $mysqli->query($sql); +$labRow = $labResult ? $labResult->fetch_assoc() : null; +$labResult?->free(); + +if (!$labRow) { + fwrite(STDERR, "No lab_request row found with non-empty Report_Data\n"); + exit(2); +} + +$labReqId = (int) ($labRow['id'] ?? 0); +$patientId = (int) ($labRow['resolved_patient_id'] ?? 0); + +$patientRow = []; +if ($patientId > 0 && tableExists($mysqli, 'patient_master')) { + $patientStmt = $mysqli->prepare('SELECT id, p_fname, p_lname, gender, dob FROM patient_master WHERE id = ? LIMIT 1'); + if ($patientStmt) { + $patientStmt->bind_param('i', $patientId); + $patientStmt->execute(); + $res = $patientStmt->get_result(); + $patientRow = $res ? ($res->fetch_assoc() ?: []) : []; + $res?->free(); + $patientStmt->close(); + } +} + +$patName = trim(trim((string) ($patientRow['p_fname'] ?? '')) . ' ' . trim((string) ($patientRow['p_lname'] ?? ''))); +if ($patName === '') { + $patName = trim((string) ($labRow['patient_name'] ?? '')); +} + +$testTitle = ''; +$chargeId = (int) ($labRow['charge_id'] ?? 0); +if ($chargeId > 0 && tableExists($mysqli, 'charge_master')) { + $chargeStmt = $mysqli->prepare('SELECT charge_name FROM charge_master WHERE id = ? LIMIT 1'); + if ($chargeStmt) { + $chargeStmt->bind_param('i', $chargeId); + $chargeStmt->execute(); + $res = $chargeStmt->get_result(); + $chargeRow = $res ? ($res->fetch_assoc() ?: []) : []; + $res?->free(); + $chargeStmt->close(); + $testTitle = trim((string) ($chargeRow['charge_name'] ?? '')); + } +} +if ($testTitle === '') { + $testTitle = mapLabTypeToTitle((int) ($labRow['lab_type'] ?? 0)); +} +if ($testTitle === '') { + $testTitle = 'Laboratory Report'; +} + +$repoLoinc = ''; +$labRepoId = (int) ($labRow['lab_repo_id'] ?? 0); +if ($labRepoId > 0 && tableExists($mysqli, 'lab_repo')) { + $repoStmt = $mysqli->prepare('SELECT loinc_code FROM lab_repo WHERE mstRepoKey = ? LIMIT 1'); + if ($repoStmt) { + $repoStmt->bind_param('i', $labRepoId); + $repoStmt->execute(); + $res = $repoStmt->get_result(); + $repoRow = $res ? ($res->fetch_assoc() ?: []) : []; + $res?->free(); + $repoStmt->close(); + $repoLoinc = trim((string) ($repoRow['loinc_code'] ?? '')); + } +} + +$hospitalName = ''; +$hfrId = ''; +if (tableExists($mysqli, 'hospital_setting')) { + $hsResult = $mysqli->query("SELECT s_name, s_value FROM hospital_setting WHERE s_name IN ('ABDM_HMS_NAME','ABDM_HFR_ID','H_Name')"); + $settings = []; + while ($hsResult && ($row = $hsResult->fetch_assoc())) { + $settings[(string) $row['s_name']] = (string) ($row['s_value'] ?? ''); + } + $hsResult?->free(); + $hospitalName = trim((string) ($settings['ABDM_HMS_NAME'] ?? $settings['H_Name'] ?? '')); + $hfrId = trim((string) ($settings['ABDM_HFR_ID'] ?? '')); +} + +$obsSql = "SELECT lri.lab_test_id, lri.lab_test_value, lri.lab_test_remark, lt.Test, lt.Unit, lt.FixedNormals, lt.loinc_code, lt.loinc_scale FROM lab_request_item lri LEFT JOIN lab_tests lt ON lt.mstTestKey = lri.lab_test_id WHERE lri.lab_request_id = ? ORDER BY lri.id ASC"; +$obsStmt = (tableExists($mysqli, 'lab_request_item') && tableExists($mysqli, 'lab_tests')) ? $mysqli->prepare($obsSql) : false; +$observations = []; +if ($obsStmt) { + $obsStmt->bind_param('i', $labReqId); + $obsStmt->execute(); + $obsRes = $obsStmt->get_result(); + while ($obsRes && ($row = $obsRes->fetch_assoc())) { + $rawValue = trim((string) ($row['lab_test_value'] ?? '')); + if ($rawValue === '' || strtolower($rawValue) === 'n/a') { + continue; + } + + $scale = strtolower(trim((string) ($row['loinc_scale'] ?? ''))); + $isNumeric = is_numeric($rawValue) && $scale !== 'nom' && $scale !== 'ord'; + $valueType = $isNumeric ? 'quantity' : 'string'; + + $refLow = ''; + $refHigh = ''; + $normals = trim((string) ($row['FixedNormals'] ?? '')); + if ($normals !== '' && str_contains($normals, '-')) { + $parts = explode('-', $normals, 2); + if (count($parts) === 2 && is_numeric(trim((string) $parts[0])) && is_numeric(trim((string) $parts[1]))) { + $refLow = trim((string) $parts[0]); + $refHigh = trim((string) $parts[1]); + } + } + + $interpretation = 'N'; + if ($isNumeric && $refLow !== '' && $refHigh !== '') { + $fVal = (float) $rawValue; + if ($fVal < (float) $refLow) { + $interpretation = 'L'; + } elseif ($fVal > (float) $refHigh) { + $interpretation = 'H'; + } + } + + $unit = trim((string) ($row['Unit'] ?? '')); + $observations[] = [ + 'test_name' => trim((string) ($row['Test'] ?? '')) ?: ('Test-' . (string) ($row['lab_test_id'] ?? 'X')), + 'loinc_code' => trim((string) ($row['loinc_code'] ?? '')), + 'value_type' => $valueType, + 'value' => $rawValue, + 'unit' => $unit, + 'ucum_code' => unitToUcum($unit), + 'ref_low' => $refLow, + 'ref_high' => $refHigh, + 'interpretation' => $interpretation, + 'status' => ((string) ($labRow['status'] ?? '0')) === '1' ? 'final' : 'preliminary', + ]; + } + $obsRes?->free(); + $obsStmt->close(); +} + +$reportedRaw = trim((string) ($labRow['reported_time'] ?? '')); +$reportedAt = $reportedRaw !== '' ? (new DateTime($reportedRaw, new DateTimeZone('Asia/Kolkata')))->format('Y-m-d\\TH:i:sP') : ''; + +$patient = [ + 'id' => (string) ($patientId > 0 ? $patientId : ('LAB-' . $labReqId)), + 'name' => $patName !== '' ? $patName : 'Unknown', + 'gender' => trim((string) ($patientRow['gender'] ?? '')), + 'birthDate' => !empty($patientRow['dob']) ? date('Y-m-d', strtotime((string) $patientRow['dob'])) : '', + 'abhaAddress' => 'unknown@abdm', +]; + +$diagnosticReport = [ + 'id' => (string) $labReqId, + 'title' => $testTitle, + 'status' => ((string) ($labRow['status'] ?? '0')) === '1' ? 'final' : 'preliminary', + 'conclusion' => trim((string) ($labRow['report_data_Impression'] ?? '')), + 'reported_at' => $reportedAt, + 'report_html' => trim((string) ($labRow['Report_Data'] ?? '')), + 'section_title' => 'Hematology report', + 'section_snomed_code' => '4321000179101', + 'section_snomed_display' => 'Hematology report', +]; +if ($repoLoinc !== '') { + $diagnosticReport['loinc_code'] = $repoLoinc; +} + +$organization = $hospitalName !== '' ? ['name' => $hospitalName, 'hfr_id' => $hfrId] : null; +$encounter = [ + 'id' => 'LAB-' . $labReqId, + 'status' => 'finished', + 'class_code' => 'AMB', + 'period_start' => $reportedAt, +]; + +$builder = new FhirR4Builder(); +$bundle = $builder->buildLabReportBundle($patient, $diagnosticReport, $observations, null, $organization, $encounter, null); + +$resources = []; +foreach (($bundle['entry'] ?? []) as $entry) { + $r = $entry['resource'] ?? null; + if (is_array($r) && isset($r['resourceType'])) { + $resources[] = $r; + } +} +$byType = []; +foreach ($resources as $res) { + $type = (string) ($res['resourceType'] ?? ''); + if ($type !== '') { + $byType[$type][] = $res; + } +} + +$composition = $byType['Composition'][0] ?? []; +$diagReport = $byType['DiagnosticReport'][0] ?? []; +$section = $composition['section'][0] ?? []; +$entries = (array) ($section['entry'] ?? []); + +$hasTypedDiagReport = false; +$hasTypedDocRef = false; +foreach ($entries as $entry) { + if (($entry['type'] ?? '') === 'DiagnosticReport') { + $hasTypedDiagReport = true; + } + if (($entry['type'] ?? '') === 'DocumentReference') { + $hasTypedDocRef = true; + } +} + +$checks = [ + ['check' => 'Bundle profile DocumentBundle', 'ok' => in_array('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentBundle', (array) (($bundle['meta']['profile'] ?? [])), true)], + ['check' => 'Composition profile DiagnosticReportRecord', 'ok' => in_array('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportRecord', (array) (($composition['meta']['profile'] ?? [])), true)], + ['check' => 'Composition section count = 1', 'ok' => count((array) ($composition['section'] ?? [])) === 1], + ['check' => 'Section entry count between 1 and 2', 'ok' => count($entries) >= 1 && count($entries) <= 2], + ['check' => 'Section has typed DiagnosticReport entry', 'ok' => $hasTypedDiagReport], + ['check' => 'DiagnosticReport profile DiagnosticReportLab', 'ok' => in_array('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportLab', (array) (($diagReport['meta']['profile'] ?? [])), true)], + ['check' => 'DiagnosticReport.resultsInterpreter present', 'ok' => count((array) ($diagReport['resultsInterpreter'] ?? [])) >= 1], + ['check' => 'DiagnosticReport.result present', 'ok' => count((array) ($diagReport['result'] ?? [])) >= 1], + ['check' => 'DiagnosticReport.conclusion non-empty', 'ok' => trim((string) ($diagReport['conclusion'] ?? '')) !== ''], +]; + +$samplePath = $root . '/writable/tmp/abdm_lab_bundle_real_sample.json'; +file_put_contents($samplePath, json_encode($bundle, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + +$failed = array_values(array_filter($checks, static fn(array $c): bool => empty($c['ok']))); + +echo "ABDM Real Lab Bundle Check\n"; +echo "==========================\n"; +echo 'Lab Request ID: ' . $labReqId . "\n"; +echo 'Patient ID: ' . ($patientId > 0 ? (string) $patientId : 'N/A') . "\n"; +echo 'Observations: ' . count($observations) . "\n"; +echo 'DocReference typed section entry present: ' . ($hasTypedDocRef ? 'yes' : 'no') . "\n\n"; + +foreach ($checks as $c) { + echo sprintf("[%s] %s\n", $c['ok'] ? 'PASS' : 'FAIL', $c['check']); +} + +echo "\nSaved real sample bundle to writable/tmp/abdm_lab_bundle_real_sample.json\n"; + +if ($failed !== []) { + echo 'Remaining gaps found: ' . count($failed) . "\n"; + exit(2); +} + +echo "No gaps found in this real-data profile sanity check.\n"; +$mysqli->close(); +exit(0); diff --git a/writable/tmp/abdm_lab_bundle_real_sample.json b/writable/tmp/abdm_lab_bundle_real_sample.json new file mode 100644 index 0000000..820367d --- /dev/null +++ b/writable/tmp/abdm_lab_bundle_real_sample.json @@ -0,0 +1,1063 @@ +{ + "resourceType": "Bundle", + "id": "46e4b9fb-c2fa-4ef5-9f72-4c8a42d3683c", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentBundle" + ], + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "V", + "display": "very restricted" + } + ] + }, + "identifier": { + "system": "https://in0510000828.hfr.abdm.gov.in", + "value": "LAB-24" + }, + "type": "document", + "timestamp": "2026-06-01T05:55:28+05:30", + "entry": [ + { + "fullUrl": "urn:uuid:86c5dc0f-b961-4613-b51a-9990ae518d6e", + "resource": { + "resourceType": "Composition", + "id": "86c5dc0f-b961-4613-b51a-9990ae518d6e", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportRecord" + ] + }, + "language": "en-IN", + "identifier": { + "system": "https://ndhm.in/phr", + "value": "86c5dc0f-b961-4613-b51a-9990ae518d6e" + }, + "status": "final", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic Report- Lab" + } + ], + "text": "Diagnostic Report- Lab" + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545", + "display": "Patient" + }, + "date": "2026-06-01T05:55:28+05:30", + "author": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "title": "Pathology / Cytology", + "section": [ + { + "title": "Hematology report", + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "4321000179101", + "display": "Hematology report" + } + ] + }, + "entry": [ + { + "reference": "urn:uuid:e1f47726-cf20-416c-a49c-6732c052023f", + "type": "DiagnosticReport" + }, + { + "reference": "urn:uuid:f96d951e-1d17-4fa0-94ae-1300052c9197", + "type": "DocumentReference" + } + ] + } + ], + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "custodian": { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + } + }, + { + "fullUrl": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf", + "resource": { + "resourceType": "Organization", + "id": "93b37534-d7ed-4d0a-89ac-ecec3967aabf", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Organization" + ] + }, + "name": "DevSoft Tech HMS", + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "PRN", + "display": "Provider number" + } + ] + }, + "system": "https://facility.ndhm.gov.in", + "value": "IN0510000828" + } + ] + } + }, + { + "fullUrl": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545", + "resource": { + "resourceType": "Patient", + "id": "6d70bbfb-30d5-4cd8-a1b1-96cb289cc545", + "name": [ + { + "text": "KAMLA SINGH 0", + "given": [ + "KAMLA SINGH" + ], + "family": "0" + } + ], + "gender": "female", + "birthDate": "1999-12-03", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "NI", + "display": "National unique individual identifier" + } + ] + }, + "system": "https://healthid.ndhm.gov.in", + "value": "unknown@abdm" + } + ] + } + }, + { + "fullUrl": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0", + "resource": { + "resourceType": "Encounter", + "id": "b047b527-5c2b-4edd-aaa8-d983d59bb2a0", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Encounter" + ] + }, + "identifier": [ + { + "system": "https://ndhm.in", + "value": "LAB-24" + } + ], + "status": "finished", + "class": { + "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", + "code": "AMB", + "display": "Ambulatory" + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545", + "display": "Patient" + }, + "period": { + "start": "2026-05-31T01:13:43+05:30" + }, + "serviceProvider": { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + } + }, + { + "fullUrl": "urn:uuid:bd2e583f-a527-4fbb-8a02-266f209425e8", + "resource": { + "resourceType": "Binary", + "id": "bd2e583f-a527-4fbb-8a02-266f209425e8", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Binary" + ] + }, + "contentType": "text/html; charset=utf-8", + "data": "PHRhYmxlIGJvcmRlcj0iMCIgY2VsbHBhZGRpbmc9IjEiIGNlbGxzcGFjaW5nPSIxIiBzdHlsZT0id2lkdGg6MTAwJSI+PHRib2R5Pjx0cj48dGQ+PGgzPkNCQyBQYW5lbDwvaDM+PC90ZD48L3RyPjwvdGJvZHk+PC90YWJsZT48cD48c3Ryb25nPkNCQyBQYW5lbDwvc3Ryb25nPjwvcD48dGFibGUgc3R5bGU9IndpZHRoOjEwMCU7Ym9yZGVyLWNvbGxhcHNlOmNvbGxhcHNlO2ZvbnQtc2l6ZToxMnB4OyIgYm9yZGVyPSIxIiBjZWxscGFkZGluZz0iNiIgY2VsbHNwYWNpbmc9IjAiPjx0aGVhZD48dHIgc3R5bGU9ImJhY2tncm91bmQ6I2Y0ZjZmODsiPjx0aCBzdHlsZT0idGV4dC1hbGlnbjpsZWZ0OyI+VGVzdCBOYW1lPC90aD48dGggc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPlJlc3VsdDwvdGg+PHRoIHN0eWxlPSJ0ZXh0LWFsaWduOmxlZnQ7Ij5Vbml0PC90aD48dGggc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPkJpby4gUmVmLiBJbnRlcnZhbDwvdGg+PC90cj48L3RoZWFkPjx0Ym9keT48dHI+PHRkPkVvc2lub3BoaWxzPC90ZD48dGQ+MzA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+SGVtb2dsb2JpbjwvdGQ+PHRkPjExPC90ZD48dGQ+Zy9kTDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlRvdGFsIExldWtvY3l0ZSBDb3VudDwvdGQ+PHRkPjEwPC90ZD48dGQ+MTBeMy91TDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk5ldXRyb3BoaWxzPC90ZD48dGQ+MTA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+THltcGhvY3l0ZXM8L3RkPjx0ZD4xMDwvdGQ+PHRkPiU8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5Nb25vY3l0ZXM8L3RkPjx0ZD4xMDwvdGQ+PHRkPiU8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5CYW5kIENlbGxzPC90ZD48dGQ+MTA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+SW1tYXR1cmUgQ2VsbHM8L3RkPjx0ZD45PC90ZD48dGQ+JTwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlJlZCBCbG9vZCBDZWxsczwvdGQ+PHRkPjEwPC90ZD48dGQ+MTBeNi91TDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlBhY2tlZCBDZWxsIFZvbHVtZTwvdGQ+PHRkPjEwPC90ZD48dGQ+JTwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk1lYW4gQ29ycHVzY3VsYXIgVm9sdW1lPC90ZD48dGQ+MTA8L3RkPjx0ZD5mTDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk1lYW4gQ29ycHVzY3VsYXIgSGVtb2dsb2JpbjwvdGQ+PHRkPjEwPC90ZD48dGQ+cGc8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5NZWFuIENvcnB1c2N1bGFyIEhlbW9nbG9iaW4gQ29uY2VudHJhdGlvbjwvdGQ+PHRkPjE8L3RkPjx0ZD5nL2RMPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+UGxhdGVsZXQgQ291bnQ8L3RkPjx0ZD4wMTA8L3RkPjx0ZD4xMF4zL3VMPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjwvdGJvZHk+PC90YWJsZT4=" + } + }, + { + "fullUrl": "urn:uuid:f96d951e-1d17-4fa0-94ae-1300052c9197", + "resource": { + "resourceType": "DocumentReference", + "id": "f96d951e-1d17-4fa0-94ae-1300052c9197", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentReference" + ] + }, + "status": "current", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "content": [ + { + "attachment": { + "contentType": "text/html; charset=utf-8", + "url": "urn:uuid:bd2e583f-a527-4fbb-8a02-266f209425e8", + "data": "PHRhYmxlIGJvcmRlcj0iMCIgY2VsbHBhZGRpbmc9IjEiIGNlbGxzcGFjaW5nPSIxIiBzdHlsZT0id2lkdGg6MTAwJSI+PHRib2R5Pjx0cj48dGQ+PGgzPkNCQyBQYW5lbDwvaDM+PC90ZD48L3RyPjwvdGJvZHk+PC90YWJsZT48cD48c3Ryb25nPkNCQyBQYW5lbDwvc3Ryb25nPjwvcD48dGFibGUgc3R5bGU9IndpZHRoOjEwMCU7Ym9yZGVyLWNvbGxhcHNlOmNvbGxhcHNlO2ZvbnQtc2l6ZToxMnB4OyIgYm9yZGVyPSIxIiBjZWxscGFkZGluZz0iNiIgY2VsbHNwYWNpbmc9IjAiPjx0aGVhZD48dHIgc3R5bGU9ImJhY2tncm91bmQ6I2Y0ZjZmODsiPjx0aCBzdHlsZT0idGV4dC1hbGlnbjpsZWZ0OyI+VGVzdCBOYW1lPC90aD48dGggc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPlJlc3VsdDwvdGg+PHRoIHN0eWxlPSJ0ZXh0LWFsaWduOmxlZnQ7Ij5Vbml0PC90aD48dGggc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPkJpby4gUmVmLiBJbnRlcnZhbDwvdGg+PC90cj48L3RoZWFkPjx0Ym9keT48dHI+PHRkPkVvc2lub3BoaWxzPC90ZD48dGQ+MzA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+SGVtb2dsb2JpbjwvdGQ+PHRkPjExPC90ZD48dGQ+Zy9kTDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlRvdGFsIExldWtvY3l0ZSBDb3VudDwvdGQ+PHRkPjEwPC90ZD48dGQ+MTBeMy91TDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk5ldXRyb3BoaWxzPC90ZD48dGQ+MTA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+THltcGhvY3l0ZXM8L3RkPjx0ZD4xMDwvdGQ+PHRkPiU8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5Nb25vY3l0ZXM8L3RkPjx0ZD4xMDwvdGQ+PHRkPiU8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5CYW5kIENlbGxzPC90ZD48dGQ+MTA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+SW1tYXR1cmUgQ2VsbHM8L3RkPjx0ZD45PC90ZD48dGQ+JTwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlJlZCBCbG9vZCBDZWxsczwvdGQ+PHRkPjEwPC90ZD48dGQ+MTBeNi91TDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlBhY2tlZCBDZWxsIFZvbHVtZTwvdGQ+PHRkPjEwPC90ZD48dGQ+JTwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk1lYW4gQ29ycHVzY3VsYXIgVm9sdW1lPC90ZD48dGQ+MTA8L3RkPjx0ZD5mTDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk1lYW4gQ29ycHVzY3VsYXIgSGVtb2dsb2JpbjwvdGQ+PHRkPjEwPC90ZD48dGQ+cGc8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5NZWFuIENvcnB1c2N1bGFyIEhlbW9nbG9iaW4gQ29uY2VudHJhdGlvbjwvdGQ+PHRkPjE8L3RkPjx0ZD5nL2RMPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+UGxhdGVsZXQgQ291bnQ8L3RkPjx0ZD4wMTA8L3RkPjx0ZD4xMF4zL3VMPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjwvdGJvZHk+PC90YWJsZT4=", + "title": "Pathology / Cytology" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:e1f47726-cf20-416c-a49c-6732c052023f", + "resource": { + "resourceType": "DiagnosticReport", + "id": "e1f47726-cf20-416c-a49c-6732c052023f", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportLab" + ] + }, + "status": "preliminary", + "code": { + "text": "Pathology / Cytology" + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545", + "display": "Patient" + }, + "issued": "2026-05-31T01:13:43+05:30", + "result": [ + { + "reference": "urn:uuid:c97dc010-8f29-4f3c-824a-5c931ce26186" + }, + { + "reference": "urn:uuid:edc1da53-7bc7-4821-a030-c09ad2bcdef8" + }, + { + "reference": "urn:uuid:967ce84c-fab7-4920-9e8e-146caa28c6b1" + }, + { + "reference": "urn:uuid:e9f0da9c-3deb-48b5-9297-7c413d0f22df" + }, + { + "reference": "urn:uuid:3957dce4-4787-463c-80d3-2f15eb7f28a2" + }, + { + "reference": "urn:uuid:60568db8-5693-463e-9ba5-a52e7c5170f7" + }, + { + "reference": "urn:uuid:2ca8a602-dad6-43c1-a248-2a4b75aedd04" + }, + { + "reference": "urn:uuid:7e8f7cf3-3cfa-4726-9dd5-384b4ccbf2d1" + }, + { + "reference": "urn:uuid:991f1e70-bf4e-4b4d-aa62-f558eed2ad0c" + }, + { + "reference": "urn:uuid:a516e2c7-270e-4b3a-9341-97336780d499" + }, + { + "reference": "urn:uuid:c0694c9e-525e-4fa4-95b2-22fe932ea175" + }, + { + "reference": "urn:uuid:0dc0a9b7-a99a-4cc8-a12e-6510706353af" + }, + { + "reference": "urn:uuid:acd9dc9e-8aaf-4959-9be1-5d259af0dd74" + }, + { + "reference": "urn:uuid:3a992302-b91f-4655-82cb-42dd80787279" + } + ], + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "resultsInterpreter": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "conclusion": "Laboratory report generated" + } + }, + { + "fullUrl": "urn:uuid:c97dc010-8f29-4f3c-824a-5c931ce26186", + "resource": { + "resourceType": "Observation", + "id": "c97dc010-8f29-4f3c-824a-5c931ce26186", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Eosinophils", + "coding": [ + { + "system": "http://loinc.org", + "code": "713-8", + "display": "Eosinophils" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 30, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:edc1da53-7bc7-4821-a030-c09ad2bcdef8", + "resource": { + "resourceType": "Observation", + "id": "edc1da53-7bc7-4821-a030-c09ad2bcdef8", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Hemoglobin", + "coding": [ + { + "system": "http://loinc.org", + "code": "718-7", + "display": "Hemoglobin" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 11, + "unit": "g/dL", + "system": "http://unitsofmeasure.org", + "code": "g/dL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:967ce84c-fab7-4920-9e8e-146caa28c6b1", + "resource": { + "resourceType": "Observation", + "id": "967ce84c-fab7-4920-9e8e-146caa28c6b1", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Total Leukocyte Count", + "coding": [ + { + "system": "http://loinc.org", + "code": "6690-2", + "display": "Total Leukocyte Count" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "10^3/uL", + "system": "http://unitsofmeasure.org", + "code": "10*3/uL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:e9f0da9c-3deb-48b5-9297-7c413d0f22df", + "resource": { + "resourceType": "Observation", + "id": "e9f0da9c-3deb-48b5-9297-7c413d0f22df", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Neutrophils", + "coding": [ + { + "system": "http://loinc.org", + "code": "770-8", + "display": "Neutrophils" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:3957dce4-4787-463c-80d3-2f15eb7f28a2", + "resource": { + "resourceType": "Observation", + "id": "3957dce4-4787-463c-80d3-2f15eb7f28a2", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Lymphocytes", + "coding": [ + { + "system": "http://loinc.org", + "code": "736-9", + "display": "Lymphocytes" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:60568db8-5693-463e-9ba5-a52e7c5170f7", + "resource": { + "resourceType": "Observation", + "id": "60568db8-5693-463e-9ba5-a52e7c5170f7", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Monocytes", + "coding": [ + { + "system": "http://loinc.org", + "code": "5905-5", + "display": "Monocytes" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:2ca8a602-dad6-43c1-a248-2a4b75aedd04", + "resource": { + "resourceType": "Observation", + "id": "2ca8a602-dad6-43c1-a248-2a4b75aedd04", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Band Cells", + "coding": [ + { + "system": "http://loinc.org", + "code": "764-1", + "display": "Band Cells" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:7e8f7cf3-3cfa-4726-9dd5-384b4ccbf2d1", + "resource": { + "resourceType": "Observation", + "id": "7e8f7cf3-3cfa-4726-9dd5-384b4ccbf2d1", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Immature Cells", + "coding": [ + { + "system": "http://loinc.org", + "code": "38518-7", + "display": "Immature Cells" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 9, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:991f1e70-bf4e-4b4d-aa62-f558eed2ad0c", + "resource": { + "resourceType": "Observation", + "id": "991f1e70-bf4e-4b4d-aa62-f558eed2ad0c", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Red Blood Cells", + "coding": [ + { + "system": "http://loinc.org", + "code": "789-8", + "display": "Red Blood Cells" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "10^6/uL", + "system": "http://unitsofmeasure.org", + "code": "10^6/uL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:a516e2c7-270e-4b3a-9341-97336780d499", + "resource": { + "resourceType": "Observation", + "id": "a516e2c7-270e-4b3a-9341-97336780d499", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Packed Cell Volume", + "coding": [ + { + "system": "http://loinc.org", + "code": "4544-3", + "display": "Packed Cell Volume" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:c0694c9e-525e-4fa4-95b2-22fe932ea175", + "resource": { + "resourceType": "Observation", + "id": "c0694c9e-525e-4fa4-95b2-22fe932ea175", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Mean Corpuscular Volume", + "coding": [ + { + "system": "http://loinc.org", + "code": "8457-4", + "display": "Mean Corpuscular Volume" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "fL", + "system": "http://unitsofmeasure.org", + "code": "fL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:0dc0a9b7-a99a-4cc8-a12e-6510706353af", + "resource": { + "resourceType": "Observation", + "id": "0dc0a9b7-a99a-4cc8-a12e-6510706353af", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Mean Corpuscular Hemoglobin", + "coding": [ + { + "system": "http://loinc.org", + "code": "785-6", + "display": "Mean Corpuscular Hemoglobin" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "pg", + "system": "http://unitsofmeasure.org", + "code": "pg" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:acd9dc9e-8aaf-4959-9be1-5d259af0dd74", + "resource": { + "resourceType": "Observation", + "id": "acd9dc9e-8aaf-4959-9be1-5d259af0dd74", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Mean Corpuscular Hemoglobin Concentration", + "coding": [ + { + "system": "http://loinc.org", + "code": "59260-0", + "display": "Mean Corpuscular Hemoglobin Concentration" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 1, + "unit": "g/dL", + "system": "http://unitsofmeasure.org", + "code": "g/dL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:3a992302-b91f-4655-82cb-42dd80787279", + "resource": { + "resourceType": "Observation", + "id": "3a992302-b91f-4655-82cb-42dd80787279", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Platelet Count", + "coding": [ + { + "system": "http://loinc.org", + "code": "777-3", + "display": "Platelet Count" + } + ] + }, + "subject": { + "reference": "urn:uuid:6d70bbfb-30d5-4cd8-a1b1-96cb289cc545" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:b047b527-5c2b-4edd-aaa8-d983d59bb2a0" + }, + "performer": [ + { + "reference": "urn:uuid:93b37534-d7ed-4d0a-89ac-ecec3967aabf" + } + ], + "valueQuantity": { + "value": 10, + "unit": "10^3/uL", + "system": "http://unitsofmeasure.org", + "code": "10*3/uL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/writable/tmp/abdm_lab_bundle_sample.json b/writable/tmp/abdm_lab_bundle_sample.json new file mode 100644 index 0000000..5e7d364 --- /dev/null +++ b/writable/tmp/abdm_lab_bundle_sample.json @@ -0,0 +1,485 @@ +{ + "resourceType": "Bundle", + "id": "8b32a3dc-90b7-4663-9706-4e1b7dddcb08", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentBundle" + ], + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "V", + "display": "very restricted" + } + ] + }, + "identifier": { + "system": "https://in0510000828.hfr.abdm.gov.in", + "value": "LAB-5234342" + }, + "type": "document", + "timestamp": "2026-05-31T02:55:24+05:30", + "entry": [ + { + "fullUrl": "urn:uuid:8dfb434d-9a89-4edd-85d2-c8d5f83b9789", + "resource": { + "resourceType": "Composition", + "id": "8dfb434d-9a89-4edd-85d2-c8d5f83b9789", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportRecord" + ] + }, + "language": "en-IN", + "identifier": { + "system": "https://ndhm.in/phr", + "value": "8dfb434d-9a89-4edd-85d2-c8d5f83b9789" + }, + "status": "final", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic Report- Lab" + } + ], + "text": "Diagnostic Report- Lab" + }, + "subject": { + "reference": "urn:uuid:20985d9e-052b-4bf3-9fd8-48536dd9a983", + "display": "Patient" + }, + "date": "2026-05-31T02:55:24+05:30", + "author": [ + { + "reference": "urn:uuid:c603b863-6bcd-42ee-bda3-a879109a5d7f" + } + ], + "title": "Diagnostic Report- Lab", + "section": [ + { + "title": "Hematology report", + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "4321000179101", + "display": "Hematology report" + } + ] + }, + "entry": [ + { + "reference": "urn:uuid:ca6f12a8-bf1a-4524-8c53-1fa799a786e4", + "type": "DiagnosticReport" + }, + { + "reference": "urn:uuid:0a00ee19-3066-4cc4-82c2-80326bb62117", + "type": "DocumentReference" + } + ] + } + ], + "encounter": { + "reference": "urn:uuid:189c3210-c036-4a61-9064-a1034314f686" + }, + "custodian": { + "reference": "urn:uuid:c603b863-6bcd-42ee-bda3-a879109a5d7f" + } + } + }, + { + "fullUrl": "urn:uuid:c603b863-6bcd-42ee-bda3-a879109a5d7f", + "resource": { + "resourceType": "Organization", + "id": "c603b863-6bcd-42ee-bda3-a879109a5d7f", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Organization" + ] + }, + "name": "XYZ Lab Pvt.Ltd.", + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "PRN", + "display": "Provider number" + } + ] + }, + "system": "https://facility.ndhm.gov.in", + "value": "IN0510000828" + } + ] + } + }, + { + "fullUrl": "urn:uuid:20985d9e-052b-4bf3-9fd8-48536dd9a983", + "resource": { + "resourceType": "Patient", + "id": "20985d9e-052b-4bf3-9fd8-48536dd9a983", + "name": [ + { + "text": "ABC Male", + "given": [ + "ABC" + ], + "family": "Male" + } + ], + "gender": "male", + "birthDate": "1981-01-12", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "NI", + "display": "National unique individual identifier" + } + ] + }, + "system": "https://healthid.ndhm.gov.in", + "value": "abha_2@sbx" + } + ] + } + }, + { + "fullUrl": "urn:uuid:189c3210-c036-4a61-9064-a1034314f686", + "resource": { + "resourceType": "Encounter", + "id": "189c3210-c036-4a61-9064-a1034314f686", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Encounter" + ] + }, + "identifier": [ + { + "system": "https://ndhm.in", + "value": "LAB-5234342" + } + ], + "status": "finished", + "class": { + "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", + "code": "AMB", + "display": "Ambulatory" + }, + "subject": { + "reference": "urn:uuid:20985d9e-052b-4bf3-9fd8-48536dd9a983", + "display": "Patient" + }, + "period": { + "start": "2020-07-10T11:45:33+05:30" + }, + "serviceProvider": { + "reference": "urn:uuid:c603b863-6bcd-42ee-bda3-a879109a5d7f" + } + } + }, + { + "fullUrl": "urn:uuid:54e676df-4db0-4cc2-b28c-5fcf2c93ce1e", + "resource": { + "resourceType": "Binary", + "id": "54e676df-4db0-4cc2-b28c-5fcf2c93ce1e", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Binary" + ] + }, + "contentType": "text/html; charset=utf-8", + "data": "PGh0bWw+PGJvZHk+PGgxPkxhYiBSZXBvcnQ8L2gxPjxwPkNCQyBhbmQgbGlwaWQgcGFuZWwuPC9wPjwvYm9keT48L2h0bWw+" + } + }, + { + "fullUrl": "urn:uuid:93486ce6-6250-4317-b0c8-32bb8a853605", + "resource": { + "resourceType": "DocumentReference", + "id": "93486ce6-6250-4317-b0c8-32bb8a853605", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentReference" + ] + }, + "status": "current", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ] + }, + "subject": { + "reference": "urn:uuid:20985d9e-052b-4bf3-9fd8-48536dd9a983" + }, + "content": [ + { + "attachment": { + "contentType": "text/html; charset=utf-8", + "url": "urn:uuid:54e676df-4db0-4cc2-b28c-5fcf2c93ce1e", + "data": "PGh0bWw+PGJvZHk+PGgxPkxhYiBSZXBvcnQ8L2gxPjxwPkNCQyBhbmQgbGlwaWQgcGFuZWwuPC9wPjwvYm9keT48L2h0bWw+", + "title": "Diagnostic Report- Lab" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:99d0bcfc-31cf-4c85-9bdc-550fe8f3bada", + "resource": { + "resourceType": "Binary", + "id": "99d0bcfc-31cf-4c85-9bdc-550fe8f3bada", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Binary" + ] + }, + "contentType": "application/pdf", + "data": "JVBERi0xLjQgbW9jayBsYWIgcmVwb3J0JQ==" + } + }, + { + "fullUrl": "urn:uuid:0a00ee19-3066-4cc4-82c2-80326bb62117", + "resource": { + "resourceType": "DocumentReference", + "id": "0a00ee19-3066-4cc4-82c2-80326bb62117", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentReference" + ] + }, + "status": "current", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ] + }, + "subject": { + "reference": "urn:uuid:20985d9e-052b-4bf3-9fd8-48536dd9a983" + }, + "content": [ + { + "attachment": { + "contentType": "application/pdf", + "url": "urn:uuid:99d0bcfc-31cf-4c85-9bdc-550fe8f3bada", + "data": "JVBERi0xLjQgbW9jayBsYWIgcmVwb3J0JQ==", + "title": "Laboratory report" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:ca6f12a8-bf1a-4524-8c53-1fa799a786e4", + "resource": { + "resourceType": "DiagnosticReport", + "id": "ca6f12a8-bf1a-4524-8c53-1fa799a786e4", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportLab" + ] + }, + "status": "final", + "code": { + "coding": [ + { + "system": "http://loinc.org", + "code": "24331-1", + "display": "Diagnostic Report- Lab" + } + ], + "text": "Diagnostic Report- Lab" + }, + "subject": { + "reference": "urn:uuid:20985d9e-052b-4bf3-9fd8-48536dd9a983", + "display": "Patient" + }, + "issued": "2020-07-10T11:45:33+05:30", + "result": [ + { + "reference": "urn:uuid:dac84873-63d4-4623-a4b6-df9f29762bc3" + }, + { + "reference": "urn:uuid:ad4d6e30-2421-4158-879e-ef0c8773817f" + } + ], + "performer": [ + { + "reference": "urn:uuid:c603b863-6bcd-42ee-bda3-a879109a5d7f" + } + ], + "resultsInterpreter": [ + { + "reference": "urn:uuid:c603b863-6bcd-42ee-bda3-a879109a5d7f" + } + ], + "encounter": { + "reference": "urn:uuid:189c3210-c036-4a61-9064-a1034314f686" + }, + "category": [ + { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "708196005", + "display": "Hematology service" + } + ] + } + ], + "conclusion": "Elevated cholesterol/high density lipoprotein ratio", + "presentedForm": [ + { + "contentType": "application/pdf", + "title": "Laboratory report", + "url": "urn:uuid:0a00ee19-3066-4cc4-82c2-80326bb62117" + } + ] + } + }, + { + "fullUrl": "urn:uuid:dac84873-63d4-4623-a4b6-df9f29762bc3", + "resource": { + "resourceType": "Observation", + "id": "dac84873-63d4-4623-a4b6-df9f29762bc3", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "final", + "code": { + "text": "Triglyceride [Mass/volume] in Serum or Plasma", + "coding": [ + { + "system": "http://loinc.org", + "code": "2571-8", + "display": "Triglyceride [Mass/volume] in Serum or Plasma" + } + ] + }, + "subject": { + "reference": "urn:uuid:20985d9e-052b-4bf3-9fd8-48536dd9a983" + }, + "effectiveDateTime": "2020-07-10T11:45:33+05:30", + "encounter": { + "reference": "urn:uuid:189c3210-c036-4a61-9064-a1034314f686" + }, + "performer": [ + { + "reference": "urn:uuid:c603b863-6bcd-42ee-bda3-a879109a5d7f" + } + ], + "valueQuantity": { + "value": 146, + "unit": "mg/dL", + "system": "http://unitsofmeasure.org", + "code": "mg/dL" + }, + "referenceRange": [ + { + "high": { + "value": 150, + "unit": "mg/dL", + "system": "http://unitsofmeasure.org", + "code": "mg/dL" + } + } + ], + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + }, + { + "fullUrl": "urn:uuid:ad4d6e30-2421-4158-879e-ef0c8773817f", + "resource": { + "resourceType": "Observation", + "id": "ad4d6e30-2421-4158-879e-ef0c8773817f", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "final", + "code": { + "text": "Cholesterol in HDL [Mass/volume] in Serum or Plasma", + "coding": [ + { + "system": "http://loinc.org", + "code": "2085-9", + "display": "Cholesterol in HDL [Mass/volume] in Serum or Plasma" + } + ] + }, + "subject": { + "reference": "urn:uuid:20985d9e-052b-4bf3-9fd8-48536dd9a983" + }, + "effectiveDateTime": "2020-07-10T11:45:33+05:30", + "encounter": { + "reference": "urn:uuid:189c3210-c036-4a61-9064-a1034314f686" + }, + "performer": [ + { + "reference": "urn:uuid:c603b863-6bcd-42ee-bda3-a879109a5d7f" + } + ], + "valueQuantity": { + "value": 45, + "unit": "mg/dL", + "system": "http://unitsofmeasure.org", + "code": "mg/dL" + }, + "referenceRange": [ + { + "low": { + "value": 40, + "unit": "mg/dL", + "system": "http://unitsofmeasure.org", + "code": "mg/dL" + } + } + ], + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "N" + } + ], + "text": "N" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/writable/tmp/abdm_radiology_bundle_real_check.php b/writable/tmp/abdm_radiology_bundle_real_check.php new file mode 100644 index 0000000..2fbcb6c --- /dev/null +++ b/writable/tmp/abdm_radiology_bundle_real_check.php @@ -0,0 +1,332 @@ +real_escape_string($table); + $res = $mysqli->query("SHOW TABLES LIKE '{$safe}'"); + if (!$res) { + return false; + } + $exists = $res->num_rows > 0; + $res->free(); + return $exists; +} + +function unitToUcum(string $unit): string +{ + $map = ['mg/dl' => 'mg/dL', 'g/dl' => 'g/dL', 'mmhg' => 'mm[Hg]']; + $u = strtolower(trim($unit)); + if ($u === '') { + return ''; + } + return $map[$u] ?? trim($unit); +} + +$root = dirname(__DIR__, 2); +$env = parseEnvFile($root . '/.env'); +$host = envVal($env, 'database.default.hostname', '127.0.0.1'); +$user = envVal($env, 'database.default.username', 'root'); +$pass = envVal($env, 'database.default.password', ''); +$db = envVal($env, 'database.default.database', ''); +$port = (int) envVal($env, 'database.default.port', '3306'); + +if ($db === '') { + fwrite(STDERR, "Could not determine database.default.database from .env\n"); + exit(1); +} + +$mysqli = @new mysqli($host, $user, $pass, $db, $port); +if ($mysqli->connect_errno) { + fwrite(STDERR, "DB connection failed: " . $mysqli->connect_error . "\n"); + exit(1); +} +$mysqli->set_charset('utf8mb4'); + +$columns = []; +$colResult = $mysqli->query('SHOW COLUMNS FROM lab_request'); +while ($colResult && ($row = $colResult->fetch_assoc())) { + $columns[] = (string) ($row['Field'] ?? ''); +} +$colResult?->free(); + +$patientIdCol = null; +foreach (['patient_id', 'pid', 'patient_master_id', 'pat_id'] as $candidate) { + if (in_array($candidate, $columns, true)) { + $patientIdCol = $candidate; + break; + } +} +$patientSelect = $patientIdCol !== null ? ", {$patientIdCol} AS resolved_patient_id" : ', 0 AS resolved_patient_id'; + +$usedFallback = false; +$sql = "SELECT id, patient_name, lab_type, charge_id, Report_Data, report_data_Impression, status, reported_time, lab_repo_id {$patientSelect} FROM lab_request WHERE lab_type = 6 AND COALESCE(Report_Data,'') <> '' ORDER BY id DESC LIMIT 1"; +$labResult = $mysqli->query($sql); +$labRow = $labResult ? $labResult->fetch_assoc() : null; +$labResult?->free(); + +if (!$labRow) { + $usedFallback = true; + $fallbackSql = "SELECT id, patient_name, lab_type, charge_id, Report_Data, report_data_Impression, status, reported_time, lab_repo_id {$patientSelect} FROM lab_request WHERE COALESCE(Report_Data,'') <> '' ORDER BY id DESC LIMIT 1"; + $fallbackResult = $mysqli->query($fallbackSql); + $labRow = $fallbackResult ? $fallbackResult->fetch_assoc() : null; + $fallbackResult?->free(); +} + +if (!$labRow) { + fwrite(STDERR, "No lab_request row found with non-empty Report_Data for fallback validation\n"); + exit(2); +} + +$labReqId = (int) ($labRow['id'] ?? 0); +$patientId = (int) ($labRow['resolved_patient_id'] ?? 0); + +$patientRow = []; +if ($patientId > 0 && tableExists($mysqli, 'patient_master')) { + $stmt = $mysqli->prepare('SELECT id, p_fname, p_lname, gender, dob FROM patient_master WHERE id = ? LIMIT 1'); + if ($stmt) { + $stmt->bind_param('i', $patientId); + $stmt->execute(); + $res = $stmt->get_result(); + $patientRow = $res ? ($res->fetch_assoc() ?: []) : []; + $res?->free(); + $stmt->close(); + } +} + +$patName = trim(trim((string) ($patientRow['p_fname'] ?? '')) . ' ' . trim((string) ($patientRow['p_lname'] ?? ''))); +if ($patName === '') { + $patName = trim((string) ($labRow['patient_name'] ?? '')); +} +if ($patName === '') { + $patName = 'Unknown'; +} + +$testTitle = 'Radiology Report'; +$chargeId = (int) ($labRow['charge_id'] ?? 0); +if ($chargeId > 0 && tableExists($mysqli, 'charge_master')) { + $chargeStmt = $mysqli->prepare('SELECT charge_name FROM charge_master WHERE id = ? LIMIT 1'); + if ($chargeStmt) { + $chargeStmt->bind_param('i', $chargeId); + $chargeStmt->execute(); + $res = $chargeStmt->get_result(); + $row = $res ? ($res->fetch_assoc() ?: []) : []; + $res?->free(); + $chargeStmt->close(); + $name = trim((string) ($row['charge_name'] ?? '')); + if ($name !== '') { + $testTitle = $name; + } + } +} + +$repoLoinc = ''; +$labRepoId = (int) ($labRow['lab_repo_id'] ?? 0); +if ($labRepoId > 0 && tableExists($mysqli, 'lab_repo')) { + $repoStmt = $mysqli->prepare('SELECT loinc_code FROM lab_repo WHERE mstRepoKey = ? LIMIT 1'); + if ($repoStmt) { + $repoStmt->bind_param('i', $labRepoId); + $repoStmt->execute(); + $res = $repoStmt->get_result(); + $row = $res ? ($res->fetch_assoc() ?: []) : []; + $res?->free(); + $repoStmt->close(); + $repoLoinc = trim((string) ($row['loinc_code'] ?? '')); + } +} + +$hospitalName = ''; +$hfrId = ''; +if (tableExists($mysqli, 'hospital_setting')) { + $hs = $mysqli->query("SELECT s_name, s_value FROM hospital_setting WHERE s_name IN ('ABDM_HMS_NAME','ABDM_HFR_ID','H_Name')"); + $settings = []; + while ($hs && ($row = $hs->fetch_assoc())) { + $settings[(string) $row['s_name']] = (string) ($row['s_value'] ?? ''); + } + $hs?->free(); + $hospitalName = trim((string) ($settings['ABDM_HMS_NAME'] ?? $settings['H_Name'] ?? '')); + $hfrId = trim((string) ($settings['ABDM_HFR_ID'] ?? '')); +} + +$observations = []; +if (tableExists($mysqli, 'lab_request_item') && tableExists($mysqli, 'lab_tests')) { + $obsSql = "SELECT lri.lab_test_id, lri.lab_test_value, lt.Test, lt.Unit, lt.FixedNormals, lt.loinc_code, lt.loinc_scale FROM lab_request_item lri LEFT JOIN lab_tests lt ON lt.mstTestKey = lri.lab_test_id WHERE lri.lab_request_id = ? ORDER BY lri.id ASC"; + $obsStmt = $mysqli->prepare($obsSql); + if ($obsStmt) { + $obsStmt->bind_param('i', $labReqId); + $obsStmt->execute(); + $obsRes = $obsStmt->get_result(); + while ($obsRes && ($row = $obsRes->fetch_assoc())) { + $rawValue = trim((string) ($row['lab_test_value'] ?? '')); + if ($rawValue === '' || strtolower($rawValue) === 'n/a') { + continue; + } + $scale = strtolower(trim((string) ($row['loinc_scale'] ?? ''))); + $isNumeric = is_numeric($rawValue) && $scale !== 'nom' && $scale !== 'ord'; + $valueType = $isNumeric ? 'quantity' : 'string'; + $unit = trim((string) ($row['Unit'] ?? '')); + $observations[] = [ + 'test_name' => trim((string) ($row['Test'] ?? '')) ?: ('Test-' . (string) ($row['lab_test_id'] ?? 'X')), + 'loinc_code' => trim((string) ($row['loinc_code'] ?? '')), + 'value_type' => $valueType, + 'value' => $rawValue, + 'unit' => $unit, + 'ucum_code' => unitToUcum($unit), + 'status' => ((string) ($labRow['status'] ?? '0')) === '1' ? 'final' : 'preliminary', + ]; + } + $obsRes?->free(); + $obsStmt->close(); + } +} + +$reportedRaw = trim((string) ($labRow['reported_time'] ?? '')); +$reportedAt = $reportedRaw !== '' ? (new DateTime($reportedRaw, new DateTimeZone('Asia/Kolkata')))->format('Y-m-d\\TH:i:sP') : ''; + +$patient = [ + 'id' => (string) ($patientId > 0 ? $patientId : ('RAD-' . $labReqId)), + 'name' => $patName, + 'gender' => trim((string) ($patientRow['gender'] ?? '')), + 'birthDate' => !empty($patientRow['dob']) ? date('Y-m-d', strtotime((string) $patientRow['dob'])) : '', + 'abhaAddress' => 'unknown@abdm', +]; + +$diagnosticReport = [ + 'id' => (string) $labReqId, + 'title' => $testTitle, + 'status' => ((string) ($labRow['status'] ?? '0')) === '1' ? 'final' : 'preliminary', + 'conclusion' => trim((string) ($labRow['report_data_Impression'] ?? '')), + 'reported_at' => $reportedAt, + 'report_html' => trim((string) ($labRow['Report_Data'] ?? '')), + 'is_imaging' => true, + 'report_domain' => 'imaging', + 'section_title' => 'Computed tomography imaging report', + 'section_snomed_code' => '371531008', + 'section_snomed_display' => 'Computed tomography imaging report', +]; +if ($repoLoinc !== '') { + $diagnosticReport['loinc_code'] = $repoLoinc; +} + +$organization = $hospitalName !== '' ? ['name' => $hospitalName, 'hfr_id' => $hfrId] : null; +$encounter = [ + 'id' => 'RAD-' . $labReqId, + 'status' => 'finished', + 'class_code' => 'AMB', + 'period_start' => $reportedAt, +]; + +$attachment = [ + 'title' => $testTitle . ' PDF Report', + 'content_type' => 'application/pdf', + 'data_base64' => base64_encode('%PDF-1.4 mock radiology report%'), +]; + +$builder = new FhirR4Builder(); +$bundle = $builder->buildLabReportBundle($patient, $diagnosticReport, $observations, null, $organization, $encounter, $attachment); + +$resources = []; +foreach (($bundle['entry'] ?? []) as $entry) { + $r = $entry['resource'] ?? null; + if (is_array($r) && isset($r['resourceType'])) { + $resources[] = $r; + } +} +$byType = []; +foreach ($resources as $res) { + $type = (string) ($res['resourceType'] ?? ''); + if ($type !== '') { + $byType[$type][] = $res; + } +} + +$composition = $byType['Composition'][0] ?? []; +$diagReport = $byType['DiagnosticReport'][0] ?? []; +$section = $composition['section'][0] ?? []; +$sectionEntries = (array) ($section['entry'] ?? []); +$hasTypedDiag = false; +$hasTypedDocRef = false; +foreach ($sectionEntries as $entry) { + if (($entry['type'] ?? '') === 'DiagnosticReport') { + $hasTypedDiag = true; + } + if (($entry['type'] ?? '') === 'DocumentReference') { + $hasTypedDocRef = true; + } +} + +$checks = [ + ['check' => 'Bundle profile DocumentBundle', 'ok' => in_array('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentBundle', (array) (($bundle['meta']['profile'] ?? [])), true)], + ['check' => 'Composition profile DiagnosticReportRecord', 'ok' => in_array('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportRecord', (array) (($composition['meta']['profile'] ?? [])), true)], + ['check' => 'DiagnosticReport profile DiagnosticReportImaging', 'ok' => in_array('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportImaging', (array) (($diagReport['meta']['profile'] ?? [])), true)], + ['check' => 'Composition section has typed DiagnosticReport entry', 'ok' => $hasTypedDiag], + ['check' => 'Composition section has typed DocumentReference entry', 'ok' => $hasTypedDocRef], + ['check' => 'DiagnosticReport.resultsInterpreter present', 'ok' => count((array) ($diagReport['resultsInterpreter'] ?? [])) >= 1], + ['check' => 'DiagnosticReport.conclusion non-empty', 'ok' => trim((string) ($diagReport['conclusion'] ?? '')) !== ''], + ['check' => 'DiagnosticReport.media present', 'ok' => count((array) ($diagReport['media'] ?? [])) >= 1], + ['check' => 'Media resource present in bundle', 'ok' => !empty($byType['Media'])], +]; + +$out = $root . '/writable/tmp/abdm_radiology_bundle_real_sample.json'; +file_put_contents($out, json_encode($bundle, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + +$failed = array_values(array_filter($checks, static fn(array $c): bool => empty($c['ok']))); + +echo "ABDM Real Radiology Bundle Check\n"; +echo "================================\n"; +echo 'Lab Request ID: ' . $labReqId . "\n"; +echo 'Source lab_type: ' . (string) ($labRow['lab_type'] ?? '') . "\n"; +echo 'Fallback source used: ' . ($usedFallback ? 'yes' : 'no') . "\n"; +echo 'Patient ID: ' . ($patientId > 0 ? (string) $patientId : 'N/A') . "\n"; +echo 'Observations: ' . count($observations) . "\n\n"; +foreach ($checks as $c) { + echo sprintf("[%s] %s\n", $c['ok'] ? 'PASS' : 'FAIL', $c['check']); +} + +echo "\nSaved radiology sample bundle to writable/tmp/abdm_radiology_bundle_real_sample.json\n"; +if ($failed !== []) { + echo 'Remaining gaps found: ' . count($failed) . "\n"; + exit(2); +} + +echo "No gaps found in this radiology profile sanity check.\n"; +$mysqli->close(); +exit(0); diff --git a/writable/tmp/abdm_radiology_bundle_real_sample.json b/writable/tmp/abdm_radiology_bundle_real_sample.json new file mode 100644 index 0000000..73e4e41 --- /dev/null +++ b/writable/tmp/abdm_radiology_bundle_real_sample.json @@ -0,0 +1,982 @@ +{ + "resourceType": "Bundle", + "id": "be5ce2e5-0e82-49e5-8d6b-137259130d72", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentBundle" + ], + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "V", + "display": "very restricted" + } + ] + }, + "identifier": { + "system": "https://in0510000828.hfr.abdm.gov.in", + "value": "LAB-24" + }, + "type": "document", + "timestamp": "2026-05-31T03:27:38+05:30", + "entry": [ + { + "fullUrl": "urn:uuid:d69a5070-6e71-4391-a6d3-c6f6db596f8c", + "resource": { + "resourceType": "Composition", + "id": "d69a5070-6e71-4391-a6d3-c6f6db596f8c", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportRecord" + ] + }, + "language": "en-IN", + "identifier": { + "system": "https://ndhm.in/phr", + "value": "d69a5070-6e71-4391-a6d3-c6f6db596f8c" + }, + "status": "final", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721979004", + "display": "Diagnostic Report- Imaging" + } + ], + "text": "Diagnostic Report- Imaging" + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71", + "display": "Patient" + }, + "date": "2026-05-31T03:27:38+05:30", + "author": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "title": "Radiology Report", + "section": [ + { + "title": "Computed tomography imaging report", + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "371531008", + "display": "Computed tomography imaging report" + } + ] + }, + "entry": [ + { + "reference": "urn:uuid:42c3c982-963f-4286-97b6-0e6f47384ec1", + "type": "DiagnosticReport" + }, + { + "reference": "urn:uuid:5ae8c7fd-3612-4dd2-8b9c-c7678fd5ea2c", + "type": "DocumentReference" + } + ] + } + ], + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "custodian": { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + } + }, + { + "fullUrl": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6", + "resource": { + "resourceType": "Organization", + "id": "0a3a334b-2fe0-4466-b528-0dffd612a1c6", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Organization" + ] + }, + "name": "DevSoft Tech HMS", + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "PRN", + "display": "Provider number" + } + ] + }, + "system": "https://facility.ndhm.gov.in", + "value": "IN0510000828" + } + ] + } + }, + { + "fullUrl": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71", + "resource": { + "resourceType": "Patient", + "id": "ddc0b57a-0e23-4064-a7bf-0f8e0918be71", + "name": [ + { + "text": "KAMLA SINGH 0", + "given": [ + "KAMLA SINGH" + ], + "family": "0" + } + ], + "gender": "2", + "birthDate": "1999-12-03", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "NI", + "display": "National unique individual identifier" + } + ] + }, + "system": "https://healthid.ndhm.gov.in", + "value": "unknown@abdm" + } + ] + } + }, + { + "fullUrl": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673", + "resource": { + "resourceType": "Encounter", + "id": "1bf6a0f5-be01-470e-b431-150383993673", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Encounter" + ] + }, + "identifier": [ + { + "system": "https://ndhm.in", + "value": "RAD-24" + } + ], + "status": "finished", + "class": { + "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", + "code": "AMB", + "display": "Ambulatory" + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71", + "display": "Patient" + }, + "period": { + "start": "2026-05-31T01:13:43+05:30" + }, + "serviceProvider": { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + } + }, + { + "fullUrl": "urn:uuid:cf3cdee1-de65-478c-b607-6fef3bb4579e", + "resource": { + "resourceType": "Binary", + "id": "cf3cdee1-de65-478c-b607-6fef3bb4579e", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Binary" + ] + }, + "contentType": "text/html; charset=utf-8", + "data": "PHRhYmxlIGJvcmRlcj0iMCIgY2VsbHBhZGRpbmc9IjEiIGNlbGxzcGFjaW5nPSIxIiBzdHlsZT0id2lkdGg6MTAwJSI+PHRib2R5Pjx0cj48dGQ+PGgzPkNCQyBQYW5lbDwvaDM+PC90ZD48L3RyPjwvdGJvZHk+PC90YWJsZT48cD48c3Ryb25nPkNCQyBQYW5lbDwvc3Ryb25nPjwvcD48dGFibGUgc3R5bGU9IndpZHRoOjEwMCU7Ym9yZGVyLWNvbGxhcHNlOmNvbGxhcHNlO2ZvbnQtc2l6ZToxMnB4OyIgYm9yZGVyPSIxIiBjZWxscGFkZGluZz0iNiIgY2VsbHNwYWNpbmc9IjAiPjx0aGVhZD48dHIgc3R5bGU9ImJhY2tncm91bmQ6I2Y0ZjZmODsiPjx0aCBzdHlsZT0idGV4dC1hbGlnbjpsZWZ0OyI+VGVzdCBOYW1lPC90aD48dGggc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPlJlc3VsdDwvdGg+PHRoIHN0eWxlPSJ0ZXh0LWFsaWduOmxlZnQ7Ij5Vbml0PC90aD48dGggc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPkJpby4gUmVmLiBJbnRlcnZhbDwvdGg+PC90cj48L3RoZWFkPjx0Ym9keT48dHI+PHRkPkVvc2lub3BoaWxzPC90ZD48dGQ+MzA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+SGVtb2dsb2JpbjwvdGQ+PHRkPjExPC90ZD48dGQ+Zy9kTDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlRvdGFsIExldWtvY3l0ZSBDb3VudDwvdGQ+PHRkPjEwPC90ZD48dGQ+MTBeMy91TDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk5ldXRyb3BoaWxzPC90ZD48dGQ+MTA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+THltcGhvY3l0ZXM8L3RkPjx0ZD4xMDwvdGQ+PHRkPiU8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5Nb25vY3l0ZXM8L3RkPjx0ZD4xMDwvdGQ+PHRkPiU8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5CYW5kIENlbGxzPC90ZD48dGQ+MTA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+SW1tYXR1cmUgQ2VsbHM8L3RkPjx0ZD45PC90ZD48dGQ+JTwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlJlZCBCbG9vZCBDZWxsczwvdGQ+PHRkPjEwPC90ZD48dGQ+MTBeNi91TDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlBhY2tlZCBDZWxsIFZvbHVtZTwvdGQ+PHRkPjEwPC90ZD48dGQ+JTwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk1lYW4gQ29ycHVzY3VsYXIgVm9sdW1lPC90ZD48dGQ+MTA8L3RkPjx0ZD5mTDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk1lYW4gQ29ycHVzY3VsYXIgSGVtb2dsb2JpbjwvdGQ+PHRkPjEwPC90ZD48dGQ+cGc8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5NZWFuIENvcnB1c2N1bGFyIEhlbW9nbG9iaW4gQ29uY2VudHJhdGlvbjwvdGQ+PHRkPjE8L3RkPjx0ZD5nL2RMPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+UGxhdGVsZXQgQ291bnQ8L3RkPjx0ZD4wMTA8L3RkPjx0ZD4xMF4zL3VMPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjwvdGJvZHk+PC90YWJsZT4=" + } + }, + { + "fullUrl": "urn:uuid:6d86bc6d-89c3-4ca4-9e64-4c3e20a0fa6d", + "resource": { + "resourceType": "DocumentReference", + "id": "6d86bc6d-89c3-4ca4-9e64-4c3e20a0fa6d", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentReference" + ] + }, + "status": "current", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "content": [ + { + "attachment": { + "contentType": "text/html; charset=utf-8", + "url": "urn:uuid:cf3cdee1-de65-478c-b607-6fef3bb4579e", + "data": "PHRhYmxlIGJvcmRlcj0iMCIgY2VsbHBhZGRpbmc9IjEiIGNlbGxzcGFjaW5nPSIxIiBzdHlsZT0id2lkdGg6MTAwJSI+PHRib2R5Pjx0cj48dGQ+PGgzPkNCQyBQYW5lbDwvaDM+PC90ZD48L3RyPjwvdGJvZHk+PC90YWJsZT48cD48c3Ryb25nPkNCQyBQYW5lbDwvc3Ryb25nPjwvcD48dGFibGUgc3R5bGU9IndpZHRoOjEwMCU7Ym9yZGVyLWNvbGxhcHNlOmNvbGxhcHNlO2ZvbnQtc2l6ZToxMnB4OyIgYm9yZGVyPSIxIiBjZWxscGFkZGluZz0iNiIgY2VsbHNwYWNpbmc9IjAiPjx0aGVhZD48dHIgc3R5bGU9ImJhY2tncm91bmQ6I2Y0ZjZmODsiPjx0aCBzdHlsZT0idGV4dC1hbGlnbjpsZWZ0OyI+VGVzdCBOYW1lPC90aD48dGggc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPlJlc3VsdDwvdGg+PHRoIHN0eWxlPSJ0ZXh0LWFsaWduOmxlZnQ7Ij5Vbml0PC90aD48dGggc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPkJpby4gUmVmLiBJbnRlcnZhbDwvdGg+PC90cj48L3RoZWFkPjx0Ym9keT48dHI+PHRkPkVvc2lub3BoaWxzPC90ZD48dGQ+MzA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+SGVtb2dsb2JpbjwvdGQ+PHRkPjExPC90ZD48dGQ+Zy9kTDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlRvdGFsIExldWtvY3l0ZSBDb3VudDwvdGQ+PHRkPjEwPC90ZD48dGQ+MTBeMy91TDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk5ldXRyb3BoaWxzPC90ZD48dGQ+MTA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+THltcGhvY3l0ZXM8L3RkPjx0ZD4xMDwvdGQ+PHRkPiU8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5Nb25vY3l0ZXM8L3RkPjx0ZD4xMDwvdGQ+PHRkPiU8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5CYW5kIENlbGxzPC90ZD48dGQ+MTA8L3RkPjx0ZD4lPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+SW1tYXR1cmUgQ2VsbHM8L3RkPjx0ZD45PC90ZD48dGQ+JTwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlJlZCBCbG9vZCBDZWxsczwvdGQ+PHRkPjEwPC90ZD48dGQ+MTBeNi91TDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPlBhY2tlZCBDZWxsIFZvbHVtZTwvdGQ+PHRkPjEwPC90ZD48dGQ+JTwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk1lYW4gQ29ycHVzY3VsYXIgVm9sdW1lPC90ZD48dGQ+MTA8L3RkPjx0ZD5mTDwvdGQ+PHRkPiZuYnNwOzwvdGQ+PC90cj48dHI+PHRkPk1lYW4gQ29ycHVzY3VsYXIgSGVtb2dsb2JpbjwvdGQ+PHRkPjEwPC90ZD48dGQ+cGc8L3RkPjx0ZD4mbmJzcDs8L3RkPjwvdHI+PHRyPjx0ZD5NZWFuIENvcnB1c2N1bGFyIEhlbW9nbG9iaW4gQ29uY2VudHJhdGlvbjwvdGQ+PHRkPjE8L3RkPjx0ZD5nL2RMPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjx0cj48dGQ+UGxhdGVsZXQgQ291bnQ8L3RkPjx0ZD4wMTA8L3RkPjx0ZD4xMF4zL3VMPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPjwvdGJvZHk+PC90YWJsZT4=", + "title": "Radiology Report" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:71fcc33b-443f-46d0-a391-b252972ce2d3", + "resource": { + "resourceType": "Binary", + "id": "71fcc33b-443f-46d0-a391-b252972ce2d3", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Binary" + ] + }, + "contentType": "application/pdf", + "data": "JVBERi0xLjQgbW9jayByYWRpb2xvZ3kgcmVwb3J0JQ==" + } + }, + { + "fullUrl": "urn:uuid:5ae8c7fd-3612-4dd2-8b9c-c7678fd5ea2c", + "resource": { + "resourceType": "DocumentReference", + "id": "5ae8c7fd-3612-4dd2-8b9c-c7678fd5ea2c", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentReference" + ] + }, + "status": "current", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "content": [ + { + "attachment": { + "contentType": "application/pdf", + "url": "urn:uuid:71fcc33b-443f-46d0-a391-b252972ce2d3", + "data": "JVBERi0xLjQgbW9jayByYWRpb2xvZ3kgcmVwb3J0JQ==", + "title": "Radiology Report PDF Report" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:de2c94ac-c652-43f1-affe-126e6fa73591", + "resource": { + "resourceType": "Media", + "id": "de2c94ac-c652-43f1-affe-126e6fa73591", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Media" + ] + }, + "status": "completed", + "type": "photo", + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "createdDateTime": "2026-05-31T01:13:43+05:30", + "content": { + "contentType": "application/pdf", + "url": "urn:uuid:71fcc33b-443f-46d0-a391-b252972ce2d3", + "data": "JVBERi0xLjQgbW9jayByYWRpb2xvZ3kgcmVwb3J0JQ==", + "title": "Radiology Report PDF Report" + } + } + }, + { + "fullUrl": "urn:uuid:42c3c982-963f-4286-97b6-0e6f47384ec1", + "resource": { + "resourceType": "DiagnosticReport", + "id": "42c3c982-963f-4286-97b6-0e6f47384ec1", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportImaging" + ] + }, + "status": "preliminary", + "code": { + "text": "Radiology Report" + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71", + "display": "Patient" + }, + "issued": "2026-05-31T01:13:43+05:30", + "result": [ + { + "reference": "urn:uuid:cc593cb8-28e4-469b-a497-11e9fb4e6ddf" + }, + { + "reference": "urn:uuid:547f3806-bf50-4d73-91a4-6c8e4fa1c186" + }, + { + "reference": "urn:uuid:2de0d2a8-0c3d-4d3a-9f21-60ebb11159d9" + }, + { + "reference": "urn:uuid:a87806b9-10de-421a-ae84-3b691c57df16" + }, + { + "reference": "urn:uuid:596c48ad-2cd3-48d2-8a8a-8a1e56567cfe" + }, + { + "reference": "urn:uuid:497992c4-b44a-46ff-8835-7e8210050e19" + }, + { + "reference": "urn:uuid:6c74b672-9d99-4176-8d8c-32c96ae68c8e" + }, + { + "reference": "urn:uuid:06fe2455-d11d-49ff-be6b-76258e0e9548" + }, + { + "reference": "urn:uuid:32f51bf0-c4ae-4be9-a25d-2f05fba2926e" + }, + { + "reference": "urn:uuid:b81a986d-8550-4679-b399-d33c61adcd68" + }, + { + "reference": "urn:uuid:86acf390-0c6d-444b-b1b4-4db516bbc3ad" + }, + { + "reference": "urn:uuid:4759bd0e-390e-4265-a099-66079e4f1abd" + }, + { + "reference": "urn:uuid:be897524-eafe-433a-bfa2-076d8b0b0cd8" + }, + { + "reference": "urn:uuid:c1c5e2a6-59f5-429d-90c2-f39b370100a7" + } + ], + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "resultsInterpreter": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "conclusion": "Laboratory report generated", + "media": [ + { + "link": { + "reference": "urn:uuid:de2c94ac-c652-43f1-affe-126e6fa73591" + } + } + ], + "presentedForm": [ + { + "contentType": "application/pdf", + "title": "Radiology Report PDF Report", + "url": "urn:uuid:5ae8c7fd-3612-4dd2-8b9c-c7678fd5ea2c" + } + ] + } + }, + { + "fullUrl": "urn:uuid:cc593cb8-28e4-469b-a497-11e9fb4e6ddf", + "resource": { + "resourceType": "Observation", + "id": "cc593cb8-28e4-469b-a497-11e9fb4e6ddf", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Eosinophils", + "coding": [ + { + "system": "http://loinc.org", + "code": "713-8", + "display": "Eosinophils" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 30, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + }, + { + "fullUrl": "urn:uuid:547f3806-bf50-4d73-91a4-6c8e4fa1c186", + "resource": { + "resourceType": "Observation", + "id": "547f3806-bf50-4d73-91a4-6c8e4fa1c186", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Hemoglobin", + "coding": [ + { + "system": "http://loinc.org", + "code": "718-7", + "display": "Hemoglobin" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 11, + "unit": "g/dL", + "system": "http://unitsofmeasure.org", + "code": "g/dL" + } + } + }, + { + "fullUrl": "urn:uuid:2de0d2a8-0c3d-4d3a-9f21-60ebb11159d9", + "resource": { + "resourceType": "Observation", + "id": "2de0d2a8-0c3d-4d3a-9f21-60ebb11159d9", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Total Leukocyte Count", + "coding": [ + { + "system": "http://loinc.org", + "code": "6690-2", + "display": "Total Leukocyte Count" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "10^3/uL", + "system": "http://unitsofmeasure.org", + "code": "10^3/uL" + } + } + }, + { + "fullUrl": "urn:uuid:a87806b9-10de-421a-ae84-3b691c57df16", + "resource": { + "resourceType": "Observation", + "id": "a87806b9-10de-421a-ae84-3b691c57df16", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Neutrophils", + "coding": [ + { + "system": "http://loinc.org", + "code": "770-8", + "display": "Neutrophils" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + }, + { + "fullUrl": "urn:uuid:596c48ad-2cd3-48d2-8a8a-8a1e56567cfe", + "resource": { + "resourceType": "Observation", + "id": "596c48ad-2cd3-48d2-8a8a-8a1e56567cfe", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Lymphocytes", + "coding": [ + { + "system": "http://loinc.org", + "code": "736-9", + "display": "Lymphocytes" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + }, + { + "fullUrl": "urn:uuid:497992c4-b44a-46ff-8835-7e8210050e19", + "resource": { + "resourceType": "Observation", + "id": "497992c4-b44a-46ff-8835-7e8210050e19", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Monocytes", + "coding": [ + { + "system": "http://loinc.org", + "code": "5905-5", + "display": "Monocytes" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + }, + { + "fullUrl": "urn:uuid:6c74b672-9d99-4176-8d8c-32c96ae68c8e", + "resource": { + "resourceType": "Observation", + "id": "6c74b672-9d99-4176-8d8c-32c96ae68c8e", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Band Cells", + "coding": [ + { + "system": "http://loinc.org", + "code": "764-1", + "display": "Band Cells" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + }, + { + "fullUrl": "urn:uuid:06fe2455-d11d-49ff-be6b-76258e0e9548", + "resource": { + "resourceType": "Observation", + "id": "06fe2455-d11d-49ff-be6b-76258e0e9548", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Immature Cells", + "coding": [ + { + "system": "http://loinc.org", + "code": "38518-7", + "display": "Immature Cells" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 9, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + }, + { + "fullUrl": "urn:uuid:32f51bf0-c4ae-4be9-a25d-2f05fba2926e", + "resource": { + "resourceType": "Observation", + "id": "32f51bf0-c4ae-4be9-a25d-2f05fba2926e", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Red Blood Cells", + "coding": [ + { + "system": "http://loinc.org", + "code": "789-8", + "display": "Red Blood Cells" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "10^6/uL", + "system": "http://unitsofmeasure.org", + "code": "10^6/uL" + } + } + }, + { + "fullUrl": "urn:uuid:b81a986d-8550-4679-b399-d33c61adcd68", + "resource": { + "resourceType": "Observation", + "id": "b81a986d-8550-4679-b399-d33c61adcd68", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Packed Cell Volume", + "coding": [ + { + "system": "http://loinc.org", + "code": "4544-3", + "display": "Packed Cell Volume" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + }, + { + "fullUrl": "urn:uuid:86acf390-0c6d-444b-b1b4-4db516bbc3ad", + "resource": { + "resourceType": "Observation", + "id": "86acf390-0c6d-444b-b1b4-4db516bbc3ad", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Mean Corpuscular Volume", + "coding": [ + { + "system": "http://loinc.org", + "code": "8457-4", + "display": "Mean Corpuscular Volume" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "fL", + "system": "http://unitsofmeasure.org", + "code": "fL" + } + } + }, + { + "fullUrl": "urn:uuid:4759bd0e-390e-4265-a099-66079e4f1abd", + "resource": { + "resourceType": "Observation", + "id": "4759bd0e-390e-4265-a099-66079e4f1abd", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Mean Corpuscular Hemoglobin", + "coding": [ + { + "system": "http://loinc.org", + "code": "785-6", + "display": "Mean Corpuscular Hemoglobin" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "pg", + "system": "http://unitsofmeasure.org", + "code": "pg" + } + } + }, + { + "fullUrl": "urn:uuid:be897524-eafe-433a-bfa2-076d8b0b0cd8", + "resource": { + "resourceType": "Observation", + "id": "be897524-eafe-433a-bfa2-076d8b0b0cd8", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Mean Corpuscular Hemoglobin Concentration", + "coding": [ + { + "system": "http://loinc.org", + "code": "59260-0", + "display": "Mean Corpuscular Hemoglobin Concentration" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 1, + "unit": "g/dL", + "system": "http://unitsofmeasure.org", + "code": "g/dL" + } + } + }, + { + "fullUrl": "urn:uuid:c1c5e2a6-59f5-429d-90c2-f39b370100a7", + "resource": { + "resourceType": "Observation", + "id": "c1c5e2a6-59f5-429d-90c2-f39b370100a7", + "meta": { + "profile": [ + "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation" + ] + }, + "status": "preliminary", + "code": { + "text": "Platelet Count", + "coding": [ + { + "system": "http://loinc.org", + "code": "777-3", + "display": "Platelet Count" + } + ] + }, + "subject": { + "reference": "urn:uuid:ddc0b57a-0e23-4064-a7bf-0f8e0918be71" + }, + "effectiveDateTime": "2026-05-31T01:13:43+05:30", + "encounter": { + "reference": "urn:uuid:1bf6a0f5-be01-470e-b431-150383993673" + }, + "performer": [ + { + "reference": "urn:uuid:0a3a334b-2fe0-4466-b528-0dffd612a1c6" + } + ], + "valueQuantity": { + "value": 10, + "unit": "10^3/uL", + "system": "http://unitsofmeasure.org", + "code": "10^3/uL" + } + } + } + ] +} \ No newline at end of file diff --git a/writable/tmp/abha_probe.php b/writable/tmp/abha_probe.php new file mode 100644 index 0000000..fa54587 --- /dev/null +++ b/writable/tmp/abha_probe.php @@ -0,0 +1,31 @@ +connect_errno) { + fwrite(STDERR, 'connect_error: ' . $mysqli->connect_error . PHP_EOL); + exit(1); +} + +$tables = ['patient_master', 'abdm_opd_tokens']; +foreach ($tables as $t) { + echo "\n== {$t} ==\n"; + $res = $mysqli->query("SHOW COLUMNS FROM `{$t}`"); + while ($row = $res->fetch_assoc()) { + if (stripos($row['Field'], 'abha') !== false) { + echo $row['Field'] . ' | ' . $row['Type'] . ' | NULL=' . $row['Null'] . PHP_EOL; + } + } +} + +echo "\n== patient_master data checks ==\n"; +$q = [ + "SELECT COUNT(*) c FROM patient_master WHERE abha_id IS NOT NULL AND abha_id <> ''", + "SELECT COUNT(*) c FROM patient_master WHERE abha_address IS NOT NULL AND abha_address <> ''", + "SELECT COUNT(*) c FROM patient_master WHERE abha_id IS NOT NULL AND abha_id <> '' AND abha_id LIKE '%@%'", + "SELECT COUNT(*) c FROM patient_master WHERE abha_id IS NOT NULL AND abha_id <> '' AND abha_address IS NOT NULL AND abha_address <> ''", +]; +foreach ($q as $sql) { + $r = $mysqli->query($sql); + $row = $r->fetch_assoc(); + echo $sql . ' => ' . (int)$row['c'] . PHP_EOL; +} +?>