/** @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;
  }
  @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;
  }