Complete Surgery/Procedure autocomplete implementation and testing docs
COMPLETED ALL TODO ITEMS: ✅ Task 1: Enhanced Surgery autocomplete with SNOMED/ICD codes ✅ Task 2: Enhanced Procedure autocomplete with SNOMED/ICD codes ✅ Task 3: Verified Master CRUD integration (modal already working) ✅ Task 4: Created comprehensive testing documentation KEY FIXES: - Removed conflicting bindSurgeryTermLookup() calls from initSurgeryTools() - Old datalist code no longer interferes with new custom dropdown - Both autocompletes work independently with separate dropdown containers TESTING DOCUMENT INCLUDES: - 10 detailed test scenarios with expected results - Database verification queries - Troubleshooting guide - Edge case handling - Performance checks - User acceptance checklist - Future enhancement suggestions FILE: SURGERY_PROCEDURE_AUTOCOMPLETE_TESTING.md Ready for QA testing and user acceptance.
This commit is contained in:
parent
b6bca785a8
commit
5dccbc8d12
346
SURGERY_PROCEDURE_AUTOCOMPLETE_TESTING.md
Normal file
346
SURGERY_PROCEDURE_AUTOCOMPLETE_TESTING.md
Normal file
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
4255
pdf_test_html.html
Normal file
4255
pdf_test_html.html
Normal file
File diff suppressed because it is too large
Load Diff
160
writable/tmp/abdm_lab_bundle_check.php
Normal file
160
writable/tmp/abdm_lab_bundle_check.php
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Libraries\FhirR4Builder;
|
||||
|
||||
require dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
|
||||
$builder = new FhirR4Builder();
|
||||
|
||||
$patient = [
|
||||
'id' => '2',
|
||||
'name' => 'ABC Male',
|
||||
'given_name' => 'ABC',
|
||||
'family_name' => 'Male',
|
||||
'gender' => 'male',
|
||||
'birthDate' => '1981-01-12',
|
||||
'abhaAddress' => 'abha_2@sbx',
|
||||
'abha_id' => '22-7225-4829-5255',
|
||||
];
|
||||
|
||||
$diagnosticReport = [
|
||||
'id' => '5234342',
|
||||
'title' => 'Diagnostic Report- Lab',
|
||||
'status' => 'final',
|
||||
'conclusion' => 'Elevated cholesterol/high density lipoprotein ratio',
|
||||
'reported_at' => '2020-07-10T11:45:33+05:30',
|
||||
'loinc_code' => '24331-1',
|
||||
'category_snomed_code' => '708196005',
|
||||
'category_snomed_display' => 'Hematology service',
|
||||
'section_title' => 'Hematology report',
|
||||
'section_snomed_code' => '4321000179101',
|
||||
'section_snomed_display' => 'Hematology report',
|
||||
'report_html' => '<html><body><h1>Lab Report</h1><p>CBC and lipid panel.</p></body></html>',
|
||||
];
|
||||
|
||||
$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);
|
||||
387
writable/tmp/abdm_lab_bundle_real_check.php
Normal file
387
writable/tmp/abdm_lab_bundle_real_check.php
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Libraries\FhirR4Builder;
|
||||
|
||||
require dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
|
||||
function parseEnvFile(string $path): array
|
||||
{
|
||||
if (!is_file($path)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$vars = [];
|
||||
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
|
||||
foreach ($lines as $line) {
|
||||
$line = trim((string) $line);
|
||||
if ($line === '' || str_starts_with($line, '#')) {
|
||||
continue;
|
||||
}
|
||||
$parts = explode('=', $line, 2);
|
||||
if (count($parts) !== 2) {
|
||||
continue;
|
||||
}
|
||||
$key = trim((string) $parts[0]);
|
||||
$value = trim((string) $parts[1]);
|
||||
$value = trim($value, " \t\n\r\0\x0B\"'");
|
||||
$vars[$key] = $value;
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
function envVal(array $env, string $key, string $fallback = ''): string
|
||||
{
|
||||
if (isset($env[$key]) && $env[$key] !== '') {
|
||||
return (string) $env[$key];
|
||||
}
|
||||
|
||||
$value = getenv($key);
|
||||
if ($value !== false && $value !== '') {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
function mapLabTypeToTitle(int $labType): string
|
||||
{
|
||||
return match ($labType) {
|
||||
1 => '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);
|
||||
1063
writable/tmp/abdm_lab_bundle_real_sample.json
Normal file
1063
writable/tmp/abdm_lab_bundle_real_sample.json
Normal file
File diff suppressed because it is too large
Load Diff
485
writable/tmp/abdm_lab_bundle_sample.json
Normal file
485
writable/tmp/abdm_lab_bundle_sample.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
332
writable/tmp/abdm_radiology_bundle_real_check.php
Normal file
332
writable/tmp/abdm_radiology_bundle_real_check.php
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Libraries\FhirR4Builder;
|
||||
|
||||
require dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
|
||||
function parseEnvFile(string $path): array
|
||||
{
|
||||
if (!is_file($path)) {
|
||||
return [];
|
||||
}
|
||||
$vars = [];
|
||||
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
|
||||
foreach ($lines as $line) {
|
||||
$line = trim((string) $line);
|
||||
if ($line === '' || str_starts_with($line, '#')) {
|
||||
continue;
|
||||
}
|
||||
$parts = explode('=', $line, 2);
|
||||
if (count($parts) !== 2) {
|
||||
continue;
|
||||
}
|
||||
$vars[trim((string) $parts[0])] = trim(trim((string) $parts[1]), " \t\n\r\0\x0B\"'");
|
||||
}
|
||||
return $vars;
|
||||
}
|
||||
|
||||
function envVal(array $env, string $key, string $fallback = ''): string
|
||||
{
|
||||
if (isset($env[$key]) && $env[$key] !== '') {
|
||||
return (string) $env[$key];
|
||||
}
|
||||
$value = getenv($key);
|
||||
if ($value !== false && $value !== '') {
|
||||
return (string) $value;
|
||||
}
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
982
writable/tmp/abdm_radiology_bundle_real_sample.json
Normal file
982
writable/tmp/abdm_radiology_bundle_real_sample.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
31
writable/tmp/abha_probe.php
Normal file
31
writable/tmp/abha_probe.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
$mysqli = new mysqli('localhost', 'root', '', 'hms_data_ci4', 3306);
|
||||
if ($mysqli->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;
|
||||
}
|
||||
?>
|
||||
Loading…
Reference in New Issue
Block a user