/********************************************************************************************************************** * ReportGenerator * * created Mar 18, 2012 by semteX * * (c) 2012 APEX gaming technology GmbH **********************************************************************************************************************/ package semtex.archery.data; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.DateFormat; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.List; import semtex.archery.data.entities.Parcour; import semtex.archery.data.entities.UserVisit; import semtex.archery.data.entities.Version; import semtex.archery.data.entities.Visit; import semtex.archery.data.reports.ParcourReportData; import semtex.archery.entities.data.reports.JsonVisitReport; import android.util.Log; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.j256.ormlite.dao.GenericRawResults; import com.lowagie.text.*; import com.lowagie.text.pdf.GrayColor; import com.lowagie.text.pdf.PdfPCell; import com.lowagie.text.pdf.PdfPTable; import com.lowagie.text.pdf.PdfWriter; /** * @author semteX * */ public class ReportGenerator { private static final int PDF_COLUMN_PADDING = 5; private static final String TAG = ReportGenerator.class.getName(); private final DatabaseHelper daoHelper; private final DateFormat dateFormatter = DateFormat.getDateInstance(); // private final DateFormat parcourVersionDateFormatter = new SimpleDateFormater("YYYY-MM-dd"); private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); private static Font titleFont = FontFactory.getFont(FontFactory.COURIER, 28, Font.BOLD); private static Font timeFont = FontFactory.getFont(FontFactory.COURIER, 14, Font.BOLD); private static Font timeFont2 = FontFactory.getFont(FontFactory.COURIER, 14); private static Font tableFont = FontFactory.getFont(FontFactory.COURIER, 12); private static Font tableFontBold = FontFactory.getFont(FontFactory.COURIER, 12, Font.BOLD); private static final GrayColor evenBg = new GrayColor(200); private static final GrayColor oddBg = new GrayColor(230); public ReportGenerator(final DatabaseHelper daoHelper) { this.daoHelper = daoHelper; } /** * generates basic statistical analysis of the parcour, featuring the name + date of the parcour, a list of all the * targets and the users that shot them * * @param visit * @return aggregated DTO */ public ParcourReportData generateReportForVisit(final Visit visit) { Log.i(TAG, "Beginning calculation"); final ParcourReportData reportData = new ParcourReportData(); final Map<String, Integer> totalNumbers = new HashMap<String, Integer>(); final Map<String, Integer> totalPoints = new HashMap<String, Integer>(); daoHelper.getVersionDao().refresh(visit.getVersion()); final Version version = visit.getVersion(); daoHelper.getParcourDao().refresh(version.getParcour()); final Parcour parcour = version.getParcour(); reportData.setParcourName(parcour.getName()); reportData.setParcourRevisionDate(version.getCreated()); reportData.setBeginTime(visit.getBeginTime()); reportData.setEndTime(visit.getEndTime()); Log.i(TAG, "STARTING NEW"); final GenericRawResults<String[]> queryRaw = daoHelper.getParcourDao().queryRaw( "SELECT u.userName, target.target_number, target_hit.points FROM visit " + "LEFT JOIN version ON visit.version_id = version.id " + "LEFT JOIN target ON target.version= version.id " + "LEFT JOIN target_hit ON target_hit.target = target.id " + "LEFT JOIN user_visit uv ON target_hit.user = uv.id " + "LEFT JOIN user u ON uv.user_id = u.id " + "WHERE visit.id=\"" + visit.getId() + "\" AND uv.visit_id=\"" + visit.getId() + "\" ORDER BY target.target_number"); final Map<Integer, Map<String, Integer>> scoringData = reportData.getScoringData(); for (final String[] objects : queryRaw) { final String userName = objects[0]; final Integer targetNumber = Integer.valueOf(objects[1]); final Integer points = objects[2] != null ? Integer.valueOf(objects[2]) : null; if (points != null) { Map<String, Integer> targetHitMap = scoringData.get(targetNumber); if (targetHitMap == null) { targetHitMap = new HashMap<String, Integer>(); scoringData.put(targetNumber, targetHitMap); } targetHitMap.put(userName, points); totalNumbers.put(userName, safeGet(totalNumbers, userName) + 1); totalPoints.put(userName, safeGet(totalPoints, userName) + points); } else { // this sanitation block will be used when there is no result for one user. if (totalNumbers.get(userName) == null) { totalNumbers.put(userName, 0); } // if if (totalPoints.get(userName) == null) { totalPoints.put(userName, 0); } // if } // else } // for final Map<String, Double> avgPoints = new HashMap<String, Double>(); for (final Map.Entry<String, Integer> totalNumberEntry : totalNumbers.entrySet()) { double avgCalcPoints; // avoid division by zero if (totalNumberEntry.getValue() != 0) { avgCalcPoints = totalPoints.get(totalNumberEntry.getKey()) * 1.0 / totalNumberEntry.getValue(); } else { // if avgCalcPoints = 0; } // else avgPoints.put(totalNumberEntry.getKey(), avgCalcPoints); } // for reportData.setAvgPoints(avgPoints); reportData.setTotalPoints(totalPoints); Log.i(TAG, "Ending calculation"); return reportData; } // GenerateReportForVisit public List<String> generateJsonObjectsForVisit(final Visit visit) { final ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setDateFormat(DateFormat.getDateTimeInstance()); final List<String> reports = new LinkedList<String>(); final ParcourReportData data = generateReportForVisit(visit); for (final UserVisit uservisit : visit.getUserVisit()) { final JsonVisitReport report = new JsonVisitReport(); report.setLabel(data.getParcourName() + " (" + sdf.format(data.getParcourRevisionDate()) + ")"); report.setName(uservisit.getUser().getUserName()); report.setVisitDate(data.getBeginTime()); report.setParcourDate(data.getParcourRevisionDate()); final SortedMap<Integer, Integer> scoreMap = new TreeMap<Integer, Integer>(); scoreMap.put(0, 0); for (final Map.Entry<Integer, Map<String, Integer>> entry : data.getScoringData().entrySet()) { scoreMap.put(entry.getKey(), entry.getValue().get(uservisit.getUser().getUserName())); } report.setData(scoreMap); try { reports.add(objectMapper.writeValueAsString(report)); } catch(final JsonGenerationException e) { Log.e(TAG, "error while serializing json", e); } catch(final JsonMappingException e) { Log.e(TAG, "error while serializing json", e); } catch(final IOException e) { Log.e(TAG, "error while serializing json", e); } } return reports; } public String generateHTMLReportForVisit(final Visit visit) { final ParcourReportData data = generateReportForVisit(visit); final StringBuilder builder = new StringBuilder(); builder.append("<h1>" + visit.getVersion().getParcour().getName() + " (Version: " + dateFormatter.format(visit.getVersion().getCreated()) + (visit.getVersion().getName() != null ? " - " + visit.getVersion().getName() : "") + ")</h2><br>"); builder.append("<h4>" + visit.getBeginTime() + " - " + visit.getEndTime() + "</h4><br>"); builder.append("<b>" + String.format("%10s", "Archers:") + "</b>"); Iterator<UserVisit> it = visit.getUserVisit().iterator(); while (it.hasNext()) { final UserVisit uv = it.next(); builder.append(String.format("%10s", uv.getUser().getUserName()) + (it.hasNext() ? ", " : "")); } builder.append("<br><b>" + String.format("%10s", "Total:") + "</b>"); it = visit.getUserVisit().iterator(); while (it.hasNext()) { final UserVisit uv = it.next(); builder.append(String.format("%10s", data.getTotalPoints().get(uv.getUser().getUserName())) + (it.hasNext() ? ", " : "")); } builder.append("<br><b>" + String.format("%10s", "Average:") + "</b>"); it = visit.getUserVisit().iterator(); while (it.hasNext()) { final UserVisit uv = it.next(); final Double value = data.getAvgPoints().get(uv.getUser().getUserName()); builder.append(String.format("%10s", value != null ? MessageFormat.format("{0,number,#.##}", value) : "-") + (it.hasNext() ? ", " : "")); } for (final Integer key : data.getScoringData().keySet()) { final Map<String, Integer> singleScoringData = data.getScoringData().get(key); builder.append("<br><b>" + String.format("%10s", key) + "</b> | "); it = visit.getUserVisit().iterator(); while (it.hasNext()) { final UserVisit uv = it.next(); builder.append(String.format( "%10s", singleScoringData.get(uv.getUser().getUserName()) != null ? singleScoringData.get(uv.getUser() .getUserName()) : "-") + (it.hasNext() ? ", " : "")); } // while } // for return builder.toString(); } public File generatePDFReportForVisit(final Visit visit) throws DocumentException, IOException { final ParcourReportData data = generateReportForVisit(visit); final Document doc = new Document(PageSize.A4, 30, 30, 30, 30); final File file = new File(ExternalStorageManager.getApplicationPath(), File.separator + visit.getId() + ".pdf"); final FileOutputStream fos = new FileOutputStream(new File(ExternalStorageManager.getApplicationPath(), File.separator + visit.getId() + ".pdf")); PdfWriter.getInstance(doc, fos); doc.open(); Paragraph p = new Paragraph(); p.add(new Chunk(visit.getVersion().getParcour().getName(), titleFont)); p.setAlignment(Element.ALIGN_CENTER); doc.add(p); // new line generateNewLines(doc, 3); p = new Paragraph(); p.add(new Chunk("Parcour created: ", timeFont)); p.add(new Chunk(dateFormatter.format(visit.getVersion().getCreated()), timeFont2)); doc.add(p); generateNewLines(doc, 1); p = new Paragraph(); p.add(new Chunk("Begin: ", timeFont)); p.add(new Chunk(visit.getBeginTime().toLocaleString() + "\n", timeFont2)); p.add(new Chunk("End: ", timeFont)); if (visit.getEndTime() != null) { p.add(new Chunk(visit.getEndTime().toLocaleString(), timeFont2)); } doc.add(p); generateNewLines(doc, 2); final PdfPTable table = new PdfPTable(visit.getUserVisit().size() + 1); PdfPCell cell = new PdfPCell(); cell.setBorderWidthBottom(2); cell.setBackgroundColor(evenBg); table.addCell(cell); for (final UserVisit uv : visit.getUserVisit()) { cell = new PdfPCell(new Phrase(uv.getUser().getUserName(), tableFontBold)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setBorderWidthBottom(2); cell.setBackgroundColor(evenBg); setCellPaddings(cell, PDF_COLUMN_PADDING); table.addCell(cell); } cell = new PdfPCell(new Phrase("total", tableFontBold)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setBackgroundColor(oddBg); setCellPaddings(cell, PDF_COLUMN_PADDING); table.addCell(cell); for (final UserVisit uv : visit.getUserVisit()) { cell = new PdfPCell(new Phrase(data.getTotalPoints().get(uv.getUser().getUserName()).toString(), tableFont)); cell.setHorizontalAlignment(Element.ALIGN_RIGHT); setCellPaddings(cell, PDF_COLUMN_PADDING); cell.setBackgroundColor(oddBg); table.addCell(cell); } cell = new PdfPCell(new Phrase("avg", tableFontBold)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setBorderWidthBottom(2); cell.setBackgroundColor(evenBg); setCellPaddings(cell, PDF_COLUMN_PADDING); table.addCell(cell); for (final UserVisit uv : visit.getUserVisit()) { final Double value = data.getAvgPoints().get(uv.getUser().getUserName()); cell = new PdfPCell(new Phrase(value != null ? MessageFormat.format("{0,number,#.##}", value) : "-", tableFont)); cell.setBorderWidthBottom(2); cell.setHorizontalAlignment(Element.ALIGN_RIGHT); cell.setBackgroundColor(evenBg); setCellPaddings(cell, PDF_COLUMN_PADDING); table.addCell(cell); } int modCounter = 0; for (final Integer key : data.getScoringData().keySet()) { final Map<String, Integer> entry = data.getScoringData().get(key); cell = new PdfPCell(new Phrase(new Phrase(key.toString(), tableFontBold))); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setBackgroundColor(modCounter % 2 == 0 ? oddBg : evenBg); table.addCell(cell); for (final UserVisit uv : visit.getUserVisit()) { cell = new PdfPCell(new Phrase(entry.get(uv.getUser().getUserName()) != null ? entry.get( uv.getUser().getUserName()).toString() : "-", tableFont)); cell.setHorizontalAlignment(Element.ALIGN_RIGHT); cell.setBackgroundColor(modCounter % 2 == 0 ? oddBg : evenBg); table.addCell(cell); } // for modCounter++; } // for doc.add(table); doc.close(); fos.close(); return file; } // generatePDFReportsForVisit private void setCellPaddings(final PdfPCell cell, final float cellPadding) { cell.setPaddingBottom(cellPadding); cell.setPaddingTop(cellPadding); } private void generateNewLines(final Document doc, final int lineCount) throws DocumentException { for (int i = 0; i < lineCount; i++) { doc.add(new Paragraph(" ")); } } private int safeGet(final Map<String, Integer> map, final String key) { final Integer val = map.get(key); if (val == null) { return 0; } return val; } private double safeGetDouble(final Map<String, Double> map, final String key) { final Double val = map.get(key); if (val == null) { return 0; } return val; } }