TSK-843: moved report structure ajustment from client to rest service.

This commit is contained in:
Mustapha Zorgati 2019-04-18 15:14:33 +02:00 committed by Holger Hagen
parent d8b1829a53
commit c5d00c020d
25 changed files with 378 additions and 569 deletions

View File

@ -24,18 +24,12 @@ public abstract class Report<I extends QueryItem, H extends ColumnHeader<? super
protected List<H> columnHeaders; protected List<H> columnHeaders;
private Map<String, Row<I>> reportRows = new LinkedHashMap<>(); private Map<String, Row<I>> reportRows = new LinkedHashMap<>();
private Row<I> sumRow; private Row<I> sumRow;
private String rowDesc; private String[] rowDesc;
private String[] expandableHeaders;
protected Report(List<H> columnHeaders, String rowDesc, String[] expandableHeaders) { protected Report(List<H> columnHeaders, String[] rowDesc) {
this.rowDesc = rowDesc; this.rowDesc = rowDesc;
sumRow = createRow(columnHeaders.size()); sumRow = createRow(columnHeaders.size());
this.columnHeaders = new ArrayList<>(columnHeaders); this.columnHeaders = new ArrayList<>(columnHeaders);
this.expandableHeaders = expandableHeaders;
}
protected Report(List<H> columnHeaders, String rowDesc) {
this(columnHeaders, rowDesc, new String[0]);
} }
public final Map<String, Row<I>> getRows() { public final Map<String, Row<I>> getRows() {
@ -50,14 +44,10 @@ public abstract class Report<I extends QueryItem, H extends ColumnHeader<? super
return columnHeaders; return columnHeaders;
} }
public final String getRowDesc() { public final String[] getRowDesc() {
return rowDesc; return rowDesc;
} }
public final String[] getExpandableHeaders() {
return expandableHeaders;
}
public Row<I> getRow(String key) { public Row<I> getRow(String key) {
return reportRows.get(key); return reportRows.get(key);
} }

View File

@ -19,7 +19,7 @@ import pro.taskana.impl.report.structure.Report;
public class CategoryReport extends Report<MonitorQueryItem, TimeIntervalColumnHeader> { public class CategoryReport extends Report<MonitorQueryItem, TimeIntervalColumnHeader> {
public CategoryReport(List<TimeIntervalColumnHeader> timeIntervalColumnHeaders) { public CategoryReport(List<TimeIntervalColumnHeader> timeIntervalColumnHeaders) {
super(timeIntervalColumnHeaders, "CLASSIFICATION CATEGORIES"); super(timeIntervalColumnHeaders, new String[] {"CLASSIFICATION CATEGORIES"});
} }
/** /**

View File

@ -17,7 +17,7 @@ import pro.taskana.impl.report.structure.Report;
public class ClassificationReport extends Report<MonitorQueryItem, TimeIntervalColumnHeader> { public class ClassificationReport extends Report<MonitorQueryItem, TimeIntervalColumnHeader> {
public ClassificationReport(List<TimeIntervalColumnHeader> timeIntervalColumnHeaders) { public ClassificationReport(List<TimeIntervalColumnHeader> timeIntervalColumnHeaders) {
super(timeIntervalColumnHeaders, "CLASSIFICATION KEYS"); super(timeIntervalColumnHeaders, new String[] {"CLASSIFICATION KEYS"});
} }
/** /**
@ -52,7 +52,7 @@ public class ClassificationReport extends Report<MonitorQueryItem, TimeIntervalC
extends Report<DetailedMonitorQueryItem, TimeIntervalColumnHeader> { extends Report<DetailedMonitorQueryItem, TimeIntervalColumnHeader> {
public DetailedClassificationReport(List<TimeIntervalColumnHeader> workbasketLevelReportColumnHeaders) { public DetailedClassificationReport(List<TimeIntervalColumnHeader> workbasketLevelReportColumnHeaders) {
super(workbasketLevelReportColumnHeaders, "TASK CLASSIFICATION KEYS"); super(workbasketLevelReportColumnHeaders, new String[] {"TASK CLASSIFICATION KEYS", "ATTACHMENT"});
} }
@Override @Override

View File

@ -19,7 +19,7 @@ import pro.taskana.impl.report.structure.Report;
public class CustomFieldValueReport extends Report<MonitorQueryItem, TimeIntervalColumnHeader> { public class CustomFieldValueReport extends Report<MonitorQueryItem, TimeIntervalColumnHeader> {
public CustomFieldValueReport(List<TimeIntervalColumnHeader> timeIntervalColumnHeaders) { public CustomFieldValueReport(List<TimeIntervalColumnHeader> timeIntervalColumnHeaders) {
super(timeIntervalColumnHeaders, "CUSTOM FIELDS"); super(timeIntervalColumnHeaders, new String[] {"CUSTOM FIELDS"});
} }
/** /**

View File

@ -21,7 +21,7 @@ public class TaskStatusReport extends Report<TaskQueryItem, TaskStatusColumnHead
public TaskStatusReport(List<TaskState> filter) { public TaskStatusReport(List<TaskState> filter) {
super((filter != null ? filter.stream() : Stream.of(TaskState.values())) super((filter != null ? filter.stream() : Stream.of(TaskState.values()))
.map(TaskStatusColumnHeader::new) .map(TaskStatusColumnHeader::new)
.collect(Collectors.toList()), "DOMAINS"); .collect(Collectors.toList()), new String[] {"DOMAINS"});
} }
/** /**

View File

@ -15,7 +15,7 @@ import pro.taskana.impl.report.structure.Report;
public class TimestampReport extends Report<TimestampQueryItem, TimeIntervalColumnHeader> { public class TimestampReport extends Report<TimestampQueryItem, TimeIntervalColumnHeader> {
public TimestampReport(List<TimeIntervalColumnHeader> dates) { public TimestampReport(List<TimeIntervalColumnHeader> dates) {
super(dates, "STATES", new String[] {"ORG LEVEL 1", "ORG LEVEL 2", "ORG LEVEL 3", "ORG LEVEL 4"}); super(dates, new String[] {"STATES", "ORG LEVEL 1", "ORG LEVEL 2", "ORG LEVEL 3", "ORG LEVEL 4"});
} }
@Override @Override

View File

@ -22,7 +22,7 @@ import pro.taskana.impl.report.structure.Report;
public class WorkbasketReport extends Report<MonitorQueryItem, TimeIntervalColumnHeader> { public class WorkbasketReport extends Report<MonitorQueryItem, TimeIntervalColumnHeader> {
public WorkbasketReport(List<TimeIntervalColumnHeader> timeIntervalColumnHeaders) { public WorkbasketReport(List<TimeIntervalColumnHeader> timeIntervalColumnHeaders) {
super(timeIntervalColumnHeaders, "WORKBASKET KEYS"); super(timeIntervalColumnHeaders, new String[] {"WORKBASKET KEYS"});
} }
/** /**

View File

@ -29,7 +29,7 @@ public class ReportTest {
@Before @Before
public void before() { public void before() {
this.report = new Report<MonitorQueryItem, TimeIntervalColumnHeader>(HEADERS, "rowDesc") { this.report = new Report<MonitorQueryItem, TimeIntervalColumnHeader>(HEADERS, new String[] {"rowDesc"}) {
}; };
@ -116,7 +116,8 @@ public class ReportTest {
@Test @Test
public void testInsertItemWithNoColumnHeaders() { public void testInsertItemWithNoColumnHeaders() {
//given //given
report = new Report<MonitorQueryItem, TimeIntervalColumnHeader>(Collections.emptyList(), "rowDesc") { report = new Report<MonitorQueryItem, TimeIntervalColumnHeader>(Collections.emptyList(),
new String[] {"rowDesc"}) {
}; };
@ -148,7 +149,7 @@ public class ReportTest {
//given //given
List<TimeIntervalColumnHeader> headers = new ArrayList<>(HEADERS); List<TimeIntervalColumnHeader> headers = new ArrayList<>(HEADERS);
headers.add(new TimeIntervalColumnHeader(0, 3)); headers.add(new TimeIntervalColumnHeader(0, 3));
report = new Report<MonitorQueryItem, TimeIntervalColumnHeader>(headers, "rowDesc") { report = new Report<MonitorQueryItem, TimeIntervalColumnHeader>(headers, new String[] {"rowDesc"}) {
}; };
item.setAgeInDays(2); item.setAgeInDays(2);

View File

@ -658,6 +658,22 @@ include::../../../{snippets}/GetTaskClassificationReportDocTest/http-response.ad
Same as task status report Same as task status report
=== Get a timestamp report
A `GET` request is used to get the number of tasks sorted by a task timestamp.
==== Example Request
include::../../../{snippets}/GetTimestampReportDocTest/http-request.adoc[]
==== Example Response
include::../../../{snippets}/GetTimestampReportDocTest/http-response.adoc[]
==== Response Structure
Same as task status report
== Other Resources (using the TaskanaEngineController) == Other Resources (using the TaskanaEngineController)
These resources are directly connected to the Taskana Engine endpoint. These resources are directly connected to the Taskana Engine endpoint.

View File

@ -68,17 +68,15 @@ public class MonitorControllerRestDocumentation {
fieldWithPath("meta.name").description("Name of the report"), fieldWithPath("meta.name").description("Name of the report"),
fieldWithPath("meta.date").description("Date of the report creation"), fieldWithPath("meta.date").description("Date of the report creation"),
fieldWithPath("meta.header").description("Column-headers of the report"), fieldWithPath("meta.header").description("Column-headers of the report"),
fieldWithPath("meta.expHeader").description(
"Expandable Column-headers which match the depth of the foldable rows within the report."),
fieldWithPath("meta.rowDesc").description("Descriptions for the rows the report"), fieldWithPath("meta.rowDesc").description("Descriptions for the rows the report"),
fieldWithPath("meta.totalDesc").description("Description for the report itself"), fieldWithPath("meta.totalDesc").description("Description for the sum column"),
subsectionWithPath("rows").description("Object holding the rows of the report.\n" fieldWithPath("rows").description("Array holding the rows of the report."),
+ "For the exact structure please check the example response above"), fieldWithPath("rows[].cells").description("Array holding all the cell values of the given row"),
fieldWithPath("sumRow").description("Object holding the sums in the columns over all rows"), fieldWithPath("rows[].total").description("Sum of all values of the given row"),
subsectionWithPath("sumRow.cells") fieldWithPath("rows[].depth").description("Depth of the row. If the depth is > 0, then this row is a sub-row of a prior row"),
.description("Contains the accumulated numbers over all columns defined in meta.header.\n" fieldWithPath("rows[].desc").description("Array containing description of the row."),
+ "For the exact structure please check the example response above"), fieldWithPath("rows[].display").description("Boolean identifying if the given row should be initially displayed or not."),
fieldWithPath("sumRow.total").description("Total number of tasks"), subsectionWithPath("sumRow").description("Array holding the sums in the columns over all rows. Structure same as 'rows'"),
fieldWithPath("_links.self.href").ignored() fieldWithPath("_links.self.href").ignored()
}; };
} }
@ -115,4 +113,15 @@ public class MonitorControllerRestDocumentation {
.andDo(MockMvcRestDocumentation.document("GetTaskClassificationReportDocTest", .andDo(MockMvcRestDocumentation.document("GetTaskClassificationReportDocTest",
responseFields(taskReportFieldDescriptors))); responseFields(taskReportFieldDescriptors)));
} }
@Test
public void getTimestampReport() throws Exception {
this.mockMvc.perform(RestDocumentationRequestBuilders
.get("http://127.0.0.1:" + port + "/v1/monitor/timestamp-report")
.accept("application/hal+json")
.header("Authorization", "Basic YWRtaW46YWRtaW4="))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcRestDocumentation.document("GetTimestampReportDocTest",
responseFields(taskReportFieldDescriptors)));
}
} }

View File

@ -4,9 +4,11 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.time.Instant; import java.time.Instant;
import java.util.HashMap; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -80,65 +82,52 @@ public class ReportAssembler {
report.getClass().getSimpleName(), report.getClass().getSimpleName(),
time.toString(), time.toString(),
header, header,
report.getExpandableHeaders(), report.getRowDesc()); report.getRowDesc());
// iterate over each Row and transform it to a RowResource while keeping the domain key. // iterate over each Row and transform it to a RowResource while keeping the domain key.
Map<String, ReportResource.RowResource> rows = report.getRows() List<ReportResource.RowResource> rows = report.getRows()
.entrySet() .entrySet()
.stream() .stream()
.collect(Collectors.toMap(Map.Entry::getKey, i -> transformRow(i.getValue(), header))); .sorted(Comparator.comparing(e -> e.getKey().toLowerCase()))
.map(i -> transformRow(i.getValue(), i.getKey(), new String[report.getRowDesc().length], 0))
.flatMap(Collection::stream)
.collect(Collectors.toList());
ReportResource.RowResource sumRow = transformRow(report.getSumRow(), header); List<ReportResource.RowResource> sumRow = transformRow(report.getSumRow(), meta.getTotalDesc(),
new String[report.getRowDesc().length], 0);
return new ReportResource(meta, rows, sumRow); return new ReportResource(meta, rows, sumRow);
} }
private <I extends QueryItem> ReportResource.RowResource transformRow(Row<I> row, String[] header) { private <I extends QueryItem> List<ReportResource.RowResource> transformRow(Row<I> row, String currentDesc,
String[] desc, int depth) {
// This is a very dirty solution.. Personally I'd prefer to use a visitor-like pattern here. // 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 // 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. // a reason to append code somewhere where it doesn't belong.
if (row.getClass() == SingleRow.class) { if (row.getClass() == SingleRow.class) {
return transformSingleRow((SingleRow<I>) row, header); return Collections.singletonList(transformSingleRow((SingleRow<I>) row, currentDesc, desc, depth));
} }
return transformFoldableRow((FoldableRow<I>) row, header); return transformFoldableRow((FoldableRow<I>) row, currentDesc, desc, depth);
} }
private <I extends QueryItem> ReportResource.SingleRowResource transformSingleRow(SingleRow<I> row, private <I extends QueryItem> ReportResource.RowResource transformSingleRow(SingleRow<I> row, String currentDesc,
String[] header) { String[] previousRowDesc, int depth) {
Map<String, Integer> result = new HashMap<>(); String[] rowDesc = new String[previousRowDesc.length];
int[] cells = row.getCells(); System.arraycopy(previousRowDesc, 0, rowDesc, 0, depth);
for (int i = 0; i < cells.length; i++) { rowDesc[depth] = currentDesc;
result.put(header[i], cells[i]); return new ReportResource.RowResource(row.getCells(), row.getTotalValue(), depth, rowDesc, depth == 0);
}
return new ReportResource.SingleRowResource(result, row.getTotalValue());
} }
private <I extends QueryItem> ReportResource.FoldableRowResource transformFoldableRow(FoldableRow<I> row, private <I extends QueryItem> List<ReportResource.RowResource> transformFoldableRow(FoldableRow<I> row,
String[] header) { String currentDesc, String[] previousRowDesc, int depth) {
ReportResource.FoldableRowResource base = new ReportResource.FoldableRowResource( ReportResource.RowResource baseRow = transformSingleRow(row, currentDesc, previousRowDesc, depth);
transformSingleRow(row, header)); List<ReportResource.RowResource> rowList = new LinkedList<>();
rowList.add(baseRow);
row.getFoldableRowKeySet().stream() row.getFoldableRowKeySet().stream()
.map(k -> new Pair<>(k, row.getFoldableRow(k))) .sorted(String.CASE_INSENSITIVE_ORDER)
.map(p -> new Pair<>(p.key, transformRow(p.value, header))) .map(s -> transformRow(row.getFoldableRow(s), s, baseRow.getDesc(), depth + 1))
.forEachOrdered(p -> base.addRow(p.key, p.value)); .flatMap(Collection::stream)
return base; .forEachOrdered(rowList::add);
return rowList;
} }
/**
* Simple Pair (tuple).
* @param <K> key
* @param <V> value
*/
private class Pair<K, V> {
private final K key;
private final V value;
Pair(K key, V value) {
this.key = key;
this.value = value;
}
}
} }

View File

@ -1,13 +1,10 @@
package pro.taskana.rest.resource; package pro.taskana.rest.resource;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.List;
import java.util.Map;
import org.springframework.hateoas.ResourceSupport; import org.springframework.hateoas.ResourceSupport;
import pro.taskana.impl.util.LoggerUtils;
/** /**
* Resource class for {@link pro.taskana.impl.report.structure.Report}. * Resource class for {@link pro.taskana.impl.report.structure.Report}.
*/ */
@ -15,11 +12,11 @@ public class ReportResource extends ResourceSupport {
private MetaInformation meta; private MetaInformation meta;
private Map<String, RowResource> rows; private List<RowResource> rows;
private RowResource sumRow; private List<RowResource> sumRow;
public ReportResource(MetaInformation meta, Map<String, RowResource> rows, RowResource sumRow) { public ReportResource(MetaInformation meta, List<RowResource> rows, List<RowResource> sumRow) {
this.meta = meta; this.meta = meta;
this.rows = rows; this.rows = rows;
this.sumRow = sumRow; this.sumRow = sumRow;
@ -29,73 +26,62 @@ public class ReportResource extends ResourceSupport {
return meta; return meta;
} }
public Map<String, RowResource> getRows() { public List<RowResource> getRows() {
return rows; return rows;
} }
public RowResource getSumRow() { public List<RowResource> getSumRow() {
return sumRow; return sumRow;
} }
/**
* Resource Interface for {@link pro.taskana.impl.report.structure.Row}.
*/
public interface RowResource {
Map<String, Integer> getCells();
int getTotal();
}
/** /**
* Resource class for {@link pro.taskana.impl.report.row.SingleRow}. * Resource class for {@link pro.taskana.impl.report.row.SingleRow}.
*/ */
public static class SingleRowResource implements RowResource { public static class RowResource {
private Map<String, Integer> cells; private int[] cells;
private int total; private int total;
private int depth;
private String[] desc;
private boolean display;
public SingleRowResource(Map<String, Integer> cells, int total) { public RowResource(int[] cells, int total, int depth, String[] desc, boolean display) {
this.cells = cells; this.cells = cells;
this.total = total; this.total = total;
this.depth = depth;
this.desc = desc;
this.display = display;
} }
@Override @SuppressWarnings("unused")
public Map<String, Integer> getCells() { public int[] getCells() {
return cells; return cells;
} }
@Override @SuppressWarnings("unused")
public int getTotal() { public int getTotal() {
return total; return total;
} }
@SuppressWarnings("unused")
public int getDepth() {
return depth;
}
@SuppressWarnings("unused")
public String[] getDesc() {
return desc;
}
@SuppressWarnings("unused")
public boolean isDisplay() {
return display;
}
@Override @Override
public String toString() { public String toString() {
return "SingleRowResource [" return String.format("RowResourde [cells=%s, total=%d, depth=%d, desc=%s",
+ "rowDesc= " + LoggerUtils.mapToString(this.cells) Arrays.toString(cells), total, depth, Arrays.toString(desc));
+ "taskId= " + this.total
+ "]";
}
}
/**
* Resource class for {@link pro.taskana.impl.report.row.FoldableRow}.
*/
public static class FoldableRowResource extends SingleRowResource {
private Map<String, RowResource> 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<String, RowResource> getFoldableRows() {
return foldableRows;
} }
} }
@ -109,14 +95,12 @@ public class ReportResource extends ResourceSupport {
private String name; private String name;
private String date; private String date;
private String[] header; private String[] header;
private String[] expHeader; private String[] rowDesc;
private String rowDesc;
public MetaInformation(String name, String date, String[] header, String[] expHeader, String rowDesc) { public MetaInformation(String name, String date, String[] header, String[] rowDesc) {
this.name = name; this.name = name;
this.date = date; this.date = date;
this.header = header; this.header = header;
this.expHeader = expHeader;
this.rowDesc = rowDesc; this.rowDesc = rowDesc;
} }
@ -136,18 +120,14 @@ public class ReportResource extends ResourceSupport {
return header; return header;
} }
public String[] getExpHeader() { public String[] getRowDesc() {
return expHeader;
}
public String getRowDesc() {
return rowDesc; return rowDesc;
} }
@Override @Override
public String toString() { public String toString() {
return String.format("MetaInformation [name= %s, date= %s, header= %s, expHeader= %s, rowDesc= %s]", return String.format("MetaInformation [name= %s, date= %s, header= %s, rowDesc= %s]",
name, date, Arrays.toString(header), Arrays.toString(expHeader), rowDesc); name, date, Arrays.toString(header), Arrays.toString(rowDesc));
} }
} }
} }

View File

@ -3,13 +3,13 @@ package pro.taskana.rest.resource;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -25,7 +25,6 @@ import pro.taskana.impl.report.header.TimeIntervalColumnHeader;
import pro.taskana.impl.report.item.DetailedMonitorQueryItem; import pro.taskana.impl.report.item.DetailedMonitorQueryItem;
import pro.taskana.impl.report.item.MonitorQueryItem; import pro.taskana.impl.report.item.MonitorQueryItem;
import pro.taskana.report.ClassificationReport; import pro.taskana.report.ClassificationReport;
import pro.taskana.report.TimestampReport;
import pro.taskana.report.WorkbasketReport; import pro.taskana.report.WorkbasketReport;
import pro.taskana.rest.TestConfiguration; import pro.taskana.rest.TestConfiguration;
@ -66,24 +65,21 @@ public class ReportResourceTest {
ReportResource.MetaInformation meta = resource.getMeta(); ReportResource.MetaInformation meta = resource.getMeta();
assertEquals("WorkbasketReport", meta.getName()); assertEquals("WorkbasketReport", meta.getName());
assertEquals("2019-01-02T00:00:00Z", meta.getDate()); assertEquals("2019-01-02T00:00:00Z", meta.getDate());
assertEquals("WORKBASKET KEYS", meta.getRowDesc()); assertArrayEquals(new String[] {"WORKBASKET KEYS"}, meta.getRowDesc());
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader()); assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
assertArrayEquals(new String[0], meta.getExpHeader());
assertEquals("Total", meta.getTotalDesc()); assertEquals("Total", meta.getTotalDesc());
// rows // rows
assertTrue(resource.getRows().isEmpty()); assertTrue(resource.getRows().isEmpty());
// sumRow // sumRow
assertEquals(ReportResource.SingleRowResource.class, resource.getSumRow().getClass()); assertEquals(1, resource.getSumRow().size());
assertEquals(0, resource.getSumRow().getTotal()); ReportResource.RowResource sumRow = resource.getSumRow().get(0);
Map<String, Integer> cells = resource.getSumRow().getCells(); assertArrayEquals(new String[] {"Total"}, sumRow.getDesc());
assertEquals(5, cells.size()); assertTrue(sumRow.isDisplay());
assertEquals(0, cells.get("2019-01-01").intValue()); assertEquals(0, sumRow.getDepth());
assertEquals(0, cells.get("2018-12-31").intValue()); assertEquals(0, sumRow.getTotal());
assertEquals(0, cells.get("2018-12-30").intValue()); assertArrayEquals(new int[] {0, 0, 0, 0, 0}, sumRow.getCells());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
} }
@Test @Test
@ -103,36 +99,29 @@ public class ReportResourceTest {
ReportResource.MetaInformation meta = resource.getMeta(); ReportResource.MetaInformation meta = resource.getMeta();
assertEquals("ClassificationReport", meta.getName()); assertEquals("ClassificationReport", meta.getName());
assertEquals("2019-01-02T00:00:00Z", meta.getDate()); assertEquals("2019-01-02T00:00:00Z", meta.getDate());
assertEquals("CLASSIFICATION KEYS", meta.getRowDesc()); assertArrayEquals(new String[] {"CLASSIFICATION KEYS"}, meta.getRowDesc());
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader()); assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
assertArrayEquals(new String[0], meta.getExpHeader());
assertEquals("Total", meta.getTotalDesc()); assertEquals("Total", meta.getTotalDesc());
// rows // rows
Map<String, ReportResource.RowResource> rows = resource.getRows(); List<ReportResource.RowResource> rows = resource.getRows();
assertEquals(1, rows.size()); assertEquals(1, rows.size());
ReportResource.RowResource row = rows.get("key"); ReportResource.RowResource row = rows.get(0);
assertEquals(ReportResource.SingleRowResource.class, row.getClass()); assertArrayEquals(new String[] {"key"}, row.getDesc());
assertEquals(0, row.getDepth());
assertEquals(2, row.getTotal()); assertEquals(2, row.getTotal());
Map<String, Integer> cells = row.getCells();
assertEquals(5, cells.size()); assertTrue(row.isDisplay());
assertEquals(2, cells.get("2019-01-01").intValue()); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
// sumRow // sumRow
ReportResource.RowResource sumRow = resource.getSumRow(); assertEquals(1, resource.getSumRow().size());
assertEquals(ReportResource.SingleRowResource.class, sumRow.getClass()); ReportResource.RowResource sumRow = resource.getSumRow().get(0);
assertArrayEquals(new String[] {"Total"}, sumRow.getDesc());
assertTrue(sumRow.isDisplay());
assertEquals(0, sumRow.getDepth());
assertEquals(2, sumRow.getTotal()); assertEquals(2, sumRow.getTotal());
cells = sumRow.getCells(); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, sumRow.getCells());
assertEquals(5, cells.size());
assertEquals(2, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
} }
@ -155,49 +144,36 @@ public class ReportResourceTest {
ReportResource.MetaInformation meta = resource.getMeta(); ReportResource.MetaInformation meta = resource.getMeta();
assertEquals("ClassificationReport", meta.getName()); assertEquals("ClassificationReport", meta.getName());
assertEquals("2019-01-02T00:00:00Z", meta.getDate()); assertEquals("2019-01-02T00:00:00Z", meta.getDate());
assertEquals("CLASSIFICATION KEYS", meta.getRowDesc()); assertArrayEquals(new String[] {"CLASSIFICATION KEYS"}, meta.getRowDesc());
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader()); assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
assertArrayEquals(new String[0], meta.getExpHeader());
assertEquals("Total", meta.getTotalDesc()); assertEquals("Total", meta.getTotalDesc());
// rows // rows
Map<String, ReportResource.RowResource> rows = resource.getRows(); List<ReportResource.RowResource> rows = resource.getRows();
assertEquals(2, rows.size()); assertEquals(2, rows.size());
ReportResource.RowResource row = rows.get("key"); ReportResource.RowResource row = rows.get(0);
assertEquals(ReportResource.SingleRowResource.class, row.getClass()); assertArrayEquals(new String[] {"key"}, row.getDesc());
assertEquals(0, row.getDepth());
assertTrue(row.isDisplay());
assertEquals(2, row.getTotal()); assertEquals(2, row.getTotal());
Map<String, Integer> cells = row.getCells(); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
assertEquals(5, cells.size());
assertEquals(2, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
row = rows.get("key2"); row = rows.get(1);
assertEquals(ReportResource.SingleRowResource.class, row.getClass()); assertArrayEquals(new String[] {"key2"}, row.getDesc());
assertEquals(0, row.getDepth());
assertTrue(row.isDisplay());
assertEquals(2, row.getTotal()); assertEquals(2, row.getTotal());
cells = row.getCells(); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
assertEquals(5, cells.size());
assertEquals(2, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
// sumRow // sumRow
ReportResource.RowResource sumRow = resource.getSumRow(); assertEquals(1, resource.getSumRow().size());
assertEquals(ReportResource.SingleRowResource.class, sumRow.getClass()); ReportResource.RowResource sumRow = resource.getSumRow().get(0);
assertArrayEquals(new String[] {"Total"}, sumRow.getDesc());
assertEquals(0, sumRow.getDepth());
assertTrue(sumRow.isDisplay());
assertEquals(4, sumRow.getTotal()); assertEquals(4, sumRow.getTotal());
cells = sumRow.getCells(); assertArrayEquals(new int[] {0, 0, 0, 0, 4}, sumRow.getCells());
assertEquals(5, cells.size());
assertEquals(4, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
} }
@Test @Test
@ -209,7 +185,7 @@ public class ReportResourceTest {
item.setAgeInDays(daysDiff - 1); item.setAgeInDays(daysDiff - 1);
item.setNumberOfTasks(2); item.setNumberOfTasks(2);
item.setKey("key"); item.setKey("key");
item.setAttachmentKey("attachement"); item.setAttachmentKey("attachment");
report.addItem(item); report.addItem(item);
item.setAttachmentKey(null); item.setAttachmentKey(null);
report.addItem(item); report.addItem(item);
@ -221,82 +197,59 @@ public class ReportResourceTest {
ReportResource.MetaInformation meta = resource.getMeta(); ReportResource.MetaInformation meta = resource.getMeta();
assertEquals("DetailedClassificationReport", meta.getName()); assertEquals("DetailedClassificationReport", meta.getName());
assertEquals("2019-01-02T00:00:00Z", meta.getDate()); assertEquals("2019-01-02T00:00:00Z", meta.getDate());
assertEquals("TASK CLASSIFICATION KEYS", meta.getRowDesc()); assertArrayEquals(new String[] {"TASK CLASSIFICATION KEYS", "ATTACHMENT"}, meta.getRowDesc());
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader()); assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
assertArrayEquals(new String[0], meta.getExpHeader());
assertEquals("Total", meta.getTotalDesc()); assertEquals("Total", meta.getTotalDesc());
// rows // rows
Map<String, ReportResource.RowResource> rows = resource.getRows(); List<ReportResource.RowResource> rows = resource.getRows();
assertEquals(1, rows.size()); assertEquals(1 + 2, rows.size());
assertEquals(ReportResource.FoldableRowResource.class, rows.get("key").getClass());
ReportResource.FoldableRowResource row = (ReportResource.FoldableRowResource) rows.get("key"); ReportResource.RowResource row = rows.get(0);
assertArrayEquals(new String[] {"key", null}, row.getDesc());
assertEquals(0, row.getDepth());
assertTrue(row.isDisplay());
assertEquals(4, row.getTotal()); assertEquals(4, row.getTotal());
Map<String, Integer> cells = row.getCells(); assertArrayEquals(new int[] {0, 0, 0, 0, 4}, row.getCells());
assertEquals(5, cells.size());
assertEquals(4, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
assertEquals(2, row.getFoldableRows().size()); row = rows.get(1);
ReportResource.RowResource foldedRow = row.getFoldableRows().get("attachement"); assertArrayEquals(new String[] {"key", "attachment"}, row.getDesc());
assertEquals(ReportResource.SingleRowResource.class, foldedRow.getClass()); assertEquals(1, row.getDepth());
assertEquals(2, foldedRow.getTotal()); assertFalse(row.isDisplay());
cells = foldedRow.getCells(); assertEquals(2, row.getTotal());
assertEquals(5, cells.size()); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
assertEquals(2, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
foldedRow = row.getFoldableRows().get("N/A"); row = rows.get(2);
assertEquals(ReportResource.SingleRowResource.class, foldedRow.getClass()); assertArrayEquals(new String[] {"key", "N/A"}, row.getDesc());
assertEquals(2, foldedRow.getTotal()); assertEquals(1, row.getDepth());
cells = foldedRow.getCells(); assertFalse(row.isDisplay());
assertEquals(5, cells.size()); assertEquals(2, row.getTotal());
assertEquals(2, cells.get("2019-01-01").intValue()); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
// sumRow // sumRow
ReportResource.RowResource sumRow = resource.getSumRow(); List<ReportResource.RowResource> sumRow = resource.getSumRow();
assertEquals(ReportResource.FoldableRowResource.class, sumRow.getClass()); assertEquals(1 + 2, sumRow.size());
row = sumRow.get(0);
assertArrayEquals(new String[] {"Total", null}, row.getDesc());
assertEquals(0, row.getDepth());
assertTrue(row.isDisplay());
assertEquals(4, row.getTotal()); assertEquals(4, row.getTotal());
cells = row.getCells(); assertArrayEquals(new int[] {0, 0, 0, 0, 4}, row.getCells());
assertEquals(5, cells.size());
assertEquals(4, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
assertEquals(2, row.getFoldableRows().size()); row = sumRow.get(1);
foldedRow = row.getFoldableRows().get("attachement"); assertArrayEquals(new String[] {"Total", "attachment"}, row.getDesc());
assertEquals(ReportResource.SingleRowResource.class, foldedRow.getClass()); assertEquals(1, row.getDepth());
assertEquals(2, foldedRow.getTotal()); assertFalse(row.isDisplay());
cells = foldedRow.getCells(); assertEquals(2, row.getTotal());
assertEquals(5, cells.size()); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
assertEquals(2, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
foldedRow = row.getFoldableRows().get("N/A"); row = sumRow.get(2);
assertEquals(ReportResource.SingleRowResource.class, foldedRow.getClass()); assertArrayEquals(new String[] {"Total", "N/A"}, row.getDesc());
assertEquals(2, foldedRow.getTotal()); assertEquals(1, row.getDepth());
cells = foldedRow.getCells(); assertFalse(row.isDisplay());
assertEquals(5, cells.size()); assertEquals(2, row.getTotal());
assertEquals(2, cells.get("2019-01-01").intValue()); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
} }
@Test @Test
@ -308,7 +261,7 @@ public class ReportResourceTest {
item.setAgeInDays(daysDiff - 1); item.setAgeInDays(daysDiff - 1);
item.setNumberOfTasks(2); item.setNumberOfTasks(2);
item.setKey("key"); item.setKey("key");
item.setAttachmentKey("attachement"); item.setAttachmentKey("attachment");
report.addItem(item); report.addItem(item);
item.setAttachmentKey(null); item.setAttachmentKey(null);
report.addItem(item); report.addItem(item);
@ -322,122 +275,75 @@ public class ReportResourceTest {
ReportResource.MetaInformation meta = resource.getMeta(); ReportResource.MetaInformation meta = resource.getMeta();
assertEquals("DetailedClassificationReport", meta.getName()); assertEquals("DetailedClassificationReport", meta.getName());
assertEquals("2019-01-02T00:00:00Z", meta.getDate()); assertEquals("2019-01-02T00:00:00Z", meta.getDate());
assertEquals("TASK CLASSIFICATION KEYS", meta.getRowDesc()); assertArrayEquals(new String[] {"TASK CLASSIFICATION KEYS", "ATTACHMENT"}, meta.getRowDesc());
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader()); assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
assertArrayEquals(new String[0], meta.getExpHeader());
assertEquals("Total", meta.getTotalDesc()); assertEquals("Total", meta.getTotalDesc());
// rows // rows
Map<String, ReportResource.RowResource> rows = resource.getRows(); List<ReportResource.RowResource> rows = resource.getRows();
assertEquals(2, rows.size()); assertEquals((1 + 2) + (1 + 1), rows.size());
assertEquals(ReportResource.FoldableRowResource.class, rows.get("key").getClass()); ReportResource.RowResource row = rows.get(0);
ReportResource.FoldableRowResource row = (ReportResource.FoldableRowResource) rows.get("key"); assertArrayEquals(new String[] {"key", null}, row.getDesc());
assertEquals(0, row.getDepth());
assertTrue(row.isDisplay());
assertEquals(4, row.getTotal()); assertEquals(4, row.getTotal());
Map<String, Integer> cells = row.getCells(); assertArrayEquals(new int[] {0, 0, 0, 0, 4}, row.getCells());
assertEquals(5, cells.size());
assertEquals(4, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
assertEquals(2, row.getFoldableRows().size()); row = rows.get(1);
ReportResource.RowResource foldedRow = row.getFoldableRows().get("attachement"); assertArrayEquals(new String[] {"key", "attachment"}, row.getDesc());
assertEquals(ReportResource.SingleRowResource.class, foldedRow.getClass()); assertEquals(1, row.getDepth());
assertEquals(2, foldedRow.getTotal()); assertFalse(row.isDisplay());
cells = foldedRow.getCells();
assertEquals(5, cells.size());
assertEquals(2, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
foldedRow = row.getFoldableRows().get("N/A");
assertEquals(ReportResource.SingleRowResource.class, foldedRow.getClass());
assertEquals(2, foldedRow.getTotal());
cells = foldedRow.getCells();
assertEquals(5, cells.size());
assertEquals(2, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
assertEquals(ReportResource.FoldableRowResource.class, rows.get("key2").getClass());
row = (ReportResource.FoldableRowResource) rows.get("key2");
assertEquals(2, row.getTotal()); assertEquals(2, row.getTotal());
cells = row.getCells(); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
assertEquals(5, cells.size());
assertEquals(2, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
assertEquals(1, row.getFoldableRows().size()); row = rows.get(2);
foldedRow = row.getFoldableRows().get("N/A"); assertArrayEquals(new String[] {"key", "N/A"}, row.getDesc());
assertEquals(ReportResource.SingleRowResource.class, foldedRow.getClass()); assertEquals(1, row.getDepth());
assertEquals(2, foldedRow.getTotal()); assertFalse(row.isDisplay());
cells = foldedRow.getCells(); assertEquals(2, row.getTotal());
assertEquals(5, cells.size()); assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
assertEquals(2, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue()); row = rows.get(3);
assertEquals(0, cells.get("2018-12-30").intValue()); assertArrayEquals(new String[] {"key2", null}, row.getDesc());
assertEquals(0, cells.get("2018-12-29").intValue()); assertEquals(0, row.getDepth());
assertEquals(0, cells.get("2018-12-28").intValue()); assertTrue(row.isDisplay());
assertEquals(2, row.getTotal());
assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
row = rows.get(4);
assertArrayEquals(new String[] {"key2", "N/A"}, row.getDesc());
assertEquals(1, row.getDepth());
assertFalse(row.isDisplay());
assertEquals(2, row.getTotal());
assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
// sumRow // sumRow
assertEquals(ReportResource.FoldableRowResource.class, resource.getSumRow().getClass());
ReportResource.FoldableRowResource sumRow = (ReportResource.FoldableRowResource) resource.getSumRow();
assertEquals(6, sumRow.getTotal());
cells = sumRow.getCells();
assertEquals(5, cells.size());
assertEquals(6, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
assertEquals(2, sumRow.getFoldableRows().size()); List<ReportResource.RowResource> sumRow = resource.getSumRow();
foldedRow = sumRow.getFoldableRows().get("attachement"); assertEquals(1 + 2, sumRow.size());
assertEquals(ReportResource.SingleRowResource.class, foldedRow.getClass());
assertEquals(2, foldedRow.getTotal()); row = sumRow.get(0);
cells = foldedRow.getCells(); assertArrayEquals(new String[] {"Total", null}, row.getDesc());
assertEquals(5, cells.size()); assertEquals(0, row.getDepth());
assertEquals(2, cells.get("2019-01-01").intValue()); assertTrue(row.isDisplay());
assertEquals(0, cells.get("2018-12-31").intValue()); assertEquals(6, row.getTotal());
assertEquals(0, cells.get("2018-12-30").intValue()); assertArrayEquals(new int[] {0, 0, 0, 0, 6}, row.getCells());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue()); row = sumRow.get(1);
assertArrayEquals(new String[] {"Total", "attachment"}, row.getDesc());
assertEquals(1, row.getDepth());
assertFalse(row.isDisplay());
assertEquals(2, row.getTotal());
assertArrayEquals(new int[] {0, 0, 0, 0, 2}, row.getCells());
row = sumRow.get(2);
assertArrayEquals(new String[] {"Total", "N/A"}, row.getDesc());
assertEquals(1, row.getDepth());
assertFalse(row.isDisplay());
assertEquals(4, row.getTotal());
assertArrayEquals(new int[] {0, 0, 0, 0, 4}, row.getCells());
foldedRow = sumRow.getFoldableRows().get("N/A");
assertEquals(ReportResource.SingleRowResource.class, foldedRow.getClass());
assertEquals(4, foldedRow.getTotal());
cells = foldedRow.getCells();
assertEquals(5, cells.size());
assertEquals(4, cells.get("2019-01-01").intValue());
assertEquals(0, cells.get("2018-12-31").intValue());
assertEquals(0, cells.get("2018-12-30").intValue());
assertEquals(0, cells.get("2018-12-29").intValue());
assertEquals(0, cells.get("2018-12-28").intValue());
} }
@Test
public void testExpandableHeader() {
//given
TimestampReport report = new TimestampReport(headers);
//when
ReportResource resource = reportAssembler.toReportResource(report, now.toInstant(ZoneOffset.UTC));
//then
ReportResource.MetaInformation meta = resource.getMeta();
assertEquals("TimestampReport", meta.getName());
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
assertEquals("STATES", meta.getRowDesc());
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
assertArrayEquals(new String[] {"ORG LEVEL 1", "ORG LEVEL 2", "ORG LEVEL 3", "ORG LEVEL 4"},
meta.getExpHeader());
assertEquals("Total", meta.getTotalDesc());
}
} }

View File

@ -1,8 +1,7 @@
export class MetaInfoData { export class MetaInfoData {
name: string;
date: string; date: string;
header: Array<string>; header: Array<string>;
expHeader: Array<string>; rowDesc: Array<string>;
name: string;
rowDesc: string;
totalDesc: string; totalDesc: string;
} }

View File

@ -1,8 +1,8 @@
import { ReportInfoData } from './report-info-data'; import {ReportRow} from './report-row';
import { MetaInfoData } from './meta-info-data'; import {MetaInfoData} from './meta-info-data';
export class ReportData { export class ReportData {
meta: MetaInfoData; meta: MetaInfoData;
rows: Map<string, ReportInfoData>; rows: Array<ReportRow>;
sumRow: ReportInfoData; sumRow: Array<ReportRow>;
} }

View File

@ -1,12 +0,0 @@
export class ReportInfoData {
cells: Map<string, number>;
foldableRows: Map<string, Map<string, ReportInfoData>>;
total: number;
}
export class ReportInfoDataIterable {
key: string;
val: ReportInfoData;
depth: number;
display = false;
}

View File

@ -0,0 +1,7 @@
export class ReportRow {
cells: Array<number>;
total: number;
depth: number;
desc: Array<string>;
display = false;
}

View File

@ -14,7 +14,6 @@ import {MonitorComponent} from './monitor.component';
import {TasksComponent} from './tasks/tasks.component'; import {TasksComponent} from './tasks/tasks.component';
import {WorkbasketComponent} from './workbasket/workbasket.component'; import {WorkbasketComponent} from './workbasket/workbasket.component';
import {ClassificationTasksComponent} from './classification-tasks/classification-tasks.component'; import {ClassificationTasksComponent} from './classification-tasks/classification-tasks.component';
import {ReportRowComponent} from './report/row/row.component';
import {TimestampComponent} from './timestamp/timestamp.component'; import {TimestampComponent} from './timestamp/timestamp.component';
import {RestConnectorService} from './services/restConnector/rest-connector.service'; import {RestConnectorService} from './services/restConnector/rest-connector.service';
@ -38,8 +37,7 @@ const DECLARATIONS = [
ReportComponent, ReportComponent,
MonitorComponent, MonitorComponent,
ClassificationTasksComponent, ClassificationTasksComponent,
TimestampComponent, TimestampComponent
ReportRowComponent
]; ];
@NgModule({ @NgModule({

View File

@ -1,30 +1,64 @@
<div *ngIf="reportData" class="report table table-body-striped"> <div *ngIf="reportData" class="report table table-striped">
<div class="table-header"> <div class="table-header">
<div class="table-row"> <div class="table-row">
<div <ng-container *ngFor="let header of reportData.meta.rowDesc; let i = index">
[ngClass]="{'table-cell--border-right': currentExpHeaders === 0}" <div *ngIf="i <= currentExpHeaders"
class="table-cell table-cell--bold table-cell--justify">{{reportData.meta.rowDesc}}</div> [ngClass]="{'table-cell--border-right': currentExpHeaders === i}"
<ng-container *ngFor="let header of reportData.meta.expHeader; let i = index">
<ng-container *ngIf="i < currentExpHeaders">
<div [ngClass]="{'table-cell--border-right': currentExpHeaders - 1 === i}"
class="table-cell table-cell--bold table-cell--justify">{{header}}</div> class="table-cell table-cell--bold table-cell--justify">{{header}}</div>
</ng-container> </ng-container>
</ng-container>
<div *ngFor="let header of reportData.meta.header" <div *ngFor="let header of reportData.meta.header"
class="table-cell table-cell--bold">{{header}}</div> class="table-cell table-cell--bold">{{header}}</div>
<div class="table-cell table-cell--bold table-cell--border-left">{{reportData.meta.totalDesc}}</div> <div class="table-cell table-cell--bold table-cell--border-left">{{reportData.meta.totalDesc}}</div>
</div> </div>
</div> </div>
<taskana-report-row (expandedDepth)="expandHeader($event, i)" <div class="table-body">
*ngFor="let row of reportData.rows | mapToIterable | orderBy:['key']; let i = index" <ng-container *ngFor="let row of reportData.rows; let i = index">
[headers]="reportData.meta.header" <div *ngIf="row.display" [ngClass]="{'table-row--highlight': row.depth === 0 && currentExpHeaders > 0,
[maxTableDepth]="currentExpHeaders" 'table-row--hover': row.depth > 0 && currentExpHeaders > 0,
[row]="row" 'table-row--white': row.depth > 0 && currentExpHeaders > 0}" class="table-row">
class="table-body"></taskana-report-row> <ng-container *ngFor="let header of row.desc; let idx = index">
<taskana-report-row (expandedDepth)="expandHeader($event, expHeaders.length - 1)" <div (click)="toggleFold(i)" *ngIf="idx <= currentExpHeaders"
[headers]="reportData.meta.header" [ngClass]="{'table-cell--border-right': currentExpHeaders === idx,
[maxTableDepth]="currentExpHeaders" 'table-cell--bold': row.depth === 0 && currentExpHeaders > 0,
[row]="_sumRow" 'table-cell--clickable': reportData.meta.rowDesc.length - 1 > row.depth}"
bold="true" class="table-cell table-cell--justify">
class="table-footer"></taskana-report-row> <span *ngIf="reportData.meta.rowDesc.length - 1 > row.depth && idx === row.depth"
class="material-icons md-18">{{ canRowCollapse(i) ? "expand_more" : "expand_less"}}</span>
{{header}}
</div>
</ng-container>
<div *ngFor="let val of row.cells"
[ngClass]="{'table-cell--bold': row.depth === 0 && currentExpHeaders > 0}" class="table-cell">
{{val}}
</div>
<div class="table-cell table-cell--bold table-cell--border-left">
{{row.total}}
</div>
</div>
</ng-container>
</div>
<div class="table-footer">
<ng-container *ngFor="let row of reportData.sumRow; let i = index">
<div *ngIf="row.display" [ngClass]="{'table-row--highlight': row.depth === 0 && currentExpHeaders > 0,
'table-row--hover': row.depth > 0 && currentExpHeaders > 0,
'table-row--white': row.depth > 0 && currentExpHeaders > 0}" class="table-row">
<ng-container *ngFor="let header of row.desc; let idx = index">
<div (click)="toggleFold(i, true)" *ngIf="idx <= currentExpHeaders"
[ngClass]="{'table-cell--border-right': currentExpHeaders === idx,
'table-cell--clickable': reportData.meta.rowDesc.length - 1 > row.depth}"
class="table-cell table-cell--bold table-cell--justify">
<span *ngIf="reportData.meta.rowDesc.length - 1 > row.depth && idx === row.depth"
class="material-icons md-18">{{ canRowCollapse(i, true) ? "expand_more" : "expand_less"}}</span>
{{header}}
</div>
</ng-container>
<div *ngFor="let val of row.cells" class="table-cell table-cell--bold">
{{val}}
</div>
<div class="table-cell table-cell--bold table-cell--border-left">
{{row.total}}
</div>
</div>
</ng-container>
</div>
</div> </div>

View File

@ -1,3 +1,20 @@
.report { .report {
margin-top: 20px; margin-top: 20px;
} }
.table-cell--clickable {
cursor: pointer;
}
.table-row--highlight,
.table-row--hover:hover {
background-color: #f9f9f9;
}
.table-row--highlight > .table-cell {
border-bottom: 2px solid #ddd;
}
.table-row--white {
background-color: white !important;
}

View File

@ -1,6 +1,5 @@
import {Component, Input, OnInit} from '@angular/core'; import {Component, Input, OnInit} from '@angular/core';
import {ReportData} from 'app/monitor/models/report-data'; import {ReportData} from 'app/monitor/models/report-data';
import {ReportInfoDataIterable} from '../models/report-info-data';
@Component({ @Component({
selector: 'taskana-report', selector: 'taskana-report',
@ -10,24 +9,10 @@ import {ReportInfoDataIterable} from '../models/report-info-data';
export class ReportComponent implements OnInit { export class ReportComponent implements OnInit {
expHeaders: Array<number>;
currentExpHeaders = 0; currentExpHeaders = 0;
_sumRow: ReportInfoDataIterable;
private _reportData: ReportData;
get reportData(): ReportData {
return this._reportData;
}
@Input() @Input()
set reportData(reportData: ReportData) { reportData: ReportData;
this._reportData = reportData;
this.expHeaders = new Array<number>(Object.keys(reportData.rows).length + 1).fill(0);
this._sumRow = new ReportInfoDataIterable();
this._sumRow.val = reportData.sumRow;
this._sumRow.key = reportData.meta.totalDesc;
}
constructor() { constructor() {
} }
@ -35,10 +20,30 @@ export class ReportComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
} }
expandHeader(depth: number, index: number) { toggleFold(index: number, sumRow: boolean = false) {
this.expHeaders[index] = depth; const rows = sumRow ? this.reportData.sumRow : this.reportData.rows;
this.currentExpHeaders = Math.max(...this.expHeaders); const toggleRow = rows[index++];
if (toggleRow.depth < this.reportData.meta.rowDesc.length - 1) {
const firstChildRow = rows[index++];
firstChildRow.display = !firstChildRow.display;
let end = false;
for (let i = index; i < rows.length && !end; i++) {
const row = rows[i];
end = row.depth <= toggleRow.depth;
if (!end) {
row.display = firstChildRow.display && row.depth === firstChildRow.depth;
}
}
this.currentExpHeaders = Math.max(
...this.reportData.rows.filter(r => r.display).map(r => r.depth),
...this.reportData.sumRow.filter(r => r.display).map(r => r.depth)
);
}
} }
canRowCollapse(index: number, sumRow: boolean = false) {
const rows = sumRow ? this.reportData.sumRow : this.reportData.rows;
return !rows[index + 1].display;
}
} }

View File

@ -1,28 +0,0 @@
<ng-container *ngIf="headers && flatRows">
<ng-container *ngFor="let row of flatRows; let i = index">
<div *ngIf="row.display"
[ngClass]="{'table-row--highlight': row.depth === 0 && currentDepth > 0,
'table-row--hover': row.depth > 0 && currentDepth > 0,
'table-row--white': row.depth > 0 && currentDepth > 0}"
class="table-row">
<div *ngFor="let _ of range(row.depth)" class="table-cell"></div>
<div (click)="toggleFold(i)"
[ngClass]="{'table-cell--clickable': maxDepth > row.depth,
'table-cell--bold' : bold || row.depth === 0 && currentDepth > 0,
'table-cell--border-right': row.depth === maxTableDepth}"
class="table-cell table-cell--justify">
<span *ngIf="maxDepth > row.depth"
class="material-icons md-18">{{ canRowCollapse(i) ? "expand_more" : "expand_less"}}</span>{{row.key}}
</div>
<div *ngFor="let _ of range(maxTableDepth - row.depth); let i = index"
[ngClass]="{'table-cell--border-right': i === maxTableDepth - row.depth - 1}" class="table-cell"></div>
<div *ngFor="let header of headers"
[ngClass]="{'table-cell--bold' : bold || row.depth === 0 && currentDepth > 0}" class="table-cell">
{{row.val.cells[header]}}
</div>
<div class="table-cell table-cell--bold table-cell--border-left">
{{row.val.total}}
</div>
</div>
</ng-container>
</ng-container>

View File

@ -1,19 +0,0 @@
@import './src/assets/_colors';
.table-cell--clickable {
cursor: pointer;
}
.table-row--highlight,
.table-row--hover:hover {
background-color: #f9f9f9;
}
.table-row--highlight > .table-cell {
border-bottom: 2px solid #ddd;
}
.table-row--white {
background-color: white;
}

View File

@ -1,81 +0,0 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {ReportInfoDataIterable} from '../../models/report-info-data';
import {MapToIterable} from '../../../shared/pipes/mapToIterable/mapToIterable';
@Component({
selector: 'taskana-report-row',
templateUrl: './row.component.html',
styleUrls: ['./row.component.scss']
})
export class ReportRowComponent implements OnInit {
@Input()
headers: Array<string>;
@Input()
bold = false;
@Input()
maxTableDepth = 0;
@Output()
expandedDepth: EventEmitter<number> = new EventEmitter<number>();
currentDepth = 0;
maxDepth: number;
flatRows: Array<ReportInfoDataIterable>;
private _row: ReportInfoDataIterable;
get row(): ReportInfoDataIterable {
return this._row;
}
@Input()
set row(row: ReportInfoDataIterable) {
this._row = row;
this.flatRows = new Array<ReportInfoDataIterable>();
this.maxDepth = this.flatten(row, 0);
}
constructor(private mapToIterable: MapToIterable) {
}
ngOnInit() {
}
toggleFold(index: number): void {
const toggleRow = this.flatRows[index++];
if (toggleRow.depth < this.maxDepth) {
const firstChildRow = this.flatRows[index++];
firstChildRow.display = !firstChildRow.display;
let end = false;
for (let i = index; i < this.flatRows.length && !end; i++) {
const row = this.flatRows[i];
end = row.depth <= toggleRow.depth;
if (!end) {
row.display = firstChildRow.display && row.depth === firstChildRow.depth;
}
}
this.currentDepth = Math.max(...this.flatRows.filter(r => r.display).map(r => r.depth));
this.expandedDepth.emit(this.currentDepth);
}
}
range(depth: number): Array<null> {
return new Array<null>(Math.max(depth, 0));
}
canRowCollapse(index: number) {
return !this.flatRows[index + 1].display;
}
private flatten(row: ReportInfoDataIterable, depth: number): number {
row.depth = depth;
row.display = depth === 0;
this.flatRows.push(row);
if (row.val.foldableRows) {
depth = Math.max(...this.mapToIterable.transform(row.val.foldableRows)
.sort((a, b) => a.key.localeCompare(b.key))
.map(r => this.flatten(r, depth + 1)));
}
return depth;
}
}

View File

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core'; import {Component, OnInit} from '@angular/core';
import { RestConnectorService } from '../services/restConnector/rest-connector.service'; import {RestConnectorService} from '../services/restConnector/rest-connector.service';
import { ReportData } from 'app/monitor/models/report-data'; import {ReportData} from 'app/monitor/models/report-data';
@Component({ @Component({
selector: 'taskana-monitor-tasks', selector: 'taskana-monitor-tasks',
@ -21,10 +21,8 @@ export class TasksComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.restConnectorService.getTaskStatusReport().subscribe((data: ReportData) => { this.restConnectorService.getTaskStatusReport().subscribe((data: ReportData) => {
this.reportData = data; this.reportData = data;
this.pieChartLabels = Object.keys(data.sumRow.cells); this.pieChartLabels = data.meta.header;
Object.keys(data.sumRow.cells).forEach(key => { data.sumRow[0].cells.forEach(c => this.pieChartData.push(c));
this.pieChartData.push(data.sumRow.cells[key]);
})
}) })