All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pro.taskana.monitor.rest.assembler.ReportRepresentationModelAssembler Maven / Gradle / Ivy

package pro.taskana.monitor.rest.assembler;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.monitor.api.reports.ClassificationReport;
import pro.taskana.monitor.api.reports.Report;
import pro.taskana.monitor.api.reports.TaskStatusReport;
import pro.taskana.monitor.api.reports.TimestampReport;
import pro.taskana.monitor.api.reports.WorkbasketReport;
import pro.taskana.monitor.api.reports.header.ColumnHeader;
import pro.taskana.monitor.api.reports.item.QueryItem;
import pro.taskana.monitor.api.reports.row.FoldableRow;
import pro.taskana.monitor.api.reports.row.Row;
import pro.taskana.monitor.api.reports.row.SingleRow;
import pro.taskana.monitor.rest.MonitorController;
import pro.taskana.monitor.rest.models.ReportRepresentationModel;
import pro.taskana.task.api.TaskState;

/** Transforms any {@link Report} into its {@link ReportRepresentationModel}. */
@Component
public class ReportRepresentationModelAssembler {

  @NonNull
  public ReportRepresentationModel toModel(
      @NonNull TaskStatusReport report,
      @NonNull List domains,
      @NonNull List states)
      throws NotAuthorizedException, InvalidArgumentException {
    ReportRepresentationModel resource = toReportResource(report);
    resource.add(
        linkTo(methodOn(MonitorController.class).getTasksStatusReport(domains, states))
            .withSelfRel()
            .expand());
    return resource;
  }

  @NonNull
  public ReportRepresentationModel toModel(@NonNull ClassificationReport report)
      throws NotAuthorizedException, InvalidArgumentException {
    ReportRepresentationModel resource = toReportResource(report);
    resource.add(
        linkTo(methodOn(MonitorController.class).getTasksClassificationReport())
            .withSelfRel()
            .expand());
    return resource;
  }

  @NonNull
  public ReportRepresentationModel toModel(
      @NonNull WorkbasketReport report, @NonNull List states)
      throws NotAuthorizedException, InvalidArgumentException {
    ReportRepresentationModel resource = toReportResource(report);
    resource.add(
        linkTo(methodOn(MonitorController.class).getTasksWorkbasketReport(states))
            .withSelfRel()
            .expand());
    return resource;
  }

  @NonNull
  public ReportRepresentationModel toModel(
      @NonNull WorkbasketReport report, int daysInPast, @NonNull List states)
      throws NotAuthorizedException, InvalidArgumentException {
    ReportRepresentationModel resource = toReportResource(report);
    resource.add(
        linkTo(
                methodOn(MonitorController.class)
                    .getTasksWorkbasketPlannedDateReport(daysInPast, states))
            .withSelfRel()
            .expand());
    return resource;
  }

  @NonNull
  public ReportRepresentationModel toModel(@NonNull TimestampReport report)
      throws NotAuthorizedException, InvalidArgumentException {
    ReportRepresentationModel resource = toReportResource(report);
    resource.add(
        linkTo(methodOn(MonitorController.class).getDailyEntryExitReport()).withSelfRel().expand());
    return resource;
  }

  public >
      ReportRepresentationModel toReportResource(Report report, Instant time) {
    String[] header =
        report.getColumnHeaders().stream().map(H::getDisplayName).toArray(String[]::new);
    ReportRepresentationModel.MetaInformation meta =
        new ReportRepresentationModel.MetaInformation(
            report.getClass().getSimpleName(), time.toString(), header, report.getRowDesc());

    // iterate over each Row and transform it to a RowResource while keeping the domain key.
    List rows =
        report.getRows().entrySet().stream()
            .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());

    List sumRow =
        transformRow(
            report.getSumRow(), meta.getTotalDesc(), new String[report.getRowDesc().length], 0);

    return new ReportRepresentationModel(meta, rows, sumRow);
  }

  >
      ReportRepresentationModel toReportResource(Report report) {
    return toReportResource(report, Instant.now());
  }

  private  List transformRow(
      Row row, String currentDesc, String[] desc, int depth) {
    // 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 Collections.singletonList(
          transformSingleRow((SingleRow) row, currentDesc, desc, depth));
    }
    return transformFoldableRow((FoldableRow) row, currentDesc, desc, depth);
  }

  private  ReportRepresentationModel.RowResource transformSingleRow(
      SingleRow row, String currentDesc, String[] previousRowDesc, int depth) {
    String[] rowDesc = new String[previousRowDesc.length];
    System.arraycopy(previousRowDesc, 0, rowDesc, 0, depth);
    rowDesc[depth] = currentDesc;
    return new ReportRepresentationModel.RowResource(
        row.getCells(), row.getTotalValue(), depth, rowDesc, depth == 0);
  }

  private  List transformFoldableRow(
      FoldableRow row, String currentDesc, String[] previousRowDesc, int depth) {
    ReportRepresentationModel.RowResource baseRow =
        transformSingleRow(row, currentDesc, previousRowDesc, depth);
    List rowList = new LinkedList<>();
    rowList.add(baseRow);
    row.getFoldableRowKeySet().stream()
        .sorted(String.CASE_INSENSITIVE_ORDER)
        .map(s -> transformRow(row.getFoldableRow(s), s, baseRow.getDesc(), depth + 1))
        .flatMap(Collection::stream)
        .forEachOrdered(rowList::add);
    return rowList;
  }
}