diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskanaEngineImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskanaEngineImpl.java index cae0cb77e..c4b1772cd 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskanaEngineImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskanaEngineImpl.java @@ -124,7 +124,7 @@ public class TaskanaEngineImpl implements TaskanaEngine { } public static boolean isPostgreSQL(String databaseProductName) { - return databaseProductName.equals("PostgreSQL"); + return "PostgreSQL".equals(databaseProductName); } @Override diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/MonitorController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/MonitorController.java index c30f116f7..a7f30fe4a 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/MonitorController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/MonitorController.java @@ -3,6 +3,7 @@ package pro.taskana.rest; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import org.slf4j.Logger; @@ -21,7 +22,6 @@ import pro.taskana.TaskState; import pro.taskana.exceptions.InvalidArgumentException; import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.impl.report.header.TimeIntervalColumnHeader; -import pro.taskana.rest.resource.ReportResource; import pro.taskana.rest.resource.ReportAssembler; import pro.taskana.rest.resource.ReportResource; @@ -91,6 +91,20 @@ public class MonitorController { return response; } + @GetMapping(path = "/daily-entry-exit-report") + @Transactional(readOnly = true, rollbackFor = Exception.class) + public ResponseEntity getDailyEntryExitReport() + throws NotAuthorizedException, InvalidArgumentException { + List columnHeaders = IntStream.range(-14, 0) + .mapToObj(TimeIntervalColumnHeader.Date::new) + .collect(Collectors.toList()); + return ResponseEntity.status(HttpStatus.OK) + .body(reportAssembler.toResource( + taskMonitorService.createDailyEntryExitReportBuilder() + .withColumnHeaders(columnHeaders) + .buildReport())); + } + private List getTaskClassificationTimeInterval() { return Stream.concat(Stream.concat( Stream.of(new TimeIntervalColumnHeader.Range(Integer.MIN_VALUE, -10), diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/ReportAssembler.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/ReportAssembler.java index f4f2d9888..61a58c1b6 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/ReportAssembler.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/ReportAssembler.java @@ -14,14 +14,16 @@ import org.springframework.stereotype.Component; import pro.taskana.TaskState; import pro.taskana.exceptions.InvalidArgumentException; import pro.taskana.exceptions.NotAuthorizedException; +import pro.taskana.impl.report.row.FoldableRow; +import pro.taskana.impl.report.row.SingleRow; import pro.taskana.report.ClassificationReport; -import pro.taskana.report.structure.ColumnHeader; import pro.taskana.report.DailyEntryExitReport; +import pro.taskana.report.TaskStatusReport; +import pro.taskana.report.WorkbasketReport; +import pro.taskana.report.structure.ColumnHeader; import pro.taskana.report.structure.QueryItem; import pro.taskana.report.structure.Report; import pro.taskana.report.structure.Row; -import pro.taskana.report.TaskStatusReport; -import pro.taskana.report.WorkbasketReport; import pro.taskana.rest.MonitorController; /** @@ -57,6 +59,13 @@ public class ReportAssembler { return resource; } + public ReportResource toResource(DailyEntryExitReport report) + throws NotAuthorizedException, InvalidArgumentException { + ReportResource resource = toReportResource(report); + resource.add(linkTo(methodOn(MonitorController.class).getDailyEntryExitReport()).withSelfRel().expand()); + return resource; + } + private > ReportResource toReportResource( Report report) { String[] header = report.getColumnHeaders() @@ -80,14 +89,52 @@ public class ReportAssembler { return new ReportResource(meta, rows, sumRow); } - private ReportResource.RowResource transformRow(Row row, + private ReportResource.RowResource transformRow(Row row, String[] header) { + // This is a very dirty solution.. Personally I'd prefer to use a visitor-like pattern here. + // The issue with that: Addition of the visitor code within taskana-core - and having clean code is not + // a reason to append code somewhere where it doesn't belong. + if (row.getClass() == SingleRow.class) { + return transformSingleRow((SingleRow) row, header); + } + return transformFoldableRow((FoldableRow) row, header); + } + + private ReportResource.SingleRowResource transformSingleRow(SingleRow row, String[] header) { Map result = new HashMap<>(); int[] cells = row.getCells(); for (int i = 0; i < cells.length; i++) { result.put(header[i], cells[i]); } - return new ReportResource.RowResource(result, row.getTotalValue()); + return new ReportResource.SingleRowResource(result, row.getTotalValue()); + } + + private ReportResource.FoldableRowResource transformFoldableRow(FoldableRow row, + String[] header) { + ReportResource.FoldableRowResource base = new ReportResource.FoldableRowResource( + transformSingleRow(row, header)); + row.getFoldableRowKeySet().stream() + .map(k -> new Pair<>(k, row.getFoldableRow(k))) + .map(p -> new Pair<>(p.key, transformRow(p.value, header))) + .forEachOrdered(p -> base.addRow(p.key, p.value)); + return base; + } + + /** + * Simple Pair (tuple). + * @param key + * @param value + */ + private class Pair { + + private final K key; + private final V value; + + Pair(K key, V value) { + this.key = key; + this.value = value; + } + } } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/ReportResource.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/ReportResource.java index 5808be275..f430e1ff0 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/ReportResource.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/ReportResource.java @@ -1,15 +1,15 @@ package pro.taskana.rest.resource; +import java.util.Arrays; +import java.util.HashMap; import java.util.Map; import org.springframework.hateoas.ResourceSupport; import pro.taskana.impl.util.LoggerUtils; -import pro.taskana.report.structure.Report; -import pro.taskana.report.structure.Row; /** - * Resource class for {@link Report}. + * Resource class for {@link pro.taskana.report.structure.Report}. */ public class ReportResource extends ResourceSupport { @@ -37,6 +37,68 @@ public class ReportResource extends ResourceSupport { return sumRow; } + /** + * Resource Interface for {@link pro.taskana.report.structure.Row}. + */ + public interface RowResource { + + Map getCells(); + + int getTotal(); + } + + /** + * Resource class for {@link pro.taskana.impl.report.row.SingleRow}. + */ + public static class SingleRowResource implements RowResource { + + private Map cells; + private int total; + + public SingleRowResource(Map cells, int total) { + this.cells = cells; + this.total = total; + } + + @Override + public Map getCells() { + return cells; + } + + @Override + public int getTotal() { + return total; + } + + @Override + public String toString() { + return "SingleRowResource [" + + "rowDesc= " + LoggerUtils.mapToString(this.cells) + + "taskId= " + this.total + + "]"; + } + } + + /** + * Resource class for {@link pro.taskana.impl.report.row.FoldableRow}. + */ + public static class FoldableRowResource extends SingleRowResource { + + private Map foldableRows = new HashMap<>(); + + public FoldableRowResource(SingleRowResource row) { + super(row.getCells(), row.getTotal()); + } + + public void addRow(String desc, RowResource row) { + foldableRows.put(desc, row); + } + + public Map getFoldableRows() { + return foldableRows; + } + } + /** * Meta Information about this ReportResource. */ @@ -81,39 +143,9 @@ public class ReportResource extends ResourceSupport { return "MetaInformation [" + "name= " + this.name + "date= " + this.date - + "header= " + this.header + + "header= " + Arrays.toString(this.header) + "rowDesc= " + this.rowDesc + "]"; } } - - /** - * Resource class for {@link Row}. - */ - public static class RowResource { - - private Map cells; - private int total; - - public RowResource(Map cells, int total) { - this.cells = cells; - this.total = total; - } - - public Map getCells() { - return cells; - } - - public int getTotal() { - return total; - } - - @Override - public String toString() { - return "RowResource [" - + "rowDesc= " + LoggerUtils.mapToString(this.cells) - + "taskId= " + this.total - + "]"; - } - } }