Integrating OpenMRS Reporting with External Systems

Integrating OpenMRS Reporting with External Systems

This page is derived from older reporting documentation and may not reflect the current behavior of the reporting module. Contributions to align it with the latest version are welcome.

Comprehensive guide to integrating OpenMRS reporting capabilities with external systems, web applications, and other OpenMRS modules.

Modern healthcare environments require seamless integration between different systems and applications. OpenMRS reporting provides multiple integration pathways that enable organizations to embed reporting functionality into existing workflows, external applications, and automated processes without requiring users to directly access the OpenMRS interface.

Table of Contents


HTML Form Entry Integration

Why HTML Form Entry Integration Enhances Workflows

HTML Form Entry (HFE) is a popular OpenMRS module that provides flexible form creation capabilities. Integrating reporting functionality directly into HFE forms enables real-time data visualization, automated report generation triggered by form submissions, and embedded analytics that provide immediate feedback to data entry users.

HFE integration benefits:

  • Real-time feedback - Display relevant metrics during data entry

  • Contextual reporting - Show patient-specific or program-specific reports within forms

  • Automated triggers - Generate reports automatically when forms are submitted

  • Enhanced user experience - Reduce context switching between forms and reports

  • Data validation - Use reports to validate data entry patterns and identify outliers

Embedding Reports in HFE Forms

Understanding HFE Report Integration

HFE forms can include embedded reports through custom tags and JavaScript integration. This enables forms to display relevant data visualizations, summary statistics, or patient-specific information that enhances the data entry experience.

<!-- HFE form with embedded reporting --> <htmlform formUuid="12345-form-uuid" formName="Patient Assessment with Reports"> <!-- Standard form fields --> <section> <h3>Patient Information</h3> <p> <label>Date:</label> <encounterDate default="today" /> </p> <p> <label>Location:</label> <encounterLocation /> </p> </section> <!-- Embedded patient-specific report --> <section> <h3>Patient History Summary</h3> <div id="patient-history-report"> <script type="text/javascript"> // Load patient-specific report loadPatientReport({ reportId: 'patient-history-uuid', patientId: '<lookup expression="patient.patientId"/>', renderingMode: 'WEB' }); </script> </div> </section> <!-- Clinical data entry --> <section> <h3>Current Assessment</h3> <p> <label>Blood Pressure:</label> <obs conceptId="5085" />/<obs conceptId="5086" /> </p> <p> <label>Weight:</label> <obs conceptId="5089" units="kg" /> </p> </section> <!-- Contextual program reports --> <section> <h3>Program Performance</h3> <div id="program-metrics"> <script type="text/javascript"> // Load program-specific metrics loadProgramMetrics({ programId: '<lookup expression="patient.activePrograms[0].program.programId"/>', period: 'current-month' }); </script> </div> </section> </htmlform>

JavaScript Integration Functions

// JavaScript functions for HFE report integration function loadPatientReport(config) { const reportUrl = buildReportUrl('/openmrs', { reportId: config.reportId, renderingMode: 'org.openmrs.module.reporting.report.renderer.WebReportRenderer', patientId: config.patientId }); fetch(reportUrl, { credentials: 'same-origin' }) .then(response => response.text()) .then(html => { document.getElementById('patient-history-report').innerHTML = html; }) .catch(error => { console.error('Failed to load patient report:', error); document.getElementById('patient-history-report').innerHTML = '<p class="error">Unable to load patient history</p>'; }); } function loadProgramMetrics(config) { const metricsUrl = `/openmrs/ws/rest/v1/reportingrest/dataSet/${config.programId}?format=json&period=${config.period}`; fetch(metricsUrl, { credentials: 'same-origin' }) .then(response => response.json()) .then(data => { renderProgramMetrics(data); }) .catch(error => { console.error('Failed to load program metrics:', error); }); } function renderProgramMetrics(data) { const metricsHtml = ` <div class="program-metrics"> <div class="metric"> <span class="label">Active Patients:</span> <span class="value">${data.activePatients || 0}</span> </div> <div class="metric"> <span class="label">New Enrollments:</span> <span class="value">${data.newEnrollments || 0}</span> </div> <div class="metric"> <span class="label">Completion Rate:</span> <span class="value">${data.completionRate || 0}%</span> </div> </div> `; document.getElementById('program-metrics').innerHTML = metricsHtml; } // Real-time data validation using reports function validateDataEntry(fieldName, value) { const validationUrl = `/openmrs/ws/rest/v1/reportingrest/validate/${fieldName}`; fetch(validationUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value: value, patientId: getPatientId(), context: getFormContext() }), credentials: 'same-origin' }) .then(response => response.json()) .then(validation => { if (!validation.valid) { showValidationWarning(fieldName, validation.message); } else { clearValidationWarning(fieldName); } }); }

Form Submission Triggers

Why Automated Report Generation Improves Workflows

Form submissions often represent significant clinical events that require immediate reporting to stakeholders. Automated report generation triggered by form submissions ensures timely communication without requiring manual intervention from clinical staff.

HFE JavaScript Hooks for Report Generation

// HFE form submission hooks for automated reporting beforeSubmit.push(function() { // Pre-submission validation using reports return validateFormData(); }); beforeSave.push(function() { // Generate reports before saving the encounter prepareTriggeredReports(); }); afterSubmit.push(function() { // Generate and send reports after successful submission triggerAutomaticReports(); }); function validateFormData() { return new Promise((resolve, reject) => { const validationReportUrl = buildReportUrl('/openmrs', { reportId: 'form-validation-report-uuid', renderingMode: 'org.openmrs.module.reporting.report.renderer.JsonReportRenderer', patientId: getPatientId(), formData: getFormData() }); fetch(validationReportUrl, { credentials: 'same-origin' }) .then(response => response.json()) .then(validationResults => { if (validationResults.hasErrors) { displayValidationErrors(validationResults.errors); resolve(false); // Prevent submission } else { resolve(true); // Allow submission } }) .catch(error => { console.error('Validation failed:', error); resolve(true); // Allow submission on validation error }); }); } function triggerAutomaticReports() { const formType = getFormType(); const patientId = getPatientId(); const encounterId = getEncounterId(); // Define report triggers based on form type const reportTriggers = { 'hiv_care_visit': [ { reportId: 'hiv-care-summary-uuid', recipients: ['hiv-coordinator@clinic.com'], condition: 'viral_load_available' } ], 'tb_screening': [ { reportId: 'tb-screening-alert-uuid', recipients: ['tb-nurse@clinic.com', 'physician@clinic.com'], condition: 'high_risk_screening' } ], 'maternal_health': [ { reportId: 'anc-visit-summary-uuid', recipients: ['midwife@clinic.com'], condition: 'anc_visit' } ] }; const triggers = reportTriggers[formType] || []; triggers.forEach(trigger => { if (evaluateCondition(trigger.condition)) { generateTriggeredReport(trigger, { patientId: patientId, encounterId: encounterId, formType: formType }); } }); } function generateTriggeredReport(trigger, context) { const reportRequest = { reportId: trigger.reportId, renderingMode: 'org.openmrs.module.reporting.report.renderer.ExcelReportRenderer', patientId: context.patientId, encounterId: context.encounterId, generateTimestamp: new Date().toISOString(), recipients: trigger.recipients }; // Queue report for generation and delivery fetch('/openmrs/ws/rest/v1/reportingrest/queue', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(reportRequest), credentials: 'same-origin' }) .then(response => response.json()) .then(result => { console.log('Report queued:', result.requestId); showNotification(`Report ${trigger.reportId} queued for delivery`); }) .catch(error => { console.error('Failed to queue report:', error); }); } function evaluateCondition(condition) { // Evaluate trigger conditions based on form data const formData = getFormData(); switch (condition) { case 'viral_load_available': return formData.viralLoad && formData.viralLoad !== ''; case 'high_risk_screening': return formData.tbSymptoms && formData.tbSymptoms.length > 2; case 'anc_visit': return formData.visitType === 'anc'; default: return true; } }

Using in Other Modules

Why Inter-Module Integration is Powerful

OpenMRS's modular architecture enables reporting functionality to be integrated into other modules, creating seamless user experiences and powerful cross-module workflows. This integration allows specialized modules to leverage reporting capabilities without duplicating code or requiring users to switch contexts.

Inter-module integration benefits:

  • Contextual reporting - Display relevant reports within specialized module interfaces

  • Shared functionality - Leverage reporting infrastructure across multiple modules

  • Consistent user experience - Uniform reporting interface regardless of module

  • Code reusability - Avoid duplicating reporting logic in multiple modules

  • Enhanced workflows - Embed reporting into specialized business processes

Module API Integration

Understanding OpenMRS Module Integration Patterns

OpenMRS modules can integrate with reporting functionality through service interfaces, web controllers, and JavaScript APIs. Understanding these integration patterns enables robust cross-module functionality.

// Java API integration for custom modules @Component public class CustomModuleReportingService { @Autowired private ReportService reportService; @Autowired private ReportDefinitionService reportDefinitionService; /** * Generate report for custom module context */ public ReportData generateModuleReport(String reportUuid, Map<String, Object> parameters) { try { ReportDefinition reportDef = reportDefinitionService.getDefinitionByUuid(reportUuid); if (reportDef == null) { throw new IllegalArgumentException("Report definition not found: " + reportUuid); } // Create evaluation context with module-specific parameters EvaluationContext context = new EvaluationContext(); // Add standard parameters context.addParameterValue("startDate", parameters.get("startDate")); context.addParameterValue("endDate", parameters.get("endDate")); context.addParameterValue("location", parameters.get("location")); // Add module-specific parameters parameters.entrySet().stream() .filter(entry -> !isStandardParameter(entry.getKey())) .forEach(entry -> context.addParameterValue(entry.getKey(), entry.getValue())); // Generate report ReportData reportData = reportService.evaluate(reportDef, context); // Log report generation for audit logReportGeneration(reportUuid, parameters); return reportData; } catch (Exception e) { log.error("Failed to generate module report: " + reportUuid, e); throw new RuntimeException("Report generation failed", e); } } /** * Generate and render report in specified format */ public byte[] generateAndRenderReport(String reportUuid, String renderingMode, Map<String, Object> parameters) { ReportData reportData = generateModuleReport(reportUuid, parameters); // Get rendering mode ReportRenderer renderer = getReportRenderer(renderingMode); if (renderer == null) { throw new IllegalArgumentException("Unsupported rendering mode: " + renderingMode); } // Render report ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { RenderingMode mode = new RenderingMode(renderer, "default", null, 100); renderer.render(reportData, mode.getArgument(), outputStream); return outputStream.toByteArray(); } catch (Exception e) { log.error("Failed to render report: " + reportUuid, e); throw new RuntimeException("Report rendering failed", e); } } /** * Get available reports for module context */ public List<ReportDefinition> getModuleReports(String moduleContext) { return reportDefinitionService.getAllDefinitions(false) .stream() .filter(reportDef -> isRelevantForModule(reportDef, moduleContext)) .collect(Collectors.toList()); } private boolean isStandardParameter(String paramName) { Set<String> standardParams = Set.of("startDate", "endDate", "location", "program", "user", "provider"); return standardParams.contains(paramName); } private boolean isRelevantForModule(ReportDefinition reportDef, String moduleContext) { // Check report metadata for module context String reportContext = reportDef.getParameter("moduleContext"); return moduleContext.equals(reportContext) || "global".equals(reportContext); } private ReportRenderer getReportRenderer(String renderingMode) { List<ReportRenderer> renderers = Context.getRegisteredComponents(ReportRenderer.class); return renderers.stream() .filter(renderer -> renderer.getClass().getName().equals(renderingMode)) .findFirst() .orElse(null); } private void logReportGeneration(String reportUuid, Map<String, Object> parameters) { log.info("Module report generated: {} with parameters: {}", reportUuid, parameters); // Could also create audit log entry in database // auditService.createAuditEntry("REPORT_GENERATED", reportUuid, parameters); } }

Web Controller Integration