/**
   * @see
   *     PatientDataEvaluator#evaluate(org.openmrs.module.reporting.data.patient.definition.PatientDataDefinition,
   *     org.openmrs.module.reporting.evaluation.EvaluationContext)
   */
  public EvaluatedPatientData evaluate(PatientDataDefinition definition, EvaluationContext context)
      throws EvaluationException {

    CalculationDataDefinition def = (CalculationDataDefinition) definition;
    EvaluatedPatientData c = new EvaluatedPatientData(def, context);

    // return right away if there is nothing to evaluate
    if (context.getBaseCohort() != null && context.getBaseCohort().isEmpty()) {
      return c;
    }

    // evaluate the calculation
    PatientCalculationService service = Context.getService(PatientCalculationService.class);
    CalculationResultMap resultMap =
        service.evaluate(
            context.getBaseCohort().getMemberIds(),
            def.getCalculation(),
            def.getCalculationParameters(),
            context);

    // move data into return object
    for (Map.Entry<Integer, CalculationResult> entry : resultMap.entrySet()) {
      c.addData(entry.getKey(), entry.getValue());
    }

    return c;
  }
  /** @see Moh731CohortLibrary#revisitsArt() */
  @Test
  public void revisitsArt() throws Exception {
    EncounterType hivConsult =
        MetadataUtils.getEncounterType(HivMetadata._EncounterType.HIV_CONSULTATION);
    Concept stavudine = Context.getConceptService().getConcept(84309);

    // Start patient #6 this month and give them a visit in the reporting period + 2 months
    TestUtils.saveDrugOrder(TestUtils.getPatient(6), stavudine, TestUtils.date(2012, 6, 10), null);
    TestUtils.saveEncounter(TestUtils.getPatient(6), hivConsult, TestUtils.date(2012, 6, 20));

    // Start patient #7 in previous month and give them a visit in the reporting period + 2 months
    TestUtils.saveDrugOrder(TestUtils.getPatient(7), stavudine, TestUtils.date(2012, 5, 10), null);
    TestUtils.saveEncounter(TestUtils.getPatient(7), hivConsult, TestUtils.date(2012, 6, 20));

    // Start patient #8 and give them visit outside of reporting period + 2 month window
    TestUtils.saveDrugOrder(TestUtils.getPatient(8), stavudine, TestUtils.date(2012, 1, 10), null);
    TestUtils.saveEncounter(TestUtils.getPatient(8), hivConsult, TestUtils.date(2012, 1, 20));

    CohortDefinition cd = moh731Cohorts.revisitsArt();
    context.addParameterValue("fromDate", PERIOD_START);
    context.addParameterValue("toDate", PERIOD_END);
    EvaluatedCohort evaluated =
        Context.getService(CohortDefinitionService.class).evaluate(cd, context);
    ReportingTestUtils.assertCohortEquals(Arrays.asList(7), evaluated);
  }
  /** @should return patient data for each obs in the passed context */
  @Override
  public EvaluatedObsData evaluate(
      ObsDataDefinition definition, EvaluationContext obsEvaluationContext)
      throws EvaluationException {

    DataSetQueryService dqs = Context.getService(DataSetQueryService.class);
    EvaluatedObsData c = new EvaluatedObsData(definition, obsEvaluationContext);

    // create a map of obs ids -> patient ids (note assumption that personId = patientId)
    Set<Integer> obsIds = ObsDataUtil.getObsIdsForContext(obsEvaluationContext, true);
    Map<Integer, Integer> convertedIds =
        dqs.convertData(Person.class, "personId", null, Obs.class, "person.personId", obsIds);

    // create a new (patient) evaluation context using the retrieved ids
    EvaluationContext patientEvaluationContext = new EvaluationContext();
    patientEvaluationContext.setBaseCohort(new Cohort(convertedIds.values()));

    // evaluate the joined definition via this patient context
    PatientToObsDataDefinition def = (PatientToObsDataDefinition) definition;
    EvaluatedPatientData pd =
        Context.getService(PatientDataService.class)
            .evaluate(def.getJoinedDefinition(), patientEvaluationContext);

    // now create the result set by mapping the results in the patient data set to obs ids
    for (Integer obsId : obsIds) {
      c.addData(obsId, pd.getData().get(convertedIds.get(obsId)));
    }
    return c;
  }
 /**
  * @see
  *     org.openmrs.module.reporting.definition.service.DefinitionService#evaluate(org.openmrs.module.reporting.evaluation.Definition,
  *     org.openmrs.module.reporting.evaluation.EvaluationContext)
  * @should evaluate a Appointment data definition
  */
 @Transactional(readOnly = true)
 public EvaluatedAppointmentData evaluate(
     AppointmentDataDefinition definition, EvaluationContext context) throws EvaluationException {
   if (context.getBaseCohort() != null && context.getBaseCohort().isEmpty()) {
     return new EvaluatedAppointmentData(definition, context);
   }
   return (EvaluatedAppointmentData) super.evaluate(definition, context);
 }
 /**
  * @see CohortDefinitionEvaluator#evaluateCohort(CohortDefinition, EvaluationContext)
  * @should support integer parameter
  * @should support string parameter
  * @should support patient parameter
  * @should support integer list parameter
  * @should support patient list parameter
  * @should support cohort parameter
  * @should support date parameter
  * @should should protect SQL Query Against database modifications
  */
 public EvaluatedCohort evaluate(CohortDefinition cohortDefinition, EvaluationContext context) {
   SqlCohortDefinition sqlCohortDefinition = (SqlCohortDefinition) cohortDefinition;
   CohortQueryService cqs = Context.getService(CohortQueryService.class);
   String sql = SqlScriptParser.parse(new StringReader(sqlCohortDefinition.getQuery()))[0];
   Cohort c = cqs.executeSqlQuery(sql, context.getParameterValues());
   if (context.getBaseCohort() != null) {
     c = Cohort.intersect(c, context.getBaseCohort());
   }
   return new EvaluatedCohort(c, cohortDefinition, context);
 }
  @Override
  public EncounterQueryResult evaluate(EncounterQuery definition, EvaluationContext context)
      throws EvaluationException {
    context = ObjectUtil.nvl(context, new EvaluationContext());

    BasicEncounterQuery query = (BasicEncounterQuery) definition;
    EncounterQueryResult result = new EncounterQueryResult(query, context);

    if (context.getBaseCohort() != null && context.getBaseCohort().isEmpty()) {
      return result;
    }
    if (context instanceof EncounterEvaluationContext) {
      EncounterIdSet baseEncounters = ((EncounterEvaluationContext) context).getBaseEncounters();
      if (baseEncounters != null && baseEncounters.getMemberIds().isEmpty()) {
        return result;
      }
    }

    HqlQueryBuilder q = new HqlQueryBuilder();
    q.select("e.encounterId");
    q.from(Encounter.class, "e");
    q.whereIn("e.encounterType", query.getEncounterTypes());
    q.whereIn("e.form", query.getForms());
    q.whereGreaterOrEqualTo("e.encounterDatetime", query.getOnOrAfter());
    q.whereLessOrEqualTo("e.encounterDatetime", query.getOnOrBefore());
    q.whereIn("e.location", query.getLocationList());
    q.whereEncounterIn("e.encounterId", context);

    if (query.getWhich() == null || query.getWhich() == TimeQualifier.ANY) {
      List<Integer> results = evaluationService.evaluateToList(q, Integer.class);
      result.getMemberIds().addAll(results);
    } else {
      q.innerJoin("e.patient", "p");
      q.select("p.patientId");
      if (query.getWhich() == TimeQualifier.LAST) {
        q.orderDesc("e.encounterDatetime");
      } else {
        q.orderAsc("e.encounterDatetime");
      }

      ListMap<Integer, Integer> foundEncountersForPatients = new ListMap<Integer, Integer>();
      int maxNumPerPatient = ObjectUtil.nvl(query.getWhichNumber(), 1);
      for (Object[] row : evaluationService.evaluateToList(q)) {
        Integer encounterId = (Integer) row[0];
        Integer patientId = (Integer) row[1];
        foundEncountersForPatients.putInList(patientId, encounterId);
        if (foundEncountersForPatients.get(patientId).size() <= maxNumPerPatient) {
          result.add(encounterId);
        }
      }
    }

    return result;
  }
  @Override
  public EvaluatedPersonData evaluate(PersonDataDefinition definition, EvaluationContext context)
      throws EvaluationException {

    FirstEncounterAtFacilityDataDefinition def =
        (FirstEncounterAtFacilityDataDefinition) definition;
    EvaluatedPersonData c = new EvaluatedPersonData(def, context);

    if (context.getBaseCohort() == null || context.getBaseCohort().isEmpty()) {
      return c;
    }

    // find the facility number
    MOHFacility facility = (MOHFacility) context.getParameterValue("facility");

    // fail quickly if the facility does not exist
    if (facility == null) {
      log.warn("No facility provided; returning empty data.");
      return c;
    }

    // use HQL to do our bidding
    String hql =
        "from Encounter"
            + " where voided=false"
            + " and patientId in (:patientIds)"
            + " and location in (:locationList)"
            + " and encounterDatetime <= :onOrBefore"
            + " order by encounterDatetime asc";

    Map<String, Object> m = new HashMap<String, Object>();
    m.put("patientIds", context.getBaseCohort());
    m.put("locationList", facility.getLocations());
    m.put("onOrBefore", context.getEvaluationDate());

    DataSetQueryService qs = Context.getService(DataSetQueryService.class);
    List<Object> queryResult = qs.executeHqlQuery(hql, m);

    ListMap<Integer, Encounter> encForPatients = new ListMap<Integer, Encounter>();
    for (Object o : queryResult) {
      Encounter enc = (Encounter) o;
      encForPatients.putInList(enc.getPatientId(), enc);
    }

    for (Integer pId : encForPatients.keySet()) {
      List<Encounter> l = encForPatients.get(pId);
      c.addData(pId, l.get(0));
    }

    return c;
  }
  /** @see Moh731CohortLibrary#currentlyInCare() */
  @Test
  public void currentlyInCare() throws Exception {
    EncounterType triage = MetadataUtils.getEncounterType(CommonMetadata._EncounterType.TRIAGE);
    EncounterType hivConsult =
        MetadataUtils.getEncounterType(HivMetadata._EncounterType.HIV_CONSULTATION);

    // Give patient #2 irrelevant encounter during 90 day window
    TestUtils.saveEncounter(TestUtils.getPatient(2), triage, TestUtils.date(2012, 6, 15));

    // Give patient #6 relevant encounter before and after 90 day window
    TestUtils.saveEncounter(TestUtils.getPatient(6), hivConsult, TestUtils.date(2012, 3, 31));
    TestUtils.saveEncounter(TestUtils.getPatient(6), hivConsult, TestUtils.date(2012, 7, 1));

    // Give patient #7 relevant encounter at start of 90 day window
    TestUtils.saveEncounter(TestUtils.getPatient(7), hivConsult, TestUtils.date(2012, 4, 1));

    // Give patient #8 relevant encounter at end of 90 day window
    TestUtils.saveEncounter(TestUtils.getPatient(8), hivConsult, TestUtils.date(2012, 6, 30));

    CohortDefinition cd = moh731Cohorts.currentlyInCare();
    context.addParameterValue("onDate", PERIOD_END);
    EvaluatedCohort evaluated =
        Context.getService(CohortDefinitionService.class).evaluate(cd, context);
    ReportingTestUtils.assertCohortEquals(Arrays.asList(7, 8), evaluated);
  }
  /**
   * @should start on PCP_PROPHYLAXIS_STARTED with not null answer
   * @should not start on PCP_PROPHYLAXIS_STARTED with null answer
   * @should stop on REASON_PCP_PROPHYLAXIS_STOPPED with not null answer
   * @should not stop on REASON_PCP_PROPHYLAXIS_STOPPED with null answer
   * @should start on CURRENT_MEDICATIONS equal to TRIMETHOPRIM_AND_SULFAMETHOXAZOLE
   * @should not start on CURRENT_MEDICATIONS equal to something other than
   *     TRIMETHOPRIM_AND_SULFAMETHOXAZOLE
   * @should start on PATIENT_REPORTED_CURRENT_PCP_PROPHYLAXIS equal to
   *     TRIMETHOPRIM_AND_SULFAMETHOXAZOLE
   * @should not start on PATIENT_REPORTED_CURRENT_PCP_PROPHYLAXIS equal to something other than
   *     TRIMETHOPRIM_AND_SULFAMETHOXAZOLE
   */
  @Override
  public EvaluatedPersonData evaluate(
      final PersonDataDefinition definition, final EvaluationContext context)
      throws EvaluationException {
    EvaluatedPersonData data = new EvaluatedPersonData(definition, context);

    if (context.getBaseCohort().isEmpty()) return data;

    Map<String, Object> m = new HashMap<String, Object>();
    m.put("personIds", context.getBaseCohort());

    String sql =
        "select person_id, obs_datetime"
            + "  from obs"
            + "  where"
            + "    person_id in (:personIds)"
            + "    and ("
            + "      (concept_id = 1263 and value_coded is not null) "
            + "      or (concept_id in (1193, 1109) and value_coded = 916) "
            + "    )"
            + "    and voided = 0";

    ListMap<Integer, Date> mappedStartDates = makeDatesMapFromSQL(sql, m);

    sql =
        "select person_id, obs_datetime"
            + "  from obs"
            + "  where"
            + "    person_id in (:personIds)"
            + "    and ("
            + "      (concept_id in (1262, 1925) and value_coded is not null) "
            + "      or (concept_id = 1261 and value_coded = 1260) "
            + "    )"
            + "    and voided = 0";

    ListMap<Integer, Date> mappedStopDates = makeDatesMapFromSQL(sql, m);

    for (Integer memberId : context.getBaseCohort().getMemberIds()) {
      Set<Date> stopDates = safeFind(mappedStopDates, memberId);
      Set<Date> startDates = safeFind(mappedStartDates, memberId);
      String rangeInformation =
          buildRangeInformation(startDates, stopDates, context.getEvaluationDate());
      data.addData(memberId, rangeInformation);
    }

    return data;
  }
  @Override
  protected Map<String, Object> getSubstitutions(EvaluationContext context) {

    Map<String, Object> m = new HashMap<String, Object>();
    m.put("encounterTypeIds", Arrays.asList(1, 2, 3, 4, 13));
    m.put("onOrBefore", context.getEvaluationDate());

    return m;
  }
  @Override
  public EncounterQueryResult evaluate(EncounterQuery definition, EvaluationContext context)
      throws EvaluationException {
    context = ObjectUtil.nvl(context, new EvaluationContext());

    BasicEncounterQuery query = (BasicEncounterQuery) definition;
    EncounterQueryResult result = new EncounterQueryResult(query, context);

    Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Encounter.class);
    criteria.setProjection(Projections.id());
    criteria.add(Restrictions.eq("voided", false));

    if (query.getOnOrAfter() != null) {
      criteria.add(Restrictions.ge("encounterDatetime", query.getOnOrAfter()));
    }
    if (query.getOnOrBefore() != null) {
      criteria.add(
          Restrictions.le(
              "encounterDatetime", DateUtil.getEndOfDayIfTimeExcluded(query.getOnOrBefore())));
    }

    if (context.getBaseCohort() != null) {
      if (context.getBaseCohort().size() == 0) {
        return result;
      } else {
        criteria.add(Restrictions.in("patient.id", context.getBaseCohort().getMemberIds()));
      }
    }
    if (context instanceof EncounterEvaluationContext) {
      EncounterIdSet baseEncounters = ((EncounterEvaluationContext) context).getBaseEncounters();
      if (baseEncounters != null) {
        if (baseEncounters.getSize() == 0) {
          return result;
        } else {
          criteria.add(Restrictions.in("id", baseEncounters.getMemberIds()));
        }
      }
    }

    for (Integer encounterId : ((List<Integer>) criteria.list())) {
      result.add(encounterId);
    }
    return result;
  }
  /**
   * @see PersonDataEvaluator#evaluate(PersonDataDefinition, EvaluationContext)
   * @should return the most preferred name for each person in the passed context
   * @should return empty result set for an empty base cohort
   * @should return the preferred name for all persons
   */
  public EvaluatedPersonData evaluate(PersonDataDefinition definition, EvaluationContext context)
      throws EvaluationException {
    EvaluatedPersonData c = new EvaluatedPersonData(definition, context);

    if (context.getBaseCohort() != null && context.getBaseCohort().isEmpty()) {
      return c;
    }

    HqlQueryBuilder q = new HqlQueryBuilder();
    q.select("pn.person.personId", "pn");
    q.from(PersonName.class, "pn");
    q.wherePersonIn("pn.person.personId", context);
    q.orderAsc("pn.preferred");

    Map<Integer, Object> data = evaluationService.evaluateToMap(q, Integer.class, Object.class);
    c.setData(data);

    return c;
  }
  /**
   * @see DataSetEvaluator#evaluate(DataSetDefinition, EvaluationContext)
   * @should evaluate an EncounterAndObsDataSetDefinition
   */
  public DataSet evaluate(DataSetDefinition dataSetDefinition, EvaluationContext context)
      throws EvaluationException {

    EncounterAndObsDataSetDefinition definition =
        (EncounterAndObsDataSetDefinition) dataSetDefinition;
    SimpleDataSet dataSet = new SimpleDataSet(dataSetDefinition, context);
    context = ObjectUtil.nvl(context, new EvaluationContext());
    Cohort cohort = context.getBaseCohort();
    if (context.getLimit() != null) {
      CohortUtil.limitCohort(cohort, context.getLimit());
    }

    // Retrieve the rows for the dataset

    BasicEncounterQuery encounterQuery = new BasicEncounterQuery();
    encounterQuery.setWhich(definition.getWhichEncounterQualifier());
    encounterQuery.setWhichNumber(definition.getQuantity());
    encounterQuery.setEncounterTypes(definition.getEncounterTypes());
    encounterQuery.setForms(definition.getForms());
    encounterQuery.setOnOrBefore(definition.getEncounterDatetimeOnOrBefore());
    encounterQuery.setOnOrAfter(definition.getEncounterDatetimeOnOrAfter());

    EncounterQueryResult encounterIdRows = encounterQueryService.evaluate(encounterQuery, context);

    HqlQueryBuilder q = new HqlQueryBuilder();
    EncounterEvaluationContext eec = new EncounterEvaluationContext();
    eec.setBaseEncounters(encounterIdRows);
    q.from(Encounter.class, "e").whereEncounterIn("e.encounterId", eec);
    List<Encounter> encounters = evaluationService.evaluateToList(q, Encounter.class);

    // Determine what columns to display in the dataset

    List<EncounterAndObsDataSetDefinition.ObsOptionalColumn> optionalColumns =
        definition.getOptionalColumns();
    List<PatientIdentifierType> patientIdentifierTypes = definition.getPatientIdentifierTypes();
    List<EncounterAndObsDataSetDefinition.ColumnDisplayFormat> columnDisplayFormat =
        definition.getColumnDisplayFormat();
    Integer maxColumnHeaderWidth = definition.getMaxColumnHeaderWidth();

    if (patientIdentifierTypes == null) {
      patientIdentifierTypes = new ArrayList<PatientIdentifierType>();
    }

    if (columnDisplayFormat == null) {
      columnDisplayFormat = new ArrayList<ColumnDisplayFormat>();
    }

    if (columnDisplayFormat.size() == 0) {
      columnDisplayFormat.add(EncounterAndObsDataSetDefinition.ColumnDisplayFormat.ID);
    }

    // section index should be added here

    // Store all encounters within a data structure that keeps track of obs, obs groups, and their
    // occurrences
    // in order that column headers are unique and children obs are grouped by their parent
    Map<Encounter, Map<ObsColumnDescriptor, Obs>> populatedFieldMap = populateFieldMap(encounters);

    // Keeps track of the column headers for all obs-related columns
    Set<ObsColumnDescriptor> allColumns = new TreeSet<ObsColumnDescriptor>();

    for (Encounter encounter : encounters) {
      Map<ObsColumnDescriptor, Obs> obsInEncounter = populatedFieldMap.get(encounter);
      // Not all encounters will have data for all columns but
      // each encounter row should have all column headers
      // so encounters line up properly under a common set of column headers
      allColumns.addAll(obsInEncounter.keySet());
    }

    // add the data to the DataSet
    return addData(
        dataSet,
        encounters,
        patientIdentifierTypes,
        optionalColumns,
        columnDisplayFormat,
        maxColumnHeaderWidth,
        allColumns,
        populatedFieldMap);
  }
  @RequestMapping(method = RequestMethod.POST)
  public void process(
      final @RequestParam(required = false, value = "cohort") Integer cohortId,
      final @RequestParam(required = true, value = "template") MultipartFile template,
      final HttpServletResponse response)
      throws IOException, EvaluationException {

    EvaluationContext context = new EvaluationContext();
    context.addParameterValue("currentDate", new Date());
    if (cohortId != null) {
      context.setBaseCohort(Context.getCohortService().getCohort(cohortId));
    }

    PatientDataSetDefinition definition = new PatientDataSetDefinition();

    definition.addColumn(
        "id", new PatientIdDataDefinition(), StringUtils.EMPTY, new ObjectFormatter());

    ListConverter listConverter = new ListConverter();
    listConverter.setMaxNumberOfItems(1);

    PatientIdentifierDataDefinition preferredIdentifier = new PatientIdentifierDataDefinition();
    preferredIdentifier.addType(Context.getPatientService().getPatientIdentifierType(1));
    definition.addColumn("identifier", preferredIdentifier, StringUtils.EMPTY, listConverter);

    definition.addColumn(
        "name",
        new PreferredNameDataDefinition(),
        StringUtils.EMPTY,
        new ObjectFormatter("{familyName}, {givenName}"));

    AgeDataDefinition ageOnDate = new AgeDataDefinition();
    ageOnDate.addParameter(new Parameter("effectiveDate", "effective date", Date.class));
    definition.addColumn("age", ageOnDate, "effectiveDate=${currentDate}", new AgeConverter());

    definition.addColumn(
        "birthdate",
        new BirthdateDataDefinition(),
        StringUtils.EMPTY,
        new BirthdateConverter("dd-MMM-yyyy"));
    definition.addColumn(
        "gender", new GenderDataDefinition(), StringUtils.EMPTY, new ObjectFormatter());

    ReportDefinition reportDefinition = new ReportDefinition();
    reportDefinition.setName("Test Report");
    reportDefinition.addDataSetDefinition("PatientDataSetDefinition", definition, null);

    final ReportDesign design = new ReportDesign();
    design.setName("Test Report Design With Excel Template Renderer");
    design.setReportDefinition(reportDefinition);
    design.setRendererType(ExcelTemplateRenderer.class);

    Properties properties = new Properties();
    properties.put("repeatingSections", "sheet:1,dataset:PatientDataSetDefinition");
    design.setProperties(properties);

    ReportDesignResource resource = new ReportDesignResource();
    resource.setName("excel-template.xls");
    InputStream inputStream = template.getInputStream();
    resource.setContents(IOUtils.toByteArray(inputStream));
    IOUtils.closeQuietly(inputStream);
    design.addResource(resource);

    ExcelTemplateRenderer renderer =
        new ExcelTemplateRenderer() {
          public ReportDesign getDesign(String argument) {
            return design;
          }
        };

    ReportDefinitionService rs = Context.getService(ReportDefinitionService.class);
    ReportData data = rs.evaluate(reportDefinition, context);

    CsvReportRenderer csvReportRenderer = new CsvReportRenderer();
    csvReportRenderer.render(data, "output:csv", System.out);

    File file = File.createTempFile("excel", "summary");
    BufferedOutputStream bufferedOutputStream =
        new BufferedOutputStream(new FileOutputStream(file));
    renderer.render(data, "output:xls", bufferedOutputStream);
    bufferedOutputStream.close();

    response.setHeader("Content-Disposition", "attachment; filename=patient-summary.xls");
    response.setContentType("application/vnd.ms-excel");
    response.setContentLength((int) file.length());

    FileCopyUtils.copy(new FileInputStream(file), response.getOutputStream());
    if (file.delete()) log.info("Temporary file deleted!");
  }
  /** @see DataSetEvaluator#evaluate(DataSetDefinition, EvaluationContext) */
  @SuppressWarnings("unchecked")
  public DataSet evaluate(DataSetDefinition dataSetDefinition, EvaluationContext context)
      throws EvaluationException {

    PatientDataSetDefinition dsd = (PatientDataSetDefinition) dataSetDefinition;
    context = ObjectUtil.nvl(context, new EvaluationContext());

    SimpleDataSet dataSet = new SimpleDataSet(dsd, context);
    dataSet.setSortCriteria(dsd.getSortCriteria());

    // Construct a new EvaluationContext based on the passed filters
    Cohort c = context.getBaseCohort();
    if (dsd.getRowFilters() != null) {
      for (Mapped<? extends CohortDefinition> q : dsd.getRowFilters()) {
        Cohort s = Context.getService(CohortDefinitionService.class).evaluate(q, context);
        c = CohortUtil.intersectNonNull(c, s);
      }
    }
    if (c == null) {
      c =
          Context.getService(CohortDefinitionService.class)
              .evaluate(new AllPatientsCohortDefinition(), context);
    }

    EvaluationContext ec = context.shallowCopy();
    if (!CohortUtil.areEqual(ec.getBaseCohort(), c)) {
      ec.setBaseCohort(c);
    }

    // Evaluate each specified ColumnDefinition for all of the included rows and add these to the
    // dataset
    for (RowPerObjectColumnDefinition cd : dsd.getColumnDefinitions()) {

      if (log.isDebugEnabled()) {
        log.debug("Evaluating column: " + cd.getName());
        log.debug(
            "With Data Definition: "
                + DefinitionUtil.format(cd.getDataDefinition().getParameterizable()));
        log.debug("With Mappings: " + cd.getDataDefinition().getParameterMappings());
        log.debug("With Parameters: " + ec.getParameterValues());
      }
      StopWatch sw = new StopWatch();
      sw.start();

      MappedData<? extends PatientDataDefinition> dataDef =
          (MappedData<? extends PatientDataDefinition>) cd.getDataDefinition();
      EvaluatedPatientData data =
          Context.getService(PatientDataService.class).evaluate(dataDef, ec);

      for (Integer id : c.getMemberIds()) {
        for (DataSetColumn column : cd.getDataSetColumns()) {
          Object val = data.getData().get(id);
          val = DataUtil.convertData(val, dataDef.getConverters());
          dataSet.addColumnValue(id, column, val);
        }
      }

      sw.stop();
      if (log.isDebugEnabled()) {
        log.debug("Evaluated column. Duration: " + sw.toString());
      }
    }

    return dataSet;
  }
  public PatientDataResult calculateResult(
      List<PatientDataResult> results, EvaluationContext context) {

    PatientAttributeResult alert = new PatientAttributeResult(null, null);

    ProgramWorkflowState state = (ProgramWorkflowState) context.getParameterValue("state");

    StringBuffer alerts = new StringBuffer();

    double height = 0;
    double weight = 0;

    for (PatientDataResult result : results) {

      if (result.getName().equals("CD4Test")) {
        AllObservationValuesResult cd4 = (AllObservationValuesResult) result;

        if (cd4.getValue() != null) {
          int decline = calculateDecline(cd4.getValue());

          if (decline > 50
              && (state.toString().contains("GROUP") || state.toString().contains("FOLLOWING"))) {
            alerts.append("CD4 decline(");
            alerts.append(decline);
            alerts.append(").\n");
          }

          Obs lastCd4 = null;

          if (cd4.getValue().size() > 0) {
            lastCd4 = cd4.getValue().get(cd4.getValue().size() - 1);
          }

          if (lastCd4 == null) {
            alerts.append("No CD4 recorded.\n");
          } else {
            Date dateCd4 = lastCd4.getObsDatetime();
            Date date = Calendar.getInstance().getTime();

            int diff = calculateMonthsDifference(date, dateCd4);

            if (diff > 12) {
              alerts.append("Very late CD4(" + diff + " months ago).\n");
            } else if ((diff > 6) && state.toString().contains("FOLLOWING")) {
              alerts.append("Late CD4(" + diff + " months ago).\n");
            }

            if (state.toString().contains("FOLLOWING")
                && lastCd4.getValueNumeric() != null
                && lastCd4.getValueNumeric() < 500) {
              alerts.append("Eligible for Treatment.\n");
            }
          }
        }
      }
      if (result.getName().equals("viralLoadTest")) {
        AllObservationValuesResult viraload = (AllObservationValuesResult) result;

        if (viraload.getValue() != null) {
          Obs lastviraload = null;

          if (viraload.getValue().size() > 0) {
            lastviraload = viraload.getValue().get(viraload.getValue().size() - 1);
          }

          if (state.toString().contains("GROUP") && (lastviraload == null)) {
            alerts.append("No VL recorded.\n");
          } else {
            try {
              Date dateVl = lastviraload.getObsDatetime();
              Date date = Calendar.getInstance().getTime();

              int diff = calculateMonthsDifference(date, dateVl);

              if (state.toString().contains("GROUP")) {
                if (diff > 12) {
                  alerts.append("Late VL(" + diff + " months ago).\n");
                }

                if (lastviraload.getValueNumeric() != null
                    && lastviraload.getValueNumeric() > 1000) {
                  alerts.append("VL Failure " + lastviraload.getValueNumeric() + ".\n");
                }
              }
            } catch (Exception e) {
            }
          }
        }
      }
      if (result.getName().equals("weightObs")) {
        AllObservationValuesResult wt = (AllObservationValuesResult) result;

        if (wt.getValue() != null) {
          int decline = calculatePercentageDecline(wt.getValue());

          if (decline > 5) {
            alerts.append("WT decline(");
            alerts.append(decline);
            alerts.append("%, ");
            int kilosLost = calculateDecline(wt.getValue());
            alerts.append(kilosLost);
            alerts.append("kg)\n");
          }

          if (wt.getValue().size() > 0) {
            weight = wt.getValue().get(wt.getValue().size() - 1).getValueNumeric();
          }
        }

        if (wt.getValue() == null || wt.getValue().size() == 0) {
          alerts.append("No weight recorded.\n");
        }
      }

      if (result.getName().equals("RecentHeight")) {
        ObservationResult heightOb = (ObservationResult) result;

        if (heightOb.getValue() == null || heightOb.getValue().trim().length() == 0) {
          alerts.append("No height recorded.\n");
        } else {
          height = Double.parseDouble(heightOb.getValue());
        }
      }

      if (result.getName().equals("lastEncInMonth")) {
        DateValueResult encinmonths = (DateValueResult) result;
        if (encinmonths.getValue() != null) {
          Date dateVl = encinmonths.getDateOfObservation();
          Date date = Calendar.getInstance().getTime();
          int diff = calculateMonthsDifference(date, dateVl);
          if (diff > 12) {
            alerts.append("LTFU determine status.\n");
          }
        }
      }

      if (result.getName().equals("IO") && result.getValue() != null) {
        alerts.append("OI reported last visit: " + result.getValue() + "\n");
      }

      if (result.getName().equals("SideEffects") && result.getValue() != null) {
        alerts.append("Side effects reported last visit: " + result.getValue() + "\n");
      }
    }

    if (height > 0 && weight > 0) {
      double bmi = weight / (height / 100 * height / 100);
      int decimalPlace = 1;
      BigDecimal bd = new BigDecimal(Double.toString(bmi));
      bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);

      if (bmi < 16) {
        alerts.append("Very low BMI (" + bd.doubleValue() + ").\n");
      } else if (bmi < 18.5) {
        alerts.append("Low BMI (" + bd.doubleValue() + ").\n");
      }
    }

    alert.setValue(alerts.toString().trim());
    return alert;
  }