package org.openmrs.module.reporting.web.reports; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.User; import org.openmrs.api.context.Context; import org.openmrs.module.ModuleUtil; import org.openmrs.module.reporting.ReportingConstants; import org.openmrs.module.reporting.common.DateUtil; import org.openmrs.module.reporting.common.ObjectUtil; import org.openmrs.module.reporting.propertyeditor.ReportDefinitionEditor; import org.openmrs.module.reporting.report.Report; import org.openmrs.module.reporting.report.ReportData; import org.openmrs.module.reporting.report.ReportProcessorConfiguration; import org.openmrs.module.reporting.report.ReportProcessorConfiguration.ProcessorMode; import org.openmrs.module.reporting.report.ReportRequest; import org.openmrs.module.reporting.report.ReportRequest.PriorityComparator; import org.openmrs.module.reporting.report.ReportRequest.Status; import org.openmrs.module.reporting.report.definition.ReportDefinition; import org.openmrs.module.reporting.report.processor.ReportProcessor; import org.openmrs.module.reporting.report.renderer.RenderingMode; import org.openmrs.module.reporting.report.service.ReportService; import org.openmrs.module.reporting.report.util.ReportUtil; import org.openmrs.module.reporting.web.renderers.WebReportRenderer; import org.openmrs.module.reporting.web.util.AjaxUtil; import org.openmrs.propertyeditor.UserEditor; import org.openmrs.util.OpenmrsConstants; import org.openmrs.util.OpenmrsUtil; import org.openmrs.web.WebConstants; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.beans.propertyeditors.CustomNumberEditor; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.RedirectView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @Controller public class ReportHistoryController { private final Log log = LogFactory.getLog(getClass()); @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = Context.getDateFormat(); dateFormat.setLenient(false); binder.registerCustomEditor(ReportDefinition.class, new ReportDefinitionEditor()); binder.registerCustomEditor(User.class, new UserEditor()); binder.registerCustomEditor(Integer.class, new CustomNumberEditor(Integer.class, true)); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true, 10)); } @RequestMapping("/module/reporting/reports/reportHistory") public void showReportHistory(ModelMap model, @RequestParam(value="reportDefinition", required=false) ReportDefinition reportDefinition, @RequestParam(value="requestedBy", required=false) User requestedBy, @RequestParam(value="statuses", required=false) Status[] statuses, @RequestParam(value="requestOnOrAfter", required=false) Date requestOnOrAfter, @RequestParam(value="requestOnOrBefore", required=false) Date requestOnOrBefore) { Status[] historyStatuses = new Status[] {Status.COMPLETED, Status.SAVED, Status.FAILED}; model.addAttribute("historyStatuses", historyStatuses); if (statuses == null) { statuses = historyStatuses; } model.addAttribute("reportDefinition", reportDefinition); model.addAttribute("requestedBy", requestedBy); model.addAttribute("statuses", Arrays.asList(statuses)); model.addAttribute("requestOnOrAfter", requestOnOrAfter); model.addAttribute("requestOnOrBefore", requestOnOrBefore); requestOnOrBefore = DateUtil.getEndOfDayIfTimeExcluded(requestOnOrBefore); List<ReportRequest> history = getReportService().getReportRequests(reportDefinition, requestOnOrAfter, requestOnOrBefore, statuses); if (requestedBy != null) { for (Iterator<ReportRequest> i = history.iterator(); i.hasNext();) { ReportRequest rr = i.next(); if (!rr.getRequestedBy().equals(requestedBy)) { i.remove(); } } } Collections.sort(history, new PriorityComparator()); Collections.reverse(history); model.addAttribute("history", history); List<RenderingMode> renderingModes = new ArrayList<RenderingMode>(); for (ReportRequest reportRequest : history) { for (RenderingMode mode : getReportService().getRenderingModes(reportRequest.getReportDefinition().getParameterizable())) { if (OpenmrsUtil.nullSafeEquals(mode, reportRequest.getRenderingMode())) { reportRequest.setRenderingMode(mode); } } } model.addAttribute("renderingModes", renderingModes); } @RequestMapping("/module/reporting/reports/deleteReportRequest") public String deleteReportRequest(@RequestParam("uuid") String uuid, @RequestParam(value="returnUrl", required=false) String returnUrl) { ReportService rs = Context.getService(ReportService.class); ReportRequest request = rs.getReportRequestByUuid(uuid); rs.purgeReportRequest(request); return "redirect:" + ObjectUtil.nvlStr(returnUrl, "reportHistory.form"); } @RequestMapping("/module/reporting/reports/loadReportStatus") public String loadReportStatus(ModelMap model, @RequestParam("uuid") String uuid) { ReportService rs = Context.getService(ReportService.class); ReportRequest request = rs.getReportRequestByUuid(uuid); String status = request.getStatus().toString(); List<String> reportLog = rs.loadReportLog(request); if ("REQUESTED".equals(status)) { for (String s : reportLog) { if (s.indexOf("Starting to process report") != -1) { status = "PROCESSING"; // This shouldn't be needed, and is a hack. Needed until we can work out txns } } } Map<String, Object> statusMap = new HashMap<String, Object>(); statusMap.put("status", status); statusMap.put("log", reportLog); // This is needed due to differences in Spring versions in OpenMRS core and how Spring handles JSON Object json = ModuleUtil.compareVersion(OpenmrsConstants.OPENMRS_VERSION_SHORT, "1.11") >= 0 ? statusMap : AjaxUtil.toJson(statusMap); model.addAttribute("json", json); return "/module/reporting/json"; } @RequestMapping("/module/reporting/reports/viewErrorDetails") public void viewErrorDetails(HttpServletResponse response, @RequestParam("uuid") String uuid) throws IOException { ReportRequest rr = Context.getService(ReportService.class).getReportRequestByUuid(uuid); String error = Context.getService(ReportService.class).loadReportError(rr); response.getWriter().write(error); } @RequestMapping("/module/reporting/reports/reportHistorySave") public String saveHistoryElement(@RequestParam("uuid") String uuid, @RequestParam(value="description", required=false) String description) { ReportService rs = Context.getService(ReportService.class); ReportRequest rr = rs.getReportRequestByUuid(uuid); Report report = rs.loadReport(rr); rs.saveReport(report, description); return "redirect:/module/reporting/reports/reportHistoryOpen.form?uuid="+uuid; } @RequestMapping("/module/reporting/reports/reportHistoryOpen") public String openFromHistory(@RequestParam("uuid") String uuid, HttpServletResponse response, WebRequest request, ModelMap model) throws IOException { ReportService rs = Context.getService(ReportService.class); ReportRequest req = rs.getReportRequestByUuid(uuid); if (req == null) { log.warn("Cannot load report request " + uuid); request.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "Cannot load report request", WebRequest.SCOPE_SESSION); return "redirect:/module/reporting/reports/reportHistory.form"; } for (RenderingMode mode : getReportService().getRenderingModes(req.getReportDefinition().getParameterizable())) { if (OpenmrsUtil.nullSafeEquals(mode, req.getRenderingMode())) { req.setRenderingMode(mode); } } model.addAttribute("request", req); if (req.getStatus() == Status.REQUESTED) { model.addAttribute("positionInQueue", rs.getPositionInQueue(req)); } if (req.getStatus() == Status.FAILED) { model.addAttribute("errorDetails", rs.loadReportError(req)); } List<ReportProcessorConfiguration> onDemandProcessors = new ArrayList<ReportProcessorConfiguration>(); List<ReportProcessorConfiguration> automaticProcessors = new ArrayList<ReportProcessorConfiguration>(); for (ReportProcessorConfiguration c : ReportUtil.getAvailableReportProcessorConfigurations(req, ProcessorMode.values())) { ProcessorMode m = c.getProcessorMode(); if (m == ProcessorMode.ON_DEMAND || m == ProcessorMode.ON_DEMAND_AND_AUTOMATIC) { onDemandProcessors.add(c); } if (m == ProcessorMode.AUTOMATIC || m == ProcessorMode.ON_DEMAND_AND_AUTOMATIC) { automaticProcessors.add(c); } } model.addAttribute("onDemandProcessors", onDemandProcessors); model.addAttribute("automaticProcessors", automaticProcessors); return "/module/reporting/reports/reportHistoryOpen"; } @RequestMapping("/module/reporting/reports/viewReport") public ModelAndView viewReport(@RequestParam("uuid") String uuid, HttpServletResponse response, HttpServletRequest request) throws IOException { ReportRequest req = getReportService().getReportRequestByUuid(uuid); RenderingMode rm = req.getRenderingMode(); String linkUrl = "/module/reporting/reports/reportHistoryOpen"; if (rm.getRenderer() instanceof WebReportRenderer) { WebReportRenderer webRenderer = (WebReportRenderer) rm.getRenderer(); linkUrl = webRenderer.getLinkUrl(req.getReportDefinition().getParameterizable()); linkUrl = request.getContextPath() + (linkUrl.startsWith("/") ? "" : "/") + linkUrl; if (req != null) { ReportData reportData = getReportService().loadReportData(req); if (reportData != null) { request.getSession().setAttribute(ReportingConstants.OPENMRS_REPORT_DATA, reportData); request.getSession().setAttribute(ReportingConstants.OPENMRS_REPORT_ARGUMENT, rm.getArgument()); request.getSession().setAttribute(ReportingConstants.OPENMRS_LAST_REPORT_URL, linkUrl); } } return new ModelAndView(new RedirectView(linkUrl)); } else { String filename = rm.getRenderer().getFilename(req).replace(" ", "_"); response.setContentType(rm.getRenderer().getRenderedContentType(req)); byte[] data = getReportService().loadRenderedOutput(req); if (data != null) { response.setHeader("Content-Disposition", "attachment; filename=" + filename); response.setHeader("Pragma", "no-cache"); IOUtils.write(data, response.getOutputStream()); } else { response.getWriter().write("There was an error retrieving the report"); } return null; } } @RequestMapping("/module/reporting/reports/reportHistoryProcess") public String runOnDemandPostProcessor(@RequestParam("uuid") String requestUuid, @RequestParam("processorUuid") String processorUuid, HttpServletResponse response, HttpServletRequest request) throws IOException { ReportRequest req = getReportService().getReportRequestByUuid(requestUuid); ReportProcessorConfiguration rpc = getReportService().getReportProcessorConfigurationByUuid(processorUuid); try { boolean completed = req.getStatus() == Status.COMPLETED || req.getStatus() == Status.SAVED; if ((completed && rpc.getRunOnSuccess()) || (req.getStatus() == Status.FAILED && rpc.getRunOnError())) { getReportService().logReportMessage(req, "Processing Report with " + rpc.getName() + "..."); Class<?> processorType = Context.loadClass(rpc.getProcessorType()); ReportProcessor processor = (ReportProcessor) processorType.newInstance(); Report report = getReportService().loadReport(req); processor.process(report, rpc.getConfiguration()); } } catch (Exception e) { log.warn("Report Processor Failed: " + rpc.getName(), e); getReportService().logReportMessage(req, "Report Processor Failed: " + rpc.getName()); e.printStackTrace(); request.getSession().setAttribute(WebConstants.OPENMRS_ERROR_ATTR, rpc.getName() + " " + Context.getMessageSourceService().getMessage("reporting.runReport.processorFailed")); } //send back a success message request.getSession().setAttribute(WebConstants.OPENMRS_MSG_ATTR, rpc.getName() + " " + Context.getMessageSourceService().getMessage("reporting.runReport.processorSuccess")); return "redirect:reportHistoryOpen.form?uuid=" + requestUuid; } private ReportService getReportService() { return Context.getService(ReportService.class); } }