From bb3741e9d7cc066db945aea7fad6a73168a6728c Mon Sep 17 00:00:00 2001
From: Lars Johansson <lars.johansson@ess.eu>
Date: Wed, 8 Jun 2022 10:23:03 +0200
Subject: [PATCH] Add report about Naming

Metrics for Naming - ESS names, System structure, Device structure
Metrics for Naming - # entries - all, active, deleted, pending, cancelled, rejected, other
---
 .../rest/controller/ReportController.java     | 526 ++++++++++++++++++
 1 file changed, 526 insertions(+)
 create mode 100644 src/main/java/org/openepics/names/rest/controller/ReportController.java

diff --git a/src/main/java/org/openepics/names/rest/controller/ReportController.java b/src/main/java/org/openepics/names/rest/controller/ReportController.java
new file mode 100644
index 00000000..aa37b329
--- /dev/null
+++ b/src/main/java/org/openepics/names/rest/controller/ReportController.java
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2022 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.rest.controller;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.lang3.StringUtils;
+import org.openepics.names.rest.beans.Status;
+import org.openepics.names.rest.beans.Type;
+import org.openepics.names.rest.beans.element.NameElement;
+import org.openepics.names.rest.beans.element.StructureElement;
+import org.openepics.names.rest.beans.response.ResponsePageNameElements;
+import org.openepics.names.rest.beans.response.ResponsePageStructureElements;
+import org.openepics.names.service.NamesService;
+import org.openepics.names.service.StructuresService;
+import org.openepics.names.util.NamingConventionUtil;
+import org.openepics.names.util.StructureElementUtil.StructureChoice;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+@Tag(name        = "Report",
+     description = "provide reports for Naming application")
+@RestController
+@RequestMapping("/report")
+@EnableAutoConfiguration
+public class ReportController {
+
+    // note
+    //     global exception handler available
+
+    private static final Logger LOGGER = Logger.getLogger(ReportController.class.getName());
+
+    private static final String NEWLINE       = "\n";
+    private static final String SPACE         = " ";
+
+    private static final String DIVIDER_32    = "--------------------------------";
+    private static final String DIVIDER_64    = DIVIDER_32 + DIVIDER_32;
+    private static final String DIVIDER_96    = DIVIDER_32 + DIVIDER_32 + DIVIDER_32;
+    private static final String DIVIDER_128   = DIVIDER_32 + DIVIDER_32 + DIVIDER_32 + DIVIDER_32;
+
+    private static final String NBR           = "#: ";
+    private static final String NBR_ACTIVE    = "# active: ";
+    private static final String NBR_ALL       = "# all: ";
+    private static final String NBR_CANCELLED = "# cancelled: ";
+    private static final String NBR_DELETED   = "# deleted: ";
+    private static final String NBR_OTHER     = "# other: ";
+    private static final String NBR_PENDING   = "# pending: ";
+    private static final String NBR_REJECTED  = "# rejected: ";
+
+
+    private NamesService namesService;
+    private StructuresService structuresService;
+
+    @Autowired
+    public ReportController(
+            NamesService namesService,
+            StructuresService structuresService) {
+
+        this.namesService = namesService;
+        this.structuresService = structuresService;
+    }
+
+    /**
+     * Return report about Naming.
+     *
+     * @return report about Naming (text)
+     */
+    @Operation(
+            summary     = "About Naming",
+            description = """
+                          About Naming.
+
+                          Return report about Naming (text).
+
+                          Content
+                          - Metrics for Naming
+                              1) Overview
+                              2) ESS names
+                              3) System structure
+                              4) Device structure
+                          """
+    )
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "Method completed OK. Return report about Naming (text).",
+                    content = @Content(mediaType = "text/plain", schema = @Schema(implementation = String.class)))
+    })
+    @GetMapping(
+            path     = {"/about"},
+            produces = {"text/plain"})
+    public String reportAbout() {
+        // report metrics about Naming
+        //     ess names, system structure, device structure
+
+        // status, latest, deleted
+        //     status     names - APPROVED, structures - APPROVED, ARCHIVED, CANCELLED, PENDING, REJECTED
+        //     latest     true (default)
+        //     deleted    null, false, true
+
+        // metrics can be read with service layer or repository layer
+
+        // prepare metrics
+        ResponsePageNameElements nameElementsEssNames = namesService.readNames(null, null, null, Boolean.FALSE, null, null, null, null);
+        ResponsePageNameElements nameElementsEssNamesDeleted = namesService.readNames(Boolean.TRUE, null, null, Boolean.FALSE, null, null, null, null);
+        ResponsePageNameElements nameElementsEssNamesNotDeleted = namesService.readNames(Boolean.FALSE, null, null, Boolean.FALSE, null, null, null, null);
+
+        long nbrEssNameSystemstructure = 0;
+        long nbrEssNameSystemstructureDevicestructure = 0;
+        long nbrEssNameSystemstructureDevicestructureIndex = 0;
+
+        for (NameElement nameElement : nameElementsEssNames.getList()) {
+            if (Boolean.FALSE.equals(nameElement.isDeleted())
+                    && !StringUtils.isEmpty(nameElement.getSystemstructure())
+                    && StringUtils.isEmpty(nameElement.getDevicestructure())
+                    && StringUtils.isEmpty(nameElement.getIndex())) {
+                nbrEssNameSystemstructure++;
+            } else if (Boolean.FALSE.equals(nameElement.isDeleted())
+                    && !StringUtils.isEmpty(nameElement.getSystemstructure())
+                    && !StringUtils.isEmpty(nameElement.getDevicestructure())
+                    && StringUtils.isEmpty(nameElement.getIndex())) {
+                nbrEssNameSystemstructureDevicestructure++;
+            } else if (Boolean.FALSE.equals(nameElement.isDeleted())
+                    && !StringUtils.isEmpty(nameElement.getSystemstructure())
+                    && !StringUtils.isEmpty(nameElement.getDevicestructure())
+                    && !StringUtils.isEmpty(nameElement.getIndex())) {
+                nbrEssNameSystemstructureDevicestructureIndex++;
+            }
+        }
+
+        ResponsePageStructureElements structureElementsSystemgroup = structuresService.readStructures(Type.SYSTEMGROUP, null, null, null, null, Boolean.FALSE, null, null, null, null, StructureChoice.STRUCTURE);
+        ResponsePageStructureElements structureElementsSystem = structuresService.readStructures(Type.SYSTEM, null, null, null, null, Boolean.FALSE, null, null, null, null, StructureChoice.STRUCTURE);
+        ResponsePageStructureElements structureElementsSubsystem = structuresService.readStructures(Type.SUBSYSTEM, null, null, null, null, Boolean.FALSE, null, null, null, null, StructureChoice.STRUCTURE);
+        ResponsePageStructureElements structureElementsDiscipline = structuresService.readStructures(Type.DISCIPLINE, null, null, null, null, Boolean.FALSE, null, null, null, null, StructureChoice.STRUCTURE);
+        ResponsePageStructureElements structureElementsDevicegroup = structuresService.readStructures(Type.DEVICEGROUP, null, null, null, null, Boolean.FALSE, null, null, null, null, StructureChoice.STRUCTURE);
+        ResponsePageStructureElements structureElementsDevicetype = structuresService.readStructures(Type.DEVICETYPE, null, null, null, null, Boolean.FALSE, null, null, null, null, StructureChoice.STRUCTURE);
+
+        Map<UUID, Long> mapUuidCountSystemGroup = new TreeMap<>();
+        Map<UUID, Long> mapUuidCountSystem = new TreeMap<>();
+        Map<UUID, Long> mapUuidCountSubsystem = new TreeMap<>();
+        Map<UUID, Long> mapUuidCountDiscipline = new TreeMap<>();
+        Map<UUID, Long> mapUuidCountDeviceGroup = new TreeMap<>();
+        Map<UUID, Long> mapUuidCountDeviceType = new TreeMap<>();
+        long nbrActiveSystemGroup = 0;
+        long nbrActiveSystem = 0;
+        long nbrActiveSubsystem = 0;
+        long nbrActiveDiscipline = 0;
+        long nbrActiveDeviceGroup = 0;
+        long nbrActiveDeviceType = 0;
+        long nbrDeletedSystemGroup = 0;
+        long nbrDeletedSystem = 0;
+        long nbrDeletedSubsystem = 0;
+        long nbrDeletedDiscipline = 0;
+        long nbrDeletedDeviceGroup = 0;
+        long nbrDeletedDeviceType = 0;
+        long nbrPendingSystemGroup = 0;
+        long nbrPendingSystem = 0;
+        long nbrPendingSubsystem = 0;
+        long nbrPendingDiscipline = 0;
+        long nbrPendingDeviceGroup = 0;
+        long nbrPendingDeviceType = 0;
+        long nbrCancelledSystemGroup = 0;
+        long nbrCancelledSystem = 0;
+        long nbrCancelledSubsystem = 0;
+        long nbrCancelledDiscipline = 0;
+        long nbrCancelledDeviceGroup = 0;
+        long nbrCancelledDeviceType = 0;
+        long nbrRejectedSystemGroup = 0;
+        long nbrRejectedSystem = 0;
+        long nbrRejectedSubsystem = 0;
+        long nbrRejectedDiscipline = 0;
+        long nbrRejectedDeviceGroup = 0;
+        long nbrRejectedDeviceType = 0;
+        long nbrOtherSystemGroup = 0;
+        long nbrOtherSystem = 0;
+        long nbrOtherSubsystem = 0;
+        long nbrOtherDiscipline = 0;
+        long nbrOtherDeviceGroup = 0;
+        long nbrOtherDeviceType = 0;
+
+        for (StructureElement structureElement : structureElementsSystemgroup.getList()) {
+            addOne(structureElement.getUuid(), mapUuidCountSystemGroup);
+            if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.FALSE.equals(structureElement.isDeleted())) {
+                nbrActiveSystemGroup++;
+            } else if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.TRUE.equals(structureElement.isDeleted())) {
+                nbrDeletedSystemGroup++;
+            } else if (Status.PENDING.equals(structureElement.getStatus())) {
+                nbrPendingSystemGroup++;
+            } else if (Status.CANCELLED.equals(structureElement.getStatus())) {
+                nbrCancelledSystemGroup++;
+            } else if (Status.REJECTED.equals(structureElement.getStatus())) {
+                nbrRejectedSystemGroup++;
+            } else {
+                nbrOtherSystemGroup++;
+            }
+        }
+        for (StructureElement structureElement : structureElementsSystem.getList()) {
+            addOne(structureElement.getUuid(), mapUuidCountSystem);
+            if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.FALSE.equals(structureElement.isDeleted())) {
+                nbrActiveSystem++;
+            } else if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.TRUE.equals(structureElement.isDeleted())) {
+                nbrDeletedSystem++;
+            } else if (Status.PENDING.equals(structureElement.getStatus())) {
+                nbrPendingSystem++;
+            } else if (Status.CANCELLED.equals(structureElement.getStatus())) {
+                nbrCancelledSystem++;
+            } else if (Status.REJECTED.equals(structureElement.getStatus())) {
+                nbrRejectedSystem++;
+            } else {
+                nbrOtherSystem++;
+            }
+        }
+        for (StructureElement structureElement : structureElementsSubsystem.getList()) {
+            addOne(structureElement.getUuid(), mapUuidCountSubsystem);
+            if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.FALSE.equals(structureElement.isDeleted())) {
+                nbrActiveSubsystem++;
+            } else if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.TRUE.equals(structureElement.isDeleted())) {
+                nbrDeletedSubsystem++;
+            } else if (Status.PENDING.equals(structureElement.getStatus())) {
+                nbrPendingSubsystem++;
+            } else if (Status.CANCELLED.equals(structureElement.getStatus())) {
+                nbrCancelledSubsystem++;
+            } else if (Status.REJECTED.equals(structureElement.getStatus())) {
+                nbrRejectedSubsystem++;
+            } else {
+                nbrOtherSubsystem++;
+            }
+        }
+        for (StructureElement structureElement : structureElementsDiscipline.getList()) {
+            addOne(structureElement.getUuid(), mapUuidCountDiscipline);
+            if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.FALSE.equals(structureElement.isDeleted())) {
+                nbrActiveDiscipline++;
+            } else if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.TRUE.equals(structureElement.isDeleted())) {
+                nbrDeletedDiscipline++;
+            } else if (Status.PENDING.equals(structureElement.getStatus())) {
+                nbrPendingDiscipline++;
+            } else if (Status.CANCELLED.equals(structureElement.getStatus())) {
+                nbrCancelledDiscipline++;
+            } else if (Status.REJECTED.equals(structureElement.getStatus())) {
+                nbrRejectedDiscipline++;
+            } else {
+                nbrOtherDiscipline++;
+            }
+        }
+        for (StructureElement structureElement : structureElementsDevicegroup.getList()) {
+            addOne(structureElement.getUuid(), mapUuidCountDeviceGroup);
+            if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.FALSE.equals(structureElement.isDeleted())) {
+                nbrActiveDeviceGroup++;
+            } else if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.TRUE.equals(structureElement.isDeleted())) {
+                nbrDeletedDeviceGroup++;
+            } else if (Status.PENDING.equals(structureElement.getStatus())) {
+                nbrPendingDeviceGroup++;
+            } else if (Status.CANCELLED.equals(structureElement.getStatus())) {
+                nbrCancelledDeviceGroup++;
+            } else if (Status.REJECTED.equals(structureElement.getStatus())) {
+                nbrRejectedDeviceGroup++;
+            } else {
+                nbrOtherDeviceGroup++;
+            }
+        }
+        for (StructureElement structureElement : structureElementsDevicetype.getList()) {
+            addOne(structureElement.getUuid(), mapUuidCountDeviceType);
+            if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.FALSE.equals(structureElement.isDeleted())) {
+                nbrActiveDeviceType++;
+            } else if (Status.APPROVED.equals(structureElement.getStatus())
+                    && Boolean.TRUE.equals(structureElement.isLatest())
+                    && Boolean.TRUE.equals(structureElement.isDeleted())) {
+                nbrDeletedDeviceType++;
+            } else if (Status.PENDING.equals(structureElement.getStatus())) {
+                nbrPendingDeviceType++;
+            } else if (Status.CANCELLED.equals(structureElement.getStatus())) {
+                nbrCancelledDeviceType++;
+            } else if (Status.REJECTED.equals(structureElement.getStatus())) {
+                nbrRejectedDeviceType++;
+            } else {
+                nbrOtherDeviceType++;
+            }
+        }
+
+        int spaceUntilSizeStructure = 16;
+
+        String metricsOverviewEssName     = NBR_ACTIVE + nameElementsEssNamesNotDeleted.getListSize();
+        String metricsOverviewSystemGroup = NBR_ACTIVE + nbrActiveSystemGroup;
+        String metricsOverviewSystem      = NBR_ACTIVE + nbrActiveSystem;
+        String metricsOverviewSubsystem   = NBR_ACTIVE + nbrActiveSubsystem;
+        String metricsOverviewDiscipline  = NBR_ACTIVE + nbrActiveDiscipline;
+        String metricsOverviewDeviceGroup = NBR_ACTIVE + nbrActiveDeviceGroup;
+        String metricsOverviewDeviceType  = NBR_ACTIVE + nbrActiveDeviceType;
+
+        String metricsEssName = NBR_ALL + addSpaceUntilSize(nameElementsEssNames.getListSize(), spaceUntilSizeStructure)
+                + NBR_ACTIVE + addSpaceUntilSize(nameElementsEssNamesNotDeleted.getListSize(), spaceUntilSizeStructure)
+                + NBR_DELETED + addSpaceUntilSize(nameElementsEssNamesDeleted.getListSize(), spaceUntilSizeStructure);
+        String metricsEssNameSystemstructure                     = NBR + nbrEssNameSystemstructure;
+        String metricsEssNameSystemstructureDevicestructure      = NBR + nbrEssNameSystemstructureDevicestructure;
+        String metricsEssNameSystemstructureDevicestructureIndex = NBR + nbrEssNameSystemstructureDevicestructureIndex;
+
+        String metricsSystemstructureSystemGroup =
+                NBR_ALL + addSpaceUntilSize(mapUuidCountSystemGroup.size(), spaceUntilSizeStructure)
+                + NBR_ACTIVE + addSpaceUntilSize(nbrActiveSystemGroup, spaceUntilSizeStructure)
+                + NBR_DELETED + addSpaceUntilSize(nbrDeletedSystemGroup, spaceUntilSizeStructure)
+                + NBR_PENDING + addSpaceUntilSize(nbrPendingSystemGroup, spaceUntilSizeStructure)
+                + NBR_CANCELLED + addSpaceUntilSize(nbrCancelledSystemGroup, spaceUntilSizeStructure)
+                + NBR_REJECTED + addSpaceUntilSize(nbrRejectedSystemGroup, spaceUntilSizeStructure)
+                + NBR_OTHER + addSpaceUntilSize(nbrOtherSystemGroup, spaceUntilSizeStructure);
+        String metricsSystemstructureSystem =
+                NBR_ALL + addSpaceUntilSize(mapUuidCountSystem.size(), spaceUntilSizeStructure)
+                + NBR_ACTIVE + addSpaceUntilSize(nbrActiveSystem, spaceUntilSizeStructure)
+                + NBR_DELETED + addSpaceUntilSize(nbrDeletedSystem, spaceUntilSizeStructure)
+                + NBR_PENDING + addSpaceUntilSize(nbrPendingSystem, spaceUntilSizeStructure)
+                + NBR_CANCELLED + addSpaceUntilSize(nbrCancelledSystem, spaceUntilSizeStructure)
+                + NBR_REJECTED + addSpaceUntilSize(nbrRejectedSystem, spaceUntilSizeStructure)
+                + NBR_OTHER + addSpaceUntilSize(nbrOtherSystem, spaceUntilSizeStructure);
+        String metricsSystemstructureSubsystem =
+                NBR_ALL + addSpaceUntilSize(mapUuidCountSubsystem.size(), spaceUntilSizeStructure)
+                + NBR_ACTIVE + addSpaceUntilSize(nbrActiveSubsystem, spaceUntilSizeStructure)
+                + NBR_DELETED + addSpaceUntilSize(nbrDeletedSubsystem, spaceUntilSizeStructure)
+                + NBR_PENDING + addSpaceUntilSize(nbrPendingSubsystem, spaceUntilSizeStructure)
+                + NBR_CANCELLED + addSpaceUntilSize(nbrCancelledSubsystem, spaceUntilSizeStructure)
+                + NBR_REJECTED + addSpaceUntilSize(nbrRejectedSubsystem, spaceUntilSizeStructure)
+                + NBR_OTHER + addSpaceUntilSize(nbrOtherSubsystem, spaceUntilSizeStructure);
+
+        String metricsDevicestructureDiscipline =
+                NBR_ALL + addSpaceUntilSize(mapUuidCountDiscipline.size(), spaceUntilSizeStructure)
+                + NBR_ACTIVE + addSpaceUntilSize(nbrActiveDiscipline, spaceUntilSizeStructure)
+                + NBR_DELETED + addSpaceUntilSize(nbrDeletedDiscipline, spaceUntilSizeStructure)
+                + NBR_PENDING + addSpaceUntilSize(nbrPendingDiscipline, spaceUntilSizeStructure)
+                + NBR_CANCELLED + addSpaceUntilSize(nbrCancelledDiscipline, spaceUntilSizeStructure)
+                + NBR_REJECTED + addSpaceUntilSize(nbrRejectedDiscipline, spaceUntilSizeStructure)
+                + NBR_OTHER + addSpaceUntilSize(nbrOtherDiscipline, spaceUntilSizeStructure);
+        String metricsDevicestructureDeviceGroup =
+                NBR_ALL + addSpaceUntilSize(mapUuidCountDeviceGroup.size(), spaceUntilSizeStructure)
+                + NBR_ACTIVE + addSpaceUntilSize(nbrActiveDeviceGroup, spaceUntilSizeStructure)
+                + NBR_DELETED + addSpaceUntilSize(nbrDeletedDeviceGroup, spaceUntilSizeStructure)
+                + NBR_PENDING + addSpaceUntilSize(nbrPendingDeviceGroup, spaceUntilSizeStructure)
+                + NBR_CANCELLED + addSpaceUntilSize(nbrCancelledDeviceGroup, spaceUntilSizeStructure)
+                + NBR_REJECTED + addSpaceUntilSize(nbrRejectedDeviceGroup, spaceUntilSizeStructure)
+                + NBR_OTHER + addSpaceUntilSize(nbrOtherDeviceGroup, spaceUntilSizeStructure);
+        String metricsDevicestructureDeviceType =
+                NBR_ALL + addSpaceUntilSize(mapUuidCountDeviceType.size(), spaceUntilSizeStructure)
+                + NBR_ACTIVE + addSpaceUntilSize(nbrActiveDeviceType, spaceUntilSizeStructure)
+                + NBR_DELETED + addSpaceUntilSize(nbrDeletedDeviceType, spaceUntilSizeStructure)
+                + NBR_PENDING + addSpaceUntilSize(nbrPendingDeviceType, spaceUntilSizeStructure)
+                + NBR_CANCELLED + addSpaceUntilSize(nbrCancelledDeviceType, spaceUntilSizeStructure)
+                + NBR_REJECTED + addSpaceUntilSize(nbrRejectedDeviceType, spaceUntilSizeStructure)
+                + NBR_OTHER + addSpaceUntilSize(nbrOtherDeviceType, spaceUntilSizeStructure);
+
+        StringBuilder metricsPIDDiscipline = new StringBuilder();
+        for (String discipline : NamingConventionUtil.getDisciplinesPID()) {
+            metricsPIDDiscipline.append(addSpaceUntilSize(discipline, spaceUntilSizeStructure)).append(SPACE);
+        }
+
+        // report header
+        StringBuilder sb = new StringBuilder();
+        sb.append("About Naming").append(NEWLINE)
+        .append(DIVIDER_128).append(NEWLINE)
+        .append(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date())).append(NEWLINE)
+        .append("Note # means number of, active means approved latest not deleted, obsolete values are not shown unless history is requested.").append(NEWLINE)
+        .append(DIVIDER_128).append(NEWLINE)
+        .append("Metrics for Naming").append(NEWLINE)
+        .append("    1) # entries - Overview").append(NEWLINE)
+        .append("    2) # entries - ESS names").append(NEWLINE)
+        .append("    3) # entries - System structure").append(NEWLINE)
+        .append("    4) # entries - Device structure").append(NEWLINE)
+        .append("    5) Device structure - P&ID Discipline").append(NEWLINE)
+        .append(DIVIDER_128).append(NEWLINE);
+
+        // report overview
+        // report ess names
+        // report system structure
+        // report device structure
+        // report device structure - p&id discipline
+        sb.append("1) # entries - Overview").append(NEWLINE);
+        sb.append(DIVIDER_96).append(NEWLINE);
+        sb.append("ESS Name                " + metricsOverviewEssName).append(NEWLINE);
+        sb.append("System Group            " + metricsOverviewSystemGroup).append(NEWLINE);
+        sb.append("System                  " + metricsOverviewSystem).append(NEWLINE);
+        sb.append("Subsystem               " + metricsOverviewSubsystem).append(NEWLINE);
+        sb.append("Discipline              " + metricsOverviewDiscipline).append(NEWLINE);
+        sb.append("Device Group            " + metricsOverviewDeviceGroup).append(NEWLINE);
+        sb.append("Device Type             " + metricsOverviewDeviceType).append(NEWLINE);
+        sb.append(DIVIDER_96).append(NEWLINE);
+        sb.append("2) # entries - ESS names").append(NEWLINE);
+        sb.append(DIVIDER_96).append(NEWLINE);
+        // # names with system structure only - system group, system, system + subsystem
+        sb.append("ESS Name                " + metricsEssName).append(NEWLINE);
+        sb.append(DIVIDER_64).append(NEWLINE);
+        sb.append("ESS Name (System structure)                                              " + metricsEssNameSystemstructure).append(NEWLINE);
+        sb.append("ESS Name (System structure + Device structure)                           " + metricsEssNameSystemstructureDevicestructure).append(NEWLINE);
+        sb.append("ESS Name (System structure + Device structure + Index)                   " + metricsEssNameSystemstructureDevicestructureIndex).append(NEWLINE);
+        sb.append(DIVIDER_96).append(NEWLINE);
+        sb.append("3) # entries - System structure").append(NEWLINE);
+        sb.append(DIVIDER_96).append(NEWLINE);
+        // lifecycle (# active, # deleted, # pending, ...
+        sb.append("System Group            " + metricsSystemstructureSystemGroup).append(NEWLINE);
+        sb.append("System                  " + metricsSystemstructureSystem).append(NEWLINE);
+        sb.append("Subsystem               " + metricsSystemstructureSubsystem).append(NEWLINE);
+        sb.append(DIVIDER_96).append(NEWLINE);
+        sb.append("4) # entries - Device structure").append(NEWLINE);
+        sb.append(DIVIDER_96).append(NEWLINE);
+        // lifecycle (# active, # deleted, # pending, ...
+        sb.append("Discipline              " + metricsDevicestructureDiscipline).append(NEWLINE);
+        sb.append("Device Group            " + metricsDevicestructureDeviceGroup).append(NEWLINE);
+        sb.append("Device Type             " + metricsDevicestructureDeviceType).append(NEWLINE);
+        sb.append(DIVIDER_96).append(NEWLINE);
+        sb.append("5) Device structure - P&ID Disciplines").append(NEWLINE);
+        sb.append(DIVIDER_96).append(NEWLINE);
+        // discipline p&id, scientific
+        sb.append(metricsPIDDiscipline.toString()).append(NEWLINE);
+        sb.append(DIVIDER_128).append(NEWLINE);
+
+        return sb.toString();
+    }
+
+    /**
+     * Add one to count for key in map.
+     *
+     * @param key key
+     * @param map map
+     */
+    private void addOne(UUID key, Map<UUID, Long> map) {
+        addOne(key, map, null);
+    }
+    /**
+     * Add one to count for key in map.
+     *
+     * @param key key
+     * @param map map
+     * @param mapNok map nok
+     */
+    private void addOne(UUID key, Map<UUID, Long> map, Map<UUID, Long> mapNok) {
+        if (key == null) {
+            LOGGER.log(Level.INFO, "addOne, key == null");
+            return;
+        }
+
+        Long no = map.get(key);
+        if (mapNok != null && no == null) {
+            no = mapNok.get(key);
+            no = no != null ? no + 1 : 1;
+            mapNok.put(key, no);
+        } else {
+            no = no != null ? no + 1 : 1;
+            map.put(key, no);
+        }
+    }
+    /**
+     * Add space to string (for value) until it has size.
+     *
+     * @param str value
+     * @param size size
+     * @return string expanded to size
+     */
+    private String addSpaceUntilSize(String str, int size) {
+        if (str != null && str.length() < size) {
+            StringBuilder sb = new StringBuilder(str);
+            while (sb.length() < size)  {
+                sb.append(SPACE);
+            }
+            return sb.toString();
+        }
+        return str;
+    }
+    /**
+     * Add space to string (for value) until it has size.
+     *
+     * @param val value
+     * @param size size
+     * @return string expanded to size
+     */
+    private String addSpaceUntilSize(long val, int size) {
+        return addSpaceUntilSize(String.valueOf(val), size);
+    }
+
+}
-- 
GitLab