You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by of...@apache.org on 2020/03/03 16:03:29 UTC

[incubator-dlab] 01/02: Added download report feature

This is an automated email from the ASF dual-hosted git repository.

ofuks pushed a commit to branch DLAB-1571
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git

commit 8aa8ec3897a436763a97ed4a33d3234dd8f89d0b
Author: Oleh Fuks <ol...@gmail.com>
AuthorDate: Tue Mar 3 17:47:41 2020 +0200

    Added download report feature
---
 .../epam/dlab/backendapi/domain/BillingReport.java |  4 +-
 ...lingReportLines.java => BillingReportLine.java} |  2 +-
 .../dlab/backendapi/resources/BillingResource.java | 22 ++++--
 .../dlab/backendapi/service/BillingServiceNew.java |  6 +-
 .../service/impl/BillingServiceImplNew.java        | 81 ++++++++++++++-------
 .../epam/dlab/backendapi/util/BillingUtils.java    | 83 +++++++++++++++++-----
 6 files changed, 145 insertions(+), 53 deletions(-)

diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReport.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReport.java
index 3d603dd..8722e73 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReport.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReport.java
@@ -31,7 +31,7 @@ import java.util.List;
 public class BillingReport {
     private String sbn;
     @JsonProperty("report_lines")
-    private List<BillingReportLines> reportLines;
+    private List<BillingReportLine> reportLines;
     @JsonProperty("from")
     private LocalDate usageDateFrom;
     @JsonProperty("to")
@@ -39,4 +39,6 @@ public class BillingReport {
     @JsonProperty("total_cost")
     private double totalCost;
     private String currency;
+    @JsonProperty("is_full")
+    private boolean isFull;
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLines.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLine.java
similarity index 97%
rename from services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLines.java
rename to services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLine.java
index 256e1cc..ed97a44 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLines.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLine.java
@@ -29,7 +29,7 @@ import java.time.LocalDate;
 
 @Data
 @Builder
-public class BillingReportLines {
+public class BillingReportLine {
     private String dlabId;
     @JsonProperty("resource_name")
     private String resourceName;
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java
index b6b7932..10ed7bf 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java
@@ -58,20 +58,28 @@ public class BillingResource {
     }
 
     @POST
+    @Path("/report/download")
+    @Produces(MediaType.APPLICATION_OCTET_STREAM)
+    public Response downloadBillingReport(@Auth UserInfo userInfo, @Valid @NotNull BillingFilter formDTO) {
+        return Response.ok(billingService.downloadReport(userInfo, formDTO))
+                .header(HttpHeaders.CONTENT_DISPOSITION,
+                        "attachment; filename=\"" + billingService.getReportFileName(userInfo, formDTO) + "\"")
+                .build();
+    }
+
+    @POST
     @Path("/report2")
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getBillingReport2(@Auth UserInfo userInfo, @Valid @NotNull BillingFilter filter
-    ) {
+    public Response getBillingReport2(@Auth UserInfo userInfo, @Valid @NotNull BillingFilter filter) {
         return Response.ok(billingServiceNew.getBillingReport(userInfo, filter)).build();
     }
 
     @POST
-    @Path("/report/download")
+    @Path("/report2/download")
     @Produces(MediaType.APPLICATION_OCTET_STREAM)
-    public Response downloadBillingReport(@Auth UserInfo userInfo, @Valid @NotNull BillingFilter formDTO) {
-        return Response.ok(billingService.downloadReport(userInfo, formDTO))
-                .header(HttpHeaders.CONTENT_DISPOSITION,
-                        "attachment; filename=\"" + billingService.getReportFileName(userInfo, formDTO) + "\"")
+    public Response downloadBillingReport2(@Auth UserInfo userInfo, @Valid @NotNull BillingFilter filter) {
+        return Response.ok(billingServiceNew.downloadReport(userInfo, filter))
+                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"billing-report.csv\"")
                 .build();
     }
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingServiceNew.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingServiceNew.java
index f569c78..8ea0b24 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingServiceNew.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingServiceNew.java
@@ -21,7 +21,7 @@ package com.epam.dlab.backendapi.service;
 
 import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.domain.BillingReport;
-import com.epam.dlab.backendapi.domain.BillingReportLines;
+import com.epam.dlab.backendapi.domain.BillingReportLine;
 import com.epam.dlab.backendapi.resources.dto.BillingFilter;
 
 import java.util.List;
@@ -29,5 +29,7 @@ import java.util.List;
 public interface BillingServiceNew {
     BillingReport getBillingReport(UserInfo userInfo, BillingFilter filter);
 
-    List<BillingReportLines> getBillingReportLines(UserInfo userInfo, BillingFilter filter);
+    String downloadReport(UserInfo userInfo, BillingFilter filter);
+
+    List<BillingReportLine> getBillingReportLines(UserInfo userInfo, BillingFilter filter);
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImplNew.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImplNew.java
index d7c3c85..d8fb596 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImplNew.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImplNew.java
@@ -22,11 +22,13 @@ package com.epam.dlab.backendapi.service.impl;
 import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
 import com.epam.dlab.backendapi.domain.BillingReport;
-import com.epam.dlab.backendapi.domain.BillingReportLines;
+import com.epam.dlab.backendapi.domain.BillingReportLine;
 import com.epam.dlab.backendapi.domain.EndpointDTO;
 import com.epam.dlab.backendapi.domain.ProjectDTO;
 import com.epam.dlab.backendapi.domain.ProjectEndpointDTO;
 import com.epam.dlab.backendapi.resources.dto.BillingFilter;
+import com.epam.dlab.backendapi.roles.RoleType;
+import com.epam.dlab.backendapi.roles.UserRoles;
 import com.epam.dlab.backendapi.service.BillingServiceNew;
 import com.epam.dlab.backendapi.service.EndpointService;
 import com.epam.dlab.backendapi.service.ExploratoryService;
@@ -36,6 +38,7 @@ import com.epam.dlab.constants.ServiceConsts;
 import com.epam.dlab.dto.billing.BillingData;
 import com.epam.dlab.exceptions.DlabException;
 import com.epam.dlab.rest.client.RESTService;
+import com.google.common.collect.Lists;
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
 import lombok.extern.slf4j.Slf4j;
@@ -82,11 +85,11 @@ public class BillingServiceImplNew implements BillingServiceNew {
 
     @Override
     public BillingReport getBillingReport(UserInfo user, BillingFilter filter) {
-        List<BillingReportLines> billingReportLines = getBillingReportLines(user, filter);
-        LocalDate min = billingReportLines.stream().min(Comparator.comparing(BillingReportLines::getUsageDateFrom)).map(BillingReportLines::getUsageDateFrom).orElse(null);
-        LocalDate max = billingReportLines.stream().max(Comparator.comparing(BillingReportLines::getUsageDateTo)).map(BillingReportLines::getUsageDateTo).orElse(null);
-        double sum = billingReportLines.stream().mapToDouble(BillingReportLines::getCost).sum();
-        String currency = billingReportLines.stream().map(BillingReportLines::getCurrency).distinct().count() == 1 ? billingReportLines.get(0).getCurrency() : null;
+        List<BillingReportLine> billingReportLines = getBillingReportLines(user, filter);
+        LocalDate min = billingReportLines.stream().min(Comparator.comparing(BillingReportLine::getUsageDateFrom)).map(BillingReportLine::getUsageDateFrom).orElse(null);
+        LocalDate max = billingReportLines.stream().max(Comparator.comparing(BillingReportLine::getUsageDateTo)).map(BillingReportLine::getUsageDateTo).orElse(null);
+        double sum = billingReportLines.stream().mapToDouble(BillingReportLine::getCost).sum();
+        String currency = billingReportLines.stream().map(BillingReportLine::getCurrency).distinct().count() == 1 ? billingReportLines.get(0).getCurrency() : null;
         return BillingReport.builder()
                 .sbn(configuration.getServiceBaseName())
                 .reportLines(billingReportLines)
@@ -94,30 +97,47 @@ public class BillingServiceImplNew implements BillingServiceNew {
                 .usageDateTo(max)
                 .totalCost(new BigDecimal(sum).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue())
                 .currency(currency)
+                .isFull(isFullReport(user))
                 .build();
     }
 
     @Override
-    public List<BillingReportLines> getBillingReportLines(UserInfo user, BillingFilter filter) {
+    public String downloadReport(UserInfo user, BillingFilter filter) {
+        BillingReport report = getBillingReport(user, filter);
+        StringBuilder builder = new StringBuilder(BillingUtils.getFirstLine(report.getSbn(), report.getUsageDateFrom(), report.getUsageDateTo()));
+        builder.append(BillingUtils.getHeader());
+        try {
+            report.getReportLines().forEach(r -> builder.append(BillingUtils.printLine(r)));
+            builder.append(BillingUtils.getTotal(report.getTotalCost(), report.getCurrency()));
+            return builder.toString();
+        } catch (Exception e) {
+            log.error("Cannot write billing data ", e);
+            throw new DlabException("Cannot write billing file due to" + e.getMessage());
+        }
+    }
+
+    @Override
+    public List<BillingReportLine> getBillingReportLines(UserInfo user, BillingFilter filter) {
+        setUserFilter(user, filter);
         final String serviceBaseName = configuration.getServiceBaseName();
-        final Stream<BillingReportLines> ssnBillingDataStream = BillingUtils.ssnBillingDataStream(serviceBaseName);
-        final Stream<BillingReportLines> billableUserInstances = exploratoryService.findAll()
+        final Stream<BillingReportLine> ssnBillingDataStream = BillingUtils.ssnBillingDataStream(serviceBaseName);
+        final Stream<BillingReportLine> billableUserInstances = exploratoryService.findAll()
                 .stream()
                 .filter(userInstance -> Objects.nonNull(userInstance.getExploratoryId()))
                 .flatMap(BillingUtils::exploratoryBillingDataStream);
-        final Stream<BillingReportLines> billableEdges = projectService.getProjects()
+        final Stream<BillingReportLine> billableEdges = projectService.getProjects()
                 .stream()
                 .collect(Collectors.toMap(ProjectDTO::getName, ProjectDTO::getEndpoints))
                 .entrySet()
                 .stream()
                 .flatMap(e -> projectEdges(serviceBaseName, e.getKey(), e.getValue()));
 
-        final Map<String, BillingReportLines> billableResources = Stream.of(billableUserInstances, billableEdges, ssnBillingDataStream)
+        final Map<String, BillingReportLine> billableResources = Stream.of(billableUserInstances, billableEdges, ssnBillingDataStream)
                 .flatMap(s -> s)
-                .collect(Collectors.toMap(BillingReportLines::getDlabId, b -> b));
+                .collect(Collectors.toMap(BillingReportLine::getDlabId, b -> b));
         log.debug("Billable resources are: {}", billableResources);
 
-        List<BillingReportLines> billingReport = getRemoteBillingData(user)
+        List<BillingReportLine> billingReport = getRemoteBillingData(user)
                 .stream()
                 .filter(getBillingDataFilter(filter))
                 .map(bd -> toBillingData(bd, getOrDefault(billableResources, bd.getTag())))
@@ -128,15 +148,15 @@ public class BillingServiceImplNew implements BillingServiceNew {
         return billingReport;
     }
 
-    private Stream<BillingReportLines> projectEdges(String serviceBaseName, String projectName, List<ProjectEndpointDTO> endpoints) {
+    private Stream<BillingReportLine> projectEdges(String serviceBaseName, String projectName, List<ProjectEndpointDTO> endpoints) {
         return endpoints
                 .stream()
                 .flatMap(endpoint -> BillingUtils.edgeBillingDataStream(projectName, serviceBaseName, endpoint.getName(),
                         endpoint.getStatus().toString()));
     }
 
-    private BillingReportLines getOrDefault(Map<String, BillingReportLines> billableResources, String tag) {
-        return billableResources.getOrDefault(tag, BillingReportLines.builder().dlabId(tag).build());
+    private BillingReportLine getOrDefault(Map<String, BillingReportLine> billableResources, String tag) {
+        return billableResources.getOrDefault(tag, BillingReportLine.builder().dlabId(tag).build());
     }
 
     private List<BillingData> getRemoteBillingData(UserInfo userInfo) {
@@ -192,7 +212,7 @@ public class BillingServiceImplNew implements BillingServiceNew {
         });
     }
 
-    private Predicate<BillingReportLines> getBillingReportFilter(BillingFilter filter) {
+    private Predicate<BillingReportLine> getBillingReportFilter(BillingFilter filter) {
         return br -> (filter.getUsers().isEmpty() || filter.getUsers().contains(br.getUser())) &&
                 (filter.getProjects().isEmpty() || filter.getProjects().contains(br.getProject())) &&
                 (filter.getResourceTypes().isEmpty() || filter.getResourceTypes().contains(br.getResourceType().name())) &&
@@ -207,21 +227,32 @@ public class BillingServiceImplNew implements BillingServiceNew {
                 (filter.getProducts().isEmpty() || filter.getProducts().contains(br.getProduct()));
     }
 
-    private BillingReportLines toBillingData(BillingData billingData, BillingReportLines billingReportLines) {
-        return BillingReportLines.builder()
+    private boolean isFullReport(UserInfo userInfo) {
+        return UserRoles.checkAccess(userInfo, RoleType.PAGE, "/api/infrastructure_provision/billing",
+                userInfo.getRoles());
+    }
+
+    private void setUserFilter(UserInfo userInfo, BillingFilter filter) {
+        if (!isFullReport(userInfo)) {
+            filter.setUsers(Lists.newArrayList(userInfo.getName()));
+        }
+    }
+
+    private BillingReportLine toBillingData(BillingData billingData, BillingReportLine billingReportLine) {
+        return BillingReportLine.builder()
                 .cost(billingData.getCost())
                 .currency(billingData.getCurrency())
                 .product(billingData.getProduct())
-                .project(billingReportLines.getProject())
+                .project(billingReportLine.getProject())
                 .usageDateTo(billingData.getUsageDateTo())
                 .usageDateFrom(billingData.getUsageDateFrom())
                 .usageType(billingData.getUsageType())
-                .user(billingReportLines.getUser())
+                .user(billingReportLine.getUser())
                 .dlabId(billingData.getTag())
-                .resourceType(billingReportLines.getResourceType())
-                .resourceName(billingReportLines.getResourceName())
-                .status(billingReportLines.getStatus())
-                .shape(billingReportLines.getShape())
+                .resourceType(billingReportLine.getResourceType())
+                .resourceName(billingReportLine.getResourceName())
+                .status(billingReportLine.getStatus())
+                .shape(billingReportLine.getShape())
                 .build();
     }
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/util/BillingUtils.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/util/BillingUtils.java
index 27320f8..0f96e4c 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/util/BillingUtils.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/util/BillingUtils.java
@@ -19,17 +19,29 @@
 
 package com.epam.dlab.backendapi.util;
 
-import com.epam.dlab.backendapi.domain.BillingReportLines;
+import com.epam.dlab.backendapi.domain.BillingReportLine;
 import com.epam.dlab.dto.UserInstanceDTO;
 import com.epam.dlab.dto.UserInstanceStatus;
 import com.epam.dlab.dto.base.DataEngineType;
 import com.epam.dlab.dto.billing.BillingResourceType;
 import com.epam.dlab.dto.computational.UserComputationalResource;
+import jersey.repackaged.com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
 
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.stream.Stream;
 
 public class BillingUtils {
 
+    private static final String[] REPORT_HEADERS = {"DLab ID", "User", "Project", "DLab Resource Type", "Shape", "Product", "Cost"};
+    private static final String REPORT_FIRST_LINE = "Service base name: %s. Available reporting period from: %s to: %s";
+    private static final String TOTAL_LINE = "Total: %s %s";
     private static final String EDGE_FORMAT = "%s-%s-%s-edge";
     private static final String EDGE_VOLUME_FORMAT = "%s-%s-%s-edge-volume-primary";
     private static final String EDGE_BUCKET_FORMAT = "%s-%s-bucket";
@@ -42,31 +54,31 @@ public class BillingUtils {
     private static final String DATAENGINE_NAME_FORMAT = "%d x %s";
     private static final String DATAENGINE_SERVICE_NAME_FORMAT = "Master: %s%sSlave:  %d x %s";
 
-    public static Stream<BillingReportLines> edgeBillingDataStream(String project, String sbn, String endpoint, String status) {
+    public static Stream<BillingReportLine> edgeBillingDataStream(String project, String sbn, String endpoint, String status) {
         final String userEdgeId = String.format(EDGE_FORMAT, sbn, project.toLowerCase(), endpoint);
         final String edgeVolumeId = String.format(EDGE_VOLUME_FORMAT, sbn, project.toLowerCase(), endpoint);
         final String edgeBucketId = String.format(EDGE_BUCKET_FORMAT, sbn, project.toLowerCase());
         return Stream.of(
-                BillingReportLines.builder().resourceName("EDGE node").user(SHARED_RESOURCE).project(project).dlabId(userEdgeId).resourceType(BillingResourceType.EDGE)
+                BillingReportLine.builder().resourceName("EDGE node").user(SHARED_RESOURCE).project(project).dlabId(userEdgeId).resourceType(BillingResourceType.EDGE)
                         .status(UserInstanceStatus.of(status)).build(),
-                BillingReportLines.builder().resourceName("EDGE volume").user(SHARED_RESOURCE).project(project).dlabId(edgeVolumeId).resourceType(BillingResourceType.VOLUME).build(),
-                BillingReportLines.builder().resourceName("EDGE bucket").user(SHARED_RESOURCE).project(project).dlabId(edgeBucketId).resourceType(BillingResourceType.EDGE_BUCKET).build()
+                BillingReportLine.builder().resourceName("EDGE volume").user(SHARED_RESOURCE).project(project).dlabId(edgeVolumeId).resourceType(BillingResourceType.VOLUME).build(),
+                BillingReportLine.builder().resourceName("EDGE bucket").user(SHARED_RESOURCE).project(project).dlabId(edgeBucketId).resourceType(BillingResourceType.EDGE_BUCKET).build()
         );
     }
 
-    public static Stream<BillingReportLines> ssnBillingDataStream(String sbn) {
+    public static Stream<BillingReportLine> ssnBillingDataStream(String sbn) {
         final String ssnId = sbn + "-ssn";
         final String bucketName = sbn.replaceAll("_", "-");
         return Stream.of(
-                BillingReportLines.builder().user(SHARED_RESOURCE).resourceName("SSN").dlabId(ssnId).resourceType(BillingResourceType.SSN).build(),
-                BillingReportLines.builder().user(SHARED_RESOURCE).resourceName("SSN Volume").dlabId(String.format(VOLUME_PRIMARY_FORMAT, ssnId)).resourceType(BillingResourceType.VOLUME).build(),
-                BillingReportLines.builder().user(SHARED_RESOURCE).resourceName("SSN bucket").dlabId(bucketName + "-ssn" + "-bucket").resourceType(BillingResourceType.SSN_BUCKET).build(),
-                BillingReportLines.builder().user(SHARED_RESOURCE).resourceName("Collaboration bucket").dlabId(bucketName + "-shared-bucket").resourceType(BillingResourceType.SHARED_BUCKET).build()
+                BillingReportLine.builder().user(SHARED_RESOURCE).resourceName("SSN").dlabId(ssnId).resourceType(BillingResourceType.SSN).build(),
+                BillingReportLine.builder().user(SHARED_RESOURCE).resourceName("SSN Volume").dlabId(String.format(VOLUME_PRIMARY_FORMAT, ssnId)).resourceType(BillingResourceType.VOLUME).build(),
+                BillingReportLine.builder().user(SHARED_RESOURCE).resourceName("SSN bucket").dlabId(bucketName + "-ssn" + "-bucket").resourceType(BillingResourceType.SSN_BUCKET).build(),
+                BillingReportLine.builder().user(SHARED_RESOURCE).resourceName("Collaboration bucket").dlabId(bucketName + "-shared-bucket").resourceType(BillingResourceType.SHARED_BUCKET).build()
         );
     }
 
-    public static Stream<BillingReportLines> exploratoryBillingDataStream(UserInstanceDTO userInstance) {
-        final Stream<BillingReportLines> computationalStream = userInstance.getResources()
+    public static Stream<BillingReportLine> exploratoryBillingDataStream(UserInstanceDTO userInstance) {
+        final Stream<BillingReportLine> computationalStream = userInstance.getResources()
                 .stream()
                 .filter(cr -> cr.getComputationalId() != null)
                 .flatMap(cr -> Stream.of(computationalBillableResource(userInstance, cr),
@@ -75,7 +87,7 @@ public class BillingUtils {
         final String exploratoryId = userInstance.getExploratoryId();
         final String primaryVolumeId = String.format(VOLUME_PRIMARY_FORMAT, exploratoryId);
         final String secondaryVolumeId = String.format(VOLUME_SECONDARY_FORMAT, exploratoryId);
-        final Stream<BillingReportLines> exploratoryStream = Stream.of(
+        final Stream<BillingReportLine> exploratoryStream = Stream.of(
                 withExploratoryName(userInstance).resourceName(userInstance.getExploratoryName()).dlabId(exploratoryId).resourceType(BillingResourceType.EXPLORATORY)
                         .status(UserInstanceStatus.of(userInstance.getStatus())).shape(userInstance.getShape()).build(),
                 withExploratoryName(userInstance).resourceName(VOLUME_PRIMARY).dlabId(primaryVolumeId).resourceType(BillingResourceType.VOLUME).build(),
@@ -83,8 +95,8 @@ public class BillingUtils {
         return Stream.concat(computationalStream, exploratoryStream);
     }
 
-    private static BillingReportLines computationalBillableResource(UserInstanceDTO userInstance,
-                                                                    UserComputationalResource cr) {
+    private static BillingReportLine computationalBillableResource(UserInstanceDTO userInstance,
+                                                                   UserComputationalResource cr) {
         return withExploratoryName(userInstance)
                 .dlabId(cr.getComputationalId())
                 .resourceName(cr.getComputationalName())
@@ -95,8 +107,8 @@ public class BillingUtils {
                 .build();
     }
 
-    private static BillingReportLines.BillingReportLinesBuilder withExploratoryName(UserInstanceDTO userInstance) {
-        return BillingReportLines.builder().user(userInstance.getUser()).project(userInstance.getProject());
+    private static BillingReportLine.BillingReportLineBuilder withExploratoryName(UserInstanceDTO userInstance) {
+        return BillingReportLine.builder().user(userInstance.getUser()).project(userInstance.getProject());
     }
 
     private static String getComputationalShape(UserComputationalResource resource) {
@@ -104,4 +116,41 @@ public class BillingUtils {
                 String.format(DATAENGINE_NAME_FORMAT, resource.getDataengineInstanceCount(), resource.getDataengineShape()) :
                 String.format(DATAENGINE_SERVICE_NAME_FORMAT, resource.getMasterNodeShape(), System.lineSeparator(), null, null);
     }
+
+    public static String getFirstLine(String sbn, LocalDate from, LocalDate to) {
+        return CSVFormatter.formatLine(Lists.newArrayList(String.format(REPORT_FIRST_LINE, sbn,
+                Optional.ofNullable(from).map(date -> date.format(DateTimeFormatter.ISO_DATE)).orElse(StringUtils.EMPTY),
+                Optional.ofNullable(to).map(date -> date.format(DateTimeFormatter.ISO_DATE)).orElse(StringUtils.EMPTY))),
+                CSVFormatter.SEPARATOR, '\"');
+    }
+
+    public static String getHeader() {
+        return CSVFormatter.formatLine(new ArrayList<>(Arrays.asList(BillingUtils.REPORT_HEADERS)), CSVFormatter.SEPARATOR);
+    }
+
+    public static String printLine(BillingReportLine line) {
+        List<String> lines = new ArrayList<>();
+        lines.add(getOrEmpty(line.getDlabId()));
+        lines.add(getOrEmpty(line.getUser()));
+        lines.add(getOrEmpty(line.getProject()));
+        lines.add(getOrEmpty(Optional.ofNullable(line.getResourceType()).map(Enum::name).orElse(null)));
+        lines.add(getOrEmpty(line.getShape()));
+        lines.add(getOrEmpty(line.getProduct()));
+        lines.add(getOrEmpty(Optional.ofNullable(line.getCost()).map(String::valueOf).orElse(null)));
+        return CSVFormatter.formatLine(lines, CSVFormatter.SEPARATOR);
+    }
+
+    public static String getTotal(Double total, String currency) {
+        List<String> totalLine = new ArrayList<>();
+        for (int i = 0; i < REPORT_HEADERS.length - 1; i++) {
+            totalLine.add(StringUtils.EMPTY);
+        }
+        totalLine.add(REPORT_HEADERS.length - 1, String.format(TOTAL_LINE, getOrEmpty(String.valueOf(total)), getOrEmpty(currency)));
+        return CSVFormatter.formatLine(totalLine, CSVFormatter.SEPARATOR);
+
+    }
+
+    private static String getOrEmpty(String s) {
+        return Objects.nonNull(s) ? s : StringUtils.EMPTY;
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org