PepfarReportFromXmlTest.java

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.report;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.openmrs.Program;
import org.openmrs.api.CohortService;
import org.openmrs.api.ReportService;
import org.openmrs.api.context.Context;
import org.openmrs.report.impl.TsvReportRenderer;
import org.openmrs.reporting.PatientCharacteristicFilter;
import org.openmrs.reporting.PatientSearch;
import org.openmrs.reporting.PatientSearchReportObject;
import org.openmrs.reporting.ProgramStatePatientFilter;
import org.openmrs.test.BaseContextSensitiveTest;
import org.openmrs.xml.OpenmrsCycleStrategy;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.load.Persister;

/**
 *
 */
public class PepfarReportFromXmlTest extends BaseContextSensitiveTest {
	
	Log log = LogFactory.getLog(getClass());
	
	DateFormat ymd = new SimpleDateFormat("yyyy-MM-dd");
	
	Map<Parameter, Object> getUserEnteredParameters(Collection<Parameter> params) throws ParseException {
		Map<Parameter, Object> ret = new HashMap<Parameter, Object>();
		if (params != null) {
			for (Parameter p : params) {
				if (p.getName().equals("report.startDate"))
					ret.put(p, ymd.parse("2007-09-01"));
				else if (p.getName().equals("report.endDate"))
					ret.put(p, ymd.parse("2007-09-30"));
			}
		}
		return ret;
	}
	
	@Test
	public void shouldFromXml() throws Exception {
		executeDataSet("org/openmrs/report/include/PepfarReportTest.xml");
		
		StringBuilder xml = new StringBuilder();
		xml.append("<reportSchema id=\"1\">\n");
		xml.append("    <name>PEPFAR report</name>\n");
		xml.append("	<description>\n");
		xml.append("		Sample monthly PEPFAR report, modelled after the lesotho one\n");
		xml.append("	</description>\n");
		xml.append("	<parameters class=\"java.util.ArrayList\">\n");
		xml
		        .append("		<parameter clazz=\"java.util.Date\"><name>report.startDate</name><label>When does the report period start?</label></parameter>/>\n");
		xml
		        .append("		<parameter clazz=\"java.util.Date\"><name>report.endDate</name><label>When does the report period end?</label></parameter>\n");
		xml
		        .append("		<parameter clazz=\"org.openmrs.Location\"><name>report.location</name><label>For which clinic is this report?</label></parameter>\n");
		xml.append("	</parameters>\n");
		xml.append("	<dataSets class=\"java.util.ArrayList\">\n");
		xml.append("		<dataSetDefinition class=\"org.openmrs.report.CohortDataSetDefinition\" name=\"Cohorts\">\n");
		xml.append("			<strategies class=\"java.util.LinkedHashMap\">\n");
		xml.append("				<entry>\n");
		xml.append("					<string>1.a</string>\n");
		xml.append("					<cohortDefinition class=\"org.openmrs.reporting.PatientSearch\">\n");
		xml.append("						<specification>[Male]</specification>\n");
		xml.append("					</cohortDefinition>\n");
		xml.append("				</entry>\n");
		xml.append("				<entry>\n");
		xml.append("					<string>1.b</string>\n");
		xml.append("					<cohortDefinition class=\"org.openmrs.reporting.PatientSearch\">\n");
		xml
		        .append("						<specification>[Male] and [Adult] and [EnrolledOnDate|untilDate=${report.startDate-1d}]</specification>\n");
		xml.append("					</cohortDefinition>\n");
		xml.append("				</entry>\n");
		xml.append("			</strategies>\n");
		xml.append("		</dataSetDefinition>\n");
		xml.append("	</dataSets>\n");
		xml.append("</reportSchema>\n");
		//System.out.println("xml\n" + xml);
		
		// Try to get HIV PROGRAM, or else, just the first program
		Program hivProgram = Context.getProgramWorkflowService().getProgramByName("HIV PROGRAM");
		if (hivProgram == null)
			hivProgram = Context.getProgramWorkflowService().getProgram(1);
		assertNotNull("Need at least one program defined to run this test", hivProgram);
		
		// Make sure we have all required PatientSearches
		if (Context.getReportObjectService().getPatientSearch("Male") == null) {
			PatientSearch ps = PatientSearch.createFilterSearch(PatientCharacteristicFilter.class);
			ps.addArgument("gender", "m", String.class);
			Context.getReportObjectService().saveReportObject(new PatientSearchReportObject("Male", ps));
		}
		if (Context.getReportObjectService().getPatientSearch("Female") == null) {
			PatientSearch ps = PatientSearch.createFilterSearch(PatientCharacteristicFilter.class);
			ps.addArgument("gender", "f", String.class);
			Context.getReportObjectService().saveReportObject(new PatientSearchReportObject("Female", ps));
		}
		if (Context.getReportObjectService().getPatientSearch("Adult") == null) {
			PatientSearch ps = PatientSearch.createFilterSearch(PatientCharacteristicFilter.class);
			ps.addArgument("minAge", "15", Integer.class);
			Context.getReportObjectService().saveReportObject(new PatientSearchReportObject("Adult", ps));
		}
		if (Context.getReportObjectService().getPatientSearch("Child") == null) {
			PatientSearch ps = PatientSearch.createFilterSearch(PatientCharacteristicFilter.class);
			ps.addArgument("maxAge", "15", Integer.class);
			Context.getReportObjectService().saveReportObject(new PatientSearchReportObject("Child", ps));
		}
		if (Context.getReportObjectService().getPatientSearch("EnrolledOnDate") == null) {
			PatientSearch ps = PatientSearch.createFilterSearch(ProgramStatePatientFilter.class);
			ps.addArgument("program", hivProgram.getProgramId().toString(), Program.class);
			ps.addArgument("untilDate", "${date}", Date.class);
			Context.getReportObjectService().saveReportObject(new PatientSearchReportObject("EnrolledOnDate", ps));
		}
		
		Serializer serializer = new Persister(new OpenmrsCycleStrategy());
		ReportSchema schema = serializer.read(ReportSchema.class, xml.toString());
		
		log.info("Creating EvaluationContext");
		EvaluationContext evalContext = new EvaluationContext();
		
		for (Map.Entry<Parameter, Object> e : getUserEnteredParameters(schema.getReportParameters()).entrySet()) {
			log.info("adding parameter value " + e.getKey());
			evalContext.addParameterValue(e.getKey(), e.getValue());
		}
		
		ReportService rs = (ReportService) Context.getService(ReportService.class);
		ReportData data = rs.evaluate(schema, null, evalContext);
		
		TsvReportRenderer renderer = new TsvReportRenderer();
		//System.out.println("Rendering output as TSV:");
		//renderer.render(data, null, System.out);
	}
	
	@Test
	public void shouldBooleansInPatientSearch() throws Exception {
		executeDataSet("org/openmrs/report/include/ReportTests-patients.xml");
		
		// Make sure we have all required PatientSearches
		if (Context.getReportObjectService().getPatientSearch("Male") == null) {
			PatientSearch ps = PatientSearch.createFilterSearch(PatientCharacteristicFilter.class);
			ps.addArgument("gender", "m", String.class);
			Context.getReportObjectService().saveReportObject(new PatientSearchReportObject("Male", ps));
		}
		if (Context.getReportObjectService().getPatientSearch("Female") == null) {
			PatientSearch ps = PatientSearch.createFilterSearch(PatientCharacteristicFilter.class);
			ps.addArgument("gender", "f", String.class);
			Context.getReportObjectService().saveReportObject(new PatientSearchReportObject("Female", ps));
		}
		if (Context.getReportObjectService().getPatientSearch("Adult") == null) {
			PatientSearch ps = PatientSearch.createFilterSearch(PatientCharacteristicFilter.class);
			ps.addArgument("minAge", "15", Integer.class);
			Context.getReportObjectService().saveReportObject(new PatientSearchReportObject("Adult", ps));
		}
		if (Context.getReportObjectService().getPatientSearch("Child") == null) {
			PatientSearch ps = PatientSearch.createFilterSearch(PatientCharacteristicFilter.class);
			ps.addArgument("maxAge", "15", Integer.class);
			Context.getReportObjectService().saveReportObject(new PatientSearchReportObject("Child", ps));
		}
		
		EvaluationContext evalContext = new EvaluationContext();
		evalContext.addParameterValue(new Parameter("report.startDate", "Start Date", Date.class, null), ymd
		        .parse("2007-09-01"));
		evalContext
		        .addParameterValue(new Parameter("report.endDate", "End Date", Date.class, null), ymd.parse("2007-09-30"));
		
		CohortService cs = Context.getCohortService();
		
		PatientSearch male = new PatientSearch();
		PatientSearch female = new PatientSearch();
		PatientSearch maleAndFemale = new PatientSearch();
		PatientSearch maleOrFemale = new PatientSearch();
		male.setSpecificationString("[Male]");
		female.setSpecificationString("[Female]");
		maleAndFemale.setSpecificationString("[Male] and [Female]");
		maleOrFemale.setSpecificationString("[Male] or [Female]");
		int numMale = cs.evaluate(male, evalContext).size();
		int numFemale = cs.evaluate(female, evalContext).size();
		int numMaleAndFemale = cs.evaluate(maleAndFemale, evalContext).size();
		int numMaleOrFemale = cs.evaluate(maleOrFemale, evalContext).size();
		assertEquals("AND should be zero", 0, numMaleAndFemale);
		assertEquals("OR should be the sum", numMale + numFemale, numMaleOrFemale);
		
		PatientSearch complex1 = new PatientSearch();
		complex1.setSpecificationString("([Male] and [Child]) or ([Female] and [Adult])");
		assertNotSame("Should not be zero", 0, cs.evaluate(complex1, evalContext).size());
		
		PatientSearch complex2 = new PatientSearch();
		PatientSearch complex3 = new PatientSearch();
		complex2.setSpecificationString("[Male] or [Female]");
		complex3.setSpecificationString("(([Male] and [Child]) or [Female])");
		// this assertion will fail 15 years after 2008-07-01 because the birthdates are
		// set to that in the dataset for the two "children"
		assertNotSame("Complex2 and Complex3 should be different sizes", cs.evaluate(complex2, evalContext).size(), cs
		        .evaluate(complex3, evalContext).size());
	}
}