@Override
  public CalculationResultMap evaluate(
      Collection<Integer> cohort, Map<String, Object> map, PatientCalculationContext context) {
    CalculationResultMap ret = new CalculationResultMap();
    PersonService personService = Context.getPersonService();
    PersonAttributeType personAttributeType =
        personService.getPersonAttributeTypeByUuid(
            CommonMetadata._PersonAttributeType.TELEPHONE_CONTACT);

    PersonAttributeCohortDefinition cd = new PersonAttributeCohortDefinition();
    cd.setAttributeType(personAttributeType);

    EvaluatedCohort peopleWithPhoneNumbersCohort =
        CalculationUtils.evaluateWithReporting(cd, cohort, null, context);

    PersonAttributeDataDefinition personAttributeDataDefinition =
        new PersonAttributeDataDefinition();
    personAttributeDataDefinition.setPersonAttributeType(personAttributeType);

    CalculationResultMap data =
        CalculationUtils.evaluateWithReporting(
            personAttributeDataDefinition, cohort, map, null, context);

    for (Integer ptId : cohort) {
      String phoneNumber = null;
      if (peopleWithPhoneNumbersCohort.contains(ptId)) {
        phoneNumber = data.get(ptId).toString();
      }
      ret.put(ptId, new SimpleResult(phoneNumber, this));
    }
    return ret;
  }
  @DocumentedDefinition(value = "exclude test patients")
  public CohortDefinition getExcludeTestPatients() {
    PersonAttributeCohortDefinition personAttributeCohortDefinition =
        new PersonAttributeCohortDefinition();
    personAttributeCohortDefinition.setAttributeType(
        emrApiProperties.getTestPatientPersonAttributeType());
    // the method add value has a bug, using set values for now
    personAttributeCohortDefinition.setValues(Arrays.asList("true"));

    CompositionCohortDefinition excludeTestPatientsCohortDefinition =
        new CompositionCohortDefinition();
    excludeTestPatientsCohortDefinition.addSearch(
        "test", map((CohortDefinition) personAttributeCohortDefinition, ""));
    excludeTestPatientsCohortDefinition.setCompositionString("NOT test");
    return excludeTestPatientsCohortDefinition;
  }
  /** @see CohortDefinitionEvaluator#evaluate(CohortDefinition, EvaluationContext) */
  public EvaluatedCohort evaluate(CohortDefinition cohortDefinition, EvaluationContext context) {

    PersonAttributeCohortDefinition pacd = (PersonAttributeCohortDefinition) cohortDefinition;
    List<String> values = new ArrayList<String>();

    if (pacd.getValues() != null) {
      values.addAll(pacd.getValues());
    }
    if (pacd.getValueConcepts() != null) {
      for (Concept c : pacd.getValueConcepts()) {
        values.add(c.serialize());
      }
    }
    if (pacd.getValueLocations() != null) {
      for (Location l : pacd.getValueLocations()) {
        values.add(l.serialize());
      }
    }

    CohortQueryService cqs = Context.getService(CohortQueryService.class);
    Cohort c = cqs.getPatientsHavingPersonAttributes(pacd.getAttributeType(), values);
    return new EvaluatedCohort(c, cohortDefinition, context);
  }
  public EvaluatedCohort evaluate(
      final CohortDefinition cohortDefinition, final EvaluationContext evaluationContext)
      throws EvaluationException {

    MohCohortDefinition mohCohortDefinition = (MohCohortDefinition) cohortDefinition;

    EncounterService service = Context.getEncounterService();
    ConceptService conceptService = Context.getConceptService();
    CohortDefinitionService definitionService = Context.getService(CohortDefinitionService.class);

    // limit to people with adult initial or return encounters
    EncounterCohortDefinition encounterCohortDefinition = new EncounterCohortDefinition();
    encounterCohortDefinition.addEncounterType(
        service.getEncounterType(ENCOUNTER_TYPE_ADULT_INITIAL));
    encounterCohortDefinition.addEncounterType(
        service.getEncounterType(ENCOUNTER_TYPE_ADULT_RETURN));
    encounterCohortDefinition.setLocationList(mohCohortDefinition.getLocationList());

    // find people who had adult encounters at this location
    Cohort encounterCohort =
        definitionService.evaluate(encounterCohortDefinition, evaluationContext);

    // TODO set these with GPs and a settings page
    Concept firstRapidConcept = conceptService.getConcept(FIRST_HIV_RAPID_TEST_QUALITATIVE_CONCEPT);
    Concept secondRapidConcept =
        conceptService.getConcept(SECOND_HIV_RAPID_TEST_QUALITATIVE_CONCEPT);
    Concept positiveConcept = conceptService.getConcept(POSITIVE_CONCEPT);

    // define search for all people who have rapid test positive results
    CodedObsCohortDefinition firstRapidCohortDefinition = new CodedObsCohortDefinition();
    firstRapidCohortDefinition.setTimeModifier(PatientSetService.TimeModifier.ANY);
    firstRapidCohortDefinition.setLocationList(mohCohortDefinition.getLocationList());
    firstRapidCohortDefinition.setQuestion(firstRapidConcept);
    firstRapidCohortDefinition.setOperator(SetComparator.IN);
    firstRapidCohortDefinition.setValueList(Arrays.asList(positiveConcept));

    // define search all people who have rapid test 2 positive results
    CodedObsCohortDefinition secondRapidCohortDefinition = new CodedObsCohortDefinition();
    secondRapidCohortDefinition.setTimeModifier(PatientSetService.TimeModifier.ANY);
    secondRapidCohortDefinition.setLocationList(mohCohortDefinition.getLocationList());
    secondRapidCohortDefinition.setQuestion(secondRapidConcept);
    secondRapidCohortDefinition.setOperator(SetComparator.IN);
    secondRapidCohortDefinition.setValueList(Arrays.asList(positiveConcept));

    // combine rapid test definitions
    CompositionCohortDefinition rapidCompositionCohortDefinition =
        new CompositionCohortDefinition();
    rapidCompositionCohortDefinition.addSearch(
        "PositiveFirstRapid", firstRapidCohortDefinition, null);
    rapidCompositionCohortDefinition.addSearch(
        "PositiveSecondRapid", secondRapidCohortDefinition, null);
    rapidCompositionCohortDefinition.setCompositionString(
        "PositiveFirstRapid OR PositiveSecondRapid");
    Cohort rapidCompositionCohort =
        definitionService.evaluate(rapidCompositionCohortDefinition, evaluationContext);

    // set age limits
    AgeCohortDefinition ageCohortDefinition = new AgeCohortDefinition();
    ageCohortDefinition.setMinAge(18);
    ageCohortDefinition.setMinAgeUnit(DurationUnit.MONTHS);
    ageCohortDefinition.setMaxAge(14);
    ageCohortDefinition.setMaxAgeUnit(DurationUnit.YEARS);

    // TODO set this concept with a GP and settings page
    Concept elisaConcept = conceptService.getConcept(HIV_ENZYME_IMMUNOASSAY_QUALITATIVE_CONCEPT);

    // define search for all people with a positive elisa evaluation
    CodedObsCohortDefinition elisaCohortDefinition = new CodedObsCohortDefinition();
    elisaCohortDefinition.setTimeModifier(PatientSetService.TimeModifier.ANY);
    elisaCohortDefinition.setLocationList(mohCohortDefinition.getLocationList());
    elisaCohortDefinition.setQuestion(elisaConcept);
    elisaCohortDefinition.setOperator(SetComparator.IN);
    elisaCohortDefinition.setValueList(Arrays.asList(positiveConcept));

    // find patients within age limits who had elisa positive results
    CompositionCohortDefinition elisaCompositionCohortDefinition =
        new CompositionCohortDefinition();
    elisaCompositionCohortDefinition.addSearch("PaediatricAge", ageCohortDefinition, null);
    elisaCompositionCohortDefinition.addSearch("PositiveElisa", elisaCohortDefinition, null);
    elisaCompositionCohortDefinition.setCompositionString("PaediatricAge AND PositiveElisa");
    Cohort elisaCompositionCohort =
        definitionService.evaluate(elisaCompositionCohortDefinition, evaluationContext);

    // Check for the elisa to make sure the elisa happened after 18 months
    PersonAttributeCohortDefinition personAttributeCohortDefinition =
        new PersonAttributeCohortDefinition();
    personAttributeCohortDefinition.setAttributeType(
        Context.getPersonService().getPersonAttributeTypeByName("Health Center"));
    personAttributeCohortDefinition.setValueLocations(mohCohortDefinition.getLocationList());

    // TODO use GPs and a settings page to configure these concepts
    Concept transferConcept = conceptService.getConcept("TRANSFER CARE TO OTHER CENTER");
    Concept withinConcept = conceptService.getConcept("AMPATH");
    Concept missedVisitConcept = conceptService.getConcept("REASON FOR MISSED VISIT");
    Concept transferVisitConcept = conceptService.getConcept("AMPATH CLINIC TRANSFER");

    // define transfer specifications
    CodedObsCohortDefinition transferCohortDefinition = new CodedObsCohortDefinition();
    transferCohortDefinition.setTimeModifier(PatientSetService.TimeModifier.ANY);
    transferCohortDefinition.setLocationList(mohCohortDefinition.getLocationList());
    transferCohortDefinition.setQuestion(transferConcept);
    transferCohortDefinition.setOperator(SetComparator.IN);
    transferCohortDefinition.setValueList(Arrays.asList(withinConcept));

    // find all people with proper health center location and a transfer
    CompositionCohortDefinition transferCompositionCohortDefinition =
        new CompositionCohortDefinition();
    transferCompositionCohortDefinition.addSearch(
        "HealthCenterAttribute", personAttributeCohortDefinition, null);
    transferCompositionCohortDefinition.addSearch(
        "TransferWithinAmpath", transferCohortDefinition, null);
    transferCompositionCohortDefinition.setCompositionString(
        "HealthCenterAttribute AND TransferWithinAmpath");
    Cohort transferCompositionCohort =
        definitionService.evaluate(transferCompositionCohortDefinition, evaluationContext);

    // define missed visits at this location
    CodedObsCohortDefinition missedVisitCohortDefinition = new CodedObsCohortDefinition();
    missedVisitCohortDefinition.setTimeModifier(PatientSetService.TimeModifier.ANY);
    missedVisitCohortDefinition.setLocationList(mohCohortDefinition.getLocationList());
    missedVisitCohortDefinition.setQuestion(missedVisitConcept);
    missedVisitCohortDefinition.setOperator(SetComparator.IN);
    missedVisitCohortDefinition.setValueList(Arrays.asList(transferVisitConcept));

    // find all people with defined health center and a missed visit
    CompositionCohortDefinition missedVisitCompositionCohortDefinition =
        new CompositionCohortDefinition();
    missedVisitCompositionCohortDefinition.addSearch(
        "HealthCenterAttribute", personAttributeCohortDefinition, null);
    missedVisitCompositionCohortDefinition.addSearch(
        "MissedVisitTransfer", missedVisitCohortDefinition, null);
    missedVisitCompositionCohortDefinition.setCompositionString(
        "HealthCenterAttribute AND MissedVisitTransfer");
    Cohort missedVisitCompositionCohort =
        definitionService.evaluate(missedVisitCompositionCohortDefinition, evaluationContext);

    // build the patientIds by combining all found patients
    Set<Integer> patientIds = new HashSet<Integer>();
    patientIds.addAll(encounterCohort.getMemberIds());
    patientIds.addAll(rapidCompositionCohort.getMemberIds());
    patientIds.addAll(elisaCompositionCohort.getMemberIds());
    patientIds.addAll(transferCompositionCohort.getMemberIds());
    patientIds.addAll(missedVisitCompositionCohort.getMemberIds());

    // find fake patients
    PersonAttributeCohortDefinition fakePatientCohortDefinition =
        new PersonAttributeCohortDefinition();
    fakePatientCohortDefinition.setAttributeType(
        Context.getPersonService().getPersonAttributeType(28));
    fakePatientCohortDefinition.setValues(Collections.singletonList("true"));
    Cohort fakePatientCohort =
        definitionService.evaluate(fakePatientCohortDefinition, evaluationContext);

    // remove fake patients from the list
    patientIds.removeAll(fakePatientCohort.getMemberIds());

    // build the cohort from the resulting list of patient ids
    return new EvaluatedCohort(new Cohort(patientIds), cohortDefinition, evaluationContext);
  }